diff options
Diffstat (limited to 'src/who.c')
-rw-r--r-- | src/who.c | 515 |
1 files changed, 260 insertions, 255 deletions
@@ -1,10 +1,10 @@ /* GNU's who. - Copyright (C) 1992-2006 Free Software Foundation, Inc. + Copyright (C) 1992-2016 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,8 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by jla; revised by djm; revised again by mstone */ @@ -31,22 +30,24 @@ #include <sys/types.h> #include "system.h" +#include "c-ctype.h" #include "canon-host.h" #include "readutmp.h" #include "error.h" #include "hard-locale.h" -#include "inttostr.h" #include "quote.h" -#include "vasprintf.h" -/* The official name of this program (e.g., no `g' prefix). */ +#ifdef TTY_GROUP_NAME +# include <grp.h> +#endif + +/* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "who" -#define AUTHORS "Joseph Arceneaux", "David MacKenzie", "Michael Stone" - -#ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 64 -#endif +#define AUTHORS \ + proper_name ("Joseph Arceneaux"), \ + proper_name ("David MacKenzie"), \ + proper_name ("Michael Stone") #ifdef RUN_LVL # define UT_TYPE_RUN_LVL(U) UT_TYPE_EQ (U, RUN_LVL) @@ -95,17 +96,14 @@ # define UT_ID(U) "??" #endif -char *ttyname (); - -/* The name this program was run with. */ -char *program_name; +char *ttyname (int); /* If true, attempt to canonicalize hostnames via a DNS lookup. */ static bool do_lookup; /* If true, display only a list of usernames and count of the users logged on. - Ignored for `who am i'. */ + Ignored for 'who am i'. */ static bool short_list; /* If true, display only name, line, and time fields. */ @@ -119,8 +117,8 @@ static bool include_idle; /* If true, display a line at the top describing each field. */ static bool include_heading; -/* If true, display a `+' for each user if mesg y, a `-' if mesg n, - or a `?' if their tty cannot be statted. */ +/* If true, display a '+' for each user if mesg y, a '-' if mesg n, + or a '?' if their tty cannot be statted. */ static bool include_mesg; /* If true, display process termination & exit status. */ @@ -161,13 +159,13 @@ enum LOOKUP_OPTION = CHAR_MAX + 1 }; -static struct option const longopts[] = { +static struct option const longopts[] = +{ {"all", no_argument, NULL, 'a'}, {"boot", no_argument, NULL, 'b'}, {"count", no_argument, NULL, 'q'}, {"dead", no_argument, NULL, 'd'}, {"heading", no_argument, NULL, 'H'}, - {"idle", no_argument, NULL, 'i'}, /* FIXME: deprecated: remove in late 2006 */ {"login", no_argument, NULL, 'l'}, {"lookup", no_argument, NULL, LOOKUP_OPTION}, {"message", no_argument, NULL, 'T'}, @@ -198,15 +196,15 @@ idle_string (time_t when, time_t boottime) { int seconds_idle = now - when; if (seconds_idle < 60) - return " . "; + return " . "; else - { - static char idle_hhmm[IDLESTR_LEN]; - sprintf (idle_hhmm, "%02d:%02d", - seconds_idle / (60 * 60), - (seconds_idle % (60 * 60)) / 60); - return idle_hhmm; - } + { + static char idle_hhmm[IDLESTR_LEN]; + sprintf (idle_hhmm, "%02d:%02d", + seconds_idle / (60 * 60), + (seconds_idle % (60 * 60)) / 60); + return idle_hhmm; + } } return _(" old "); @@ -220,10 +218,10 @@ time_string (const STRUCT_UTMP *utmp_ent) /* Don't take the address of UT_TIME_MEMBER directly. Ulrich Drepper wrote: - ``... GNU libc (and perhaps other libcs as well) have extended + "... GNU libc (and perhaps other libcs as well) have extended utmp file formats which do not use a simple time_t ut_time field. In glibc, ut_time is a macro which selects for backward compatibility - the tv_sec member of a struct timeval value.'' */ + the tv_sec member of a struct timeval value." */ time_t t = UT_TIME_MEMBER (utmp_ent); struct tm *tmp = localtime (&t); @@ -233,7 +231,7 @@ time_string (const STRUCT_UTMP *utmp_ent) return buf; } else - return TYPE_SIGNED (time_t) ? imaxtostr (t, buf) : umaxtostr (t, buf); + return timetostr (t, buf); } /* Print formatted output line. Uses mostly arbitrary field sizes, probably @@ -241,9 +239,9 @@ time_string (const STRUCT_UTMP *utmp_ent) pids, etc. */ static void print_line (int userlen, const char *user, const char state, - int linelen, const char *line, - const char *time_str, const char *idle, const char *pid, - const char *comment, const char *exitstr) + int linelen, const char *line, + const char *time_str, const char *idle, const char *pid, + const char *comment, const char *exitstr) { static char mesg[3] = { ' ', 'x', '\0' }; char *buf; @@ -271,29 +269,29 @@ print_line (int userlen, const char *user, const char state, *x_exitstr = '\0'; err = asprintf (&buf, - "%-8.*s" - "%s" - " %-12.*s" - " %-*s" - "%s" - "%s" - " %-8s" - "%s" - , - userlen, user ? user : " .", - include_mesg ? mesg : "", - linelen, line, - time_format_width, - time_str, - x_idle, - x_pid, - /* FIXME: it's not really clear whether the following - field should be in the short_output. A strict reading - of SUSv2 would suggest not, but I haven't seen any - implementations that actually work that way... */ - comment, - x_exitstr - ); + "%-8.*s" + "%s" + " %-12.*s" + " %-*s" + "%s" + "%s" + " %-8s" + "%s" + , + userlen, user ? user : " .", + include_mesg ? mesg : "", + linelen, line, + time_format_width, + time_str, + x_idle, + x_pid, + /* FIXME: it's not really clear whether the following + field should be in the short_output. A strict reading + of SUSv2 would suggest not, but I haven't seen any + implementations that actually work that way... */ + comment, + x_exitstr + ); if (err == -1) xalloc_die (); @@ -310,6 +308,22 @@ print_line (int userlen, const char *user, const char state, free (x_exitstr); } +/* Return true if a terminal device given as PSTAT allows other users + to send messages to; false otherwise */ +static bool +is_tty_writable (struct stat const *pstat) +{ +#ifdef TTY_GROUP_NAME + /* Ensure the group of the TTY device matches TTY_GROUP_NAME, more info at + https://bugzilla.redhat.com/454261 */ + struct group *ttygr = getgrnam (TTY_GROUP_NAME); + if (!ttygr || (pstat->st_gid != ttygr->gr_gid)) + return false; +#endif + + return pstat->st_mode & S_IWGRP; +} + /* Send properly parsed USER_PROCESS info to print_line. The most recent boot time is BOOTTIME. */ static void @@ -328,27 +342,19 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime) #define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1) char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1]; + char *p = line; PIDSTR_DECL_AND_INIT (pidstr, utmp_ent); - /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not + /* Copy ut_line into LINE, prepending '/dev/' if ut_line is not already an absolute file name. Some systems may put the full, absolute file name in ut_line. */ - if (utmp_ent->ut_line[0] == '/') - { - strncpy (line, utmp_ent->ut_line, sizeof (utmp_ent->ut_line)); - line[sizeof (utmp_ent->ut_line)] = '\0'; - } - else - { - strcpy (line, DEV_DIR_WITH_TRAILING_SLASH); - strncpy (line + DEV_DIR_LEN, utmp_ent->ut_line, - sizeof (utmp_ent->ut_line)); - line[DEV_DIR_LEN + sizeof (utmp_ent->ut_line)] = '\0'; - } + if ( ! IS_ABSOLUTE_FILE_NAME (utmp_ent->ut_line)) + p = stpcpy (p, DEV_DIR_WITH_TRAILING_SLASH); + stzncpy (p, utmp_ent->ut_line, sizeof (utmp_ent->ut_line)); if (stat (line, &stats) == 0) { - mesg = (stats.st_mode & S_IWGRP) ? '+' : '-'; + mesg = is_tty_writable (&stats) ? '+' : '-'; last_change = stats.st_atime; } else @@ -370,67 +376,69 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime) char *display = NULL; /* Copy the host name into UT_HOST, and ensure it's nul terminated. */ - strncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host)); - ut_host[sizeof (utmp_ent->ut_host)] = '\0'; + stzncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host)); /* Look for an X display. */ display = strchr (ut_host, ':'); if (display) - *display++ = '\0'; + *display++ = '\0'; if (*ut_host && do_lookup) - { - /* See if we can canonicalize it. */ - host = canon_host (ut_host); - } + { + /* See if we can canonicalize it. */ + host = canon_host (ut_host); + } if (! host) - host = ut_host; + host = ut_host; if (display) - { - if (hostlen < strlen (host) + strlen (display) + 4) - { - hostlen = strlen (host) + strlen (display) + 4; - hoststr = xrealloc (hoststr, hostlen); - } - sprintf (hoststr, "(%s:%s)", host, display); - } + { + if (hostlen < strlen (host) + strlen (display) + 4) + { + hostlen = strlen (host) + strlen (display) + 4; + free (hoststr); + hoststr = xmalloc (hostlen); + } + sprintf (hoststr, "(%s:%s)", host, display); + } else - { - if (hostlen < strlen (host) + 3) - { - hostlen = strlen (host) + 3; - hoststr = xrealloc (hoststr, hostlen); - } - sprintf (hoststr, "(%s)", host); - } + { + if (hostlen < strlen (host) + 3) + { + hostlen = strlen (host) + 3; + free (hoststr); + hoststr = xmalloc (hostlen); + } + sprintf (hoststr, "(%s)", host); + } if (host != ut_host) - free (host); + free (host); } else { if (hostlen < 1) - { - hostlen = 1; - hoststr = xrealloc (hoststr, hostlen); - } + { + hostlen = 1; + free (hoststr); + hoststr = xmalloc (hostlen); + } *hoststr = '\0'; } #endif print_line (sizeof UT_USER (utmp_ent), UT_USER (utmp_ent), mesg, - sizeof utmp_ent->ut_line, utmp_ent->ut_line, - time_string (utmp_ent), idlestr, pidstr, - hoststr ? hoststr : "", ""); + sizeof utmp_ent->ut_line, utmp_ent->ut_line, + time_string (utmp_ent), idlestr, pidstr, + hoststr ? hoststr : "", ""); } static void print_boottime (const STRUCT_UTMP *utmp_ent) { - print_line (-1, "", ' ', -1, "system boot", - time_string (utmp_ent), "", "", "", ""); + print_line (-1, "", ' ', -1, _("system boot"), + time_string (utmp_ent), "", "", "", ""); } static char * @@ -452,17 +460,17 @@ print_deadprocs (const STRUCT_UTMP *utmp_ent) if (!exitstr) exitstr = xmalloc (strlen (_("term=")) - + INT_STRLEN_BOUND (UT_EXIT_E_TERMINATION (utmp_ent)) + 1 - + strlen (_("exit=")) - + INT_STRLEN_BOUND (UT_EXIT_E_EXIT (utmp_ent)) - + 1); + + INT_STRLEN_BOUND (UT_EXIT_E_TERMINATION (utmp_ent)) + 1 + + strlen (_("exit=")) + + INT_STRLEN_BOUND (UT_EXIT_E_EXIT (utmp_ent)) + + 1); sprintf (exitstr, "%s%d %s%d", _("term="), UT_EXIT_E_TERMINATION (utmp_ent), - _("exit="), UT_EXIT_E_EXIT (utmp_ent)); + _("exit="), UT_EXIT_E_EXIT (utmp_ent)); /* FIXME: add idle time? */ print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line, - time_string (utmp_ent), "", pidstr, comment, exitstr); + time_string (utmp_ent), "", pidstr, comment, exitstr); free (comment); } @@ -474,8 +482,8 @@ print_login (const STRUCT_UTMP *utmp_ent) /* FIXME: add idle time? */ - print_line (-1, "LOGIN", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line, - time_string (utmp_ent), "", pidstr, comment, ""); + print_line (-1, _("LOGIN"), ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line, + time_string (utmp_ent), "", pidstr, comment, ""); free (comment); } @@ -486,7 +494,7 @@ print_initspawn (const STRUCT_UTMP *utmp_ent) PIDSTR_DECL_AND_INIT (pidstr, utmp_ent); print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line, - time_string (utmp_ent), "", pidstr, comment, ""); + time_string (utmp_ent), "", pidstr, comment, ""); free (comment); } @@ -495,7 +503,7 @@ print_clockchange (const STRUCT_UTMP *utmp_ent) { /* FIXME: handle NEW_TIME & OLD_TIME both */ print_line (-1, "", ' ', -1, _("clock change"), - time_string (utmp_ent), "", "", "", ""); + time_string (utmp_ent), "", "", "", ""); } static void @@ -514,7 +522,7 @@ print_runlevel (const STRUCT_UTMP *utmp_ent) sprintf (comment, "%s%c", _("last="), (last == 'N') ? 'S' : last); print_line (-1, "", ' ', -1, runlevline, time_string (utmp_ent), - "", "", comment, ""); + "", "", c_isprint (last) ? comment : "", ""); return; } @@ -530,16 +538,16 @@ list_entries_who (size_t n, const STRUCT_UTMP *utmp_buf) while (n--) { if (IS_USER_PROCESS (utmp_buf)) - { - char *trimmed_name; + { + char *trimmed_name; - trimmed_name = extract_trimmed_name (utmp_buf); + trimmed_name = extract_trimmed_name (utmp_buf); - printf ("%s%s", separator, trimmed_name); - free (trimmed_name); - separator = " "; - entries++; - } + printf ("%s%s", separator, trimmed_name); + free (trimmed_name); + separator = " "; + entries++; + } utmp_buf++; } printf (_("\n# users=%lu\n"), entries); @@ -549,7 +557,7 @@ static void print_heading (void) { print_line (-1, _("NAME"), ' ', -1, _("LINE"), _("TIME"), _("IDLE"), - _("PID"), _("COMMENT"), _("EXIT")); + _("PID"), _("COMMENT"), _("EXIT")); } /* Display UTMP_BUF, which should have N entries. */ @@ -566,38 +574,38 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf) { ttyname_b = ttyname (STDIN_FILENO); if (!ttyname_b) - return; - if (strncmp (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH, DEV_DIR_LEN) == 0) - ttyname_b += DEV_DIR_LEN; /* Discard /dev/ prefix. */ + return; + if (STRNCMP_LIT (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH) == 0) + ttyname_b += DEV_DIR_LEN; /* Discard /dev/ prefix. */ } while (n--) { - if (!my_line_only || - strncmp (ttyname_b, utmp_buf->ut_line, - sizeof (utmp_buf->ut_line)) == 0) - { - if (need_users && IS_USER_PROCESS (utmp_buf)) - print_user (utmp_buf, boottime); - else if (need_runlevel && UT_TYPE_RUN_LVL (utmp_buf)) - print_runlevel (utmp_buf); - else if (need_boottime && UT_TYPE_BOOT_TIME (utmp_buf)) - print_boottime (utmp_buf); - /* I've never seen one of these, so I don't know what it should - look like :^) - FIXME: handle OLD_TIME also, perhaps show the delta? */ - else if (need_clockchange && UT_TYPE_NEW_TIME (utmp_buf)) - print_clockchange (utmp_buf); - else if (need_initspawn && UT_TYPE_INIT_PROCESS (utmp_buf)) - print_initspawn (utmp_buf); - else if (need_login && UT_TYPE_LOGIN_PROCESS (utmp_buf)) - print_login (utmp_buf); - else if (need_deadprocs && UT_TYPE_DEAD_PROCESS (utmp_buf)) - print_deadprocs (utmp_buf); - } + if (!my_line_only + || STREQ_LEN (ttyname_b, utmp_buf->ut_line, + sizeof (utmp_buf->ut_line))) + { + if (need_users && IS_USER_PROCESS (utmp_buf)) + print_user (utmp_buf, boottime); + else if (need_runlevel && UT_TYPE_RUN_LVL (utmp_buf)) + print_runlevel (utmp_buf); + else if (need_boottime && UT_TYPE_BOOT_TIME (utmp_buf)) + print_boottime (utmp_buf); + /* I've never seen one of these, so I don't know what it should + look like :^) + FIXME: handle OLD_TIME also, perhaps show the delta? */ + else if (need_clockchange && UT_TYPE_NEW_TIME (utmp_buf)) + print_clockchange (utmp_buf); + else if (need_initspawn && UT_TYPE_INIT_PROCESS (utmp_buf)) + print_initspawn (utmp_buf); + else if (need_login && UT_TYPE_LOGIN_PROCESS (utmp_buf)) + print_login (utmp_buf); + else if (need_deadprocs && UT_TYPE_DEAD_PROCESS (utmp_buf)) + print_deadprocs (utmp_buf); + } if (UT_TYPE_BOOT_TIME (utmp_buf)) - boottime = UT_TIME_MEMBER (utmp_buf); + boottime = UT_TIME_MEMBER (utmp_buf); utmp_buf++; } @@ -612,7 +620,7 @@ who (const char *filename, int options) STRUCT_UTMP *utmp_buf; if (read_utmp (filename, &n_users, &utmp_buf, options) != 0) - error (EXIT_FAILURE, errno, "%s", filename); + error (EXIT_FAILURE, errno, "%s", quotef (filename)); if (short_list) list_entries_who (n_users, utmp_buf); @@ -626,12 +634,14 @@ void usage (int status) { if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + emit_try_help (); else { printf (_("Usage: %s [OPTION]... [ FILE | ARG1 ARG2 ]\n"), program_name); fputs (_("\ +Print information about users who are currently logged in.\n\ +"), stdout); + fputs (_("\ \n\ -a, --all same as -b -d --login -p -r -t -T -u\n\ -b, --boot time of last system boot\n\ @@ -663,9 +673,9 @@ usage (int status) printf (_("\ \n\ If FILE is not specified, use %s. %s as FILE is common.\n\ -If ARG1 ARG2 given, -m presumed: `am i' or `mom likes' are usual.\n\ +If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.\n\ "), UTMP_FILE, WTMP_FILE); - printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + emit_ancillary_info (PROGRAM_NAME); } exit (status); } @@ -677,109 +687,104 @@ main (int argc, char **argv) bool assumptions = true; initialize_main (&argc, &argv); - program_name = argv[0]; + set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "abdilmpqrstuwHT", longopts, NULL)) - != -1) + while ((optc = getopt_long (argc, argv, "abdlmpqrstuwHT", longopts, NULL)) + != -1) { switch (optc) - { - case 'a': - need_boottime = true; - need_deadprocs = true; - need_login = true; - need_initspawn = true; - need_runlevel = true; - need_clockchange = true; - need_users = true; - include_mesg = true; - include_idle = true; - include_exit = true; - assumptions = false; - break; - - case 'b': - need_boottime = true; - assumptions = false; - break; - - case 'd': - need_deadprocs = true; - include_idle = true; - include_exit = true; - assumptions = false; - break; - - case 'H': - include_heading = true; - break; - - case 'l': - need_login = true; - include_idle = true; - assumptions = false; - break; - - case 'm': - my_line_only = true; - break; - - case 'p': - need_initspawn = true; - assumptions = false; - break; - - case 'q': - short_list = true; - break; - - case 'r': - need_runlevel = true; - include_idle = true; - assumptions = false; - break; - - case 's': - short_output = true; - break; - - case 't': - need_clockchange = true; - assumptions = false; - break; - - case 'T': - case 'w': - include_mesg = true; - break; - - case 'i': - error (0, 0, - _("Warning: -i will be removed in a future release; \ - use -u instead")); - /* Fall through. */ - case 'u': - need_users = true; - include_idle = true; - assumptions = false; - break; - - case LOOKUP_OPTION: - do_lookup = true; - break; - - case_GETOPT_HELP_CHAR; - - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - - default: - usage (EXIT_FAILURE); - } + { + case 'a': + need_boottime = true; + need_deadprocs = true; + need_login = true; + need_initspawn = true; + need_runlevel = true; + need_clockchange = true; + need_users = true; + include_mesg = true; + include_idle = true; + include_exit = true; + assumptions = false; + break; + + case 'b': + need_boottime = true; + assumptions = false; + break; + + case 'd': + need_deadprocs = true; + include_idle = true; + include_exit = true; + assumptions = false; + break; + + case 'H': + include_heading = true; + break; + + case 'l': + need_login = true; + include_idle = true; + assumptions = false; + break; + + case 'm': + my_line_only = true; + break; + + case 'p': + need_initspawn = true; + assumptions = false; + break; + + case 'q': + short_list = true; + break; + + case 'r': + need_runlevel = true; + include_idle = true; + assumptions = false; + break; + + case 's': + short_output = true; + break; + + case 't': + need_clockchange = true; + assumptions = false; + break; + + case 'T': + case 'w': + include_mesg = true; + break; + + case 'u': + need_users = true; + include_idle = true; + assumptions = false; + break; + + case LOOKUP_OPTION: + do_lookup = true; + break; + + case_GETOPT_HELP_CHAR; + + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + + default: + usage (EXIT_FAILURE); + } } if (assumptions) @@ -823,5 +828,5 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } - exit (EXIT_SUCCESS); + return EXIT_SUCCESS; } |