who命令的作用用于显示当前有哪些用户登录到系统。

这个命令执行的原理是读取了系统上utmp文件中记录的所有登录信息,直接显示出来的

utmp文件在哪里呢?

man who的时候,在手册下面有这么一段说明:意思就是不指定文件参数,那么读取的就是/var/run/utmp,到底是不是,验证下

If FILE is not specified, use /var/run/utmp.  /var/log/wtmp as FILE  iscommon.   If  ARG1  ARG2  given, -m presumed: 'am i' or 'mom likes' areusual.

当我指定file参数为/var/run/utmp或者省略这个参数的时候,结果都是一样, 当我用一个错误的文件时,没有任何结果,从这里可以推断,who命令确实从/var/run/utmp中读取用户登录的信息

ghostwu@ubuntu:~$ who
ghostwu  tty7         2018-01-08 09:09 (:0)
ghostwu  pts/18       2018-01-08 12:59 (:0)
ghostwu  pts/19       2018-01-08 13:00 (:0)
ghostwu  pts/20       2018-01-08 13:03 (:0)
ghostwu@ubuntu:~$ who -bsystem boot  2018-01-08 09:08
ghostwu@ubuntu:~$ who -b /var/run/utmp system boot  2018-01-08 09:08
ghostwu@ubuntu:~$ who -b /var/run/utmp2
ghostwu@ubuntu:~$ who -b /var/run/utmp3

那么utmp到底在哪里?

利用man -k utmp 查找所有的可能:    推断--->  utmp (5) - login records  这里的可能性比较大,描述说,这里是记录登录信息的

ghostwu@ubuntu:~$ man -k utmp
endutent (3)         - access utmp file entries
endutxent (3)        - access utmp file entries
getutent (3)         - access utmp file entries
getutent_r (3)       - access utmp file entries
getutid (3)          - access utmp file entries
getutid_r (3)        - access utmp file entries
getutline (3)        - access utmp file entries
getutline_r (3)      - access utmp file entries
getutmp (3)          - copy utmp structure to utmpx, and vice versa
getutmpx (3)         - copy utmp structure to utmpx, and vice versa
getutxent (3)        - access utmp file entries
getutxid (3)         - access utmp file entries
getutxline (3)       - access utmp file entries
login (3)            - write utmp and wtmp entries
logout (3)           - write utmp and wtmp entries
pututline (3)        - access utmp file entries
pututxline (3)       - access utmp file entries
sessreg (1)          - manage utmpx/wtmpx entries for non-init clients
setutent (3)         - access utmp file entries
setutxent (3)        - access utmp file entries
systemd-update-utmp (8) - Write audit and utmp updates at bootup, runlevel ch...
systemd-update-utmp-runlevel.service (8) - Write audit and utmp updates at bo...
systemd-update-utmp.service (8) - Write audit and utmp updates at bootup, run...
utmp (5)             - login records
utmpdump (1)         - dump UTMP and WTMP files in raw format
utmpname (3)         - access utmp file entries
utmpx (5)            - login records
utmpxname (3)        - access utmp file entries

接下来,我们去 man 5 utmp 看下,会发现有这么一段提示:

The file is a sequence of utmp structures, declared as follows in <utmp.h> (note that thisis only one of several definitions around; details depend on the version of libc):

意思是utmp文件的信息是一系列utmp结构体数据, 这个结构体定义在utmp.h文件中,  每个linux发行版可能不一样.

接下来,我用强大的find命令查找到了2个目标:

ghostwu@ubuntu:~$ find /usr/include -name "utmp.h"
/usr/include/x86_64-linux-gnu/bits/utmp.h
/usr/include/utmp.h

结构体的定义就在这个文件中( /usr/include/x86_64-linux-gnu/bits/utmp.h  )

这里有两个宏要注意下( ut_time和UTMP_FILE ), 下面的程序会用到

#ifndef _NO_UT_TIME
/* We have a problem here: `ut_time' is also used otherwise.  Define_NO_UT_TIME if the compiler complains.  */
# define ut_time    ut_tv.tv_sec
#endif

