Unix/Linux编程实践教程–ac在Ubuntu 14.04的实现

环境:Ubuntu 14.04 32位

为什么这回不写在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之类的选项参数都可以加。

执行效果:

ac

有个大犇的实现做得比我好多了,可以很大程序减少系统调用,时间开销比较小,而且能结合缓冲,可扩展性也挺好。链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
安装Ubuntu 14.04的步骤如下: 1. 首先,从下载Ubuntu 14.04的ISO镜像文件。 2. 使用VMware Workstation打开并创建一个虚拟机。选择自定义选项,并按照默认设置进行操作系统安装类型选择。 3. 为虚拟机设置名称和保存路径,并根据需要进行内存分配和网络连接设置。 4. 创建新的虚拟磁盘,并选择将虚拟磁盘存储为单个文件。 5. 点击“完成”来完成虚拟机的创建。 6. 在虚拟机设置,选择CD/DVD选项,并使用下载的Ubuntu 14.04的ISO镜像文件进行设置。 7. 开启虚拟机并等待初始化完成。 8. 选择文进行安装,点击“安装Ubuntu”按钮。 9. 按照安装向导选择适当的选项,如语言、主机名、用户及密码配置等。 10. 等待安装完成,这可能需要一段时间。 11. 安装完成后,重启虚拟机并享受使用Ubuntu 14.04的愉快。 请注意,这仅是Ubuntu 14.04的基本安装教程。具体的操作步骤可能因您的系统环境和个人需求而有所不同,请根据实际情况进行操作。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [ubuntu14.04安装jdk1.8的教程](https://download.csdn.net/download/weixin_38694674/14090956)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [VMware下Ubuntu 14.04详细安装教程](https://blog.csdn.net/lbh_ITsy/article/details/90738011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值