It's funny to me, because the way I came up in Unix, `utmp` was one of the first things you learned about: you were using Unix not because you installed it but because every system you might want to break into (for instance, to get to IRC through an outdial or a gateway) was running it, and they were all multi-user shell machines, and you wanted to make sure you weren't showing up in `who`. I probably knew the `utmp` format before I knew how to use `find`.
You'll find, in archives from hacking sites of the era, a whole variety of "utmp editors".
Thanks to setlogin()/getlogin() some operating systems are frustratingly close to the point where all of the relevant information can be read straight out of the kernel's process table, without need for coöperatively maintaining a data file.
On FreeBSD, for example, one can do everything except filter out the terminals where no-one is logged on yet and print a "FROM" column.
Understanding UNIX/LINUX Programming: A Guide to Theory and Practice by Bruce Molay is a work which has the reader learn system programming by re-implementing UNIX commands.
Yeah wondering if this would be interesting to do as a recreation project. Is copyright 2003 and mostly unvailable -- Amazon dares to "rent" it to me for 45$.... give me a break.
I was messing with it a bit several months ago. You'd have to look up newer data structures, but just looking up the man pages gave me the necessary information. While the implementation may be different now, the fundamentals of UNIX are pretty much the same.
I was confused because when I'm in tmux `who am i` (or `who mom hates` or `who -m`) prints nothing. I thought that might've been the joke -- I'm nobody (or something). When I detach from tmux and run it in the login shell directly it works as expected. The phrasing in the man page ("'am i' or 'mom likes' are usual.") is also pretty strange.
I love your simple demonstration of strace reverse engineering a UNIX command.
Probably a very silly question but I get this compile error?
mywho.c:8:13: error: use of undeclared identifier 'LC_ALL'
setlocale(LC_ALL, "");
I should solve it myself but since you are here?
I was hoping to see why who was better than w or similar, instead I was pleasently surprised to find someone taking a really simple unix command, reverse engineering it, and reimplementing it
I was legitimately confused for a second when I saw `Who` capitalized, but it must have been some automatic HN formatting - it's lower-case is the linked blog post.
There's something delightfully absurd about invoking who and getting told where they're logging in from. There's an Abbot and Costello skit in there waiting to come out.
This is why I love computing. It's putting abject insanity to good use.
I’ve found grep to be unreliable or behave unexpectedly with strace. Even when piping to strings first, although not sure if that is of any consequence. Anyone know why that is or experienced the same?
You know it outputs to stderr, right? Also if you give it the option "-o -" it doesn't do what you might expect. Other than that I grep the output of strace all the time and it works for me.
plan 9 who is a 3 line shell script that just greps the output of ps and sorts it. You could eliminate ps and just grep through /proc but why reinvent the wheel when an existing tool already does part of the job?
I see some comments here citing a lack of options, most of which appear to have nothing to do with who is logged into the machine.
* /proc is as portable as ps, but that doesn't really amount to much because neither is really portable at all. Pretty much every operating system's /proc is different; and no implementation of the ps command fully conforms to even the limited subset laid out in the Single Unix Specification.
this version appears to derive from rewriting unix who in the 4.4BSD era to replace AT&T code (copyright 1989 / no AT&T in header notes as basis for assumption)
Wow, I touched a nerve I don't understand, to get a -1, so I'll explain myself. The GNU code looks like it's portable to a bunch of OSes, some with and some without a given feature. That sort of portability leads to having a lot of #ifdefs. If you're only going to support a handful of OSes - because that's all most people care about - then of course your code is going to be smaller.
As a concrete example, the linked-to "who" uses:
if (entry.ut_type != USER_PROCESS)
continue;
The GNU one uses:
if (IS_USER_PROCESS (utmp_buf))
The IS_USER_PROCESS() macro is in gnulib's readutmp.h (which would need to be included in the overall line cound):
The UT_USER maps to ut_user on some systems, and ut_name on others:
/* Accessor macro for the member named ut_user or ut_name. */
# if HAVE_UTMPX_H
# if HAVE_STRUCT_UTMPX_UT_USER
# define UT_USER(Utmp) ((Utmp)->ut_user)
# endif
# if HAVE_STRUCT_UTMPX_UT_NAME
# undef UT_USER
# define UT_USER(Utmp) ((Utmp)->ut_name)
# endif
Each of those other macros supports cases where the machine supports a given test, and when it doesn't. Including for machines which don't implement ut_type! (That's what the UT_TYPE_NOT_DEFINED case is for, which falls back to using the time field ... which has its own set of variants!)
Portability is neither options nor cleverness, so I added it to the list of considerations. But then again, most people now don't need to support decades of history and scores of Unix variants.
You'll find, in archives from hacking sites of the era, a whole variety of "utmp editors".