While working on a completely different project, I started to ask myself how the who command was working under the hood. In the end, I thought it was a good topic for a blog post.
Who is who
Let’s start with the basics. the who command allows you to list the users currently logged on the system.
For example, on my machine:
It tells me that I am logged on the “physical” terminal tty2 and on three pseudo terminals. Indeed my current session of Gnome Shell is running on tty2 and I have 3 tmux windows open.
But where is it getting those information? Probably from a file as everything is a file with Linux, but let’s check which one and how the data is stored there.
A bit of reverse engineering
In order to see what the who command is doing I could try to find the source code and dig into it. But I found it fun to use strace to check what the process was doing instead. Since we are expecting who to read system files, we can only focus on the open syscalls.
| |
We can quickly filter what is interesting and what is not. The first files two files /etc/ld.so.cache, /usr/lib/libc.so.6 are shared libraries loaded by the process, those are interesting us.
Then /usr/lib/locale/locale-archive, /var/run/utmp and /etc/localtime are opened. Let’s see what those files are storing.
When exploring this kind of topics, it is always interesting to first search into the man pages before starting browsing the web. The 5th section of the manual is dedicated to “file formats and conventions” and seems a good place to start.
locale-archive
| |
Sends us to locale(5) where we can read:
| |
The page also sends us to locale(7) for more explanation about those informations:
| |
So the who command read from this file, probably using the setlocale(3) function, to find out how the information should be formated and displayed.
We can actually check it:
Indeed, the date is is not formated the same way!
localtime
| |
Sends us to localtime(5) which explains:
| |
Probably who uses this file (or uses a function that is using this file) to print timestamps (columns 4 and 5 of who’s output) using the correct timezone configured by the user.
utmp
Finally comes /var/run/utmp:
Where we can read:
| |
Great! We found where the who command is getting its data. It would be nice to be able to read this file to get those data without using the who command. Unfortunately:
| |
At this point we understood what the who command is doing: it is reading /var/run/utmp, parsing the content and formating it nicely. Let’s see if we can reproduce this simple behavior.
My own who
What we simply need to do is: open /var/run/utmp, read n bytes (where n is the size of the utmp structure), print the info contained in each structure, continue until we reach the end of the file. With a bit of formating, we can even make it look like the original who command.
| |
TADA!
We can also check with strace if the behavior is the same:
| |
Indeed our program is doing the same as the original who.
Of course, this only mocks the most basic features of the who command and doesn’t handle any option, like the famous who am i or who mom hates.
Going further
There is still a lot to say about the who command. We could for example mention the lastlog command and its corresponding file /var/log/wtmp, dig into the utmp structure, or just try to understand what utmp stands for.