linux 系统编程 -- 尝试编写who命令(五)

who命令可以显示出登陆的用户和时间等信息。
在这里插入图片描述
它的工作原理是读取/var/run/utmp文件
在这里插入图片描述
再利用man -k utmp来搜索,知道跟它密切相关的应该是man 5 utmp
在这里插入图片描述
utmp文件记录了当前一部分正在使用系统的用户信息,但是它不是所有的用户,有些程序不在utmp文件中登记。
该文件在头文件utmp.h中定义了一个结构体来存储信息

           #define UT_LINESIZE      32
           #define UT_NAMESIZE      32
           #define UT_HOSTSIZE     256
 struct utmp {
               short   ut_type;              /* Type of record */
               pid_t   ut_pid;               /* PID of login process */
               char    ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
               char    ut_id[4];             /* Terminal name suffix,
                                                or inittab(5) ID */
               char    ut_user[UT_NAMESIZE]; /* Username */
               char    ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
                                                kernel version for run-level
                                                messages */
               struct  exit_status ut_exit;  /* Exit status of a process
                                                marked as DEAD_PROCESS; not
                                                used by Linux init (1 */
               /* The ut_session and ut_tv fields must be the same size when
                  compiled 32- and 64-bit.  This allows data files and shared
                  memory to be shared between 32- and 64-bit applications. */
           #if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
               int32_t ut_session;           /* Session ID (getsid(2)),
                                                used for windowing */
               struct {
                   int32_t tv_sec;           /* Seconds */
                     int32_t tv_usec;          /* Microseconds */
               } ut_tv;                      /* Time entry was made */
           #else
                long   ut_session;           /* Session ID */
                struct timeval ut_tv;        /* Time entry was made */
           #endif

               int32_t ut_addr_v6[4];        /* Internet address of remote
                                                host; IPv4 address uses
                                                just ut_addr_v6[0] */
               char __unused[20];            /* Reserved for future use */
           };

分析该结构体,可见:
ut_user存储的用户名,ut_line存储的终端名。
ut_tv 是存储的时间,包括两个成员tv_sec和tv_usec,它们是int32_t类型,这个类型是为了可移植,也即int型。

所以可以编写出如下代码:

#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

void showinfo(struct  utmp*);

int main()
{
    struct utmp record;
    int fd;
    int record_len=sizeof(record);

    fd=open(UTMP_FILE,O_RDONLY);
    
    if(fd==-1)
    {
        perror(UTMP_FILE);
        exit(1);

    }

    while(read(fd,&record,record_len)==record_len)
        showinfo(&record); 
    
    close(fd);
    return 0;

}

void showinfo(struct  utmp*putmp)
{
   printf("%-8.8s",putmp->ut_user);
   printf(" ");
   printf("%-8.8s",putmp->ut_line);
   printf(" ");
   printf("%10d",putmp->ut_tv.tv_sec);
   printf(" ");
   printf("\n");

};

其中的UTMP_FILE是在 <utmp.h>头文件中定义。
注意read只有在读入的数据结构与缓冲区的数据结构相同时,返回值才相等。
执行结果为:
在这里插入图片描述
如果取消掉while,只读一次的话,执行结果是
在这里插入图片描述
根据ut_type的值来筛选出只需要用户的那部分。

void showinfo(struct  utmp*putmp)
{
  if (putmp->ut_type==USER_PROCESS)
  { printf("%-8.8s",putmp->ut_user);
   printf(" ");
   printf("%-8.8s",putmp->ut_line);
   printf(" ");
   printf("%10d",putmp->ut_tv.tv_sec);
   printf(" ");
   printf("\n");
  }
};

下一步是如何改进时间的显示:
在这里插入图片描述
存储时间的结构time_t实际上就是 long int,ctime(t)和 asctime(localtime(t)) 等价。
char *ctime(const time_t *timep);

void showinfo(struct  utmp*putmp)
{
    time_t ti;
  if (putmp->ut_type==USER_PROCESS)
  { printf("%-8.8s",putmp->ut_user);
   printf(" ");
   printf("%-8.8s",putmp->ut_line);
   printf(" ");
   ti=putmp->ut_tv.tv_sec;
   printf("%10s",ctime(&ti));
   printf(" ");
   printf("\n");
  }
};

但编译时出现警告:format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
ctime()的返回值是cha * 型啊,为什么会出现问题呢?
查资料知在64位的Linux 下,int是32位的,而指针(在这里是 char *)是64位的 ,加上头文件<time.h>即可解决。
话说一开始没有加<time.h>,编译时也没有报错,它也能认出ctime函数,有点怪。
在这里插入图片描述
只显示部分字符串的方法:

//从第4个字符开始显示
printf("%s",ctime(&ti)+4);

还是跟who命令有点不一样。
在这里插入图片描述
要完全一样,必须去操作tm结构

  struct tm {
               int tm_sec;    /* Seconds (0-60) */
               int tm_min;    /* Minutes (0-59) */
               int tm_hour;   /* Hours (0-23) */
               int tm_mday;   /* Day of the month (1-31) */
               int tm_mon;    /* Month (0-11) */
               int tm_year;   /* Year - 1900 */
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
               int tm_isdst;  /* Daylight saving time */
           };

注意tm_year的值是当前年-1900年的差值。而月份tm_mon是0-11月。
整个代码修改为:

#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

void showinfo(struct  utmp *);
char * myctime(const time_t *);

int main()
{
    struct utmp record;
    int fd;
    int rd;
    int record_len=sizeof(record);

    fd=open(UTMP_FILE,O_RDONLY);
    
    if(fd==-1)
    {
        perror(UTMP_FILE);
        exit(1);

    }

   while((read(fd,&record,record_len))==record_len)
            showinfo(&record); 
    
    close(fd);
    return 0;

}

void showinfo(struct  utmp*putmp)
{
  time_t ti;
  if (putmp->ut_type==USER_PROCESS)
  { printf("%-8.8s",putmp->ut_user);
   printf(" ");
   printf("%-8.8s",putmp->ut_line);
   printf(" ");
   ti=putmp->ut_tv.tv_sec;

  
   printf("%s",myctime(&ti));
   printf(" ");
   printf("\n");
  }
};

char * myctime(const time_t *timep)
{
struct tm *ptm;
static char buf[20];
ptm=localtime(timep);

sprintf(buf,"%d-%02d-%02d   %02d:%d",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min);

return buf;
};

执行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值