@@ 5,7 5,7 @@
.Os
.
.Sh NAME
-.Nm who
+.Nm who , users
.Nd list login and system state
.Sh SYNOPSIS
.Nm
@@ 26,6 26,9 @@
.Op Fl ublrpdt
.Cm am I
.
+.Nm users
+.Op Ar utmp
+.
.Sh DESCRIPTION
Processes and filters the default
.Xr utmpx 5
@@ 62,6 65,11 @@ output the login name
.Pq Fa ut_user ,
and a space separator.
The second line lists the amount of users in the first.
+.Pp
+.Nm users
+outputs
+.Sx Quick Format ,
+but sorts the login names byte-wise and removes the second line.
.
.Ss Full Format
For each
@@ 303,9 311,13 @@ Conforms to
.D1 Nm Op Fl mTu
and the output is
.D1 in an implementation-defined format, subject only to the requirement of containing the information described above .
-The XSI-marked bits are (largely) equivalent to this implementation's invocation and match
-.At V ,
-.\" TODO: which?
+The XSI-marked bits are (largely) equivalent to this implementation's invocation and are similar to
+.At V.3
+with
+.Dv USER_PROCESS
+.Sy Comment
+matching
+.Bx 4.2 ,
except for
.Fl -ips , -lookup ,
which are extensions, originating from the GNU system, and
@@ 316,9 328,7 @@ The only XSI
.Cm am I
spellings are such and
.Cm am i .
-Some implementations allow any two arguments.
-.\" TODO: all? some?
-Some implementations also allow
+Some implementations allow any two arguments or
.Ar utmp
after.
.Pp
@@ 378,3 388,707 @@ and
.Pf all- Sy 0
(unavailable)
elsewhere.
+.Pp
+.Nm users
+is compatible with
+.Bx 4.3 .
+.
+.Sh HISTORY
+.Ss Nm
+.Ss Research \s[-1z]UNIX\s0
+.\" Copied from Unix Programmer's Manual, unix72 has v2, and v2, v6, 3BSD
+Appears in the first edition of the UNIX Programmer's Manual as
+.Xr who I :
+.Bl -tag -compact -offset Ds -width ".Li DESCRIPTION"
+.It Li NAME
+.Li "who -- who is on the system"
+.It Li SYNOPSIS
+.Li "\z\(ulw\z\(ulh\z\(ulo"
+.It Li DESCRIPTION
+.Li "\z\(ulw\z\(ulh\z\(ulo lists the name, typewriter channel, and login time for each current UNIX user."
+.El
+This corresponds to all data in
+.Pa /tmp/utmp .
+.Pp
+.\" unix72:
+.\" # who
+.\" root tty8 Jan 1 00:00:15
+.\" # who /tmp/utmp
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" Jan 1 00:00:00
+.\" root 8 Jan 1 00:00:12
+.\" # who /tmp/wtmp
+.\" 8 Jan 1 00:02:11
+.\" root 8 Jan 1 00:02:13
+.\" x Jan 1 00:02:11
+.\" root 8 Jan 1 00:02:14
+.At v2 ,
+with the advent of
+.Xr wtmp V ,
+sees
+.Dl "\z\(ulw\z\(ulh\z\(ulo [ who-file ]"
+Without
+.Ar who-file ,
+the output format is
+.Bd -literal -compact -offset Ds
+# who
+root tty8 Jan 1 00:00:15
+.Ed
+and all-zero-name (dead) entries are filtered out; with
+.Ar who-file
+the format becomes
+.Bd -literal -compact -offset Ds
+# who /usr/adm/wtmp
+ x Jan 1 00:02:02
+root 8 Jan 1 00:02:11
+ 8 Jan 1 00:03:32
+.Ed
+Usernames are
+.Sy 8
+bytes, the teletype is just the bottom byte as a character
+.Pq corresponding to the Pa /dev/tty Ns " suffix"
+.Pq so doing the same on Xr utmp V No would write the null byte instead ,
+.Sy x
+indicates system reboot, as written by
+.Nm init ,
+and empty names indicate logouts.
+.Pp
+.At v4
+re-adds
+.Li tty
+to the with-file format.
+.Pp
+.At v5
+uses a
+.Sy ~
+line field indicate shutdown instead.
+.Pp
+.At v6
+sees a misleading
+.Sx SYNOPSIS
+of:
+.D1 "\fBwho\fP [ who-file ] [ \fBam I\fP ]"
+.Ar who-file
+.Pq now Pa /etc/utmp Ns " by" default
+excludes
+.Cm "am I"
+(any two arguments);
+if specified, the first line matching the teletype corresponding to the standard input streeam is written, or "Nobody.",
+but the format is otherwise equivalent.
+.Pp
+.At v7
+sees a modernised
+.Xr utmp 5
+format with
+.Fa ut_time
+and
+.Sy 8 Ns -byte
+.Fa ut_line , ut_name ,
+with present-day semantics
+.Pq and Xr date 1 No adding Sy |\& No and Sy \&{ Xr wtmp 5 No entries for before/after clock change ;
+the
+.Nm
+format gains a space after the name field
+.Pq such that even Sy 8 Ns -byte names are separated
+and the time format is truncated to
+.Qq Li "%b %e %R"
+\(em the seconds removed.
+In
+.Cm "am I"
+mode, if the corresponding entry wasn't found, there's just no output,
+or, if the standard input stream is not a teletype, a line is synthesised
+with the name equal to
+.Xr getpwuid 3
+of the current user
+.Pq or Sy ?\& ,
+the line to
+.Sy "tty??" ,
+and the current time.
+.\" and if the file couldn't be opened it exits 1 but who cares. this is already way too fucking busy
+.Pp
+On the
+Interdata 7/32,
+the non-synthetic
+.Cm "am I"
+output line is prepended with
+.Qq Li "(Interdata) " .
+Under
+.At 32v ,
+on the
+.Tn VAX ,
+with
+.Qq Li "(Vax) " .
+.
+.Ss The BSD
+.Bx 4
+uses the
+.In whoami.h
+.Dv sysname
+macro \(em the system's hostname \(em with the first character upper-cased, similarly wrapped in parenthees and followed by a space, instead.
+.Pp
+.Bx 4.2
+replaces that with
+.Xr gethostname 3
+and a
+.Qq Ar hostname Ns Sy !\&
+format, producing a bang path for the current machine,
+It also sees the introduction of a
+.Sy 16 Ns -byte
+.Fa ut_host
+field
+.Pq filled in by remote Nm rlogin Ns " &c." and Nm ftpd Ns " for" Xr wtmp 5 ,
+written, if non-empty, after the time, a tab, and in parentheses.
+.Pp
+.Bx 4.3 Tahoe
+removes the hostname prefix and falls back to the
+.Xr getpwuid 3
+username (with the current line) if nothing was found in
+.Cm "am I"
+mode.
+.Pp
+.Bx 4.3 Reno
+sees a "shutdown" name field for
+.Sy ~
+entries and "date" for
+.Sy |{\&
+ones
+.Pq and moves the files to their common modern locations at Pa /var/run/utmp Ns " and" Pa /var/log/wtmp ;
+.Nm
+is re-written once more:
+the only substantive change appears to be that the no-argument case filters by non-empty
+.Fa ut_name
+.Em and
+.Fa ut_line
+now
+.Pq for Cm "am I" , Ns " a" nonfunctional attempt was made to only filter out entries with empty usernames .
+.\" https://twitter.com/nabijaczleweli/status/1555341949011361793
+.
+.Ss System V
+.At III
+inherits
+.At 32v
+.Nm ,
+except it hides the
+.Qq Li "(Vax) "
+prefix and falls back to also probing the standard output and error streams in
+.Cm "am I"
+mode
+.Pq which it allows as any argument count \(>= Sy 2 .
+.\" index(+1) -> strrchr() for ttyname() result too but that's equivalent before Unix98 PTYs
+.Pp
+.At V.1
+sees present-day
+.Xr getutxent 3
+&c. API
+.Pq though without the Li x
+as its primary
+.Xr utmp 5
+interface and
+.Dl "Usage: who [-rbtpludAasT] [am i] [utmp_like_file]"
+.Pq with Fl u Ns " described" as \&"useful data"
+.\" 66 1217 ATT-SYSIII/src/cmd/who.c
+.\" 638 15947 ATT-SYSVr1/sysv-pdp11_usr-src/cmd/who.c
+.\" 9.(6)x line and 13.1x the byte-wise size
+at 13x the code size:
+the three optional-marked arguments can be interleft in any order at any amount,
+.Cm "am i"
+must be exactly
+.Cm "am i" ,
+probes all standard I/O streams, doesn't exit after the first found entry, and selects
+.Dv USER_PROCESS
+.Pq this is also the default filter, but Cm "am i" Fl d Ns " is" equivalent to present-day Fl mud Ns " except" it produces short format ;
+.Fl ublrpdt T
+as present-day except both
+.Fl lu
+enable wide format and are overriden with
+.Fl s
+.Pq actually the default ,
+.Fl A
+for
+.Dv ACCOUNTING
+.Pq no additional data beyond Sy Name Line Time ,
+.Fl a
+.Em actually
+enables all entry types
+.Pq incl. Dv EMPTY , No yielding Qq Li "Empty slot.\&"
+and doesn't imply
+.Fl T ,
+the write status is always present, but always a space unless
+.Fl T .
+Empty or all-blank
+.Sy Name Line
+fields are replaced with a single
+.Sy .\&
+in the middle.
+.Pp
+The write status is considered for
+.Dv USER_PROCESS , LOGIN_PROCESS , INIT_PROCESS
+and by
+.Em opening
+.Pa /dev/ Ns Fa ut_line
+write-only
+.Pq and with Dv O_NDELAY :
+if that
+.Pq or closing
+times out
+.Pq after Sy 3 No seconds per ,
+.Sy ?\&
+(described as "hung")
+is written;
+if it fails as root
+.Sy x
+is written for "exclusive use";
+.Sy -
+is written if the open fails otherwise, or it succeeds as root or the invoking user is the same as the entry's user and the
+.Xr fstat 3
+fails, or the line is not other-writable;
+.Sy +
+then means that the open succeeded for a different user's line or
+.Nm mesg Fl y
+is set for one's own line (or when run as root).
+.Pp
+The idle time is considered for
+.Dv USER_PROCESS , LOGIN_PROCESS , DEAD_PROCESS , INIT_PROCESS
+in long mode, based on "time since the last character was sent to the device" (modification time, in contrast to present-day's access time):
+.Sy .\&
+for less than a minute
+.Pq or Xr stat 2 No failure ,
+.Sy old
+for more than a day, and
+.Ar HH Ns Sy :\& Ns Ar MM
+.Pq with space-padded Ar HH
+otherwise.
+.Pp
+.Nm init
+overloads
+.Dv RUN_LVL
+.Fa ut_exit.e_termination
+and
+.Fa ut_exit.e_exit
+as the current and previous run-level, and
+.Fa ut_pid
+as the count of times it had been at the current run-level.
+Those are written verbatim by
+.Nm :
+centrer-aligned for
+.Sy Idle ,
+at the start of the
+.Sy Comment
+field, and left-aligned for
+.Sy PID ;
+this is written regardless of the shortness of the output mode.
+.Pp
+In long mode,
+.Dv USER_PROCESS , LOGIN_PROCESS , DEAD_PROCESS , INIT_PROCESS
+see the
+.Sy PID
+field, right-aligned,
+.Dv INIT_PROCESS , DEAD_PROCESS
+see
+.Sy id= Ns Fa ut_id
+with non-printables replaced with
+.Sy ^@ Ns …
+notation
+.Pq in this case they're Pa /etc/lines Ns " line" numbers
+and
+.Dv DEAD_PROCESS
+.Sy term= , exit= ,
+as present-day but left-aligned to three columns;
+.Dv USER_PROCESS , LOGIN_PROCESS ,
+instead, see
+.Pq and hence the Sy Comment No designation ,
+the comment corresponding to their
+.Xr inittab 5
+entry \(em the
+.Fa ut_id
+field matched to the first field with the end-of-line comment found via
+.Qq Li @
+or
+.Qq Li ":; " ,
+stripped of initial blanks, and written verbatim,
+or the empty string if not found or any error occurred.
+.Pp
+No processing is done to replace or prettify the lines or users for
+.Dv BOOT_TIME, RUN_LVL , OLD_TIME , NEW_TIME :
+.Nm init
+.Pq Li \&"system boot" , \&"run-level %c"
+and
+.Nm date
+.Pq Li \&"old time" , \&"new time"
+log them verbatim.
+.Pp
+.At V.2
+allows
+.Cm "am I"
+and adds
+.Fl H ,
+which writes
+an empty line if
+.Fl abrt
+(to "leave a space between stats and output")
+followed by
+.Sy NAME LINE TIME IDLE PID COMMENTS
+.Pq or ending at Sy TIME No in short mode
+the first time it encounters a
+.Dv USER_PROCESS , LOGIN_PROCESS , INIT_PROCESS , ACCOUNTING
+entry,
+and
+.Fl q
+which aborts processing all flags, selects
+.Dv USER_PROCESS ,
+and collects
+.Fa ut_user
+fields
+after the
+.Dv EMPTY
+special case
+and
+.Cm "am I"
+filtering, but overridng all usual processing
+.Pq such that Fl aq Cm "am I" Ns " collects" all Fa ut_user Ns " fields" attached to the current teletype , but Fl qa Cm am I No only the user ones attached to the current teletype .
+There's space for a reasonable
+.Sy 50
+users (more segfaults), which are then bubble-sorted, broken at
+.Sy 80
+columns, and followed with the familiar
+.Li "# users="
+and the count.
+.Pp
+.At V.3
+sees another rewrite, although largely equivalent;
+the usage string is equivalent, but flags are parsed with
+.Xr getopt 3 ,
+.Ar utmp_like_file
+must be the
+.Em only
+argument following flags
+.Pq and is explicitly validated for readability and being a multiple of Vt "struct utmp" Ns " in" size ; a if it's empty, Nm Ns " exits" instantly, since \&"we are all done" \(em neglecting to write Fl H , Ns " if" specified
+and
+.Cm "am I"
+is only recognised if they're the
+.Em only
+arguments.
+.Pp
+.Fl n Ar #
+limits the number of users per line with
+.Fl q
+(which no longer has special parsing semantics);
+the default of
+.Sy 8
+with
+.Sy 8 Ns -byte
+(now left-columnated)
+usernames yields a default wrap of
+.Sy 72
+columns, output during processing and therefore unsorted and unlimited.
+.Pp
+.Fl lrA
+set long format
+.Pq and override previous Fl s
+and
+.Fl au
+set long format but don't:
+.Fl s
+at the end always works.
+.Fl H
+is always written before processing starts
+.Pq regardless of Fl q .
+.Pp
+.Cm "am I"
+matches both the login username
+.Pq as obtained via Xr cuserid 3
+.Em and
+current teletype, and exits after writing the first matching entry.
+.Pp
+The idle time is always checked and not just not output if the
+.Xr stat 2
+failed; since this predates
+.Xr pty 7 Ns s ,
+much less dynamic ones this only realistically affects changed system configurations when passed
+.Xr wtmp 5 .
+.Pp
+The write status is derived just from the
+.Xr stat 2 ,
+as present-day, except the check is for other-writability.
+.Pp
+.Fa ut_pid
+is also output for
+.Dv OLD_TIME , NEW_TIME ,
+and is aligned for
+.Dv RUN_LVL .
+.Fa ut_id
+isn't properly limited to
+.Sy 4
+bytes (since it's not NUL-terminated), so it may include
+.Fa ut_line .
+.Sy exit=
+isn't aligned, just has two spaces afterward.
+All
+.Pf non- Dv INIT_PROCESS
+.Pq instead of just Dv LOGIN_PROCESS , USER_PROCESS
+entries are checked against
+.Xr inittab 5 ,
+with the comment only allowed to start with a
+.Li # .
+.Pp
+.At V.4
+moves both files from
+.Pa /etc
+to
+.Pa /var/adm
+and removes
+.Fl A
+from the usage string and
+.Xr getopt 3
+.Pq but, curiously, keeping the rest of its option handling ,
+describing it as "non-functional"
+.Pq for no apparent reason, it seems to be equivalent to At V.3 ;
+on i386, if
+.Pa /etc/inittab
+fails, it's retried as
+.Pa /etc/conf/cf.d/init.base
+with a diagnostic,
+.Fa ut_id
+is properly limited.
+.Sy Time
+is, for the first time, formatted explicitly as
+.Qq Li "%b %e %H:%M"
+instead of substringing
+.Xr ctime 3
+to the same effect
+.Pq unless in a non- Ns Sy C No locale .
+.Pp
+For
+.Cm "am I" ,
+if no entries matched ("must be a vt", "Assuming utmp hasn't been updated with vt name"),
+.Xr utmp 5
+is scanned again, this time matching only on the username \(em the 178-line output function is duplicated and identical,
+except it uses the saved name of the teletype attached to the standard I/O streams instead of
+.Fa ut_line .
+.
+.Ss Standards
+.Tn X/Open No Portability Guide Issue\~2 Pq Dq Tn XPG Ns \^2 \" .St -xpg2
+specifies
+.D1 "\fBwho\fP"
+.D1 "\fBwho [ options ] [ file ]\fP"
+.D1 "\fBwho am i\fP"
+.D1 "\fBwho am I\fP"
+all marked
+OF ("Output format incompletely specified"),
+the second additionally marked
+UN ("Possibly unsupportable feature"),
+loosely describing "the general format for output entries" as
+.D1 "name [state] line time activity pid [comment] [exit]"
+"[e]xcept for the default \fI-s\fP option",
+and listing
+.Bd -filled -compact -offset indent
+the user's name, terminal line, login time, elapsed time since
+activity occurred on the line and the process ID of the command interpreter for each
+current system user.
+.Ed
+with
+.Cm "am I"
+identifying the invoking user.
+.Pp
+.Bl -tag -compact -width ".Fl H"
+.It Fl q
+writes "only the names and the number of users", ignoring all other options \(em
+this matches
+.At V.4 ;
+.
+.It Fl s
+"is the default and lists only the \fIname\fP, \fIline\fP and \fItime\fP fields"
+\(em this is similar to
+.At V.4
+to some degree, except that
+.Dv RUN_LVL
+always ignores it, and
+.Fl aulrA
+make it not be the default or disable it;
+.
+.It Fl T
+"is the same as the \fI-u\fP option, except that the \fIstate\fP of the terminal line" is written:
+.Sy +
+is writable by anyone,
+.Sy -
+otherwise;
+.Sy ?\&
+means a "bad line"
+\(em except for the
+.Fl u
+part, matches
+.At V.4 ;
+.
+.It Fl H
+adds "column headings";
+.
+.It Fl a
+"[t]urns on all options" \(em
+obviously wrong.
+.
+.It Fl u
+is as present-day and
+.At V.4 ,
+including codifying the
+.Sy .\& Ns / Ns Ar HH Ns Sy :\& Ns Ar HH Ns / Ns Sy old
+format for time "since activity last occurred on that particular line"
+.
+.It Fl l
+"lists only those lines on which the system is waiting for someone to login" (sic!),
+requiring
+.Sy Name
+to be
+.Sy LOGIN ,
+and same format as for users sans the write status \(em
+this doesn't match
+.At V.4 ;
+.
+.It Fl d
+lists "all processes that have expired and not been respawned by \fIinit\fP",
+mandating that the "exit" column contain the "the termination and exit values of the dead process"
+\(em this matches
+.At V.4 ;
+.
+.It Fl b , r , p , t
+are described as laconic filters
+("time and date of the last reboot",
+"current \fIrun-level\fP of the \fIinit\fP process",
+"any other process which is currently active and has been previously spawned by \fIinit\fP",
+"last change to the system clock") with no format requirements.
+.El
+.Pp
+.St -xpg3
+mandates, marked
+IN ("Internationalised functionality", defined as optional),
+that the "time" column is affected by
+.Dv LC_TIME Ns -category
+locale, like
+.At V.4 .
+.Pp
+.St -p1003.2a-92
+\(em User Portability Extension \(em
+sees an unrelated
+.D1 Nm Op Fl mTu
+.\" CR instead of CB but meh
+Listing "various pieces of information about accessible users" (who they are is implementation-defined);
+by default it's to output "in an unspecified format: the user’s login name, terminal name, and time at which the user logged in",
+with
+.Fl m
+limiting for the "current terminal",
+.Fl u
+additionally writing "each displayed user’s »idle time«", otherwise entirely unspecified format-wise save for coming after
+.Fl T ,
+.Fl T
+writing the "state of each terminal", conversely requiring an explicit blank-delimited
+.Sy Name No write status Sy Line Time
+and write status of
+.Sy +-?\&
+for "allows/denies write access to other users"/"write-access state cannot be determined";
+.Sy Time
+is required to be
+.Pq the At V.4 Ns -identical
+.Qq Li "%b %e %H:%M"
+in the
+.Sy POSIX
+locale.
+.Pp
+The
+.Sx Rationale
+is a lot of hand-wringing over
+.At V
+overloading
+.Nm
+beyond
+determining who you can
+.Nm talk
+to and
+.Bd -filled -compact -offset indent
+The historical
+.Nm Cm "am I"
+command, while being one of the more intuitively obvious
+.Tn UNIX
+system commands, had to succumb to the tide of internationalization.
+It is replaced by the somewhat less charming
+.Fl m
+option.
+.Ed
+.Pp
+The format being unspecified (and, similarly, deviations of this implementation from the standard),
+"[i]n such an obviously user-oriented command, designed only for human consumption" is "not considered to be a deficiency".
+.Pp
+.St -xpg4.2
+"aligns" with
+.St -p1003.2 :
+this realistically means merging
+.St -xpg3 ,
+shaded
+EX ((XSI) "Extension")
+thereinto, yielding the present-day clumsy
+.Sx SYNOPSIS
+of
+.D1 Nm Oo Fl mu Oc Fl s Oo Fl bHlprt Oc Op Ar file
+.D1 Nm Oo Fl mTu Oc Oo Fl abdHlprt Oc Op Ar file
+.D1 Nm Fl q Op Ar file
+.D1 Nm Cm am i
+.D1 Nm Cm am I
+and speccing
+.Cm "am I"
+as equivalent to
+.Fl m .
+.Pp
+.St -p1003.1-2008
+requires that
+.Sy Line
+for
+.Dv BOOT_TIME
+is "system boot"
+.Pq and Sy Name No is explicitly unspecified
+and moves
+.Nm
+to the base spec, since its
+User Portability Utilities
+are exclusively interactive.
+.\" ^ same text as du.1, expand.1
+.
+.Ss Nm users
+Appeared in
+.Bx 3
+as
+.Xr users 1 :
+.D1 users \- compact list of users who are on the system
+listing "the login names of the users currently on the system in a compact, one-line format":
+that format being all non-empty login names
+.Pq up to Sy 128 , No segfaulting on overrun ,
+sorted, on one line, separated by a space.
+Despite being unmentioned in the
+.Sx SYNOPSIS ,
+one argument is allowed to override the default
+.Pa /etc/utmp
+location.
+.Pp
+.Bx 4.3
+skips the terminating newline if no logins were processed.
+This is as present-day.
+.Pp
+.Bx 4.3 Tahoe
+sees a rewrite, always using
+.Pa /etc/utmp
+and stopping processing (with a diagnostic) when it encounters too many
+.Pq now Sy 200
+non-empty login names.
+.Pp
+.Bx 4.3 Reno
+sees another, using the
+.Dv _PATH_UTMP
+.Pq Pa /var/run/utmp ,
+deduplicating login names, and (since they're not NUL-terminated) potentially merging subsequent ones into the first.
+.\" also the sorting, but it's unaffected result-wise so
+.Pp
+.Bx 4.4
+fixes this.