home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / a / ps / w.bas / w / w.bassman.c < prev   
Encoding:
C/C++ Source or Header  |  1995-01-08  |  15.5 KB  |  732 lines

  1. /*
  2.  * w.c  v1.4
  3.  *
  4.  * An alternative "w" program for Linux.
  5.  * Shows users and their processes.
  6.  *
  7.  * Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant
  8.  *         bassman@hpbbi30.bbn.hp.com (Old address)
  9.  *        bassman@muttley.soc.staffs.ac.uk
  10.  *
  11.  * Info:
  12.  *    I starting writing as an improvement of the w program included
  13.  * with linux. The idea was to add in some extra functionality to the
  14.  * program, and see if I could fix a couple of bugs which seemed to
  15.  * occur.
  16.  *                        Mr. Bassman, 10/94
  17.  *
  18.  * Acknowledgments:
  19.  *
  20.  * The original version of w:
  21.  *    Copyright (c) 1993 Larry Greenfield  (greenfie@gauss.rutgers.edu)
  22.  *
  23.  * Uptime routine and w mods:
  24.  *    Michael K. Johnson  (johnsonm@stolaf.edu)
  25.  *
  26.  *
  27.  * Distribution:
  28.  *    This program is freely distributable under the terms of copyleft.
  29.  *    No warranty, no support, use at your own risk etc.
  30.  *
  31.  * Compilation:
  32.  *    gcc -O -o w sysinfo.c whattime.c w.c
  33.  *
  34.  * Usage:
  35.  *    w [-hfusd] [user]
  36.  *
  37.  *
  38.  * $Log: w2.c,v $
  39.  * Revision 1.5  1994/10/26  17:57:35  bassman
  40.  * Loads of stuff - see comments.
  41.  *
  42.  * Revision 1.4  1994/01/01  12:57:21  johnsonm
  43.  * Added RCS, and some other fixes.
  44.  *
  45.  * Revision history:
  46.  * Jan 01, 1994 (mkj):    Eliminated GCC warnings, took out unnecessary
  47.  *            dead variables in fscanf, replacing them with
  48.  *            *'d format qualifiers.  Also added RCS stuff.
  49.  * Oct 26, 1994 (bass):    Tidied up the code, fixed bug involving corrupt
  50.  *            utmp records.  Added switch for From field;
  51.  *            default is compile-time set.  Added -d option
  52.  *            as a remnant from BSD 'w'.  Fixed bug so it now
  53.  *            behaves if the first process on a tty isn't owned
  54.  *            by the person first logged in on that tty, and
  55.  *            also detects su'd users.  Changed the tty format
  56.  *            to the short one.
  57.  */
  58.  
  59. #include <stdio.h>
  60. #include <string.h>
  61. #include <stdlib.h>
  62. #include <sys/types.h>
  63. #include <sys/stat.h>
  64. #include <sys/ioctl.h>
  65. #include <time.h>
  66. #include <utmp.h>
  67. #include <unistd.h>
  68. #include <errno.h>
  69. #include <pwd.h>
  70. #include "whattime.h"
  71.  
  72.  
  73. #define TRUE        1
  74. #define FALSE        0
  75. /*
  76.  * Default setting for whether to have a From field.  The -f switch
  77.  * toggles this - if the default is to have it, using -f will turn
  78.  * it off; if the default is not to have it, the -f switch will put
  79.  * it in.  Possible values are TRUE (to have the field by default),
  80.  * and FALSE.
  81.  */
  82. #define DEFAULT_FROM    TRUE
  83. #define ZOMBIE        "<zombie>"
  84.  
  85.  
  86. void put_syntax();
  87. char *idletime();
  88. char *logintime();
  89.  
  90. static char rcsid[]="$Id: w.c,v 1.4 1994/10/26 17:57:35 bassman Exp $";
  91.  
  92.  
  93. void main (argc, argv)
  94.  
  95. int argc;
  96. char *argv[];
  97.  
  98. {
  99.     int header=TRUE, long_format=TRUE, ignore_user=FALSE,
  100.     from_switch=DEFAULT_FROM, show_pid=FALSE, line_length;
  101.     int i, j;
  102.     struct utmp *utmp_rec;
  103.     struct stat stat_rec;
  104.     struct passwd *passwd_entry;
  105.     uid_t uid;
  106.     char username[9], tty[13], rhost[17], login_time[27];
  107.     char idle_time[7], what[1024], pid[10];
  108.     char out_line[1024], file_name[256];
  109.     char search_name[9];
  110.     int  jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime;
  111.     char /*ch,*/ state, comm[1024], *columns_ptr;
  112.     FILE *fp;
  113.  
  114.  
  115.     search_name[0] = '\0';
  116.  
  117.  
  118.     /*
  119.      * Process the command line
  120.      */
  121.     if (argc > 1)
  122.     {
  123.     /*
  124.      * Args that start with '-'
  125.      */
  126.     for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++)
  127.     {
  128.         for (j = 1; argv[i][j] != '\0'; j++)
  129.         {
  130.         switch (argv[i][j])
  131.         {
  132.             case 'h':
  133.             header = FALSE;
  134.             break;
  135.             case 's':
  136.             long_format = FALSE;
  137.             break;
  138.             case 'u':
  139.             ignore_user = TRUE;
  140.             break;
  141.             case 'd':
  142.             show_pid = TRUE;
  143.             break;
  144.             case 'f':
  145.             if (DEFAULT_FROM == TRUE)
  146.                 from_switch = FALSE;
  147.             else
  148.                 from_switch = TRUE;
  149.             break;
  150.             default:
  151.             fprintf (stderr, "w: unknown option: '%c'\n",
  152.                 argv[i][j]);
  153.             put_syntax ();
  154.             break;
  155.         }
  156.         }
  157.     }
  158.  
  159.  
  160.     /*
  161.      * Check for arg not starting with '-' (ie: username)
  162.      */
  163.     if (argc > i)
  164.     {
  165.         strncpy (search_name, argv[i], 8);
  166.         search_name[8] = '\0';
  167.         i ++;
  168.  
  169.         if (argc > i)
  170.         {
  171.         fprintf (stderr, "w: syntax error\n");
  172.         put_syntax ();
  173.         }
  174.     }
  175.     }
  176.  
  177.  
  178.  
  179.     /*
  180.      * Check that /proc is actually there, or else we can't
  181.      * get all the information.
  182.      */
  183.     if (chdir ("/proc"))
  184.     {
  185.     fprintf (stderr, "w: fatal error: cannot access /proc\n");
  186.     perror (strerror(errno));
  187.     exit (-1);
  188.     }
  189.  
  190.  
  191.  
  192.     /*
  193.      * Find out our screen width from $COLUMNS
  194.      */
  195.     columns_ptr = getenv ("COLUMNS");
  196.     if (columns_ptr == NULL)
  197.     {
  198.     struct winsize window;
  199.  
  200.     /*
  201.      * Try getting it directly
  202.      */
  203.     if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0))
  204.         line_length = window.ws_col;
  205.     else
  206.         line_length = 80;        /* Default length assumed */
  207.     }
  208.     else
  209.     line_length = atoi (columns_ptr);
  210.  
  211.     /*
  212.      * Maybe we should check whether there is enough space on
  213.      * the lines for the options selected...
  214.      */
  215.     if (line_length < 60)
  216.     long_format = FALSE;
  217.  
  218.     line_length --;
  219.  
  220.  
  221.     /*
  222.      * Print whatever headers
  223.      */
  224.     if (header == TRUE)
  225.     {
  226.     /*
  227.      * uptime: from MKJ's uptime routine,
  228.      * found in whattime.c
  229.      */
  230.     print_uptime();
  231.  
  232.  
  233.     /*
  234.      * Print relevant header bits
  235.      */
  236.     printf ("User     tty  ");
  237.  
  238.     if (long_format == TRUE)
  239.     {
  240.         if (from_switch == TRUE)
  241.         printf ("From             ");
  242.  
  243.         printf (" login@   idle  JCPU  PCPU  ");
  244.  
  245.         if (show_pid == TRUE)
  246.         printf (" PID  ");
  247.  
  248.         printf ("what\n");
  249.     }
  250.     else
  251.     {
  252.         printf (" idle  ");
  253.  
  254.         if (show_pid == TRUE)
  255.         printf (" PID  ");
  256.  
  257.         printf ("what\n");
  258.     }
  259.     }
  260.  
  261.  
  262.  
  263.  
  264.     /*
  265.      * Process user information.
  266.      */
  267.     while ((utmp_rec = getutent()))
  268.     {
  269.     /*
  270.      * Check we actually want to see this record.
  271.      * It must be a valid active user process,
  272.      * and match a specified search name.
  273.      */
  274.     if ( (utmp_rec->ut_type == USER_PROCESS)
  275.       && (strcmp(utmp_rec->ut_user, ""))
  276.       && ( (search_name[0] == '\0')
  277.         || ( (search_name[0] != '\0')
  278.         && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) )
  279.     {
  280.         /*
  281.          * Get the username
  282.          */
  283.         strncpy (username, utmp_rec->ut_user, 8);
  284.         username[8] = '\0';        /* Set end terminator */
  285.  
  286.  
  287.         /*
  288.          * Find out the uid of that user (from their
  289.          * passwd entry)
  290.          */
  291.         passwd_entry = getpwnam (username);
  292.         uid = passwd_entry->pw_uid;
  293.  
  294.  
  295.         /*
  296.          * Get (and clean up) the tty line
  297.          */
  298.         for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < 6); i ++)
  299.         tty[i] = utmp_rec->ut_line[i];
  300.  
  301.         utmp_rec->ut_line[i] = '\0';
  302.         tty[i] = '\0';
  303.  
  304.  
  305.         /*
  306.          * Don't bother getting info if it's not asked for
  307.          */
  308.         if (long_format == TRUE)
  309.         {
  310.  
  311.         /*
  312.          * Get the remote hostname; this can be up to 16 chars,
  313.          * but if any chars are invalid (ie: [^a-zA-Z0-9\.])
  314.          * then the char is changed to a string terminator.
  315.          */
  316.         if (from_switch == TRUE)
  317.         {
  318.             strncpy (rhost, utmp_rec->ut_host, 16);
  319.             rhost[16] = '\0';
  320.  
  321.             for (i = 0; rhost[i] != '\0'; i ++)
  322.             {
  323.             if (((rhost[i] < 'a') || (rhost[i] > 'z'))
  324.              && ((rhost[i] < 'A') || (rhost[i] > 'Z'))
  325.              && ((rhost[i] < '0') || (rhost[i] > '9'))
  326.              &&  (rhost[i] != '.') )
  327.             {
  328.                 rhost[i] = '\0';
  329.                 break;
  330.             }
  331.             }
  332.         }
  333.  
  334.  
  335.         /*
  336.          * Get the login time
  337.          * (Calculated by LG's routine, below)
  338.          */
  339.         strcpy (login_time, logintime(utmp_rec->ut_time));
  340.         }
  341.  
  342.  
  343.  
  344.         /*
  345.          * Get the idle time.
  346.          * (Calculated by LG's routine, below)
  347.          */
  348.         strcpy (idle_time, idletime (tty));
  349.  
  350.  
  351.  
  352.         /*
  353.          * That's all the info out of /etc/utmp.
  354.          * The rest is more difficult.  We use the pid from
  355.          * utmp_rec->ut_pid to look in /proc for the info.
  356.          * NOTE: This is not necessarily the active pid, so we chase
  357.          * down the path of parent -> child pids until we find it,
  358.          * according to the information given in /proc/<pid>/stat.
  359.          */
  360.  
  361.         sprintf (pid, "%d", utmp_rec->ut_pid);
  362.  
  363.         what[0] = '\0';
  364.         strcpy (file_name, pid);
  365.         strcat (file_name, "/stat");
  366.         jcpu = 0;
  367.         pcpu = 0;
  368.  
  369.         if ((fp = fopen(file_name, "r")))
  370.         {
  371.         while (what[0] == '\0')
  372.         {
  373.             /*
  374.              * Check /proc/<pid>/stat to see if the process
  375.              * controlling the tty is the current one
  376.              */
  377.             fscanf (fp, "%d %s %c %*d %*d %*d %*d %d "
  378.             "%*u %*u %*u %*u %*u %d %d %d %d",
  379.             &curr_pid, comm, &state, &tpgid,
  380.             &utime, &stime, &cutime, &cstime);
  381.  
  382.             fclose (fp);
  383.  
  384.             if (comm[0] == '\0')
  385.             strcpy (comm, "-");
  386.  
  387.             /*
  388.              * Calculate jcpu and pcpu.
  389.              * JCPU is the time used by all processes and their
  390.              * children, attached to the tty.
  391.              * PCPU is the time used by the current process
  392.              * (calculated once after the loop, using last
  393.              * obtained values).
  394.              */
  395.             if (!jcpu)
  396.             jcpu = cutime + cstime;
  397.  
  398.             /*
  399.              * Check for a zombie first...
  400.              */
  401.             if (state == 'Z')
  402.             strcpy (what, ZOMBIE);
  403.             else if (curr_pid == tpgid)
  404.             {
  405.             /*
  406.              * If it is the current process, read cmdline
  407.              * If that's empty, then the process is swapped out,
  408.              * or is a zombie, so we use the command given in stat
  409.              * which is in normal round brackets, ie: "()".
  410.              */
  411.             strcpy (file_name, pid);
  412.             strcat (file_name, "/cmdline");
  413.             if ((fp = fopen(file_name, "r")))
  414.             {
  415.                 i = 0;
  416.                 j = fgetc (fp);
  417.                 while ((j != EOF) && (i < 256))
  418.                 {
  419.                 if (j == '\0')
  420.                     j = ' ';
  421.  
  422.                 what[i] = j;
  423.                 i++;
  424.                 j = fgetc (fp);
  425.                 }
  426.                 what[i] = '\0';
  427.                 fclose (fp);
  428.             }
  429.  
  430.             if (what[0] == '\0')
  431.                 strcpy (what, comm);
  432.             }
  433.             else
  434.             {
  435.             /* 
  436.              * Check out the next process
  437.              * If we can't open it, use info from this process,
  438.              * so we have to check out cmdline first.
  439.              *
  440.              * If we're not using "-u" then should we just
  441.              * say "-" (or "-su") instead of a command line ?
  442.              * If so, we should strpcy(what, "-"); when we
  443.              * fclose() in the if after the stat() below.
  444.              */
  445.             strcpy (file_name, pid);
  446.             strcat (file_name, "/cmdline");
  447.  
  448.             if ((fp = fopen (file_name, "r")))
  449.             {
  450.                 i = 0;
  451.                 j = fgetc (fp);
  452.                 while ((j != EOF) && (i < 256))
  453.                 {
  454.                 if (j == '\0')
  455.                     j = ' ';
  456.  
  457.                 what[i] = j;
  458.                 i++;
  459.                 j = fgetc (fp);
  460.                 }
  461.                 what[i] = '\0';
  462.                 fclose (fp);
  463.             }
  464.  
  465.             if (what[0] == '\0')
  466.                 strcpy (what, comm);
  467.  
  468.             /*
  469.              * Now we have something in the what variable,
  470.              * in case we can't open the next process.
  471.              */
  472.             sprintf (pid, "%d", tpgid);
  473.             strcpy (file_name, pid);
  474.             strcat (file_name, "/stat");
  475.  
  476.             fp = fopen (file_name, "r");
  477.  
  478.             if (fp && (ignore_user == FALSE))
  479.             {
  480.                 /*
  481.                  * We don't necessarily go onto the next process,
  482.                  * unless we are either ignoring who the effective
  483.                  * user is, or it's the same uid
  484.                  */
  485.                 stat (file_name, &stat_rec);
  486.  
  487.                 /*
  488.                  * If the next process is not owned by this
  489.                  * user finish the loop.
  490.                  */
  491.                 if (stat_rec.st_uid != uid)
  492.                 {
  493.                 fclose (fp);
  494.  
  495.                 strcpy (what, "-su");
  496.                 /*
  497.                  * See comment above somewhere;  I've used
  498.                  * "-su" here, as the next process is owned
  499.                  * by someone else; this is generally
  500.                  * because the user has done an "su" which
  501.                  * then exec'd something else.
  502.                  */
  503.                 }
  504.                 else
  505.                 what[0] = '\0';
  506.             }
  507.             else if (fp)    /* else we are ignoring uid's */
  508.                 what[0] = '\0';
  509.             }
  510.         }
  511.         }
  512.         else    /* Could not open first process for user */
  513.         strcpy (what, "?");
  514.  
  515.  
  516.         /*
  517.          * There is a bug somewhere in my version of linux
  518.          * which means that utmp records are not cleaned
  519.          * up properly when users log out. However, we
  520.          * can detect this, by the users first process
  521.          * not being there when we look in /proc.
  522.          */
  523.  
  524.  
  525.         /*
  526.          * Don't output a line for "dead" users.
  527.          * This gets round a bug which doesn't update utmp/wtmp
  528.          * when users log out.
  529.          */
  530.         if (what[0] != '?')
  531.         {
  532.         /*
  533.          * Remove the letters 'tty' from the tty id
  534.          */
  535.         if (!strncmp (tty, "tty", 3))
  536.         {
  537.             for (i = 3; tty[i - 1] != '\0'; i ++)
  538.             tty[i - 3] = tty[i];
  539.         }
  540.  
  541.  
  542.         /*
  543.          * Common fields
  544.          */
  545.         sprintf (out_line, "%-9.8s%3s ", username, tty);
  546.  
  547.  
  548.         /*
  549.          * Format the line for output
  550.          */
  551.         if (long_format == TRUE)
  552.         {
  553.             /*
  554.              * Calculate CPU usage
  555.              */
  556.             pcpu = utime + stime;
  557.             jcpu /= 100;
  558.             pcpu /= 100;
  559.  
  560.             if (from_switch == TRUE)
  561.             sprintf (out_line, "%s %-16.15s", out_line, rhost);
  562.  
  563.             sprintf (out_line, "%s%8.8s ", out_line, login_time);
  564.  
  565.         }
  566.  
  567.         sprintf (out_line, "%s%6s", out_line, idle_time);
  568.  
  569.  
  570.         if (long_format == TRUE)
  571.         {
  572.             if (!jcpu)
  573.             strcat (out_line, "      ");
  574.             else if (jcpu/60)
  575.             sprintf (out_line, "%s%3d:%02d", out_line,
  576.                 jcpu/60, jcpu%60);
  577.             else
  578.             sprintf (out_line, "%s    %2d", out_line, jcpu);
  579.  
  580.             if (!pcpu)
  581.             strcat (out_line, "      ");
  582.                     else if (pcpu/60)
  583.             sprintf (out_line, "%s%3d:%02d", out_line,
  584.                 pcpu/60, pcpu%60);
  585.             else
  586.             sprintf (out_line, "%s    %2d", out_line, pcpu);
  587.         }
  588.  
  589.         if (show_pid == TRUE)
  590.             sprintf (out_line, "%s %5.5s", out_line, pid);
  591.  
  592.  
  593.         strcat (out_line, "  ");
  594.         strcat (out_line, what);
  595.  
  596.  
  597.         /*
  598.          * Try not to exceed the line length
  599.          */
  600.         out_line[line_length] = '\0';
  601.  
  602.         printf ("%s\n", out_line);
  603.         }
  604.     }
  605.     }
  606. }
  607.  
  608.  
  609.  
  610. /*
  611.  * put_syntax()
  612.  *
  613.  * Routine to print the correct syntax to call this program,
  614.  * and then exit out appropriately
  615.  */
  616. void put_syntax ()
  617. {
  618.     fprintf (stderr, "usage: w [-hfsud] [user]\n");
  619.     exit (-1);
  620. }
  621.  
  622.  
  623.  
  624. /*
  625.  * idletime()
  626.  *
  627.  * Routine which returns a string containing
  628.  * the idle time of a given user.
  629.  *
  630.  * This routine was lifted from the original w program
  631.  * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
  632.  * Copyright (c) 1993 Larry Greenfield
  633.  *
  634.  */
  635. char *idletime (tty)
  636.  
  637. char *tty;
  638.  
  639. {
  640.     struct stat terminfo;
  641.     unsigned long idle;
  642.     char ttytmp[40];
  643.     static char give[20];
  644.     time_t curtime;
  645.  
  646.     curtime = time (NULL);
  647.  
  648.     sprintf (ttytmp, "/dev/%s", tty);
  649.     stat (ttytmp, &terminfo);
  650.     idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime;
  651.  
  652.     if (idle >= (60 * 60))        /* more than an hour */
  653.     {
  654.     if (idle >= (60 * 60 * 48))    /* more than two days */
  655.         sprintf (give, "%2ludays", idle / (60 * 60 * 24));
  656.     else
  657.         sprintf (give, " %2lu:%02u", idle / (60 * 60), 
  658.           (unsigned) ((idle / 60) % 60));
  659.     }
  660.     else
  661.     {
  662.     if (idle / 60)
  663.         sprintf (give, "%6lu", idle / 60);
  664.     else
  665.         give[0]=0;
  666.     }
  667.  
  668.     return give;
  669. }
  670.  
  671.  
  672.  
  673. /*
  674.  * logintime()
  675.  *
  676.  * Returns the time given in a suitable format
  677.  *
  678.  * This routine was lifted from the original w program
  679.  * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
  680.  * Copyright (c) 1993 Larry Greenfield
  681.  *
  682.  */
  683. char *logintime(ut_time)
  684.  
  685. time_t ut_time;
  686.  
  687. {
  688.     time_t curtime;
  689.     struct tm *logintime, *curtm;
  690.     int hour, am, curday, logday;
  691.     static char give[20];
  692.     static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
  693.                 "Sat" };
  694.     static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
  695.                 "Aug", "Sep", "Oct", "Nov", "Dec" };
  696.  
  697.     curtime = time(NULL);
  698.     curtm = localtime(&curtime);
  699.     curday = curtm->tm_yday;
  700.     logintime = localtime(&ut_time);
  701.     hour = logintime->tm_hour;
  702.     logday = logintime->tm_yday;
  703.     am = (hour < 12);
  704.  
  705.     if (!am)
  706.     hour -= 12;
  707.  
  708.     if (hour == 0)
  709.     hour = 12;
  710.  
  711.     /*
  712.      * This is a newer behavior: it waits 12 hours and the next day, and then
  713.      * goes to the 2nd time format. This should reduce confusion.
  714.      * It then waits only 6 days (not till the last moment) to go the last
  715.      * time format.
  716.      */
  717.     if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday))
  718.     {
  719.     if (curtime > (ut_time + (60 * 60 * 24 * 6)))
  720.         sprintf(give, "%2d%3s%2d", logintime->tm_mday,
  721.         month[logintime->tm_mon], (logintime->tm_year % 100));
  722.     else
  723.         sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday],
  724.         hour, am ? "am" : "pm");
  725.     }
  726.     else
  727.     sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm");
  728.  
  729.     return give;
  730. }
  731.  
  732.