Unix/Linux编程实践教程–ac在Ubuntu 14.04的实现
2017-06-07 14:56
399 查看
环境:Ubuntu 14.04 32位
为什么这回不写在OS x上的实现了?因为OS X使用的是utmpx,然后我用
默认没有带
好的,开始我们的节目。
那我们就
然后就可以开始尝试写代码了,初版可能是这样的。
运行一下看看wtmp里都写啥了。
我这里只截取了6月4日和6月5日的记录,再拿
这里的Today 指的是6月5日,可以看到6月4日,superxc总共连接了0.45个小时。怎么算的呢?注意看wtmp的输出。关于superxc用户的登录有三条,分别是:
退出的话,注意刚才文档里写的那句话,也就是我标重点的部分,如果用户名为空的话,说明用户在对应的终端上退出了,所以可以找到下面这两行,就是退出的记录。
这里要注意到tty1没有退出,直接就关机了,所有没有对应的记录。
同一个用户在三个终端登录,时间是怎么算的?由计算得知是累加的。
刚好和ac的结果一致。
wtmp文件中有些记录是我们并不需要的,ut_type为6、 2、 1的不需要,但是ut_type为1且ty_user为shutdown为需要保留(用于作为关机时没有退出的用户的退出时间)。
所以ac的实现大概是这样的:
Step1: 在ut_type为7的记录中找到第一条未处理的信息、记录下是哪个终端,ut_time记为t1。
Step2: 向后找ut_type为8的记录中与Step1中记录的终端相匹配的(如果遇到到shutdown,就不再向下查找,如果找到EOF,就以当前时间为准),对应的ut_time记为t2。
Step3: t2 - t1的结果累加到sum中。
Step4: 重复Step1-3直到所有记录处理完毕。
所以代码可能是这样的:
代码中使用了lseek进行回滚指针,本程序并没有使用缓存,有兴趣的可以自己加上,还有比如
执行效果:
有个大犇的实现做得比我好多了,可以很大程序减少系统调用,时间开销比较小,而且能结合缓冲,可扩展性也挺好。链接
为什么这回不写在OS x上的实现了?因为OS X使用的是utmpx,然后我用
getutxent_wtmp()这个函数也没有办法正确获取wtmp的日志信息,所以先在Ubuntu上实现好了。
默认没有带
ac这个程序,需要自行安装。(
sudo apt-get insall acct)
好的,开始我们的节目。
man ac,可以看到ac是一个统计用户连接时长(以小时为单位)的工具。用户的登录、退出的信息保存在utmp这个文件中。
那我们就
man 5 wtmp,注意到它的描述里有这么一句话。
The wtmp file records all logins and logouts. Its format is exactly like utmp except that a null username indicates a logout on the associated terminal.这个文件的格式是和utmp类似的,而且如果用户名为空的话,说明用户在对应的终端上退出了。
然后就可以开始尝试写代码了,初版可能是这样的。
/* ubuntu 14.04 */ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <utmp.h> #include <string.h> void utmpOut(struct utmp *p); int main(void) { int fd; struct utmp utmpBuf; int siz = sizeof(struct utmp); if((fd = open(WTMP_FILE, O_RDONLY)) == -1){ perror("ac_ubuntu"); exit(1); } while(read(fd, &utmpBuf, siz) == siz) utmpOut(&utmpBuf); close(fd); return 0; } void utmpOut(struct utmp *p) { //if(strcmp(p->ut_user, "superxc")) // return ; printf("%d\t%s\t%s\t%s", p->ut_type, p->ut_user, p->ut_line, asctime(localtime(&p->ut_time))); }
运行一下看看wtmp里都写啥了。
1 shutdown ~ Mon May 29 09:11:18 2017 2 reboot ~ Sun Jun 4 18:46:09 2017 1 runlevel ~ Sun Jun 4 18:46:09 2017 6 LOGIN tty4 Sun Jun 4 18:46:09 2017 6 LOGIN tty5 Sun Jun 4 18:46:09 2017 6 LOGIN tty2 Sun Jun 4 18:46:09 2017 6 LOGIN tty3 Sun Jun 4 18:46:09 2017 6 LOGIN tty6 Sun Jun 4 18:46:09 2017 6 LOGIN tty1 Sun Jun 4 18:46:09 2017 7 superxc tty1 Sun Jun 4 18:46:42 2017 7 superxc pts/0 Sun Jun 4 18:57:52 2017 7 superxc pts/2 Sun Jun 4 18:58:07 2017 1 runlevel ~ Sun Jun 4 19:03:16 2017 8 tty4 Sun Jun 4 19:03:17 2017 8 tty5 Sun Jun 4 19:03:17 2017 8 tty2 Sun Jun 4 19:03:17 2017 8 tty3 Sun Jun 4 19:03:17 2017 8 tty6 Sun Jun 4 19:03:17 2017 8 pts/0 Sun Jun 4 19:03:17 2017 8 pts/2 Sun Jun 4 19:03:17 2017 1 shutdown ~ Sun Jun 4 19:03:17 2017 1 shutdown ~ Sun Jun 4 19:03:17 2017 2 reboot ~ Mon Jun 5 08:21:34 2017 1 runlevel ~ Mon Jun 5 08:21:34 2017 6 LOGIN tty4 Mon Jun 5 08:21:34 2017 6 LOGIN tty5 Mon Jun 5 08:21:34 2017 6 LOGIN tty2 Mon Jun 5 08:21:34 2017 6 LOGIN tty3 Mon Jun 5 08:21:34 2017 6 LOGIN tty6 Mon Jun 5 08:21:34 2017 6 LOGIN tty1 Mon Jun 5 08:21:34 2017 7 superxc pts/0 Mon Jun 5 08:22:25 2017 7 superxc pts/2 Mon Jun 5 08:22:38 2017 7 root tty1 Mon Jun 5 09:05:05 2017 8 tty1 Mon Jun 5 09:07:35 2017 6 LOGIN tty1 Mon Jun 5 09:07:35 2017
我这里只截取了6月4日和6月5日的记录,再拿
ac -pd的记录对比一下。
superxc 0.45 Jun 4 total 0.45 superxc 2.47 root 0.04 Today total 2.51
这里的Today 指的是6月5日,可以看到6月4日,superxc总共连接了0.45个小时。怎么算的呢?注意看wtmp的输出。关于superxc用户的登录有三条,分别是:
7 superxc tty1 Sun Jun 4 18:46:42 2017 7 superxc pts/0 Sun Jun 4 18:57:52 2017 7 superxc pts/2 Sun Jun 4 18:58:07 2017
退出的话,注意刚才文档里写的那句话,也就是我标重点的部分,如果用户名为空的话,说明用户在对应的终端上退出了,所以可以找到下面这两行,就是退出的记录。
8 pts/0 Sun Jun 4 19:03:17 2017 8 pts/2 Sun Jun 4 19:03:17 2017
这里要注意到tty1没有退出,直接就关机了,所有没有对应的记录。
同一个用户在三个终端登录,时间是怎么算的?由计算得知是累加的。
(63 - 46 + (17 - 42)/60 + /* tty1 */ 63 - 57 + (17 - 52)/60 + /* pts/0 */ 63 - 58 + (17 - 07)/60 /* pts/2 */ )/60 = 0.45277
刚好和ac的结果一致。
wtmp文件中有些记录是我们并不需要的,ut_type为6、 2、 1的不需要,但是ut_type为1且ty_user为shutdown为需要保留(用于作为关机时没有退出的用户的退出时间)。
所以ac的实现大概是这样的:
Step1: 在ut_type为7的记录中找到第一条未处理的信息、记录下是哪个终端,ut_time记为t1。
Step2: 向后找ut_type为8的记录中与Step1中记录的终端相匹配的(如果遇到到shutdown,就不再向下查找,如果找到EOF,就以当前时间为准),对应的ut_time记为t2。
Step3: t2 - t1的结果累加到sum中。
Step4: 重复Step1-3直到所有记录处理完毕。
所以代码可能是这样的:
/* ubuntu 14.04 */ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <utmp.h> #include <string.h> #include <time.h> #define UTMPSIZ sizeof(struct utmp) void utmpOut(struct utmp *p); int cntTime(int fd, struct utmp *p); int main(void) { int fd; struct utmp utmpBuf; int sum = 0; /* seconds */ if((fd = open(WTMP_FILE, O_RDONLY)) == -1){ perror("ac_ubuntu"); exit(1); } while(read(fd, &utmpBuf, UTMPSIZ) == UTMPSIZ){ sum += cntTime(fd, &utmpBuf); // utmpOut(&utmpBuf); } close(fd); printf("%d seconds => %f hours\n", sum, sum / (60.0 * 60)); return 0; } void utmpOut(struct utmp *p) { //if(strcmp(p->ut_user, "superxc")) // return ; printf("%d\t%s\t%s\t%s", p->ut_type, p->ut_user, p->ut_line, asctime(localtime(&p->ut_time))); } int cntTime(int fd, struct utmp *p) { char line[256]; struct utmp utmpBuf; int cnt = 0; /* forward how many time */ int flag = 0; int t1, t2; if(p->ut_type != USER_PROCESS) /* USER_PROCESS 7 */ return 0; strcpy(line, p->ut_line); t1 = (int)p->ut_time; /* printf("user %s login on %s at %s", p->ut_user, p->ut_line, asctime(localtime(&p->ut_time))); */ while(read(fd, &utmpBuf, UTMPSIZ) == UTMPSIZ){ ++cnt; /* DEAD_PROCESS 8, RUN_LVL 1 */ /* printf("test record: "); */ /* utmpOut(&utmpBuf); */ if((utmpBuf.ut_type == DEAD_PROCESS && strcmp(line, utmpBuf.ut_line) == 0) || (utmpBuf.ut_type == RUN_LVL && strcmp("shutdown", utmpBuf.ut_user) == 0)){ flag = 1; /* printf("find!\n"); */ break; } } t2 = flag ? utmpBuf.ut_time : (int)time(NULL); /* if(flag){ printf("log out at %s", asctime(localtime(&utmpBuf.ut_time))); }else{ printf("log out at now\n"); } printf(" tabkes %d seconds\n", t2 - t1); */ lseek(fd, -cnt * UTMPSIZ, SEEK_CUR); /* roll back */ return t2 - t1; }
代码中使用了lseek进行回滚指针,本程序并没有使用缓存,有兴趣的可以自己加上,还有比如
-p、
-d之类的选项参数都可以加。
执行效果:
有个大犇的实现做得比我好多了,可以很大程序减少系统调用,时间开销比较小,而且能结合缓冲,可扩展性也挺好。链接
相关文章推荐
- Unix/Linux编程实践教程–last在Ubuntu 14.04的实现
- Unix/Linux编程实践教程–cp在OS X的实现
- Unix/Linux编程实践教程–head在OS X的实现
- Unix/Linux编程实践教程–chmod在Centos7.3的实现
- Unix/Linux编程实践教程–cat在OS X的实现
- Unix/Linux编程实践教程–tail在OS X的实现
- Unix/Linux编程实践教程–od在OS X的实现
- Unix/Linux编程实践教程--who在OS X的实现
- ubuntu14.04环境中github的安装与使用入门教程(最新,2016-06-03)
- Unix/Linux编程实践教程--函数记录:文件、目录
- Windows 7下硬盘安装Ubuntu 14.04图文教程
- iOS推送实现/ 服务器向iOS APP推送消息 - 最新实践填坑版教程
- Unix/Linux编程实践教程 笔记8 进程和程序:编写命令解释器sh
- Ubuntu 14.04+Django 1.7.1+Nginx+uwsgi部署教程
- 在Ubuntu 14.04.5 LTS 安装最新版 docker-17.11.0-CE 权威实践教程
- Win7系统下利用U盘安装Ubuntu_12.04实现双系统教程
- Ubuntu 14.04 64bit上解析wireshark抓包pcap文件格式和源码实现
- Ubuntu 14.04 + ROS Indigo+Turtlebot2(Kuboki)+Kinect+Huyoko建立gmapping地图,实现自主避障导航
- 在Ubuntu 14.04使用cron实现作业自动化
- 转载很经典的--Ubuntu14.04安装Theano详细教程