home *** CD-ROM | disk | FTP | other *** search
- /*
- * w.c v1.2
- *
- * An alternative "w" program for Linux.
- * Shows users and their processes.
- *
- * Copyright (c) Dec 1993 Steve "Mr. Bassman" Bryant
- * bassman@hpbbi30.bbn.hp.com
- *
- * 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.
- * The code was written from scratch by myself, and initial versions
- * simply returned information from /etc/utmp and /proc. However, I since
- * received a copy of the procps source, and have changed my code to call
- * some of the routines there.
- * Thanks to the other writers, without whom I'd have probably still
- * been stuck on one or two things !
- *
- * Mr. Bassman, 12/93
- *
- * 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 w sysinfo.c whattime.c w.c
- *
- * Usage:
- * w [-hus] [user]
- *
- * $Log: w2.c,v $
- * Revision 1.4 1994/01/01 12:57:21 johnsonm
- * Fixed log messages...
- *
- * Revision 1.3 1994/01/01 10:37:44 johnsonm
- * More changes, still doesn't quite work.
- *
- * Revision 1.2 1994/01/01 08:47:02 johnsonm
- * More changes, still doesn't quite work.
- *
- * Revision 1.1 1993/12/31 20:16:37 johnsonm
- * Eliminated GCC warnings, took out unnecessary dead variables in
- * fscanf, replacing them with *'d format qualifiers.
- * Also added RCS stuff.
- *
- *
- */
-
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <time.h>
- #include <utmp.h>
- #include <unistd.h>
- #include <errno.h>
- #include <pwd.h>
- #include "whattime.h"
-
-
- #define TRUE 1
- #define FALSE 0
- #define ZOMBIE "<zombie>"
-
-
- void put_syntax();
- char *idletime();
- char *logintime();
-
- static char rcsid[]="$Id: w2.c,v 1.4 1994/01/01 12:57:21 johnsonm Exp $";
-
- int main (argc, argv)
-
- int argc;
- char *argv[];
-
- {
- int header=TRUE, long_format=TRUE, ignore_user=FALSE;
- int i, j, k;
- struct utmp *utmp_rec;
- struct stat stat_rec;
- struct passwd *passwd_entry;
- uid_t uid=0;
- char username[9], tty[13], rhost[17], login_time[27];
- char idle_time[7], what[256], pid[10];
- char out_line[256], file_name[256];
- char search_name[9];
- int jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime;
- char state, comm[256];
- FILE *fp;
-
-
- search_name[0] = '\0';
-
- /*
- * Process the command line
- */
- if (argc > 1)
- {
- if (argv[1][0] == '-') /* 1st arg starts with '-' */
- {
- for (j = 1; j < strlen(argv[1]); j++)
- {
- switch (argv[1][j])
- {
- case 'h':
- header = FALSE;
- break;
- case 's':
- long_format = FALSE;
- break;
- case 'l':
- long_format = TRUE;
- break;
- case 'u':
- ignore_user = TRUE;
- break;
- default:
- fprintf(stderr, "w: unknown option: '%c'\n", argv[1][j]);
- put_syntax();
- break;
- }
- }
-
- if (argc > 2)
- {
- strncpy(search_name, argv[2], 8);
-
- if (argc > 3)
- {
- fprintf(stderr, "w: syntax error\n");
- put_syntax();
- }
- }
- }
- else
- {
- strncpy(search_name, argv[1], 8);
-
- if (argc > 2)
- {
- 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);
- }
-
-
-
-
- /*
- * Print whatever headers
- */
- if (header == TRUE)
- {
- print_uptime();
-
- if (long_format == TRUE)
- printf("User tty From login@ idle JCPU PCPU what\n");
- else
- printf("User tty idle what\n");
- }
-
-
-
-
- /*
- * Process user information.
- *
- * getutent() opens the utmp file for us,
- * and reads off the first record.
- * If the file was already opened, it reads the
- * subsequent record for us. See man page for more info.
- */
- 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) ) ) )
- {
-
- /*
- * Important note:
- *
- * Strings in the utmp struct are *NOT*
- * necessarily null terminated :-(
- * This only occurs if the string fills
- * up its allocated space exactly.
- * If it is shorter, it is terminated by a null.
- * Because of this, strcpy breaks, and strncpy
- * won't necessarily add a terminator, so we have to
- * do it ourselves. The non-null-terminated string
- * can also cause problems for printf type calls,
- * otherwise I'd just read it direct !
- */
-
- /* Get the username */
- for (i = 0; i < 8; i++)
- {
- username[i] = '\0';
-
- if (utmp_rec->ut_user[i] != '\0')
- username[i] = utmp_rec->ut_user[i];
- else
- i = 8;
- }
-
-
- /* If necessary, find out the uid of that user (from their passwd entry) */
- if (ignore_user == FALSE)
- {
- passwd_entry = getpwnam(username);
- uid = passwd_entry->pw_uid;
- }
-
-
- /* Get the tty line */
- for (i = 0; i < 6; i++)
- {
- tty[i] = '\0';
-
- if (utmp_rec->ut_line[i] != '\0')
- tty[i] = utmp_rec->ut_line[i];
- else
- i = 6;
- }
-
-
- /* Don't other getting info if it's not asked for */
- if (long_format == TRUE)
- {
-
- /* Get the remote hostname */
- for (i = 0; i < 15; i++)
- {
- rhost[i] = '\0';
-
- if (utmp_rec->ut_host[i] != '\0')
- rhost[i] = utmp_rec->ut_host[i];
- else
- i = 15;
- }
-
-
- /*
- * 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)
- */
- 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"))
- {
- if (ignore_user == FALSE)
- {
- /*
- * Use stat to find out who owns the file,
- * and therefore the process.
- */
- stat(file_name, &stat_rec);
- if (stat_rec.st_uid != uid)
- strcpy(what, "-");
- }
-
- 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, "-");
-
- /* Now get rid of the parentheses. It doesn't work to put () around
- * the %s in the fscanf above, unfortunately.
- */
- if (comm[0] == '(') /* this ought to be true*/
- {
- k = strlen(comm);
- k -= 2;
- memmove(&comm[0], &comm[1], k);
- comm[k] = '\0';
- }
-
- /*
- * 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).
- *
- * NOTE:
- * While pcpu gives correct readings, jcpu does not.
- * I have tried two different methods (below).
- * One reads from cutime and cstime, the first time the loop is
- * executed. These variables should give information about that process
- * and all its children.
- * The other method adds everything up as we go. This second
- * method is probably less accurate, as not all processes are checked,
- * only those which have had control of the terminal, have given control
- * to another process, and are waiting for it to return, plus the current
- * controlling process; ie: it ignores background jobs. This should be
- * reasonably accurate if the user isn't running any background jobs.
- * However, both seem to return inaccurate information, and I don't
- * know why !
- * The method used in the original program was to chase a
- * linked list. I may patch a version of that in, in addition to my
- * method. There are three possibilities why the results are wrong:
- * one, I've misinterpreted (but pcpu is ok, and I've processed both the
- * same); two, the info was wrong in the first place (is this possible ?);
- * three, it's something else. If anyone knows, please mail me, and I'll
- * patch it !!
- * One more thought: cutime and cstime will retain records of process
- * time used by processes which no longer exist, so does this mean that they
- * are more accurate than going through the process list, and adding up
- * times from current processes ?
- */
- if (!jcpu)
- jcpu = cutime + cstime;
- /*
- jcpu += utime + stime;
- */
-
-
- /* 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"))
- {
- fgets(what, 255, fp);
- 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 "-" instead of a command line ?
- * If so, we should strpcy(what, "-"); when we fclose()
- * in the if after the stat().
- */
- strcpy(file_name, pid);
- strcat(file_name, "/cmdline");
- if (fp = fopen(file_name, "r"))
- {
- fgets(what, 255, fp);
- 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 (stat_rec.st_uid != uid)
- /* The next process is not owned by this user; finish loop. */
- fclose(fp);
- /* strcpy(what, "-"); see comment above somewhere */
- 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] != '?')
- {
-
- /*
- * Format the line for output
- */
- if (long_format == TRUE)
- {
- pcpu = utime + stime;
- jcpu /= 100;
- pcpu /= 100;
-
-
- sprintf(out_line, "%-9.8s%-7.6s%-15.16s%8.8s%7.6s",
- username, tty, rhost, login_time, idle_time);
-
- if (!jcpu)
- sprintf(out_line, "%s ", 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)
- sprintf(out_line, "%s ", 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);
-
- strcat(out_line, " ");
- strcat(out_line, what);
- }
- else
- {
- sprintf(out_line, "%-9.8s%-7.6s%7.6s %s",
- username, tty, idle_time, what);
- }
-
-
- out_line[79] = '\0'; /* So we aren't too long */
- printf("%s\n", out_line);
- }
- }
- }
- return(0);
- }
-
-
-
- /*
- * put_syntax()
- *
- * Routine to print the correct syntax to call this program,
- * and then exit out appropriately
- */
- void put_syntax()
- {
- fprintf(stderr, "usage: w [-hus] [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;
- long idle;
- char ttytmp[40];
- static char give[20];
- time_t curtime;
-
- curtime = time(NULL);
- sprintf(ttytmp, "/dev/%s", tty);
- stat(ttytmp, &terminfo);
- idle = (curtime - terminfo.st_atime);
-
- if (idle >= (60 * 60)) /* more than an hour */
- {
- if (idle >= (60 * 60 * 48)) /* more than two days */
- sprintf(give, "%2ddays", idle / (60 * 60 * 24));
- else
- sprintf(give, " %2d:%s%d", idle / (60 * 60),
- (idle / 60) % 60 < 10 ? "0" : "", (idle / 60) % 60);
- }
- else
- {
- if (idle / 60)
- sprintf(give, "%6d", 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
- *
- */
- 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;
- }
-
-