在完成了对who指令的实现后(参考链接:https://www.cnblogs.com/czw52460183/p/10999434.html),我们已经知道Linux系统的用户登录信息存储在utmp这个文件里,因此要实现终端注销,就是修改这个文件的内容。
终端注销的基本思路:
1.打开utmp文件,要考虑到打开失败的情况。
2.循环读取每一个utmp结构体,要考虑读取异常的情况。对读取到的每一个结构体,判断其终端名是否和传入的名字一致。
3.一旦一致,修改其登录状态和登录时间,登录时间修改时要考虑到获取系统时间失败的情况。
4.重定位光标到刚刚读取的地方,把修改后的信息重新写入文件。重定位和写入都要考虑到失败的情况。注意,3,4两点中出现任意一种失败情况,都要直接跳出循环,不能再继续读取下一个utmp结构体,因为匹配只能匹配一条。
5.循环结束后,关闭打开的文件,也要考虑到关闭失败的情况。
由此看出,写程序的时候一定要注意对各种异常情况要考虑周全,这样写出的程序才能有更好的健壮性。
之前的文章说过了,我们在Mac下的环境中对utmp的访问已经做了封装,直接访问会有乱码,所以书上的代码就不进行测试了,直接加上注释后贴出来:
#include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <utmp.h> //终端注销函数,传入参数为本终端名 //返回值0代表成功,返回值-1代表失败 int logout_tty(char *line) { int fd; //先要打开utmp文件,打开时要考虑到打开失败的情况 if((fd = open(UTMP_FILE,O_RDWR)) == -1) { return -1; } struct utmp rec; int len = sizeof(struct utmp); int retval = -1; //循环读取文件中的结构体,要考虑读取异常的情况 while(read(fd,&rec,len) == len) { //比较终端名是否匹配 if(strncmp(rec.ut_line,line,sizeof(rec.ut_line)) == 0) { //修改登录状态 rec.ut_type = DEAD_PROCESS; //修改登录时间,要判断是否成功 if(time(&rec.ut_time) != -1) { //将光标定位到之前读取的结构体的位置 if(lseek(fd,-len,SEEK_CUR) != -1) { //写入修改后的信息 if(write(fd,&rec,len) == len) { retval = 0; } } } //终端名一旦匹配后在某一个修改环节出错,则直接退出,不会进行后续读取匹配 break; } } //关闭文件,也要判断是否成功 if(close(fd) == -1) { retval = -1; } return retval; }