home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3259 < prev    next >
Text File  |  1991-04-29  |  14KB  |  466 lines

  1. Newsgroups: alt.sources
  2. From: 231b3679@fergvax.unl.edu (Mike Gleason)
  3. Subject: whom.c - another who (and whoson) replacement
  4. Message-ID: <231b3679.672991846@fergvax>
  5. Date: 30 Apr 91 06:10:46 GMT
  6.  
  7. I had also been working on a 'who' program.  I tried yesterday's posting,
  8. whoson, but it didn't work because my unix host uses fopen(file, "r") for
  9. both binary and text files, and the author of whoson used "rb".  Anyway, you
  10. may want to try this out.  Along with the usual stuff 'who' prints out, it
  11. also prints the user's full names.  You could get this effect by just typing
  12. 'finger', but this program is much, much faster.  Whom will also tell you
  13. which tty's are writeable (so you can phone, talk, or write them) by denoting
  14. them with an asterisk.  Here is a sample of the output:
  15.  
  16. User:     TTY: Where:             Time On:  Idle:  Name:
  17. 252u3693  16*                     3:32      0      Brian Guenther
  18. tdavis    p1   mod801.unomaha.e   0:47             Thomas Davis
  19. 252u3744  p2*  tsx-wsec103.unl.   0:34             Jerry Annin
  20. gunnit    p3   mandala.unl.edu    1:41      1:31   Gunnit S. Khurana
  21. 231b3630  p4*  tsx-wsec104.unl.   1:50      35     Peter Fields
  22. chaddan   p5*  tsx-wsec103.unl.   0:40             Christopher Neal Haddan
  23.  
  24. I have spent way too much time optimizing this silly thing, so it is quite
  25. quick.  If you use it, please let me know, and I'll keep working on it.
  26.  
  27. ---------cut here--------
  28. /*
  29.  * NCEMRS whom (C) 1991 Mike Gleason, NCEMRSoft.
  30.  *    version 1.0 -- 08 Mar 91
  31.  *    version 2.0 -- 18 Apr 91
  32.  *    version 2.1 -- 29 Apr 91
  33.  * Compile with CC for the smallest executable.
  34.  * Until 10 May 91, I am emailable at 231b3679@fergvax.unl.edu.
  35.  */
  36.  
  37.  
  38. #include <stdio.h>
  39. #include <time.h>
  40. #include <string.h>
  41.  
  42. #ifdef THINK_C
  43. #include "utmp.h"
  44. #include <stdlib.h>
  45. #include "stat.h"
  46. #else
  47. #include <utmp.h>
  48. #include <sys/types.h>
  49. #include <sys/stat.h>
  50. #endif
  51.  
  52. /* The only things you should need to configure are:
  53.  *   PASSWORD_FILE
  54.  *   DFILE
  55.  *   READ_BINARY
  56.  *   WRITE_BINARY
  57.  */
  58.  
  59. /* I developed this using a Macintosh and Think C, and it needs the
  60.    binary mode specifier char.  I think all non-unix machines will
  61.    need that too, but I doubt this will be run on a non-unix machine
  62.    anyway. */
  63. #ifdef THINK_C
  64. #define WRITE_BINARY "wb"
  65. #define READ_BINARY "rb"
  66. #else
  67. #define WRITE_BINARY "w"    /* On my unix host, you don't use the b's */
  68. #define READ_BINARY "r"
  69. #endif
  70.  
  71. #define ADD_SPACE *lyne++ = ' '
  72. #define PUBLIC_WRITE_PERM 00002           /*  write permission: other */
  73.  
  74.  
  75.  
  76. #ifndef NO_REAL_NAMES
  77.  
  78.     /* One of whom's best features is that it can print out the real name
  79.        of each user along with the other stuff.  There are a few minor
  80.        disadvantages.  (1), it needs to munge through the password file,
  81.        and (2) it needs to keep it's own private data file on disk somewhere.
  82.        If your password file does not have the real names of the users
  83.        in it, or if you can't spare the disk space (but it's not THAT big),
  84.        or just don't like it, then define NO_REAL_NAMES. The reason for
  85.        the private datafile is because Whom uses an optimized version of
  86.        the password file so it rapidly find information about any user.
  87.        If it didn't have this file, you might as well use finger, because
  88.        it will take forever to do this otherwise. */
  89.        
  90.     /* Point me to the location of your system's password file.  I need that
  91.        some stuff I need to make the data file, namely the login and real
  92.        name of each user. */
  93. #define PASSWORD_FILE "/etc/passwd"
  94.  
  95.     /* Point me to where you want to store the data file.  The size of this
  96.        will vary on the total number of users, but my machine has about 700
  97.        users with only a 30k data file.  The data file will be sorted and
  98.        each record will be the same size so we can use lightning fast
  99.        searching.  Programs like finger and getpwnam() probably have to
  100.        search the entire passwd file every time through because it isn't
  101.        sorted, and each line is of variable length.  For my machine it could
  102.        take up to 700 searches just to find one user with those programs,
  103.        but for this little ditty, it would only take a maximum of 10
  104.        searches. The ideal path for this file would be in the /etc direcotry
  105.        along with the passwd file, but my system admins don't like me
  106.        writing stuff there!  */
  107.    
  108. #define DFILE "/u3/231b3679/.whom"
  109. #define MAX_NAME_LEN 30
  110. #define MAX_LOGIN_LEN 8
  111. #define COLONS_TO_SKIP 4    /* Skip this many fields in passwd file */
  112. typedef struct
  113. {
  114.     char    login[MAX_LOGIN_LEN + 2]; /* leave room for ' \0'*/
  115.     char    name[MAX_NAME_LEN + 2]; /* leave room for ' \0' */
  116. } User;
  117.  
  118. #endif
  119.  
  120.  
  121.  
  122. /* These should be declared in utmp.h, but I've found an exception so... */
  123. #ifndef UTMP_FILE
  124. #define    UTMP_FILE    "/etc/utmp"
  125. #endif
  126.  
  127. #ifndef WTMP_FILE
  128. #define    WTMP_FILE    "/usr/adm/wtmp"
  129. #endif
  130.  
  131. /* Bonus Features:
  132.  *   1. #define ANSI, and when whom does it's thang, it will first
  133.  *      clear the screen and print the header line in boldface (oooh).
  134.  *
  135.  *   2. When running the program, if you pass an arbitrary argument
  136.  *      (ex.  whom -useWtmpDude) will try to use the wtmp file.
  137.  */
  138.  
  139. /* Prototypes */
  140. extern void *bsearch();
  141. extern void *malloc();
  142. char *strnpcat (/* dst, src, howMany */);
  143.  
  144.  
  145.  
  146. /* main, finally! */
  147. int                 main (argc, argv)
  148.     int                 argc;
  149.     char              **argv;
  150. {
  151.     FILE               *in, *dev;
  152.     char                fname[31], dname[31], str[31];
  153.     char                *lyne, line[128];
  154.     struct utmp         info;
  155.     short               wtmp, writeable;
  156.     time_t              Now;
  157.     int                 result;
  158.     struct stat         stbuf;
  159. #ifndef NO_REAL_NAMES
  160.     User               *Users, *u;
  161.     long                numUsers;
  162. #endif
  163.    
  164.     if ((wtmp = (argc != 1)))
  165.         strcpy (fname, WTMP_FILE);
  166.     else
  167.         strcpy (fname, UTMP_FILE);
  168.  
  169.     if (!(in = fopen (fname, READ_BINARY)))
  170.     {
  171.         fprintf (stderr, "%s: Could not open the file \"%s\".\n", argv[0], fname
  172.             );
  173.         exit (1);
  174.     }
  175.     
  176. #ifndef NO_REAL_NAMES
  177.  
  178.     result = OpenDataFile (&Users, &numUsers);
  179.     if (result < 0)
  180.     {
  181.         Thrash ();
  182.         result = OpenDataFile (&Users, &numUsers);
  183.     }
  184.  
  185. #ifndef ANSI
  186.     printf ("\nUser:     TTY: Where:             Time On:  Idle:  %s\n",
  187.         result==0 ? "Name:" : "");
  188. #else
  189.     /* clear screen, home cursor, and turn bold face on. */
  190.     printf ("\n\033[2J \033[H\033[1m");  
  191.     printf ("User:     TTY: Where:             Time On:  Idle:  %s\033[0m\n",
  192.         result==0 ? "Name:" : "");
  193. #endif
  194.  
  195. #else
  196.  
  197. #ifndef ANSI
  198.     printf ("\nUser:     TTY: Where:             Time On:  Idle:\n");
  199. #else
  200.     /* clear screen, home cursor, and turn bold face on. */
  201.     printf ("\n\033[2J \033[H\033[1m");  
  202.     printf ("User:     TTY: Where:             Time On:  Idle:\033[0m\n");
  203. #endif
  204.  
  205. #endif
  206.  
  207.     (void) time (&Now);
  208.  
  209.     while ((fread (&info, (long) sizeof (info), 1, in)) == 1L)
  210.     {
  211.         if (!*info.ut_name)
  212.             continue;
  213.  
  214.         lyne = (char *) line;
  215.         *lyne = '\0';
  216.         lyne = strnpcat (lyne, info.ut_name, 8L);
  217.         ADD_SPACE; ADD_SPACE;
  218.  
  219.         writeable = 0;
  220.  
  221.         strcpy (dname, "/dev/");
  222.         strcat (dname, info.ut_line);   /* form the full path of tty */
  223.         stat (dname, &stbuf);
  224.         writeable = stbuf.st_mode | PUBLIC_WRITE_PERM;
  225.  
  226.         if (*info.ut_line == 't')   /* is it in the form ttyxx? */
  227.         {
  228.             /* This assumes that after 'tty', there are only two other
  229.                characters. */
  230.             if (writeable)
  231.                 info.ut_line[5] = '*';
  232.                 
  233.             lyne = strnpcat (lyne, info.ut_line+3, 3L);
  234.         }
  235.         else
  236.             lyne = strnpcat (lyne, info.ut_line, 3L);
  237.             /* else its probably 'console' */
  238.             
  239.         ADD_SPACE; ADD_SPACE;
  240.         
  241.         lyne = strnpcat (lyne, info.ut_host, 16L);
  242.         ADD_SPACE; ADD_SPACE; ADD_SPACE;
  243.  
  244.         TimeOn (Now - info.ut_time, str);
  245.         lyne = strnpcat (lyne, str, 10L);
  246.  
  247.         IdleTime ((Now - stbuf.st_mtime), str);
  248.         lyne = strnpcat (lyne, str, 7L);
  249.  
  250. #ifndef NO_REAL_NAMES
  251.  
  252.         if (result == 0)
  253.         {
  254.             info.ut_name[8] = '\0';
  255.             u = (User *) bsearch (info.ut_name, Users, numUsers,
  256.                 (long) sizeof (User), strcmp);
  257.  
  258.             if (u)
  259.                 strcpy (str, u->name);
  260.             else
  261.                 strcpy (str, "(Unknown)");
  262.  
  263.             lyne = strnpcat (lyne, str, 28L);
  264.         }
  265.  
  266. #endif        
  267.         puts (line);    /* finally, dump the whole line to stdout */
  268.     }
  269.  
  270.     fputc ('\n', stdout);
  271.     fclose (in);
  272. }                               /* main */
  273.  
  274.  
  275.  
  276.  
  277. /* strnpcat: given two strings, this function will copy up to 'howMany'
  278.    characters to the destination.  If the source string is shorter than
  279.    'howMany' characters, it will pad the destinaton string with spaces
  280.    until it's length is howMany.  In addition, this will also return the
  281.    pointer where we left off.  It'd be silly to use strcat all the time,
  282.    since every time you called strcat it would have to loop through the
  283.    whole string just to find the end. */
  284.    
  285. char                 *strnpcat (dst, src, howMany)
  286.     register char      *dst, *src;
  287.     long                howMany;
  288. {
  289.     register int        echoSpaces;
  290.  
  291.     for (echoSpaces = 0; howMany > 0; dst++, src++, --howMany)
  292.     {
  293.         if (!*src)
  294.             echoSpaces = 1;
  295.         if (echoSpaces)
  296.             *dst = ' ';
  297.         else
  298.             *dst = *src;
  299.     }
  300.     *dst = '\0';
  301.     return (dst);
  302. }                               /* strnpcat */
  303.  
  304.  
  305.  
  306.  
  307.  
  308. int                 TimeOn (tyme, tstr)
  309.     long                tyme;
  310.     char               *tstr;
  311. {
  312.     long                hr, min, day;
  313.  
  314.     tyme /= 60L;
  315.     day = tyme / 1440L;
  316.     hr = (tyme - day * 1440L) / 60L;
  317.     min = (tyme - (day * 1440L) - (hr * 60L));
  318.  
  319.     sprintf (tstr, "%ld:%02ld", hr, min);
  320. }                               /* TimeOn */
  321.  
  322.  
  323.  
  324.  
  325.  
  326. int                 IdleTime (tyme, tstr)
  327.     long                tyme;
  328.     char               *tstr;
  329. {
  330.     long                hr, min, day;
  331.  
  332.     if (((tyme + 30L) / 60L) > 0L)
  333.     {
  334.         tyme /= 60L;
  335.         day = tyme / 1440L;
  336.         hr = (tyme - day * 1440L) / 60L;
  337.         min = (tyme - (day * 1440L) - (hr * 60L));
  338.         if (hr > 0L)
  339.             sprintf (tstr, "%ld:%02ld", hr, min);
  340.         else
  341.             sprintf (tstr, "%ld", min);
  342.     }
  343.     else *tstr = '\0';
  344. }                               /* IdleTime */
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351. #ifndef NO_REAL_NAMES
  352.  
  353. int                 OpenDataFile (Users, numUsers)
  354.     User              **Users;
  355.     long               *numUsers;
  356. {
  357.     FILE               *data;
  358.  
  359.     if (!(data = fopen (DFILE, READ_BINARY)))
  360.         return (-1);
  361.  
  362.     if (fread (numUsers, (long) sizeof (*numUsers), 1L, data) != 1L)
  363.         return (1);
  364.  
  365.     *Users = (User *) malloc ((long) sizeof (User) * (*numUsers));
  366.     if (!*Users)
  367.         return (2);
  368.  
  369.     if (fread (*Users, (long) sizeof (User) * (*numUsers), 1L, data) != 1L)
  370.         return (3);
  371.  
  372.     fclose (data);
  373.     return (0);                 /* noErr */
  374. }                               /* OpenDataFile */
  375.  
  376.  
  377.  
  378.  
  379.  
  380. /* Thrash: This creates the data file, which is needed so you can print
  381.    the real names of the users along with their logins.  This is
  382.    extremely useful on machines (like mine) whose logins are mostly
  383.    numbers or alphanumeric garbage. */
  384.    
  385. int                 Thrash ()
  386. {
  387.     FILE               *passwd, *out;
  388.     long                nUsers, i;
  389.     char                lyne[256];
  390.     User               *users;
  391.     int                 skippedcolons, j;
  392.     register char      *p, *q;
  393.  
  394.     passwd = fopen (PASSWORD_FILE, "r");
  395.     if (!passwd)
  396.         return (0);
  397.  
  398.     nUsers = 0L;
  399.     while (fgets (lyne, (int) sizeof (lyne), passwd))
  400.         nUsers++;
  401.  
  402.     rewind (passwd);
  403.  
  404.     users = (User *) calloc (nUsers, (long) sizeof (User));
  405.     if (!users)
  406.         return (0);
  407.  
  408.     for (i = 0; fgets (lyne, (int) sizeof (lyne), passwd); i++)
  409.     {
  410.         p = lyne;
  411.  
  412.         q = users[i].login;
  413.         while (*p != ':')       /* get login */
  414.             *q++ = *p++;
  415.         *q = '\0';              /* add terminating null */
  416.  
  417.         /* After getting the login, skip over the fields in between the
  418.            login and real name (encoded password, userid, groupid). */
  419.         skippedcolons = 0;
  420.         while (*p)
  421.         {
  422.             if (*p++ == ':')
  423.                 skippedcolons++;
  424.             if (skippedcolons >= COLONS_TO_SKIP)
  425.                 break;
  426.         }
  427.         if (skippedcolons < COLONS_TO_SKIP)
  428.             return (0);
  429.  
  430.         /* Copy stuff until we hit a colon or a comma.  I only want to
  431.            keep the names, and on our machine at least, after the real
  432.            name there are commas followed by junk like office address
  433.            and such ("Herbie H. Husker,104 Nebraska Union,2-3970").
  434.            This example would only copy "Herbie H. Husker" to the name
  435.            field. */
  436.         for (j = 0, q = users[i].name;
  437.              (*p && j < MAX_NAME_LEN && *p != ':' && *p != ',');
  438.              p++, q++, j++)
  439.         {
  440.             *q = *p;
  441.         }
  442.  
  443.         *q = '\0';    /* add null terminator */
  444.     }
  445.  
  446.     fclose (passwd);
  447.  
  448.     qsort (users, i, (long) sizeof (User), strcmp);
  449.  
  450.     out = fopen (DFILE, WRITE_BINARY);
  451.     if (!out)
  452.         return (0);
  453.     if (fwrite (&i, (long) sizeof (i), 1L, out) != 1L)
  454.         return (0);
  455.     if (fwrite (users, (long) sizeof (User) * i, 1L, out) != 1L)
  456.         return (0);
  457.     fclose (out);
  458.     return (1);
  459. }                               /* Thrash */
  460.  
  461. #endif
  462.  
  463. /* eof */
  464.  
  465.  
  466.