Logo Search packages:      
Sourcecode: w-bassman version File versions  Download package

w.bassman.c

/*
 * w.c  v1.4
 *
 * An alternative "w" program for Linux.
 * Shows users and their processes.
 *
 * Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant
 *          bassman@hpbbi30.bbn.hp.com (Old address)
 *          bassman@muttley.soc.staffs.ac.uk
 *
 * Info:
 *    I starting writing as an improvement of the w program included
 * with linux. The idea was to add in some extra functionality to the
 * program, and see if I could fix a couple of bugs which seemed to
 * occur.
 *                                  Mr. Bassman, 10/94
 *
 * Acknowledgments:
 *
 * The original version of w:
 *    Copyright (c) 1993 Larry Greenfield  (greenfie@gauss.rutgers.edu)
 *
 * Uptime routine and w mods:
 *    Michael K. Johnson  (johnsonm@stolaf.edu)
 *
 *
 * Distribution:
 *    This program is freely distributable under the terms of copyleft.
 *    No warranty, no support, use at your own risk etc.
 *
 * Compilation:
 *    gcc -O -o w sysinfo.c whattime.c w.c
 *
 * Usage:
 *    w [-hfusd] [user]
 *
 *
 * $Log: w2.c,v $
 * Revision 1.5  1994/10/26  17:57:35  bassman
 * Loads of stuff - see comments.
 *
 * Revision 1.4  1994/01/01  12:57:21  johnsonm
 * Added RCS, and some other fixes.
 *
 * Revision history:
 * Jan 01, 1994 (mkj):  Eliminated GCC warnings, took out unnecessary
 *                dead variables in fscanf, replacing them with
 *                *'d format qualifiers.  Also added RCS stuff.
 * Oct 26, 1994 (bass): Tidied up the code, fixed bug involving corrupt
 *                utmp records.  Added switch for From field;
 *                default is compile-time set.  Added -d option
 *                as a remnant from BSD 'w'.  Fixed bug so it now
 *                behaves if the first process on a tty isn't owned
 *                by the person first logged in on that tty, and
 *                also detects su'd users.  Changed the tty format
 *                to the short one.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <utmp.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include "proc/whattime.h"


#define TRUE            1
#define FALSE           0
/*
 * Default setting for whether to have a From field.  The -f switch
 * toggles this - if the default is to have it, using -f will turn
 * it off; if the default is not to have it, the -f switch will put
 * it in.  Possible values are TRUE (to have the field by default),
 * and FALSE.
 */
#define DEFAULT_FROM    TRUE
#define ZOMBIE          "<zombie>"


void put_syntax();
char *idletime();
char *logintime();

static char rcsid[]="$Id: w.c,v 1.4 1994/10/26 17:57:35 bassman Exp $";


int main (argc, argv)

int argc;
char *argv[];

