731 lines
16 KiB
C
731 lines
16 KiB
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: tmp-junk.c,v $
|
|
* Revision 1.1 2002/02/01 22:46:37 csmall
|
|
* Initial revision
|
|
*
|
|
* 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: tmp-junk.c,v 1.1 2002/02/01 22:46:37 csmall Exp $";
|
|
|
|
|
|
void 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[13], 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 < 6); 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)
|
|
*/
|
|
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] != '?')
|
|
{
|
|
#ifdef 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.7s ", 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|