ghostwu@ubuntu:~$ grep "UTMP_FILE" /usr/include/utmp.h
#define UTMP_FILE    _PATH_UTMP
#define UTMP_FILENAME    _PATH_UTMP
ghostwu@ubuntu:~$ grep "_PATH_UTMP" /usr/include/utmp.h
#define UTMP_FILE    _PATH_UTMP
#define UTMP_FILENAME    _PATH_UTMP
ghostwu@ubuntu:~$ grep "_PATH_UTMP" /usr/include/x86_64-linux-gnu/bits/utmp.h
ghostwu@ubuntu:~$ grep "_PATH_UTMP" /usr/include/*.h
/usr/include/paths.h:#define    _PATH_UTMP    "/var/run/utmp"
/usr/include/utmp.h:#define UTMP_FILE    _PATH_UTMP
/usr/include/utmp.h:#define UTMP_FILENAME    _PATH_UTMP
/usr/include/utmpx.h:# define UTMPX_FILE    _PATH_UTMPX
/usr/include/utmpx.h:# define UTMPX_FILENAME    _PATH_UTMPX
ghostwu@ubuntu:~$

UTMP_FILE的查找思路: 首先grep两个目录下面的文件utmp.h,在/usr/include/utmp.h找到一个宏定义 _PATH_UTMP,下一步就是确定 _PATH_UTMP到底是什么,利用grep "_PATH_UTMP" /usr/include/*.h

最终在paths.h头文件中,发现了他的真面目

who命令书写思路:

1)从/var/run/utmp读取文件,每次读取一个struct utmp结构体这么大,如果长度每次都有这么大,继续读取

2)格式化4个信息:用户名,主机,地址,时间

3)只打印当前活动的用户(当前登录的用户)

4)格式化时间( 小时,分钟,秒, >10的补0, <10的原样返回 )

源代码

  1 /*================================================================
  2 *   Copyright (C) 2018 . All rights reserved.
  3 *
  4 *   文件名称:mywho.c
  5 *   创 建 者:ghostwu(吴华)
  6 *   创建日期:2018年01月08日
  7 *   描    述:
  8 *
  9 ================================================================*/
 10
 11 #include <stdio.h>
 12 #include <utmp.h>
 13 #include <sys/types.h>
 14 #include <sys/stat.h>
 15 #include <fcntl.h>
 16 #include <stdlib.h>
 17 #include <unistd.h>
 18 #include <time.h>
 19 #include <string.h>
 20
 21 #ifndef UTMP_FILE
 22 #define UTMP_FILE "/var/run/utmp"
 23 #endif
 24
 25 int count = 0;
 26
 27 //格式化时间, <10 就补0, >10 原样返回
 28 char* format_time( char* s, const char *time ) {
 29     if( strlen( time ) < 2 ) {
 30         return strcat( s, time );
 31     }
 32     return strcpy( s, time );
 33 }
 34
 35 void show_info( struct utmp* t_utmp ) {
 36     if ( t_utmp->ut_type != USER_PROCESS ) //不显示 非活跃的用户信息
 37       return;
 38
 39     printf( "%-8.8s", t_utmp->ut_user );
 40     printf( " " );
 41     printf( "%-8.8s", t_utmp->ut_line );
 42     printf( " " );
 43
 44     //printf( " " );
 45     //printf( "%12.12s", ctime( (time_t*)&(t_utmp->ut_time) ) + 4 ); //+4--->去除天(day)和后面的空格
 46
 47     /*测试localtime用法
 48     //当前时间
 49     time_t now;
 50     struct tm* pNow;
 51     time( &now );
 52     pNow = localtime( &now );
 53     printf( "%d-%d-%d %d:%d", pNow->tm_year + 1900, pNow->tm_mon + 1, pNow->tm_mday, pNow->tm_hour, pNow->tm_min );
 54     */
 55
 56     struct tm* ptm;
 57     time_t u_time = t_utmp->ut_time;
 58     ptm = localtime( &u_time );
 59     int ihour = ptm->tm_hour;
 60     int imin = ptm->tm_min;
 61
 62     char hour[3] = "0";
 63     char hour2[3] = "0";
 64     sprintf( hour2, "%d", ihour );
 65     format_time( hour, hour2 );
 66
 67     char min[3] = "0";
 68     char min2[3] = "0";
 69     sprintf( min2, "%d", imin );
 70     format_time( min, min2 );
 71
 72     //printf( "%d-%d-%d %d:%d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ihour, imin );
 73     printf( "%d-%d-%d %s:%s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, hour, min );
 74
 75     printf( " " );
 76     printf( "%-8.8s", t_utmp->ut_host );
 77
 78     printf( "\n" );
 79 }
 80
 81 int main(int argc, char *argv[])
 82 {
 83     struct utmp myutmp;
 84     int fd = -1;
 85     int reclen = sizeof( myutmp );
 86
 87     fd = open( UTMP_FILE, O_RDONLY );
 88
 89     if( -1 == fd ) {
 90         perror( "open utmp" );
 91         exit( -1 );
 92     }
 93
 94     //printf( "fd = %d\n", fd );
 95
 96     while( read( fd, &myutmp, reclen ) == reclen ) {
 97         count++;
 98         show_info( &myutmp );
 99     }
100     printf( "文件读取的次数:%d\n", count );
101     close( fd );
102
103
104     return 0;
105 }

View Code

总结:

一个非常小的功能,囊括以下知识点:

1)文件读取