{
    int header=TRUE, long_format=TRUE, ignore_user=TRUE,
      from_switch=DEFAULT_FROM, show_pid=FALSE, line_length;
    int i, j;
    struct utmp *utmp_rec;
    struct stat stat_rec;
    struct passwd *passwd_entry;
    uid_t uid;
    char username[9], tty[UT_LINESIZE], rhost[17], login_time[27];
    char idle_time[7], what[1024], pid[10];
    char out_line[1024], file_name[256];
    char search_name[9];
    int  jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime;
    char /*ch,*/ state, comm[1024], *columns_ptr;
    FILE *fp;


    search_name[0] = '\0';


    /*
     * Process the command line
     */
    if (argc > 1)
    {
      /*
       * Args that start with '-'
       */
      for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++)
      {
          for (j = 1; argv[i][j] != '\0'; j++)
          {
            switch (argv[i][j])
            {
                case 'h':
                  header = FALSE;
                  break;
                case 's':
                  long_format = FALSE;
                  break;
                case 'u':
                  ignore_user = FALSE;
                  break;
                case 'd':
                  show_pid = TRUE;
                  break;
                case 'f':
                  if (DEFAULT_FROM == TRUE)
                      from_switch = FALSE;
                  else
                      from_switch = TRUE;
                  break;
                default:
                  fprintf (stderr, "w: unknown option: '%c'\n",
                      argv[i][j]);
                  put_syntax ();
                  break;
            }
          }
      }


      /*
       * Check for arg not starting with '-' (ie: username)
       */
      if (argc > i)
      {
          strncpy (search_name, argv[i], 8);
          search_name[8] = '\0';
          i ++;

          if (argc > i)
          {
            fprintf (stderr, "w: syntax error\n");
            put_syntax ();
          }
      }
    }



    /*
     * Check that /proc is actually there, or else we can't
     * get all the information.
     */
    if (chdir ("/proc"))
    {
      fprintf (stderr, "w: fatal error: cannot access /proc\n");
      perror (strerror(errno));
      exit (-1);
    }



    /*
     * Find out our screen width from $COLUMNS
     */
    columns_ptr = getenv ("COLUMNS");
    if (columns_ptr == NULL)
    {
      struct winsize window;

      /*
       * Try getting it directly
       */
      if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0))
          line_length = window.ws_col;
      else
          line_length = 80;         /* Default length assumed */
    }
    else
      line_length = atoi (columns_ptr);

    /*
     * Maybe we should check whether there is enough space on
     * the lines for the options selected...
     */
    if (line_length < 60)
      long_format = FALSE;

    line_length --;


    /*
     * Print whatever headers
     */
    if (header == TRUE)
    {
      /*
       * uptime: from MKJ's uptime routine,
       * found in whattime.c
       */
      print_uptime();


      /*
       * Print relevant header bits
       */
      printf ("User     tty     ");

      if (long_format == TRUE)
      {
          if (from_switch == TRUE)
            printf ("From             ");

          printf (" login@   idle  JCPU  PCPU  ");

          if (show_pid == TRUE)
            printf (" PID  ");

          printf ("what\n");
      }
      else
      {
          printf (" idle  ");

          if (show_pid == TRUE)
            printf (" PID  ");

          printf ("what\n");
      }
    }




    /*
     * Process user information.
     */
    while ((utmp_rec = getutent()))
    {
      /*
       * Check we actually want to see this record.
       * It must be a valid active user process,
       * and match a specified search name.
       */
      if ( (utmp_rec->ut_type == USER_PROCESS)
        && (strcmp(utmp_rec->ut_user, ""))
        && ( (search_name[0] == '\0')
          || ( (search_name[0] != '\0')
          && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) )
      {
          /*
           * Get the username
           */
          strncpy (username, utmp_rec->ut_user, 8);
          username[8] = '\0';       /* Set end terminator */


          /*
           * Find out the uid of that user (from their
           * passwd entry)
           */
          uid = -1;
          if ((passwd_entry = getpwnam (username)) != NULL)
          {
           uid = passwd_entry->pw_uid;
          }

          /*
           * Get (and clean up) the tty line
           */
          for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < UT_LINESIZE); i ++)
            tty[i] = utmp_rec->ut_line[i];

          utmp_rec->ut_line[i] = '\0';
          tty[i] = '\0';


          /*
           * Don't bother getting info if it's not asked for
           */
          if (long_format == TRUE)
          {

            /*
             * Get the remote hostname; this can be up to 16 chars,
             * but if any chars are invalid (ie: [^a-zA-Z0-9\.])
             * then the char is changed to a string terminator.
             */
            if (from_switch == TRUE)
            {
                strncpy (rhost, utmp_rec->ut_host, 16);
                rhost[16] = '\0';

            }


            /*
             * Get the login time
             * (Calculated by LG's routine, below)
             */
            strcpy (login_time, logintime(utmp_rec->ut_time));
          }



          /*
           * Get the idle time.
           * (Calculated by LG's routine, below)
           */
          if (strchr(tty,':')) {    /* idle unknown for xdm logins */
                strcpy(idle_time," ?xdm? ");
                /* 
                 * Also, trim the hostname down to just what's before 
                 * the first '.'
                 */
                if (strchr(tty,'.')) {
                      *(strchr(tty,'.')) = '\0';
                }
          }
          else
                strcpy (idle_time, idletime (tty));

          /*
           * That's all the info out of /etc/utmp.
           * The rest is more difficult.  We use the pid from
           * utmp_rec->ut_pid to look in /proc for the info.
           * NOTE: This is not necessarily the active pid, so we chase
           * down the path of parent -> child pids until we find it,
           * according to the information given in /proc/<pid>/stat.
           */

          sprintf (pid, "%d", utmp_rec->ut_pid);

          what[0] = '\0';
          strcpy (file_name, pid);
          strcat (file_name, "/stat");
          jcpu = 0;
          pcpu = 0;

          if ((fp = fopen(file_name, "r")))
          {
            while (what[0] == '\0')
            {
                /*
                 * Check /proc/<pid>/stat to see if the process
                 * controlling the tty is the current one
                 */
                fscanf (fp, "%d %s %c %*d %*d %*d %*d %d "
                  "%*u %*u %*u %*u %*u %d %d %d %d",
                  &curr_pid, comm, &state, &tpgid,
                  &utime, &stime, &cutime, &cstime);

                fclose (fp);

                if (comm[0] == '\0')
                  strcpy (comm, "-");

                /*
                 * Calculate jcpu and pcpu.
                 * JCPU is the time used by all processes and their
                 * children, attached to the tty.
                 * PCPU is the time used by the current process
                 * (calculated once after the loop, using last
                 * obtained values).
                 */
                if (!jcpu)
                  jcpu = cutime + cstime;

                /*
                 * Check for a zombie first...
                 */
                if (state == 'Z')
                  strcpy (what, ZOMBIE);
                else if (curr_pid == tpgid)
                {
                  /*
                   * If it is the current process, read cmdline
                   * If that's empty, then the process is swapped out,
                   * or is a zombie, so we use the command given in stat
                   * which is in normal round brackets, ie: "()".
                   */
                  strcpy (file_name, pid);
                  strcat (file_name, "/cmdline");
                  if ((fp = fopen(file_name, "r")))
                  {
                      i = 0;
                      j = fgetc (fp);
                      while ((j != EOF) && (i < 256))
                      {
                        if (j == '\0')
                            j = ' ';

                        what[i] = j;
                        i++;
                        j = fgetc (fp);
                      }
                      what[i] = '\0';
                      fclose (fp);
                  }

                  if (what[0] == '\0')
                      strcpy (what, comm);
                }
                else
                {
                  /* 
                   * Check out the next process
                   * If we can't open it, use info from this process,
                   * so we have to check out cmdline first.
                   *
                   * If we're not using "-u" then should we just
                   * say "-" (or "-su") instead of a command line ?
                   * If so, we should strpcy(what, "-"); when we
                   * fclose() in the if after the stat() below.
                   */
                  strcpy (file_name, pid);
                  strcat (file_name, "/cmdline");

                  if ((fp = fopen (file_name, "r")))
                  {
                      i = 0;
                      j = fgetc (fp);
                      while ((j != EOF) && (i < 256))
                      {
                        if (j == '\0')
                            j = ' ';

                        what[i] = j;
                        i++;
                        j = fgetc (fp);
                      }
                      what[i] = '\0';
                      fclose (fp);
                  }

                  if (what[0] == '\0')
                      strcpy (what, comm);

                  /*
                   * Now we have something in the what variable,
                   * in case we can't open the next process.
                   */
                  sprintf (pid, "%d", tpgid);
                  strcpy (file_name, pid);
                  strcat (file_name, "/stat");

                  fp = fopen (file_name, "r");

                  if (fp && (ignore_user == FALSE))
                  {
                      /*
                       * We don't necessarily go onto the next process,
                       * unless we are either ignoring who the effective
                       * user is, or it's the same uid
                       */
                      stat (file_name, &stat_rec);

                      /*
                       * If the next process is not owned by this
                       * user finish the loop.
                       */
                      if (stat_rec.st_uid != uid)
                      {
                        fclose (fp);

                        strcpy (what, "-su");
                        /*
                         * See comment above somewhere;  I've used
                         * "-su" here, as the next process is owned
                         * by someone else; this is generally
                         * because the user has done an "su" which
                         * then exec'd something else.
                         */
                      }
                      else
                        what[0] = '\0';
                  }
                  else if (fp)      /* else we are ignoring uid's */
                      what[0] = '\0';
                }
            }
          }
          else    /* Could not open first process for user */
            strcpy (what, "?");


          /*
           * There is a bug somewhere in my version of linux
           * which means that utmp records are not cleaned
           * up properly when users log out. However, we
           * can detect this, by the users first process
           * not being there when we look in /proc.
           */


          /*
           * Don't output a line for "dead" users.
           * This gets round a bug which doesn't update utmp/wtmp
           * when users log out.
           */
          if (what[0] != '?')
          {
#if 0
/* This makes unix98 pty's not line up, so has been disabled - JEH. */
            /*
             * Remove the letters 'tty' from the tty id
             */
            if (!strncmp (tty, "tty", 3))
            {
                for (i = 3; tty[i - 1] != '\0'; i ++)
                  tty[i - 3] = tty[i];
            }
#endif

            /*
             * Common fields
             */
            sprintf (out_line, "%-9.8s%-6.6s ", username, tty);


            /*
             * Format the line for output
             */
            if (long_format == TRUE)
            {
                /*
                 * Calculate CPU usage
                 */
                pcpu = utime + stime;
                jcpu /= 100;
                pcpu /= 100;

                if (from_switch == TRUE)
                  sprintf (out_line, "%s %-16.15s", out_line, rhost);

                sprintf (out_line, "%s%8.8s ", out_line, login_time);

            }

            sprintf (out_line, "%s%6s", out_line, idle_time);


            if (long_format == TRUE)
            {
                if (!jcpu)
                  strcat (out_line, "      ");
                else if (jcpu/60)
                  sprintf (out_line, "%s%3d:%02d", out_line,
                        jcpu/60, jcpu%60);
                else
                  sprintf (out_line, "%s    %2d", out_line, jcpu);

                if (!pcpu)
                  strcat (out_line, "      ");
                    else if (pcpu/60)
                  sprintf (out_line, "%s%3d:%02d", out_line,
                        pcpu/60, pcpu%60);
                else
                  sprintf (out_line, "%s    %2d", out_line, pcpu);
            }

            if (show_pid == TRUE)
                sprintf (out_line, "%s %5.5s", out_line, pid);


            strcat (out_line, "  ");
            strcat (out_line, what);


            /*
             * Try not to exceed the line length
             */
            out_line[line_length] = '\0';

            printf ("%s\n", out_line);
          }
      }
    }
    return 0;

    /* dummy instruction to avoid gcc -Wall warning
     * should not reach here */
    if (0 != 0)
      printf("%s\n", rcsid);
    return 1;
}



