Unix`who`命令

2020-09-04 06:08:42

在从事一个完全不同的项目时,我开始问自己,世卫组织指挥部是如何在幕后工作的。最后,我觉得这是一个写博客的好话题。

让我们从基础开始吧。Who命令允许您列出当前登录到系统的用户。例如,在我的计算机上:

$whogauthier tty2 2020-08-30 15:06(Tty2)Gauthier pts/1 2020-08-30 15:06(tmux(1555.%0)Gauthier pts/2 2020-08-30 16:41(tmux(1555).%6)Gauthier pts/4 2020-08-30 15:57(tmux(1555).%3)。

它告诉我我登录了“物理”终端tty2和三个伪终端。事实上,我当前的Gnome Shell会话是在tty2上运行的,并且我打开了3个tmux窗口。

但它是从哪里获得这些信息的呢?可能来自一个文件,因为Linux中的所有东西都是一个文件,但是让我们检查一下是哪一个文件以及数据是如何存储在那里的。

为了了解who命令在做什么,我可以尝试找到源代码并深入研究它。但是我发现使用strace检查进程正在做什么是很有趣的。因为我们期望由谁来读取系统文件,所以我们只能关注打开的syscall。

$strace Who 2&>&;1|grep openopenat(AT_FDCWD,";/etc/ld.so.cache";,O_RDONLY|O_CLOEXEC)=3openat(AT_FDCWD,";/usr/lib/libc.so.6";,O_RDONLY|O_CLOEXEC)=3openat(AT_FDCWD。,O_RDONLY|O_CLOEXEC)=3openat(AT_FDCWD,";/etc/localtime";,O_RDONLY|O_CLOEXEC)=3。

我们可以快速筛选出什么是有趣的,什么是不有趣的。前两个文件/etc/ld.so.cache、/usr/lib/libc.so.6是进程加载的共享库,我们对此很感兴趣。

然后打开/usr/lib/locale/locale-archive、/var/run/utmp和/etc/localtime。让我们看看这些文件存储的是什么。

在浏览这类主题时,在开始浏览Web之前先搜索手册页总是很有趣的。手册的第5部分致力于“文件格式和约定”,似乎是一个很好的起点。

区域设置定义文件包含localedef(1)命令将其转换为二进制区域设置数据库所需的所有信息。

地区是一套语言和文化规则。这些内容涵盖消息语言、不同字符集、词汇图形约定等方面。程序需要能够确定其区域设置并相应地执行操作,以便能够移植到不同的文化。

因此,who命令可能使用setlocale(3)函数从该文件中读取信息,以了解信息应该如何格式化和显示。

$LC_ALL=';fr_FR.utf8';作者tty2 9月2 11:38(:1)Gauthier pts/1 9月2 12:11(tmux(2445).%0)Gauthier pts/2 9月2 12:37(tmux(2445).%1)Gauthier pts/3 9月2 13:04(tmux(2445).%2)。

Etc/localtime文件配置应用程序用于呈现给用户的本地系统的系统范围时区。

可能是使用此文件(或使用使用此文件的函数)的用户使用用户配置的正确时区打印时间戳(WHO输出的第4列和第5列)。

Utmp文件允许用户发现有关当前谁在使用系统的信息。当前可能有更多用户使用该系统,因为并非所有程序都使用utmp日志记录。

太棒了!我们找到了WHO命令获取数据的位置。如果能够读取该文件来获取这些数据,而不使用Who命令,那就太好了。不幸的是:

该文件是一系列utmp结构,在<;utmp.h>;中声明如下(请注意,这只是几个定义之一;详细信息取决于libc的版本):

此时,我们了解了who命令在做什么:它正在读取/var/run/utmp,解析内容并很好地形成它。让我们看看我们是否能重现这个简单的行为。

我们只需要做的就是:打开/var/run/utmp,读取n个字节(其中n是utmp结构的大小),打印每个结构中包含的信息,继续直到我们到达文件的末尾。稍加整理,我们甚至可以让它看起来像最初的“谁指挥”(Who Command)。

#include<;utmp.h>;#include<;stdlib.h>;#include<;stdio.h>;#include<;time.h>;int main(){//设置正确的区域设置以正确显示信息setlocale(LC_ALL,";";);//打开文件*file=fopen(";/var/run/utmp&#。//仅为安全起见,if(file==null){return 1;}//初始化utmp结构struct utmp条目;//从文件中逐个读取条目,同时(Fread(&;entry,sizeof(Struct Utmp),1,file)!=0){if(entry.ut_type!=user_process)Continue;//格式化日期(还记得谁使用/etc/localtime吗?)。Char date[80];time_t raw_time=entry.ut_tw.tv_sec;struct tm*ts=localtime(&;raw_time);strftime(date,sizeof(Date),";%Y-%m-%d%H:%M";,ts);//打印此条目的输出,尝试模拟';用户的输出printf(";%-8s%-12s%s。,entry.ut_user,entry.ut_line,date,entry.ut_host);}fclose(File);}。

$clang-o wh.c$./whogier tty2 2020-08-31 10:02(Tty2)Gauthier pts/1 2020-08-31 10:03(tmux(2220).%0)Gauthier pts/2 2020-08-31 10:08(tmux(2220).%1)Gauthier pts/3 2020-08-31 10:33(tmux(2220).%5)。

$strace./WHO 2>;&;1|grep-e openat(AT_FDCWD,";/etc/ld.so.cache";,O_RDONLY|O_CLOEXEC)=3openat(AT_FDCWD,";/usr/lib/libc.so.6";,O_RDONLY|O_CLOEXEC)=3openat(AT_FDCWD,";/usr/lib/libc.so.6";,O_RDONLY|O_CLOEXEC)=3openat(AT_FDCWD。/var/run/utmp";,O_RDONLY)=3openat(AT_FDCWD,";/etc/localtime";,O_RDONLY|O_CLOEXEC)=4

当然,这只是对“谁”命令最基本的功能的嘲弄,并不处理任何选项,比如著名的“我是谁”或“妈妈讨厌谁”。

关于世界卫生组织的命令,还有很多要说的。例如,我们可以提到lastlog命令及其对应的文件/var/log/wtmp,深入研究utmp结构,或者只是尝试理解utmp代表什么。