home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sh-utils-1.12-src.tgz / tar.out / fsf / sh-utils / src / who.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  13KB  |  594 lines

  1. /* GNU's users/who.
  2.    Copyright (C) 92, 93, 1994 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 2, or (at your option)
  7.    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, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by jla; revised by djm */
  19.  
  20. /* Output format:
  21.    name [state] line time [idle] host
  22.    state: -T
  23.    name, line, time: not -q
  24.    idle: -u
  25.  
  26.    Options:
  27.    -m        Same as 'who am i', for POSIX.
  28.    -q        Only user names and # logged on; overrides all other options.
  29.    -s        Name, line, time (default).
  30.    -i, -u    Idle hours and minutes; '.' means active in last minute;
  31.         'old' means idle for >24 hours.
  32.    -H        Print column headings at top.
  33.    -w, -T    -s plus mesg (+ or -, or ? if bad line). */
  34.  
  35. #include <config.h>
  36. #include <stdio.h>
  37. #include <sys/types.h>
  38.  
  39. #ifdef HAVE_UTMPX_H
  40. #include <utmpx.h>
  41. #define STRUCT_UTMP struct utmpx
  42. #else
  43. #include <utmp.h>
  44. #define STRUCT_UTMP struct utmp
  45. #endif
  46.  
  47. #include <time.h>
  48. #include <getopt.h>
  49. #ifdef HAVE_SYS_PARAM_H
  50. #include <sys/param.h>
  51. #endif
  52.  
  53. #include "system.h"
  54. #include "version.h"
  55. #include "safe-stat.h"
  56.  
  57. #if !defined (UTMP_FILE) && defined (_PATH_UTMP)    /* 4.4BSD.  */
  58. #define UTMP_FILE _PATH_UTMP
  59. #endif
  60.  
  61. #if defined (UTMPX_FILE)    /* Solaris, SysVr4 */
  62. #undef  UTMP_FILE
  63. #define UTMP_FILE UTMPX_FILE
  64. #endif
  65.  
  66. #ifndef UTMP_FILE
  67. #define UTMP_FILE "/etc/utmp"
  68. #endif
  69.  
  70. #ifndef MAXHOSTNAMELEN
  71. #define MAXHOSTNAMELEN 64
  72. #endif
  73.  
  74. #ifndef S_IWGRP
  75. #define S_IWGRP 020
  76. #endif
  77.  
  78. #ifdef WHO
  79. #define COMMAND_NAME "who"
  80. #else
  81. #ifdef USERS
  82. #define COMMAND_NAME "users"
  83. #else
  84.  error You must define one of WHO and USERS.
  85. #endif /* USERS */
  86. #endif /* WHO */
  87.  
  88. char *xmalloc ();
  89. void error ();
  90. char *ttyname ();
  91. int gethostname ();
  92.  
  93. static int read_utmp ();
  94. #ifdef WHO
  95. static const char *idle_string ();
  96. static STRUCT_UTMP *search_entries ();
  97. static void print_entry ();
  98. static void print_heading ();
  99. static void scan_entries ();
  100. static void who_am_i ();
  101. #endif /* WHO */
  102. static void list_entries ();
  103. static void usage ();
  104. static void who ();
  105.  
  106. /* The name this program was run with. */
  107. char *program_name;
  108.  
  109. /* If non-zero, display usage information and exit.  */
  110. static int show_help;
  111.  
  112. /* If non-zero, print the version on standard output and exit.  */
  113. static int show_version;
  114.  
  115. #ifdef WHO
  116. /* If nonzero, display only a list of usernames and count of
  117.    the users logged on.
  118.    Ignored for `who am i'. */
  119. static int short_list;
  120.  
  121. /* If nonzero, display the hours:minutes since each user has touched
  122.    the keyboard, or "." if within the last minute, or "old" if
  123.    not within the last day. */
  124. static int include_idle;
  125.  
  126. /* If nonzero, display a line at the top describing each field. */
  127. static int include_heading;
  128.  
  129. /* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
  130.    or a `?' if their tty cannot be statted. */
  131. static int include_mesg;
  132. #endif /* WHO */
  133.  
  134. static struct option const longopts[] =
  135. {
  136. #ifdef WHO
  137.   {"count", no_argument, NULL, 'q'},
  138.   {"idle", no_argument, NULL, 'u'},
  139.   {"heading", no_argument, NULL, 'H'},
  140.   {"message", no_argument, NULL, 'T'},
  141.   {"mesg", no_argument, NULL, 'T'},
  142.   {"writable", no_argument, NULL, 'T'},
  143. #endif /* WHO */
  144.   {"help", no_argument, &show_help, 1},
  145.   {"version", no_argument, &show_version, 1},
  146.   {NULL, 0, NULL, 0}
  147. };
  148.  
  149. main (argc, argv)
  150.      int argc;
  151.      char **argv;
  152. {
  153.   int optc, longind;
  154. #ifdef WHO
  155.   int my_line_only = 0;
  156. #endif /* WHO */
  157.  
  158.   program_name = argv[0];
  159.  
  160. #ifdef WHO
  161.   while ((optc = getopt_long (argc, argv, "imqsuwHT", longopts, &longind))
  162. #else
  163.   while ((optc = getopt_long (argc, argv, "", longopts, &longind))
  164. #endif /* WHO */
  165.      != EOF)
  166.     {
  167.       switch (optc)
  168.     {
  169.     case 0:
  170.       break;
  171.  
  172. #ifdef WHO
  173.     case 'm':
  174.       my_line_only = 1;
  175.       break;
  176.  
  177.     case 'q':
  178.       short_list = 1;
  179.       break;
  180.  
  181.     case 's':
  182.       break;
  183.  
  184.     case 'i':
  185.     case 'u':
  186.       include_idle = 1;
  187.       break;
  188.  
  189.     case 'H':
  190.       include_heading = 1;
  191.       break;
  192.  
  193.     case 'w':
  194.     case 'T':
  195.       include_mesg = 1;
  196.       break;
  197. #endif /* WHO */
  198.  
  199.     default:
  200.       error (0, 0, "too many arguments");
  201.       usage (1);
  202.     }
  203.     }
  204.  
  205.   if (show_version)
  206.     {
  207.       printf ("%s - %s\n", COMMAND_NAME, version_string);
  208.       exit (0);
  209.     }
  210.  
  211.   if (show_help)
  212.     usage (0);
  213.  
  214.   switch (argc - optind)
  215.     {
  216.     case 0:            /* who */
  217. #ifdef WHO
  218.       if (my_line_only)
  219.     who_am_i (UTMP_FILE);
  220.       else
  221. #endif /* WHO */
  222.     who (UTMP_FILE);
  223.       break;
  224.  
  225.     case 1:            /* who <utmp file> */
  226. #ifdef WHO
  227.       if (my_line_only)
  228.     who_am_i (argv[optind]);
  229.       else
  230. #endif /* WHO */
  231.     who (argv[optind]);
  232.       break;
  233.  
  234. #ifdef WHO
  235.     case 2:            /* who <blurf> <glop> */
  236.       who_am_i (UTMP_FILE);
  237.       break;
  238. #endif /* WHO */
  239.  
  240.     default:            /* lose */
  241.       usage (1);
  242.     }
  243.  
  244.   exit (0);
  245. }
  246.  
  247. static STRUCT_UTMP *utmp_contents;
  248.  
  249. /* Display a list of who is on the system, according to utmp file FILENAME. */
  250.  
  251. static void
  252. who (filename)
  253.      char *filename;
  254. {
  255.   int users;
  256.  
  257.   users = read_utmp (filename);
  258. #ifdef WHO
  259.   if (short_list)
  260.     list_entries (users);
  261.   else
  262.     scan_entries (users);
  263. #else
  264. #ifdef USERS
  265.   list_entries (users);
  266. #endif /* USERS */
  267. #endif /* WHO */
  268. }
  269.  
  270. /* Read the utmp file FILENAME into UTMP_CONTENTS and return the
  271.    number of entries it contains. */
  272.  
  273. static int
  274. read_utmp (filename)
  275.      char *filename;
  276. {
  277.   FILE *utmp;
  278.   struct stat file_stats;
  279.   int n_read;
  280.   size_t size;
  281.  
  282.   utmp = fopen (filename, "r");
  283.   if (utmp == NULL)
  284.     error (1, errno, "%s", filename);
  285.  
  286.   fstat (fileno (utmp), &file_stats);
  287.   size = file_stats.st_size;
  288.   if (size > 0)
  289.     utmp_contents = (STRUCT_UTMP *) xmalloc (size);
  290.   else
  291.     {
  292.       fclose (utmp);
  293.       return 0;
  294.     }
  295.  
  296.   /* Use < instead of != in case the utmp just grew.  */
  297.   n_read = fread (utmp_contents, 1, size, utmp);
  298.   if (ferror (utmp) || fclose (utmp) == EOF
  299.       || n_read < size)
  300.     error (1, errno, "%s", filename);
  301.  
  302.   return size / sizeof (STRUCT_UTMP);
  303. }
  304.  
  305. #ifdef WHO
  306. /* Display a line of information about entry THIS. */
  307.  
  308. static void
  309. print_entry (this)
  310.      STRUCT_UTMP *this;
  311. {
  312.   struct stat stats;
  313.   time_t last_change;
  314.   char mesg;
  315.  
  316. #define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
  317. #define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
  318.  
  319.   char line[sizeof (this->ut_line) + DEV_DIR_LEN + 1];
  320.  
  321.   /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
  322.      already an absolute pathname.  Some system may put the full,
  323.      absolute pathname in ut_line.  */
  324.   if (this->ut_line[0] == '/')
  325.     {
  326.       strncpy (line, this->ut_line, sizeof (this->ut_line));
  327.       line[sizeof (this->ut_line)] = '\0';
  328.     }
  329.   else
  330.     {
  331.       strcpy(line, DEV_DIR_WITH_TRAILING_SLASH);
  332.       strncpy (line + DEV_DIR_LEN, this->ut_line, sizeof (this->ut_line));
  333.       line[DEV_DIR_LEN + sizeof (this->ut_line)] = '\0';
  334.     }
  335.  
  336.   if (SAFE_STAT (line, &stats) == 0)
  337.     {
  338.       mesg = (stats.st_mode & S_IWGRP) ? '+' : '-';
  339.       last_change = stats.st_atime;
  340.     }
  341.   else
  342.     {
  343.       mesg = '?';
  344.       last_change = 0;
  345.     }
  346.   
  347.   printf ("%-8.*s", (int) sizeof (this->ut_name), this->ut_name);
  348.   if (include_mesg)
  349.     printf ("  %c  ", mesg);
  350.   printf (" %-8.*s", (int) sizeof (this->ut_line), this->ut_line);
  351.  
  352. #ifdef HAVE_UTMPX_H
  353.   printf (" %-12.12s", ctime (&this->ut_tv.tv_sec) + 4);
  354. #else
  355.   printf (" %-12.12s", ctime (&this->ut_time) + 4);
  356. #endif
  357.  
  358.   if (include_idle)
  359.     {
  360.       if (last_change)
  361.     printf (" %s", idle_string (last_change));
  362.       else
  363.     printf ("   .  ");
  364.     }
  365. #ifdef HAVE_UT_HOST
  366.   if (this->ut_host[0])
  367.     printf (" (%-.*s)", (int) sizeof (this->ut_host), this->ut_host);
  368. #endif
  369.  
  370.   putchar ('\n');
  371. }
  372. #endif /* WHO */
  373.  
  374. #if defined (WHO) || defined (USERS)
  375. /* Print the username of each valid entry and the number of valid entries
  376.    in `utmp_contents', which should have N elements. */
  377.  
  378. static void
  379. list_entries (n)
  380.      int n;
  381. {
  382.   register STRUCT_UTMP *this = utmp_contents;
  383.   register int entries = 0;
  384.  
  385.   while (n--)
  386.     {
  387.       if (this->ut_name[0]
  388. #ifdef USER_PROCESS
  389.       && this->ut_type == USER_PROCESS
  390. #endif
  391.      )
  392.     {
  393.       char trimmed_name[sizeof (this->ut_name) + 1];
  394.       int i;
  395.  
  396.       strncpy (trimmed_name, this->ut_name, sizeof (this->ut_name));
  397.       trimmed_name[sizeof (this->ut_name)] = ' ';
  398.       for (i = 0; i <= sizeof (this->ut_name); i++)
  399.         {
  400.           if (trimmed_name[i] == ' ')
  401.         break;
  402.         }
  403.       trimmed_name[i] = '\0';
  404.  
  405.       printf ("%s ", trimmed_name);
  406.       entries++;
  407.     }
  408.       this++;
  409.     }
  410. #ifdef WHO
  411.   printf ("\n# users=%u\n", entries);
  412. #else
  413.   putchar('\n');
  414. #endif /* WHO */
  415. }
  416. #endif /* defined (WHO) || defined (USERS) */
  417.  
  418. #ifdef WHO
  419.  
  420. static void
  421. print_heading ()
  422. {
  423.   printf ("%-8s ", "USER");
  424.   if (include_mesg)
  425.     printf ("MESG ");
  426.   printf ("%-8s ", "LINE");
  427.   printf ("LOGIN-TIME   ");
  428.   if (include_idle)
  429.     printf ("IDLE  ");
  430.   printf ("FROM\n");
  431. }
  432.  
  433. /* Display `utmp_contents', which should have N entries. */
  434.  
  435. static void
  436. scan_entries (n)
  437.      int n;
  438. {
  439.   register STRUCT_UTMP *this = utmp_contents;
  440.  
  441.   if (include_heading)
  442.     print_heading ();
  443.  
  444.   while (n--)
  445.     {
  446.       if (this->ut_name[0]
  447. #ifdef USER_PROCESS
  448.       && this->ut_type == USER_PROCESS
  449. #endif
  450.      )
  451.     print_entry (this);
  452.       this++;
  453.     }
  454. }
  455.  
  456. /* Search `utmp_contents', which should have N entries, for
  457.    an entry with a `ut_line' field identical to LINE.
  458.    Return the first matching entry found, or NULL if there
  459.    is no matching entry. */
  460.  
  461. static STRUCT_UTMP *
  462. search_entries (n, line)
  463.      int n;
  464.      char *line;
  465. {
  466.   register STRUCT_UTMP *this = utmp_contents;
  467.  
  468.   while (n--)
  469.     {
  470.       if (this->ut_name[0]
  471. #ifdef USER_PROCESS
  472.       && this->ut_type == USER_PROCESS
  473. #endif
  474.       && !strncmp (line, this->ut_line, sizeof (this->ut_line)))
  475.     return this;
  476.       this++;
  477.     }
  478.   return NULL;
  479. }
  480.  
  481. /* Display the entry in utmp file FILENAME for this tty on standard input,
  482.    or nothing if there is no entry for it. */
  483.  
  484. static void
  485. who_am_i (filename)
  486.      char *filename;
  487. {
  488.   register STRUCT_UTMP *utmp_entry;
  489.   char hostname[MAXHOSTNAMELEN + 1];
  490.   char *tty;
  491.  
  492.   if (gethostname (hostname, MAXHOSTNAMELEN + 1))
  493.     *hostname = 0;
  494.  
  495.   if (include_heading)
  496.     {
  497.       printf ("%*s ", (int) strlen (hostname), " ");
  498.       print_heading ();
  499.     }
  500.  
  501.   tty = ttyname (0);
  502.   if (tty == NULL)
  503.     return;
  504.   tty += 5;            /* Remove "/dev/".  */
  505.   
  506.   utmp_entry = search_entries (read_utmp (filename), tty);
  507.   if (utmp_entry == NULL)
  508.     return;
  509.  
  510.   printf ("%s!", hostname);
  511.   print_entry (utmp_entry);
  512. }
  513.  
  514. /* Return a string representing the time between WHEN and the time
  515.    that this function is first run. */
  516.  
  517. static const char *
  518. idle_string (when)
  519.      time_t when;
  520. {
  521.   static time_t now = 0;
  522.   static char idle[10];
  523.   time_t seconds_idle;
  524.  
  525.   if (now == 0)
  526.     time (&now);
  527.  
  528.   seconds_idle = now - when;
  529.   if (seconds_idle < 60)    /* One minute. */
  530.     return "  .  ";
  531.   if (seconds_idle < (24 * 60 * 60)) /* One day. */
  532.     {
  533.       sprintf (idle, "%02d:%02d",
  534.            (int) (seconds_idle / (60 * 60)),
  535.            (int) ((seconds_idle % (60 * 60)) / 60));
  536.       return (const char *) idle;
  537.     }
  538.   return " old ";
  539. }
  540.  
  541. static void
  542. usage (status)
  543.      int status;
  544. {
  545.   if (status != 0)
  546.     fprintf (stderr, "Try `%s --help' for more information.\n",
  547.          program_name);
  548.   else
  549.     {
  550.       printf ("Usage: %s [OPTION]... [ FILE | ARG1 ARG2 ]\n", program_name);
  551.       printf ("\
  552. \n\
  553.   -H, --heading     print line of column headings\n\
  554.   -T, -w, --mesg    add user's message status as +, - or ?\n\
  555.   -i, -u, --idle    add user idle time as HOURS:MINUTES, . or old\n\
  556.   -m                only hostname and user associated with stdin\n\
  557.   -q, --count       all login names and number of users logged on\n\
  558.   -s                (ignored)\n\
  559.       --help        display this help and exit\n\
  560.       --message     same as -T\n\
  561.       --version     output version information and exit\n\
  562.       --writeable   same as -T\n\
  563. \n\
  564. If FILE not given, uses /etc/utmp.  /etc/wtmp as FILE is common.\n\
  565. If ARG1 ARG2 given, -m presumed: `am i' or `mom likes' are usual.\n\
  566. ");
  567.     }
  568.   exit (status);
  569. }
  570. #endif /* WHO */
  571.  
  572. #if defined (USERS)
  573. static void
  574. usage (status)
  575.      int status;
  576. {
  577.   if (status != 0)
  578.     fprintf (stderr, "Try `%s --help' for more information.\n",
  579.          program_name);
  580.   else
  581.     {
  582.       printf ("Usage: %s [OPTION]... [ FILE ]\n", program_name);
  583.       printf ("\
  584. \n\
  585.       --help        display this help and exit\n\
  586.       --version     output version information and exit\n\
  587. \n\
  588. If FILE not given, uses /etc/utmp.  /etc/wtmp as FILE is common.\n\
  589. ");
  590.     }
  591.   exit (status);
  592. }
  593. #endif /* USERS */
  594.