/*
 * put_syntax()
 *
 * Routine to print the correct syntax to call this program,
 * and then exit out appropriately
 */
void put_syntax ()
{
    fprintf (stderr, "usage: w [-hfsud] [user]\n");
    exit (-1);
}



/*
 * idletime()
 *
 * Routine which returns a string containing
 * the idle time of a given user.
 *
 * This routine was lifted from the original w program
 * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
 * Copyright (c) 1993 Larry Greenfield
 *
 */
char *idletime (tty)

char *tty;

{
    struct stat terminfo;
    unsigned long idle;
    char ttytmp[40];
    static char give[20];
    time_t curtime;

    curtime = time (NULL);

    sprintf (ttytmp, "/dev/%s", tty);
    stat (ttytmp, &terminfo);
    idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime;

    if (idle >= (60 * 60))          /* more than an hour */
    {
      if (idle >= (60 * 60 * 48))   /* more than two days */
          sprintf (give, "%2ludays", idle / (60 * 60 * 24));
      else
          sprintf (give, " %2lu:%02u", idle / (60 * 60), 
            (unsigned) ((idle / 60) % 60));
    }
    else
    {
      if (idle / 60)
          sprintf (give, "%6lu", idle / 60);
      else
          give[0]=0;
    }

    return give;
}



