LCOV - code coverage report
Current view: top level - src - pinky.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 142 244 58.2 %
Date: 2018-01-30 Functions: 9 12 75.0 %

          Line data    Source code
       1             : /* GNU's pinky.
       2             :    Copyright (C) 1992-1997, 1999-2006 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu */
      18             : 
      19             : #include <config.h>
      20             : #include <getopt.h>
      21             : #include <pwd.h>
      22             : #include <stdio.h>
      23             : 
      24             : #include <sys/types.h>
      25             : #include "system.h"
      26             : 
      27             : #include "canon-host.h"
      28             : #include "error.h"
      29             : #include "hard-locale.h"
      30             : #include "inttostr.h"
      31             : #include "readutmp.h"
      32             : 
      33             : /* The official name of this program (e.g., no `g' prefix).  */
      34             : #define PROGRAM_NAME "pinky"
      35             : 
      36             : #define AUTHORS "Joseph Arceneaux", "David MacKenzie", "Kaveh Ghazi"
      37             : 
      38             : #ifndef MAXHOSTNAMELEN
      39             : # define MAXHOSTNAMELEN 64
      40             : #endif
      41             : 
      42             : char *ttyname ();
      43             : 
      44             : /* The name this program was run with. */
      45             : const char *program_name;
      46             : 
      47             : /* If true, display the hours:minutes since each user has touched
      48             :    the keyboard, or blank if within the last minute, or days followed
      49             :    by a 'd' if not within the last day. */
      50             : static bool include_idle = true;
      51             : 
      52             : /* If true, display a line at the top describing each field. */
      53             : static bool include_heading = true;
      54             : 
      55             : /* if true, display the user's full name from pw_gecos. */
      56             : static bool include_fullname = true;
      57             : 
      58             : /* if true, display the user's ~/.project file when doing long format. */
      59             : static bool include_project = true;
      60             : 
      61             : /* if true, display the user's ~/.plan file when doing long format. */
      62             : static bool include_plan = true;
      63             : 
      64             : /* if true, display the user's home directory and shell
      65             :    when doing long format. */
      66             : static bool include_home_and_shell = true;
      67             : 
      68             : /* if true, use the "short" output format. */
      69             : static bool do_short_format = true;
      70             : 
      71             : /* if true, display the ut_host field. */
      72             : #ifdef HAVE_UT_HOST
      73             : static bool include_where = true;
      74             : #endif
      75             : 
      76             : /* The strftime format to use for login times, and its expected
      77             :    output width.  */
      78             : static char const *time_format;
      79             : static int time_format_width;
      80             : 
      81             : static struct option const longopts[] =
      82             : {
      83             :   {GETOPT_HELP_OPTION_DECL},
      84             :   {GETOPT_VERSION_OPTION_DECL},
      85             :   {NULL, 0, NULL, 0}
      86             : };
      87             : 
      88             : /* Count and return the number of ampersands in STR.  */
      89             : 
      90             : static size_t
      91           6 : count_ampersands (const char *str)
      92             : {
      93           6 :   size_t count = 0;
      94             :   do
      95             :     {
      96          26 :       if (*str == '&')
      97           0 :         count++;
      98          26 :     } while (*str++);
      99           6 :   return count;
     100             : }
     101             : 
     102             : /* Create a string (via xmalloc) which contains a full name by substituting
     103             :    for each ampersand in GECOS_NAME the USER_NAME string with its first
     104             :    character capitalized.  The caller must ensure that GECOS_NAME contains
     105             :    no `,'s.  The caller also is responsible for free'ing the return value of
     106             :    this function.  */
     107             : 
     108             : static char *
     109           6 : create_fullname (const char *gecos_name, const char *user_name)
     110             : {
     111           6 :   size_t rsize = strlen (gecos_name) + 1;
     112             :   char *result;
     113             :   char *r;
     114           6 :   size_t ampersands = count_ampersands (gecos_name);
     115             : 
     116           6 :   if (ampersands != 0)
     117             :     {
     118           0 :       size_t ulen = strlen (user_name);
     119           0 :       size_t product = ampersands * ulen;
     120           0 :       rsize += product - ampersands;
     121           0 :       if (xalloc_oversized (ulen, ampersands) || rsize < product)
     122           0 :         xalloc_die ();
     123             :     }
     124             : 
     125           6 :   r = result = xmalloc (rsize);
     126             : 
     127          32 :   while (*gecos_name)
     128             :     {
     129          20 :       if (*gecos_name == '&')
     130             :         {
     131           0 :           const char *uname = user_name;
     132           0 :           if (islower (to_uchar (*uname)))
     133           0 :             *r++ = toupper (to_uchar (*uname++));
     134           0 :           while (*uname)
     135           0 :             *r++ = *uname++;
     136             :         }
     137             :       else
     138             :         {
     139          20 :           *r++ = *gecos_name;
     140             :         }
     141             : 
     142          20 :       gecos_name++;
     143             :     }
     144           6 :   *r = 0;
     145             : 
     146           6 :   return result;
     147             : }
     148             : 
     149             : /* Return a string representing the time between WHEN and the time
     150             :    that this function is first run. */
     151             : 
     152             : static const char *
     153           0 : idle_string (time_t when)
     154             : {
     155             :   static time_t now = 0;
     156             :   static char buf[INT_STRLEN_BOUND (long int) + 2];
     157             :   time_t seconds_idle;
     158             : 
     159           0 :   if (now == 0)
     160           0 :     time (&now);
     161             : 
     162           0 :   seconds_idle = now - when;
     163           0 :   if (seconds_idle < 60)     /* One minute. */
     164           0 :     return "     ";
     165           0 :   if (seconds_idle < (24 * 60 * 60)) /* One day. */
     166             :     {
     167           0 :       int hours = seconds_idle / (60 * 60);
     168           0 :       int minutes = (seconds_idle % (60 * 60)) / 60;
     169           0 :       sprintf (buf, "%02d:%02d", hours, minutes);
     170             :     }
     171             :   else
     172             :     {
     173           0 :       unsigned long int days = seconds_idle / (24 * 60 * 60);
     174           0 :       sprintf (buf, "%lud", days);
     175             :     }
     176           0 :   return buf;
     177             : }
     178             : 
     179             : /* Return a time string.  */
     180             : static const char *
     181           0 : time_string (const STRUCT_UTMP *utmp_ent)
     182             : {
     183             :   static char buf[INT_STRLEN_BOUND (intmax_t) + sizeof "-%m-%d %H:%M"];
     184             : 
     185             :   /* Don't take the address of UT_TIME_MEMBER directly.
     186             :      Ulrich Drepper wrote:
     187             :      ``... GNU libc (and perhaps other libcs as well) have extended
     188             :      utmp file formats which do not use a simple time_t ut_time field.
     189             :      In glibc, ut_time is a macro which selects for backward compatibility
     190             :      the tv_sec member of a struct timeval value.''  */
     191           0 :   time_t t = UT_TIME_MEMBER (utmp_ent);
     192           0 :   struct tm *tmp = localtime (&t);
     193             : 
     194           0 :   if (tmp)
     195             :     {
     196           0 :       strftime (buf, sizeof buf, time_format, tmp);
     197           0 :       return buf;
     198             :     }
     199             :   else
     200           0 :     return TYPE_SIGNED (time_t) ? imaxtostr (t, buf) : umaxtostr (t, buf);
     201             : }
     202             : 
     203             : /* Display a line of information about UTMP_ENT. */
     204             : 
     205             : static void
     206           0 : print_entry (const STRUCT_UTMP *utmp_ent)
     207             : {
     208             :   struct stat stats;
     209             :   time_t last_change;
     210             :   char mesg;
     211             : 
     212             : #define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
     213             : #define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
     214             : 
     215             :   char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
     216             : 
     217             :   /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
     218             :      already an absolute file name.  Some system may put the full,
     219             :      absolute file name in ut_line.  */
     220           0 :   if (utmp_ent->ut_line[0] == '/')
     221             :     {
     222           0 :       strncpy (line, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
     223           0 :       line[sizeof (utmp_ent->ut_line)] = '\0';
     224             :     }
     225             :   else
     226             :     {
     227           0 :       strcpy (line, DEV_DIR_WITH_TRAILING_SLASH);
     228           0 :       strncpy (line + DEV_DIR_LEN, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
     229           0 :       line[DEV_DIR_LEN + sizeof (utmp_ent->ut_line)] = '\0';
     230             :     }
     231             : 
     232           0 :   if (stat (line, &stats) == 0)
     233             :     {
     234           0 :       mesg = (stats.st_mode & S_IWGRP) ? ' ' : '*';
     235           0 :       last_change = stats.st_atime;
     236             :     }
     237             :   else
     238             :     {
     239           0 :       mesg = '?';
     240           0 :       last_change = 0;
     241             :     }
     242             : 
     243           0 :   printf ("%-8.*s", UT_USER_SIZE, UT_USER (utmp_ent));
     244             : 
     245           0 :   if (include_fullname)
     246             :     {
     247             :       struct passwd *pw;
     248             :       char name[UT_USER_SIZE + 1];
     249             : 
     250           0 :       strncpy (name, UT_USER (utmp_ent), UT_USER_SIZE);
     251           0 :       name[UT_USER_SIZE] = '\0';
     252           0 :       pw = getpwnam (name);
     253           0 :       if (pw == NULL)
     254           0 :         printf (" %19s", "        ???");
     255             :       else
     256             :         {
     257           0 :           char *const comma = strchr (pw->pw_gecos, ',');
     258             :           char *result;
     259             : 
     260           0 :           if (comma)
     261           0 :             *comma = '\0';
     262             : 
     263           0 :           result = create_fullname (pw->pw_gecos, pw->pw_name);
     264           0 :           printf (" %-19.19s", result);
     265           0 :           free (result);
     266             :         }
     267             :     }
     268             : 
     269           0 :   printf (" %c%-8.*s",
     270           0 :           mesg, (int) sizeof (utmp_ent->ut_line), utmp_ent->ut_line);
     271             : 
     272           0 :   if (include_idle)
     273             :     {
     274           0 :       if (last_change)
     275           0 :         printf (" %-6s", idle_string (last_change));
     276             :       else
     277           0 :         printf (" %-6s", "???");
     278             :     }
     279             : 
     280           0 :   printf (" %s", time_string (utmp_ent));
     281             : 
     282             : #ifdef HAVE_UT_HOST
     283           0 :   if (include_where && utmp_ent->ut_host[0])
     284             :     {
     285             :       char ut_host[sizeof (utmp_ent->ut_host) + 1];
     286           0 :       char *host = NULL;
     287           0 :       char *display = NULL;
     288             : 
     289             :       /* Copy the host name into UT_HOST, and ensure it's nul terminated. */
     290           0 :       strncpy (ut_host, utmp_ent->ut_host, (int) sizeof (utmp_ent->ut_host));
     291           0 :       ut_host[sizeof (utmp_ent->ut_host)] = '\0';
     292             : 
     293             :       /* Look for an X display.  */
     294           0 :       display = strchr (ut_host, ':');
     295           0 :       if (display)
     296           0 :         *display++ = '\0';
     297             : 
     298           0 :       if (*ut_host)
     299             :         /* See if we can canonicalize it.  */
     300           0 :         host = canon_host (ut_host);
     301           0 :       if ( ! host)
     302           0 :         host = ut_host;
     303             : 
     304           0 :       if (display)
     305           0 :         printf (" %s:%s", host, display);
     306             :       else
     307           0 :         printf (" %s", host);
     308             : 
     309           0 :       if (host != ut_host)
     310           0 :         free (host);
     311             :     }
     312             : #endif
     313             : 
     314           0 :   putchar ('\n');
     315           0 : }
     316             : 
     317             : /* Display a verbose line of information about UTMP_ENT. */
     318             : 
     319             : static void
     320           8 : print_long_entry (const char name[])
     321             : {
     322             :   struct passwd *pw;
     323             : 
     324           8 :   pw = getpwnam (name);
     325             : 
     326           8 :   printf (_("Login name: "));
     327           8 :   printf ("%-28s", name);
     328             : 
     329           8 :   printf (_("In real life: "));
     330           8 :   if (pw == NULL)
     331             :     {
     332           2 :       printf (" %s", _("???\n"));
     333           2 :       return;
     334             :     }
     335             :   else
     336             :     {
     337           6 :       char *const comma = strchr (pw->pw_gecos, ',');
     338             :       char *result;
     339             : 
     340           6 :       if (comma)
     341           0 :         *comma = '\0';
     342             : 
     343           6 :       result = create_fullname (pw->pw_gecos, pw->pw_name);
     344           6 :       printf (" %s", result);
     345           6 :       free (result);
     346             :     }
     347             : 
     348           6 :   putchar ('\n');
     349             : 
     350           6 :   if (include_home_and_shell)
     351             :     {
     352           5 :       printf (_("Directory: "));
     353           5 :       printf ("%-29s", pw->pw_dir);
     354           5 :       printf (_("Shell: "));
     355           5 :       printf (" %s", pw->pw_shell);
     356           5 :       putchar ('\n');
     357             :     }
     358             : 
     359           6 :   if (include_project)
     360             :     {
     361             :       FILE *stream;
     362             :       char buf[1024];
     363           5 :       const char *const baseproject = "/.project";
     364           5 :       char *const project =
     365           5 :       xmalloc (strlen (pw->pw_dir) + strlen (baseproject) + 1);
     366             : 
     367           5 :       strcpy (project, pw->pw_dir);
     368           5 :       strcat (project, baseproject);
     369             : 
     370           5 :       stream = fopen (project, "r");
     371           5 :       if (stream)
     372             :         {
     373             :           size_t bytes;
     374             : 
     375           0 :           printf (_("Project: "));
     376             : 
     377           0 :           while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
     378           0 :             fwrite (buf, 1, bytes, stdout);
     379           0 :           fclose (stream);
     380             :         }
     381             : 
     382           5 :       free (project);
     383             :     }
     384             : 
     385           6 :   if (include_plan)
     386             :     {
     387             :       FILE *stream;
     388             :       char buf[1024];
     389           5 :       const char *const baseplan = "/.plan";
     390           5 :       char *const plan =
     391           5 :       xmalloc (strlen (pw->pw_dir) + strlen (baseplan) + 1);
     392             : 
     393           5 :       strcpy (plan, pw->pw_dir);
     394           5 :       strcat (plan, baseplan);
     395             : 
     396           5 :       stream = fopen (plan, "r");
     397           5 :       if (stream)
     398             :         {
     399             :           size_t bytes;
     400             : 
     401           0 :           printf (_("Plan:\n"));
     402             : 
     403           0 :           while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
     404           0 :             fwrite (buf, 1, bytes, stdout);
     405           0 :           fclose (stream);
     406             :         }
     407             : 
     408           5 :       free (plan);
     409             :     }
     410             : 
     411           6 :   putchar ('\n');
     412             : }
     413             : 
     414             : /* Print the username of each valid entry and the number of valid entries
     415             :    in UTMP_BUF, which should have N elements. */
     416             : 
     417             : static void
     418          17 : print_heading (void)
     419             : {
     420          17 :   printf ("%-8s", _("Login"));
     421          17 :   if (include_fullname)
     422          14 :     printf (" %-19s", _("Name"));
     423          17 :   printf (" %-9s", _(" TTY"));
     424          17 :   if (include_idle)
     425          16 :     printf (" %-6s", _("Idle"));
     426          17 :   printf (" %-*s", time_format_width, _("When"));
     427             : #ifdef HAVE_UT_HOST
     428          17 :   if (include_where)
     429          15 :     printf (" %s", _("Where"));
     430             : #endif
     431          17 :   putchar ('\n');
     432          17 : }
     433             : 
     434             : /* Display UTMP_BUF, which should have N entries. */
     435             : 
     436             : static void
     437          19 : scan_entries (size_t n, const STRUCT_UTMP *utmp_buf,
     438             :               const int argc_names, char *const argv_names[])
     439             : {
     440          19 :   if (hard_locale (LC_TIME))
     441             :     {
     442           0 :       time_format = "%Y-%m-%d %H:%M";
     443           0 :       time_format_width = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2;
     444             :     }
     445             :   else
     446             :     {
     447          19 :       time_format = "%b %e %H:%M";
     448          19 :       time_format_width = 3 + 1 + 2 + 1 + 2 + 1 + 2;
     449             :     }
     450             : 
     451          19 :   if (include_heading)
     452          17 :     print_heading ();
     453             : 
     454          38 :   while (n--)
     455             :     {
     456           0 :       if (IS_USER_PROCESS (utmp_buf))
     457             :         {
     458           0 :           if (argc_names)
     459             :             {
     460             :               int i;
     461             : 
     462           0 :               for (i = 0; i < argc_names; i++)
     463           0 :                 if (strncmp (UT_USER (utmp_buf), argv_names[i], UT_USER_SIZE)
     464             :                     == 0)
     465             :                   {
     466           0 :                     print_entry (utmp_buf);
     467           0 :                     break;
     468             :                   }
     469             :             }
     470             :           else
     471           0 :             print_entry (utmp_buf);
     472             :         }
     473           0 :       utmp_buf++;
     474             :     }
     475          19 : }
     476             : 
     477             : /* Display a list of who is on the system, according to utmp file FILENAME. */
     478             : 
     479             : static void
     480          19 : short_pinky (const char *filename,
     481             :              const int argc_names, char *const argv_names[])
     482             : {
     483             :   size_t n_users;
     484             :   STRUCT_UTMP *utmp_buf;
     485             : 
     486          19 :   if (read_utmp (filename, &n_users, &utmp_buf, 0) != 0)
     487           0 :     error (EXIT_FAILURE, errno, "%s", filename);
     488             : 
     489          19 :   scan_entries (n_users, utmp_buf, argc_names, argv_names);
     490          19 : }
     491             : 
     492             : static void
     493           7 : long_pinky (const int argc_names, char *const argv_names[])
     494             : {
     495             :   int i;
     496             : 
     497          15 :   for (i = 0; i < argc_names; i++)
     498           8 :     print_long_entry (argv_names[i]);
     499           7 : }
     500             : 
     501             : void
     502           7 : usage (int status)
     503             : {
     504           7 :   if (status != EXIT_SUCCESS)
     505           5 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     506             :              program_name);
     507             :   else
     508             :     {
     509           2 :       printf (_("Usage: %s [OPTION]... [USER]...\n"), program_name);
     510           2 :       fputs (_("\
     511             : \n\
     512             :   -l              produce long format output for the specified USERs\n\
     513             :   -b              omit the user's home directory and shell in long format\n\
     514             :   -h              omit the user's project file in long format\n\
     515             :   -p              omit the user's plan file in long format\n\
     516             :   -s              do short format output, this is the default\n\
     517             : "), stdout);
     518           2 :       fputs (_("\
     519             :   -f              omit the line of column headings in short format\n\
     520             :   -w              omit the user's full name in short format\n\
     521             :   -i              omit the user's full name and remote host in short format\n\
     522             :   -q              omit the user's full name, remote host and idle time\n\
     523             :                   in short format\n\
     524             : "), stdout);
     525           2 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     526           2 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     527           2 :       printf (_("\
     528             : \n\
     529             : A lightweight `finger' program;  print user information.\n\
     530             : The utmp file will be %s.\n\
     531             : "), UTMP_FILE);
     532           2 :       emit_bug_reporting_address ();
     533             :     }
     534           7 :   exit (status);
     535             : }
     536             : 
     537             : int
     538          35 : main (int argc, char **argv)
     539             : {
     540             :   int optc;
     541             :   int n_users;
     542             : 
     543             :   initialize_main (&argc, &argv);
     544          35 :   program_name = argv[0];
     545          35 :   setlocale (LC_ALL, "");
     546             :   bindtextdomain (PACKAGE, LOCALEDIR);
     547             :   textdomain (PACKAGE);
     548             : 
     549          35 :   atexit (close_stdout);
     550             : 
     551          94 :   while ((optc = getopt_long (argc, argv, "sfwiqbhlp", longopts, NULL)) != -1)
     552             :     {
     553          32 :       switch (optc)
     554             :         {
     555           5 :         case 's':
     556           5 :           do_short_format = true;
     557           5 :           break;
     558             : 
     559           8 :         case 'l':
     560           8 :           do_short_format = false;
     561           8 :           break;
     562             : 
     563           2 :         case 'f':
     564           2 :           include_heading = false;
     565           2 :           break;
     566             : 
     567           1 :         case 'w':
     568           1 :           include_fullname = false;
     569           1 :           break;
     570             : 
     571           1 :         case 'i':
     572           1 :           include_fullname = false;
     573             : #ifdef HAVE_UT_HOST
     574           1 :           include_where = false;
     575             : #endif
     576           1 :           break;
     577             : 
     578           1 :         case 'q':
     579           1 :           include_fullname = false;
     580             : #ifdef HAVE_UT_HOST
     581           1 :           include_where = false;
     582             : #endif
     583           1 :           include_idle = false;
     584           1 :           break;
     585             : 
     586           2 :         case 'h':
     587           2 :           include_project = false;
     588           2 :           break;
     589             : 
     590           2 :         case 'p':
     591           2 :           include_plan = false;
     592           2 :           break;
     593             : 
     594           2 :         case 'b':
     595           2 :           include_home_and_shell = false;
     596           2 :           break;
     597             : 
     598           2 :         case_GETOPT_HELP_CHAR;
     599             : 
     600           2 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     601             : 
     602           4 :         default:
     603           4 :           usage (EXIT_FAILURE);
     604             :         }
     605             :     }
     606             : 
     607          27 :   n_users = argc - optind;
     608             : 
     609          27 :   if (!do_short_format && n_users == 0)
     610             :     {
     611           1 :       error (0, 0, _("no username specified; at least one must be\
     612             :  specified when using -l"));
     613           1 :       usage (EXIT_FAILURE);
     614             :     }
     615             : 
     616          26 :   if (do_short_format)
     617          19 :     short_pinky (UTMP_FILE, n_users, argv + optind);
     618             :   else
     619           7 :     long_pinky (n_users, argv + optind);
     620             : 
     621          26 :   exit (EXIT_SUCCESS);
     622             : }

Generated by: LCOV version 1.10