Hacker News new | ask | show | jobs
by chard_slicks_ 2118 days ago
His "who" implementation is 73 lines (21 if you remove blank lines and comments). Compare that to 836 in GNU Coreutils[0].

[0] https://github.com/coreutils/coreutils/blob/master/src/who.c

5 comments

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.

Source for the mentioned script: https://github.com/0intro/plan9/blob/master/rc/bin/who. Also, other utilities implemented as scripts are fshalt (P9's shutdown), kill, lookman (P9's apropos), uptime and whois.
Does anybody know why there are line breaks in the sed command?
It runs multiple expressions. That is

  sed '/pattern1/d
  pattern2/d'
is the same with

  sed '/pattern1/d; /pattern2/d'
and

  sed -e '/pattern1/d' -e '/pattern2/d'
I believe those are portable across all `sed` implementations, and they at least work with GNU `sed --posix` and plan9port's sed.
Thank you! This is exactly the kind of reply I was hoping for.
Is /proc as portable as `ps`?
There's a long explanation when it comes to ps, but the very short answer is twofold:

* Don't parse the output of ps, unless it is my ps command (or plan 9's one, as here). See https://unix.stackexchange.com/a/593198/5132 and https://unix.stackexchange.com/a/578816/5132 , and their further reading, especially Greg Wooledge's article.

* /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.

I guess that Linux `/proc` is different from Plan 9 `/proc`, and Linux `ps` is different from Plan 9 `ps`.

So maybe yes.

openbsd 'who' is in the 300 range:

http://cvsweb.openbsd.org/src/usr.bin/who/who.c?rev=1.29&con...

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)

Its interesting. FreeBSD's is about the same size, but was again re-written in 2002 to "to add some features required by SUSv3" : https://github.com/freebsd/freebsd/commit/1894db5ac7af64acc7...
V7 `who` was 62 lines; by V10 it was bloated up to 93.
> 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`.

it's not quite an apples to apples comparison as his rewrite doesn't handle any options or cleverness, but it's still a massive jump in LOC.

I know that it isn't a full implementation and my comment wasn't meant as a real comparison.
Nor is it anywhere near as portability. Though that's not a big issue these days.
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):

   # define IS_USER_PROCESS(U)                                     \
      (UT_USER (U)[0]                                              \
       && (UT_TYPE_USER_PROCESS (U)                                \
           || (UT_TYPE_NOT_DEFINED && UT_TIME_MEMBER (U) != 0)))
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
This is because, as https://www.gnu.org/software/libc/manual/html_node/Logging-I... points out, "Note that the ut_user member of struct utmp is called ut_name in BSD. Therefore, ut_name is defined as an alias for ut_user in utmp.h"

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.

If you don't care about all the features then Busybox's implementation will work fine at 170 lines inclusive of comments.