home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / A / PS / PROCPS-0.000 / PROCPS-0 / procps-0.97 / w2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-25  |  14.9 KB  |  655 lines

  1. /*
  2.  * w.c  v1.2
  3.  *
  4.  * An alternative "w" program for Linux.
  5.  * Shows users and their processes.
  6.  *
  7.  * Copyright (c) Dec 1993 Steve "Mr. Bassman" Bryant
  8.  *               bassman@hpbbi30.bbn.hp.com
  9.  *
  10.  * Info:
  11.  *    I starting writing as an improvement of the w program included
  12.  * with linux. The idea was to add in some extra functionality to the
  13.  * program, and see if I could fix a couple of bugs which seemed to
  14.  * occur.
  15.  *    The code was written from scratch by myself, and initial versions
  16.  * simply returned information from /etc/utmp and /proc. However, I since
  17.  * received a copy of the procps source, and have changed my code to call
  18.  * some of the routines there.
  19.  *    Thanks to the other writers, without whom I'd have probably still
  20.  * been stuck on one or two things !
  21.  *
  22.  *                        Mr. Bassman, 12/93
  23.  *
  24.  * Acknowledgments:
  25.  *
  26.  * The original version of w:
  27.  *    Copyright (c) 1993 Larry Greenfield  (greenfie@gauss.rutgers.edu)
  28.  *
  29.  * Uptime routine and w mods:
  30.  *    Michael K. Johnson  (johnsonm@stolaf.edu)
  31.  *
  32.  *
  33.  * Distribution:
  34.  *    This program is freely distributable under the terms of copyleft.
  35.  *    No warranty, no support, use at your own risk etc.
  36.  *
  37.  * Compilation:
  38.  *    gcc -o w sysinfo.c whattime.c w.c
  39.  *
  40.  * Usage:
  41.  *    w [-hus] [user]
  42.  *
  43.  * $Log: w2.c,v $
  44.  * Revision 1.4  1994/01/01  12:57:21  johnsonm
  45.  * Fixed log messages...
  46.  *
  47.  * Revision 1.3  1994/01/01  10:37:44  johnsonm
  48.  * More changes, still doesn't quite work.
  49.  *
  50.  * Revision 1.2  1994/01/01  08:47:02  johnsonm
  51.  * More changes, still doesn't quite work.
  52.  *
  53.  * Revision 1.1  1993/12/31  20:16:37  johnsonm
  54.  * Eliminated GCC warnings, took out unnecessary dead variables in
  55.  * fscanf, replacing them with *'d format qualifiers.
  56.  * Also added RCS stuff.
  57.  *
  58.  *
  59.  */
  60.  
  61. #include <stdio.h>
  62. #include <string.h>
  63. #include <sys/types.h>
  64. #include <sys/stat.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. #define ZOMBIE "<zombie>"
  76.  
  77.  
  78. void put_syntax();
  79. char *idletime();
  80. char *logintime();
  81.  
  82. static char rcsid[]="$Id: w2.c,v 1.4 1994/01/01 12:57:21 johnsonm Exp $";
  83.  
  84. int main (argc, argv)
  85.  
  86. int argc;
  87. char *argv[];
  88.  
  89. {
  90.     int header=TRUE, long_format=TRUE, ignore_user=FALSE;
  91.     int i, j, k;
  92.     struct utmp *utmp_rec;
  93.     struct stat stat_rec;
  94.     struct passwd *passwd_entry;
  95.     uid_t uid=0;
  96.     char username[9], tty[13], rhost[17], login_time[27];
  97.     char idle_time[7], what[256], pid[10];
  98.     char out_line[256], file_name[256];
  99.     char search_name[9];
  100.     int jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime;
  101.     char state, comm[256];
  102.     FILE *fp;
  103.  
  104.  
  105.     search_name[0] = '\0';
  106.  
  107. /*
  108.  * Process the command line
  109.  */
  110.     if (argc > 1)
  111.     {
  112.     if (argv[1][0] == '-')        /* 1st arg starts with '-' */
  113.     {
  114.         for (j = 1; j < strlen(argv[1]); j++)
  115.         {
  116.         switch (argv[1][j])
  117.         {
  118.             case 'h':
  119.             header = FALSE;
  120.             break;
  121.             case 's':
  122.             long_format = FALSE;
  123.             break;
  124.                     case 'l':
  125.                         long_format = TRUE;
  126.                         break;
  127.             case 'u':
  128.             ignore_user = TRUE;
  129.             break;
  130.             default:
  131.             fprintf(stderr, "w: unknown option: '%c'\n", argv[1][j]);
  132.             put_syntax();
  133.             break;
  134.         }
  135.         }
  136.  
  137.         if (argc > 2)
  138.         {
  139.         strncpy(search_name, argv[2], 8);
  140.  
  141.         if (argc > 3)
  142.         {
  143.             fprintf(stderr, "w: syntax error\n");
  144.             put_syntax();
  145.         }
  146.         }
  147.     }
  148.     else
  149.     {
  150.         strncpy(search_name, argv[1], 8);
  151.  
  152.         if (argc > 2)
  153.         {
  154.         fprintf(stderr, "w: syntax error\n");
  155.         put_syntax();
  156.         }
  157.     }
  158.     }
  159.  
  160.  
  161.  
  162. /*
  163.  * Check that /proc is actually there, or else we can't
  164.  * get all the information.
  165.  */
  166.  
  167.     if (chdir("/proc"))
  168.     {
  169.     fprintf(stderr, "w: fatal error: cannot access /proc\n");
  170.     perror(strerror(errno));
  171.     exit(-1);
  172.     }
  173.  
  174.  
  175.  
  176.  
  177. /*
  178.  * Print whatever headers
  179.  */
  180.     if (header == TRUE)
  181.     {
  182.     print_uptime();
  183.  
  184.     if (long_format == TRUE)
  185.         printf("User     tty    From             login@   idle  JCPU  PCPU  what\n");
  186.     else
  187.         printf("User     tty       idle  what\n");
  188.     }
  189.  
  190.  
  191.  
  192.  
  193. /*
  194.  * Process user information.
  195.  *
  196.  * getutent() opens the utmp file for us,
  197.  * and reads off the first record.
  198.  * If the file was already opened, it reads the
  199.  * subsequent record for us. See man page for more info.
  200.  */
  201.     while (utmp_rec = getutent())
  202.     {
  203.  
  204. /*
  205.  * Check we actually want to see this record.
  206.  * It must be a valid active user process,
  207.  * and match a specified search name.
  208.  */
  209.     if ( (utmp_rec->ut_type == USER_PROCESS)
  210.       && (strcmp(utmp_rec->ut_user, ""))
  211.       && ( (search_name[0] == '\0')
  212.         || ( (search_name[0] != '\0')
  213.         && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) )
  214.     {
  215.  
  216. /*
  217.  * Important note:
  218.  *
  219.  * Strings in the utmp struct are *NOT*
  220.  * necessarily null terminated :-(
  221.  * This only occurs if the string fills
  222.  * up its allocated space exactly.
  223.  * If it is shorter, it is terminated by a null.
  224.  * Because of this, strcpy breaks, and strncpy
  225.  * won't necessarily add a terminator, so we have to
  226.  * do it ourselves. The non-null-terminated string
  227.  * can also cause problems for printf type calls,
  228.  * otherwise I'd just read it direct !
  229.  */
  230.  
  231. /* Get the username */
  232.         for (i = 0; i < 8; i++)
  233.         {
  234.         username[i] = '\0';
  235.  
  236.         if (utmp_rec->ut_user[i] != '\0')
  237.             username[i] = utmp_rec->ut_user[i];
  238.         else
  239.             i = 8;
  240.         }
  241.  
  242.  
  243. /* If necessary, find out the uid of that user (from their passwd entry) */
  244.         if (ignore_user == FALSE)
  245.         {
  246.         passwd_entry = getpwnam(username);
  247.         uid = passwd_entry->pw_uid;
  248.         }
  249.  
  250.  
  251. /* Get the tty line */
  252.         for (i = 0; i < 6; i++)
  253.         {
  254.         tty[i] = '\0';
  255.  
  256.         if (utmp_rec->ut_line[i] != '\0')
  257.             tty[i] = utmp_rec->ut_line[i];
  258.         else
  259.             i = 6;
  260.         }
  261.  
  262.  
  263. /* Don't other getting info if it's not asked for */
  264.         if (long_format == TRUE)
  265.         {
  266.  
  267. /* Get the remote hostname */
  268.         for (i = 0; i < 15; i++)
  269.         {
  270.             rhost[i] = '\0';
  271.  
  272.             if (utmp_rec->ut_host[i] != '\0')
  273.             rhost[i] = utmp_rec->ut_host[i];
  274.             else
  275.             i = 15;
  276.         }
  277.  
  278.  
  279. /*
  280.  * Get the login time
  281.  * (Calculated by LG's routine, below)
  282.  */
  283.         strcpy(login_time, logintime(utmp_rec->ut_time));
  284.  
  285.         }
  286.  
  287.  
  288.  
  289. /*
  290.  * Get the idle time.
  291.  * (Calculated by LG's routine, below)
  292.  */
  293.         strcpy(idle_time, idletime(tty));
  294.  
  295.  
  296. /*
  297.  * That's all the info out of /etc/utmp.
  298.  * The rest is more difficult.
  299.  * We use the pid from utmp_rec->ut_pid to look in /proc for the info.
  300.  * NOTE: This is not necessarily the active pid, so we chase
  301.  * down the path of parent -> child pids until we find it,
  302.  * according to the information given in /proc/<pid>/stat.
  303.  */
  304.  
  305.         sprintf(pid, "%d", utmp_rec->ut_pid);
  306.  
  307.         what[0] = '\0';
  308.         strcpy(file_name, pid);
  309.         strcat(file_name, "/stat");
  310.         jcpu = 0;
  311.         pcpu = 0;
  312.  
  313.         if (fp = fopen(file_name, "r"))
  314.         {
  315.         if (ignore_user == FALSE)
  316.         {
  317. /*
  318.  * Use stat to find out who owns the file,
  319.  * and therefore the process.
  320.  */
  321.             stat(file_name, &stat_rec);
  322.             if (stat_rec.st_uid != uid)
  323.             strcpy(what, "-");
  324.         }
  325.  
  326.         while (what[0] == '\0')
  327.         {
  328. /*
  329.  * Check /proc/<pid>/stat to see if the process
  330.  * controlling the tty is the current one
  331.  */
  332.             fscanf(fp, "%d %s %c %*d %*d %*d %*d %d %*u %*u %*u %*u %*u %d %d %d %d",
  333.             &curr_pid, comm, &state, &tpgid,
  334.             &utime, &stime, &cutime, &cstime);
  335.  
  336.             fclose(fp);
  337.  
  338.             if (comm[0] == '\0')
  339.             strcpy(comm, "-");
  340.  
  341. /* Now get rid of the parentheses.  It doesn't work to put () around
  342.  * the %s in the fscanf above, unfortunately.
  343.  */
  344.             if (comm[0] == '(') /* this ought to be true*/
  345.             {
  346.                 k = strlen(comm);
  347.             k -= 2;
  348.             memmove(&comm[0], &comm[1], k);
  349.             comm[k] = '\0';
  350.             }
  351.  
  352. /*
  353.  * Calculate jcpu and pcpu.
  354.  * jcpu is the time used by all processes and their
  355.  * children, attached to the tty.
  356.  * pcpu is the time used by the current process
  357.  * (calculated once after the loop, using last obtained values).
  358.  *
  359.  * NOTE:
  360.  *     While pcpu gives correct readings, jcpu does not.
  361.  * I have tried two different methods (below).
  362.  *    One reads from cutime and cstime, the first time the loop is
  363.  * executed. These variables should give information about that process
  364.  * and all its children.
  365.  *    The other method adds everything up as we go. This second
  366.  * method is probably less accurate, as not all processes are checked,
  367.  * only those which have had control of the terminal, have given control
  368.  * to another process, and are waiting for it to return, plus the current
  369.  * controlling process; ie: it ignores background jobs. This should be
  370.  * reasonably accurate if the user isn't running any background jobs.
  371.  * However, both seem to return inaccurate information, and I don't
  372.  * know why !
  373.  *    The method used in the original program was to chase a
  374.  * linked list. I may patch a version of that in, in addition to my
  375.  * method. There are three possibilities why the results are wrong:
  376.  * one, I've misinterpreted (but pcpu is ok, and I've processed both the
  377.  * same); two, the info was wrong in the first place (is this possible ?);
  378.  * three, it's something else. If anyone knows, please mail me, and I'll
  379.  * patch it !!
  380.  *    One more thought: cutime and cstime will retain records of process
  381.  * time used by processes which no longer exist, so does this mean that they
  382.  * are more accurate than going through the process list, and adding up
  383.  * times from current processes ?
  384.  */
  385.             if (!jcpu)
  386.             jcpu = cutime + cstime;
  387. /*
  388.             jcpu += utime + stime;
  389. */
  390.  
  391.  
  392. /* Check for a zombie first... */
  393.             if (state == 'Z')
  394.             strcpy(what, ZOMBIE);
  395.             else if (curr_pid == tpgid)
  396.             {
  397. /*
  398.  * If it is the current process, read cmdline
  399.  * If that's empty, then the process is swapped out,
  400.  * or is a zombie, so we use the command given in stat
  401.  * which is in normal round brackets, ie: "()".
  402.  */
  403.             strcpy(file_name, pid);
  404.             strcat(file_name, "/cmdline");
  405.             if (fp = fopen(file_name, "r"))
  406.             {
  407.                 fgets(what, 255, fp);
  408.                 fclose(fp);
  409.             }
  410.  
  411.             if (what[0] != '\0')
  412.                 strcpy(what, comm);
  413.             }
  414.             else
  415.             {
  416. /* 
  417.  * Check out the next process
  418.  * If we can't open it, use info from this process,
  419.  * so we have to check out cmdline first.
  420.  * If we're not using "-u" then should we just
  421.  * say "-" instead of a command line ?
  422.  * If so, we should strpcy(what, "-"); when we fclose()
  423.  * in the if after the stat().
  424.  */
  425.             strcpy(file_name, pid);
  426.             strcat(file_name, "/cmdline");
  427.             if (fp = fopen(file_name, "r"))
  428.             {
  429.                 fgets(what, 255, fp);
  430.                 fclose(fp);
  431.             }
  432.  
  433.             if (what[0] != '\0')
  434.                 strcpy(what, comm);
  435.  
  436. /*
  437.  * Now we have something in the what variable,
  438.  * in case we can't open the next process.
  439.  */
  440.             sprintf(pid, "%d", tpgid);
  441.             strcpy(file_name, pid);
  442.             strcat(file_name, "/stat");
  443.  
  444.             fp = fopen(file_name, "r");
  445.  
  446.             if (fp && (ignore_user == FALSE))
  447.             {
  448. /*
  449.  * We don't necessarily go onto the next process,
  450.  * unless we are either ignoring who the effective
  451.  * user is, or it's the same uid
  452.  */
  453.                 stat(file_name, &stat_rec);
  454.                 if (stat_rec.st_uid != uid)
  455. /* The next process is not owned by this user; finish loop. */
  456.                 fclose(fp);
  457. /* strcpy(what, "-");  see comment above somewhere */
  458.                 else
  459.                 what[0] = '\0';
  460.             }
  461.             else if (fp)
  462. /* else we are ignoring uid's */
  463.                 what[0] = '\0';
  464.             }
  465.         }
  466.         }
  467.         else    /* Could not open first process for user */
  468.         strcpy(what, "?");
  469. /*
  470.  * There is a bug somewhere in my version of linux
  471.  * which means that utmp records are not cleaned
  472.  * up properly when users log out. However, we
  473.  * can detect this, by the users first process
  474.  * not being there when we look in /proc.
  475.  */
  476.  
  477.  
  478. /*
  479.  * Don't output a line for "dead" users.
  480.  * This gets round a bug which doesn't update utmp/wtmp
  481.  * when users log out.
  482.  */
  483.         if (what[0] != '?')
  484.         {
  485.  
  486. /*
  487.  * Format the line for output
  488.  */
  489.         if (long_format == TRUE)
  490.         {
  491.             pcpu = utime + stime;
  492.             jcpu /= 100;
  493.             pcpu /= 100;
  494.  
  495.  
  496.             sprintf(out_line, "%-9.8s%-7.6s%-15.16s%8.8s%7.6s",
  497.                 username, tty, rhost, login_time, idle_time);
  498.  
  499.             if (!jcpu)
  500.             sprintf(out_line, "%s      ", out_line);
  501.             else if (jcpu/60)
  502.             sprintf(out_line, "%s%3d:%02d", out_line,
  503.                 jcpu/60, jcpu%60);
  504.             else
  505.             sprintf(out_line, "%s    %2d", out_line, jcpu);
  506.  
  507.             if (!pcpu)
  508.             sprintf(out_line, "%s      ", out_line);
  509.                     else if (pcpu/60)
  510.             sprintf(out_line, "%s%3d:%02d", out_line,
  511.                 pcpu/60, pcpu%60);
  512.             else
  513.             sprintf(out_line, "%s    %2d", out_line, pcpu);
  514.  
  515.             strcat(out_line, "  ");
  516.             strcat(out_line, what);
  517.         }
  518.         else
  519.         {
  520.             sprintf(out_line, "%-9.8s%-7.6s%7.6s  %s",
  521.             username, tty, idle_time, what);
  522.         }
  523.  
  524.  
  525.         out_line[79] = '\0';        /* So we aren't too long */
  526.         printf("%s\n", out_line);
  527.         }
  528.     }
  529.     }
  530.     return(0);
  531. }
  532.  
  533.  
  534.  
  535. /*
  536.  * put_syntax()
  537.  *
  538.  * Routine to print the correct syntax to call this program,
  539.  * and then exit out appropriately
  540.  */
  541. void put_syntax()
  542. {
  543.     fprintf(stderr, "usage: w [-hus] [user]\n");
  544.     exit(-1);
  545. }
  546.  
  547.  
  548.  
  549. /*
  550.  * idletime()
  551.  *
  552.  * Routine which returns a string containing
  553.  * the idle time of a given user.
  554.  *
  555.  * This routine was lifted from the original w program
  556.  * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
  557.  * Copyright (c) 1993 Larry Greenfield
  558.  *
  559.  */
  560. char *idletime(tty)
  561.  
  562. char *tty;
  563.  
  564. {
  565.     struct stat terminfo;
  566.     long idle;
  567.     char ttytmp[40];
  568.     static char give[20];
  569.     time_t curtime;
  570.  
  571.     curtime = time(NULL);
  572.     sprintf(ttytmp, "/dev/%s", tty);
  573.     stat(ttytmp, &terminfo);
  574.     idle = (curtime - terminfo.st_atime);
  575.  
  576.     if (idle >= (60 * 60))        /* more than an hour */
  577.     {
  578.     if (idle >= (60 * 60 * 48))    /* more than two days */
  579.         sprintf(give, "%2ddays", idle / (60 * 60 * 24));
  580.     else
  581.         sprintf(give, " %2d:%s%d", idle / (60 * 60), 
  582.         (idle / 60) % 60 < 10 ? "0" : "", (idle / 60) % 60);
  583.     }
  584.     else
  585.     {
  586.     if (idle / 60)
  587.         sprintf(give, "%6d", idle / 60);
  588.     else
  589.         give[0]=0;
  590.     }
  591.  
  592.     return give;
  593. }
  594.  
  595.  
  596.  
  597. /*
  598.  * logintime()
  599.  *
  600.  * Returns the time given in a suitable format
  601.  *
  602.  * This routine was lifted from the original w program
  603.  * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
  604.  * Copyright (c) 1993 Larry Greenfield
  605.  *
  606.  */
  607. char *logintime(ut_time)
  608.  
  609. time_t ut_time;
  610.  
  611. {
  612.     time_t curtime;
  613.     struct tm *logintime, *curtm;
  614.     int hour, am, curday, logday;
  615.     static char give[20];
  616.     static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  617.     static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
  618.                 "Aug", "Sep", "Oct", "Nov", "Dec" };
  619.  
  620.     curtime = time(NULL);
  621.     curtm = localtime(&curtime);
  622.     curday = curtm->tm_yday;
  623.     logintime = localtime(&ut_time);
  624.     hour = logintime->tm_hour;
  625.     logday = logintime->tm_yday;
  626.     am = (hour < 12);
  627.  
  628.     if (!am)
  629.     hour -= 12;
  630.  
  631.     if (hour == 0)
  632.     hour = 12;
  633.  
  634. /*
  635.  * This is a newer behavior: it waits 12 hours and the next day, and then
  636.  * goes to the 2nd time format. This should reduce confusion.
  637.  * It then waits only 6 days (not till the last moment) to go the last
  638.  * time format.
  639.  */
  640.     if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday))
  641.     {
  642.     if (curtime > (ut_time + (60 * 60 * 24 * 6)))
  643.         sprintf(give, "%2d%3s%2d", logintime->tm_mday,
  644.         month[logintime->tm_mon], (logintime->tm_year % 100));
  645.     else
  646.         sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday],
  647.         hour, am ? "am" : "pm");
  648.     }
  649.     else
  650.     sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm");
  651.  
  652.     return give;
  653. }
  654.  
  655.