linux系统编程:自己动手写一个who命令
who命令的作用用于显示当前有哪些用户登录到系统。
这个命令执行的原理是读取了系统上utmp文件中记录的所有登录信息,直接显示出来的
utmp文件在哪里呢?
man who的时候,在手册下面有这么一段说明:意思就是不指定文件参数,那么读取的就是/var/run/utmp,到底是不是,验证下
If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is
common. If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are
usual.
当我指定file参数为/var/run/utmp或者省略这个参数的时候,结果都是一样, 当我用一个错误的文件时,没有任何结果,从这里可以推断,who命令确实从/var/run/utmp中读取用户登录的信息
ghostwu@ubuntu:~$ who
ghostwu tty7 -- : (:)
ghostwu pts/ -- : (:)
ghostwu pts/ -- : (:)
ghostwu pts/ -- : (:)
ghostwu@ubuntu:~$ who -b
system boot -- :
ghostwu@ubuntu:~$ who -b /var/run/utmp
system boot -- :
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 () - access utmp file entries
endutxent () - access utmp file entries
getutent () - access utmp file entries
getutent_r () - access utmp file entries
getutid () - access utmp file entries
getutid_r () - access utmp file entries
getutline () - access utmp file entries
getutline_r () - access utmp file entries
getutmp () - copy utmp structure to utmpx, and vice versa
getutmpx () - copy utmp structure to utmpx, and vice versa
getutxent () - access utmp file entries
getutxid () - access utmp file entries
getutxline () - access utmp file entries
login () - write utmp and wtmp entries
logout () - write utmp and wtmp entries
pututline () - access utmp file entries
pututxline () - access utmp file entries
sessreg () - manage utmpx/wtmpx entries for non-init clients
setutent () - access utmp file entries
setutxent () - access utmp file entries
systemd-update-utmp () - Write audit and utmp updates at bootup, runlevel ch...
systemd-update-utmp-runlevel.service () - Write audit and utmp updates at bo...
systemd-update-utmp.service () - Write audit and utmp updates at bootup, run...
utmp () - login records
utmpdump () - dump UTMP and WTMP files in raw format
utmpname () - access utmp file entries
utmpx () - login records
utmpxname () - access utmp file entries
接下来,我们去 man 5 utmp 看下,会发现有这么一段提示:
The file is a sequence of utmp structures, declared as follows in <utmp.h> (note that this
is 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的原样返回 )
源代码
/*================================================================
* Copyright (C) 2018 . All rights reserved.
*
* 文件名称:mywho.c
* 创 建 者:ghostwu(吴华)
* 创建日期:2018年01月08日
* 描 述:
*
================================================================*/ #include <stdio.h>
#include <utmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h> #ifndef UTMP_FILE
#define UTMP_FILE "/var/run/utmp"
#endif int count = ; //格式化时间, <10 就补0, >10 原样返回
char* format_time( char* s, const char *time ) {
if( strlen( time ) < ) {
return strcat( s, time );
}
return strcpy( s, time );
} void show_info( struct utmp* t_utmp ) {
if ( t_utmp->ut_type != USER_PROCESS ) //不显示 非活跃的用户信息
return; printf( "%-8.8s", t_utmp->ut_user );
printf( " " );
printf( "%-8.8s", t_utmp->ut_line );
printf( " " ); //printf( " " );
//printf( "%12.12s", ctime( (time_t*)&(t_utmp->ut_time) ) + 4 ); //+4--->去除天(day)和后面的空格 /*测试localtime用法
//当前时间
time_t now;
struct tm* pNow;
time( &now );
pNow = localtime( &now );
printf( "%d-%d-%d %d:%d", pNow->tm_year + 1900, pNow->tm_mon + 1, pNow->tm_mday, pNow->tm_hour, pNow->tm_min );
*/ struct tm* ptm;
time_t u_time = t_utmp->ut_time;
ptm = localtime( &u_time );
int ihour = ptm->tm_hour;
int imin = ptm->tm_min; char hour[] = "";
char hour2[] = "";
sprintf( hour2, "%d", ihour );
format_time( hour, hour2 ); char min[] = "";
char min2[] = "";
sprintf( min2, "%d", imin );
format_time( min, min2 ); //printf( "%d-%d-%d %d:%d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ihour, imin );
printf( "%d-%d-%d %s:%s", ptm->tm_year + , ptm->tm_mon + , ptm->tm_mday, hour, min ); printf( " " );
printf( "%-8.8s", t_utmp->ut_host ); printf( "\n" );
} int main(int argc, char *argv[])
{
struct utmp myutmp;
int fd = -;
int reclen = sizeof( myutmp ); fd = open( UTMP_FILE, O_RDONLY ); if( - == fd ) {
perror( "open utmp" );
exit( - );
} //printf( "fd = %d\n", fd ); while( read( fd, &myutmp, reclen ) == reclen ) {
count++;
show_info( &myutmp );
}
printf( "文件读取的次数:%d\n", count );
close( fd ); return ;
}
总结:
一个非常小的功能,囊括以下知识点:
1)文件读取
2)man手册与系统命令使用技巧
3)指针用法
4)字符串函数用法
5)时间函数用法
6)宏与typedef的用法
linux系统编程:自己动手写一个who命令的更多相关文章
- Linux系统编程【2】——编写who命令
学到的知识点 通过实现who命令,学到了: 1.使用man命令寻找相关信息 2.基于文件编程 3.体会到c库函数与系统调用的不同 4.加深对缓冲技术的理解 who命令的作用 who命令的使用 在控制终 ...
- linux系统编程综合练习-实现一个小型的shell程序(一)
之前已经花了不少篇幅学习了linux系统编程的很多知识点:文件与io.进程.信号.管道,而零散的知识点,怎么能够综合的串接起来是学习的一个很重要的目的,当然最好的方式就是用所学的知识点做一个项目了,所 ...
- Linux系统编程【3.2】——ls命令优化版和ls -l实现
前情提要 在笔者的上一篇博客Linux系统编程[3.1]--编写ls命令中,实现了初级版的ls命令,但是与原版ls命令相比,还存在着显示格式和无颜色标记的不同.经过笔者近两天的学习,基本解决了这两个问 ...
- linux系统编程:自己动手写一个cp命令
cp命令的基本用法: cp 源文件 目标文件 如果目标文件不存在 就创建, 如果存在就覆盖 实现一个cp命令其实就是读写文件的操作: 对于源文件: 把内容全部读取到缓存中,用到的函数read 对于目标 ...
- linux系统编程综合练习-实现一个小型的shell程序(四)
上节中已经对后台作业进行了简单处理,基本上要实现的功能已经完了,下面回过头来,对代码进行一个调整,把写得不好的地方梳理一下,给代码加入适当的注释,这种习惯其实是比较好了,由于在开发的时候时间都比较紧, ...
- linux系统编程综合练习-实现一个小型的shell程序(三)
上节中已经实现了对普通命令的解析,包括输入重定向,输出重定向,管道,后台作业,这次就来执行已经解析好的命令,对应的函数为:execute_command(),首先对带有管道的命令进行执行: 比如:&q ...
- linux系统编程综合练习-实现一个小型的shell程序(二)
上节minishell当中,已经初步实现了一个简单命令的解析,这节来继续对更加复杂命令进行解析,包含:输入重定向的解析.管道行的解析.输出重定向的解析以及是否有后台作业的解析,如下: 下面对其进行实现 ...
- Linux系统编程【1】——编写more命令
背景介绍 笔者知识背景 笔者接触Linux快一年了.理论知识方面:学习了操作系统基础知识,了解进程调度.内存分配.文件管理.磁盘I/O这些基本的概念. 实操方面:会使用Linux简单命令,在嵌入式系统 ...
- Linux系统编程(14)——shell常用命令
1. ls命令 ls命令是列出目录内容(ListDirectory Contents)的意思.运行它就是列出文件夹里的内容,可能是文件也可能是文件夹. "ls -l"命令已详情模式 ...
随机推荐
- jenkins+github持续集成中的坑
1.前言 刚开始开发自己的独立博客的时候,每次发布都要手动打包,上传服务器,杀tomcat进程,重启,来回这么重复性工作,很快就有点不耐烦了.如果能自动化的东西,就绝不要手动了,所以自己搭建了个持续集 ...
- 巧用第三方高速开发Android App 热门第三方SDK及框架
巧用第三方高速开发Android App 热门第三方SDK及框架 历经大半年的时间,最终是把这门课程给录制出来了,也就在今天,正式在慕课网上上线了 项目地址:巧用第三方高速开发Android App ...
- SVN 的一些操作
SVN是一个非常好的代码管理工具. 之前一直是使用现有的SVN环境,认为非常方便. 可是近期要自己搭建一个SVN环境来托管自己的代码,并且代码放在独立的server上,所以决定自己搭建一个SVN环境. ...
- JAVA随笔篇一(Timer源代码分析和scheduleAtFixedRate的使用)
写完了基础篇,想了非常久要不要去写进阶篇.去写JSP等等的用法.最后决定先不去写.由于自己并非JAVA方面的大牛.眼下也在边做边学,所以决定先将自己不懂的拿出来学并记下来. Timer是Java自带的 ...
- HDU 1715 大菲波数
/* 中文题意: 中文翻译: 题目大意:求1000以内的菲波数. 解题思路:用大数来计算.用二维数组来存数. 难点具体解释:用二维数组存数,開始的一维存的是1000个Pi,后面那一维是用来存数字的. ...
- AVL树 & 重平衡概念
AVL树是有平衡条件的二叉搜索树.这个平衡条件必须容易保持,而且需要保证树的深度是O(logN). AVL=BBST 作为二叉搜索树的最后一部分,我们来介绍最为经典的一种平衡二叉搜索树:AVL树.回顾 ...
- ASP.NET WebAPI使用Swagger生成测试文档
ASP.NET WebAPI使用Swagger生成测试文档 SwaggerUI是一个简单的Restful API测试和文档工具.简单.漂亮.易用(官方demo).通过读取JSON配置显示API .项目 ...
- lock锁速记
1.Lock关键字主要实现锁互斥,确保一个线程A在请求此操作时不会被其线程B请求中断(假设A先请求并在没有未完成的操作情况下申请了此互斥锁).lock的参数必须是基于引用类型的对象,不要是基本类型像b ...
- C#:MVC引用Log4Net生成错误日志
第一步:引用log4net配置文件 第二步:在自己项目下新建文件夹LogNet,再在里面建立类Log.cs log.cs内容如下: 第三步:在自己项目下新建Log4Net.config Log4Net ...
- Python进阶之迭代器和生成器
可迭代对象 Python中任意的对象,只要它定义了可以返回一个迭代器的__iter__方法,或者定义了可以支持下标索引的__getitem__方法,那么它就是一个可迭代对象.简单来说,可迭代对象就是能 ...