/*
 * logintime()
 *
 * Returns the time given in a suitable format
 *
 * This routine was lifted from the original w program
 * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
 * Copyright (c) 1993 Larry Greenfield
 *
 */

#undef ut_time

char *logintime(ut_time)

time_t ut_time;

{
    time_t curtime;
    struct tm *logintime, *curtm;
    int hour, am, curday, logday;
    static char give[20];
    static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
                        "Sat" };
    static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                        "Aug", "Sep", "Oct", "Nov", "Dec" };

    curtime = time(NULL);
    curtm = localtime(&curtime);
    curday = curtm->tm_yday;
    logintime = localtime(&ut_time);
    hour = logintime->tm_hour;
    logday = logintime->tm_yday;
    am = (hour < 12);

    if (!am)
      hour -= 12;

    if (hour == 0)
      hour = 12;

    /*
     * This is a newer behavior: it waits 12 hours and the next day, and then
     * goes to the 2nd time format. This should reduce confusion.
     * It then waits only 6 days (not till the last moment) to go the last
     * time format.
     */
    if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday))
    {
      if (curtime > (ut_time + (60 * 60 * 24 * 6)))
          sprintf(give, "%2d%3s%2d", logintime->tm_mday,
            month[logintime->tm_mon], (logintime->tm_year % 100));
      else
          sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday],
            hour, am ? "am" : "pm");
    }
    else
      sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm");

    return give;
}


Generated by  Doxygen 1.6.0   Back to index