Decoded: who (coreutils)

[Back to Project Main Page]

Note: This page explores the design of command-line utilities. It is not a user guide.
[GNU Manual] [POSIX requirement] [Linux man] [FreeBSD man]

Logical flow of who command (coreutils)

Summary

who - print who is currently logged in

[Source] [Code Walkthrough]

Lines of code: 837
Principal syscall: None
Support syscalls: None
Options: 31 (14 short, 17 long, does not include legacy digits for field skip)

Oldest spiritual ancestor is WHO from CTSS 2ed. (1969)
Added to Shellutils in November 1992 [First version]
Number of revisions: 171

A substantial amount of work behind who determines what data to display and how to the format it. The source of most data is the utmp file under /var or /etc

Helpers:
  • idle_string() - Builds a string representation of the idle item
  • is_tty_writable() - Checks if a user terminal can be written to (for +/- display)
  • list_entries_who() - Performs the quick user listing (-q option)
  • make_id_equals_comment() - Adds the 'id' comment field
  • print_boottime() - Outputs the system boot time
  • print_clockchange() - Prints the last system clock change
  • print_deadprocs() - Prints information about dead processes
  • print_heading() - Prints the header above all output entries
  • print_initspawn() - Prints processes spawned by init/systemd
  • print_line() - Prints a formatted output line. Used by most print functions in who
  • print_login() - Prints processes information for pending logins
  • print_runlevel() - Prints current and previous (if possible) runlevels
  • print_user() - Parses and prints user information
  • scan_entries() - Walks through each utmp entry to print requested information
  • time_string() - Builds a timestamp string
  • who() - The top level procedure for who
External non-standard helpers:
  • die() - Exit with mandatory non-zero error and message to stderr
  • error() - Outputs error message to standard error with possible process termination
  • read_utmp() - Reads the contents of system utmp file in to a buffer

Setup

who.c uses existing macros to define the capabilities for the specific system. Not all of the possible data fields are always available. These include:

  • DEAD_PROCESS - System identifies processes from exited users
  • HAVE_STRUCT_XTMP_UT_PID - The system can search utmp by pid
  • HAVE_STRUCT_XTMP_UT_ID - The system can search utmp by terminal source
  • INIT_PROCESS - System identifies processes from init/systemd
  • LOGIN_PROCESS - System identifies processes where user login is in progress
  • NEW_TIME - The most recent system time
  • RUN_LVL - The system's current runlevel

Global variables are set during parsing to control execution behavior. These include:

  • do_lookup - Flag to attempt hostname resolution via DNS (--lookup)
  • include_exit - Flag to include process exit status (-d)
  • include_heading - Flag to display headers for output records (-H)
  • include_idle - Flag to include idle time display
  • include_mesg - Flag to include user message status (-w, --mesg)
  • my_line_only - Flag to include only current user line (-m)
  • need_boottime - Flag to display the last boot time
  • need_clockchange - Flag to display the previous clock change
  • need_deadprocs - Flag to display dead processes
  • need_initspawn - Flag to display processes started by init/systemd
  • need_login - Flag to display processes waiting for user login
  • need_runlevel - Flag to display the current runlevel
  • need_users - Flag to display user processes
  • short_list - Flag to display only user names and counts
  • short_output - Flag to display only name, line, and times
  • time_format - The login time format
  • time_format_width - The expected output width of login time
  • main() adds two local variables:

    • assumptions - Flag indiciating default output options used.
    • optc - The character for the next option to process

    Parsing

    Parsing answers the following questions to define the execution parameters

    • What information should be displayed? (boot times, dead/init processes, runlevels, time, etc)
    • Should headers be displayed at the top?
    • Should output be limited to only the current user?

    Parsing failures

    These failure cases are explicitly checked:

    • Providing too many operands (more than 2)
    • Unknown option used

    User specified parsing failures result in a short error message followed by the usage instructions. Access related parsing errors die with an error message.


    Execution

    who begins by accessing the utmp file, either the system default, or one provided directly by the user. There is a short print procedure for displaying only user names and a longer case for multiple records. We'll look at the longer case.

    • Get the current user's tty name
    • Check each entry in utmp and print requested data in this order:
      • Print user name
      • Print run level
      • Print boot time
      • Print clock changes
      • Print init/systemd processes
      • Print unassigned processes (users not logged in yet)
      • Print dead processes (user has logged off)

    The only failure case explicity checked is if who is unable to access the utmp file. The result is an error message to STDERR.


    [Back to Project Main Page]