2)man手册与系统命令使用技巧

3)指针用法

4)字符串函数用法

5)时间函数用法

6)宏与typedef的用法

转载于:https://www.cnblogs.com/ghostwu/p/8243534.html

linux系统编程:自己动手写一个who命令相关推荐

  1. 学习较底层编程:动手写一个C语言编译器

    动手编写一个编译器,学习一下较为底层的编程方式,是一种学习计算机到底是如何工作的非常有效方法. 编译器通常被看作是十分复杂的工程.事实上,编写一个产品级的编译器也确实是一个庞大的任务.但是写一个小巧可 ...

  2. linux系统编程:自己动手写一个cp命令

    cp命令的基本用法: cp 源文件 目标文件 如果目标文件不存在 就创建, 如果存在就覆盖 实现一个cp命令其实就是读写文件的操作: 对于源文件: 把内容全部读取到缓存中,用到的函数read 对于目标 ...

  3. linux系统编程综合练习-实现一个小型的shell程序(四)

    上节中已经对后台作业进行了简单处理,基本上要实现的功能已经完了,下面回过头来,对代码进行一个调整,把写得不好的地方梳理一下,给代码加入适当的注释,这种习惯其实是比较好了,由于在开发的时候时间都比较紧, ...

  4. linux原子过程,linux系统编程:IO读写过程的原子性操作实验

    所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...

  5. 资深程序员带你攻克 Linux 系统编程

    作者简介:宇文拓,近十年 Linux C/C++ 开发经验,现就职于某创业公司,负责服务器架构与系统设计.曾就职于某通信业知名美企,负责核心网和防火墙产品研发.在 GitHub 上发布了开源项目 An ...

  6. linux系统发送信号的系统调用是,linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction...

    信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...

  7. Linux系统编程25:基础IO之亲自实现一个动静态库

    本文接:Linux系统编程24:基础IO之在Linux下深刻理解C语言中的动静态库以及头文件和库的关系 文章目录 A:说明 B:实现静态库 C:实现动态库 A:说明 前面说过,库其实就是头文件和和.a ...

  8. 外网访问arm嵌入式linux_嵌入式Linux系统编程——文件读写访问、属性、描述符、API

    Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...

  9. Linux 系统编程总结 (未完待续)

    前言 我记得去年说过要讲一些干货分享给大家,Linux 系统编程是linux 驱动开发入门的条件 有人说为什么? mmap select read write open ioctl socket po ...

最新文章

  1. 2012-4-2 通过MdiParent设置窗体最前
  2. linux内核网络接收数据流程图【转】
  3. 将整型字符串转成整数值
  4. 忘掉 Java 并发,先听完这个故事。。。
  5. iOS之仿QQ好友列表展开收缩效果的实现
  6. 分析flv文件的信息
  7. form表单ajax提交 ac,請求Ajax 帶返回值的通用方法, 自動獲取頁面控件值(form表單post方法提交 ),自動給控件賦值...
  8. 所有的面试问题都可以归结为这三类(附回答套路)
  9. Socket网络编程--聊天程序(9)
  10. 用Python实现BP神经网络(附代码)
  11. 零基础自学Java要多久,是不是很难?
  12. SAP BAPI 教程 – 在 ABAP 中创建 BAPI 的分步指南-020
  13. java 调用 swf 文件上传,swfupload 文件 上传
  14. 使用google翻译免费翻译文档,这里以pdf为例
  15. 使用dns-over-https 手动测试域名解析
  16. 运动耳机哪个好?六款耳机教会你选运动耳机
  17. 基于net-snmp的代理端及管理端开发手册
  18. NB-IoT应用3年,揭秘它为何被称 “为抄表而生”
  19. ASM磁盘空间假装耗尽,ORA-15041: diskgroup space exhausted
  20. Github每日精选(第24期):python的浏览器MechanicalSoup

热门文章

  1. Hive的两种操作模式
  2. RabbitMQ 交换器、持久化
  3. android support Percent支持库开发
  4. fatal error RC1004: unexpected end of file found处理方法
  5. js获取select选中的内容
  6. 移动客户端与服务器端安全通信方案
  7. rds_dbsync数据源同步工具
  8. 建议 Solr 用户更新 Apache POI
  9. 基于 Android NDK 的学习之旅-----JNI 数据类型
  10. Linux ifconfig命令