1
0
Files
2022-09-29 17:59:04 +03:00

608 lines
14 KiB
C

/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* Changes Copyright (c) 1989 Silicon Graphics, Inc.
* based on @(#)w.c 5.3 (Berkeley) 2/23/86
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
/*
* w - print system status (who and what)
*
* This program is similar to the systat command on Tenex/Tops 10/20
* It used to need read permission on /dev/mem and /dev/kmem but
* has been converted to use sysget.
*/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <utmpx.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/sysmp.h>
#include <sys/sysget.h>
#include <sys/var.h>
#include <sys/ioctl.h>
#include <sys/procfs.h>
#include <dirent.h>
#include <fcntl.h>
#include <paths.h>
#include <errno.h>
/*
* The proc filesystem (see proc(4) manual page)
* is scanned for information
*/
struct prcred pcred;
struct prpsinfo pinfo;
#define equal(a,b) (!strcmp((a),(b)))
#define NMAX sizeof(utmp->ut_name)
#define LMAX NMAX
#define ARGWIDTH 35 /* # chars left on 80 col crt for args */
struct pr {
pid_t w_pid; /* proc.p_pid */
time_t w_time; /* CPU time used by this process */
time_t w_ctime; /* CPU time used by children */
time_t w_start; /* time process started */
dev_t w_tty; /* tty device of process */
uid_t w_uid; /* uid of process */
char w_comm[PRCOMSIZ+1]; /* proc name, null terminated */
char w_args[ARGWIDTH+1]; /* args if interesting process */
} *pr;
dev_t tty;
uid_t uid;
char doing[520]; /* process attached to terminal */
time_t proctime; /* cpu time of process in doing */
int avenrun[3];
#define DIV60(t) ((t+30)/60) /* x/60 rounded */
#define TTYEQ (tty == pr[i].w_tty && uid == pr[i].w_uid)
#define IGINT (1+3*1) /* ignoring both SIGINT & SIGQUIT */
static time_t findidle(char *);
static void readpr(void);
static long getnamelist(void);
static void gettty(char *devstr);
static void putline(void);
static void prttime(time_t, char *);
static void prtat(long *);
int debug; /* true if -d flag: debugging output */
int ttywidth = 80; /* width of tty */
int header = 1; /* true if -h flag: don't print heading */
int lflag = 1; /* true if -l flag: long style output */
#ifdef UTMPX_FILE
int wide_from = 0; /* print "from" info on separate line */
int prfrom = 1; /* true if not -f flag: print host from */
#else
int prfrom = 0;
#endif
int login; /* true if invoked as login shell */
time_t idle; /* number of minutes user is idle */
int nusers; /* number of users logged in now */
char * sel_user; /* login of particular user selected */
char firstchar; /* first char of name of prog invoked as */
time_t jobtime; /* total cpu time visible */
time_t now; /* the current time of day */
time_t uptime = 0; /* time of last reboot & elapsed time since */
int np; /* number of processes currently active */
int use_tty_flag = 1; /* Default output is assumed to be the terminal. */
struct utmp *utmp;
#ifdef UTMPX_FILE
struct utmpx *utmpx;
#endif
int
main(int argc, char **argv)
{
int days, hrs, mins;
register int i, j;
char *cp;
register int curstart;
struct winsize win;
struct tms tms;
sgt_cookie_t ck;
int sgt_res;
login = (argv[0][0] == '-');
cp = rindex(argv[0], '/');
firstchar = login ? argv[0][1] : (cp==0) ? argv[0][0] : cp[1];
cp = argv[0]; /* for Usage */
while (argc > 1) {
if (argv[1][0] == '-') {
for (i=1; argv[1][i]; i++) {
switch(argv[1][i]) {
case 'd':
debug++;
break;
#ifdef UTMPX_FILE
case 'f':
prfrom = !prfrom;
break;
case 'W':
wide_from++;
prfrom = 0;
break;
#endif
case 'h':
header = 0;
break;
case 'l':
lflag++;
break;
case 's':
lflag = 0;
break;
case 'u':
case 'w':
firstchar = argv[1][i];
break;
default:
printf("Bad flag %s\n", argv[1]);
exit(1);
}
}
} else {
if (!isalnum(argv[1][0]) || argc > 2) {
printf("Usage: %s [ -hlsuw ] [ user ]\n", cp);
exit(1);
} else
sel_user = argv[1];
}
argc--; argv++;
}
if (firstchar != 'u') {
readpr();
if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 70)
ttywidth = win.ws_col;
/* Fix for output not being sent to screen -- check ws.col
and if 0, the a boolean flag (global) is reset.
*/
if (win.ws_col == 0)
use_tty_flag = 0;
}
time(&now);
if (header) {
/* Print time of day */
prtat(&now);
while ( ( utmp = getutent()) != NULL ) {
if (utmp->ut_type == USER_PROCESS )
nusers++;
else if (utmp->ut_type == BOOT_TIME) {
uptime = now -utmp->ut_time;
}
}
/* get uptime via times() only if it is not
* available via the utmp entry.
*/
if (!uptime) uptime = times(&tms) / HZ;
uptime += 30;
days = uptime / (60*60*24);
uptime %= (60*60*24);
hrs = uptime / (60*60);
uptime %= (60*60);
mins = uptime / 60;
printf(" up");
if (days > 0)
printf(" %d day%s,", days, days>1?"s":"");
if (hrs > 0 && mins > 0) {
printf(" %2d:%02d,", hrs, mins);
} else {
if (hrs > 0)
printf(" %d hr%s,", hrs, hrs>1?"s":"");
if (mins > 0)
printf(" %d min%s,", mins, mins>1?"s":"");
}
printf(" %d user%s", nusers, nusers>1?"s":"");
/*
* Print 1, 5, and 15 minute load averages.
* (Found by looking in kernel for avenrun).
* On a cell system there is one per kernel so we
* take the average by dividing by the cells.
*/
printf(", load average:");
SGT_COOKIE_INIT(&ck);
SGT_COOKIE_SET_KSYM(&ck, KSYM_AVENRUN);
sgt_res = sysget(SGT_KSYM, (char *)avenrun, sizeof(avenrun),
SGT_READ | SGT_SUM, &ck);
if (sgt_res < 0) {
perror("sysget");
exit(errno);
}
sgt_res /= sizeof(avenrun); /* Number of cells */
for (i=0; i < (sizeof avenrun/sizeof avenrun[0]); i++) {
avenrun[i] /= sgt_res;
if (i > 0)
printf(",");
printf(" %.2f", (((double)avenrun[i])/1024.0));
}
printf("\n");
if (firstchar == 'u')
exit(0);
/* Headers for rest of output */
if (lflag && prfrom)
printf("User tty from login@ idle JCPU PCPU what\n");
else if (lflag)
printf("User tty login@ idle JCPU PCPU what\n");
else if (prfrom)
printf("User tty from idle what\n");
else
printf("User tty idle what\n");
fflush(stdout);
}
setutent();
while ( ( utmp = getutent() ) != NULL )
{
static struct utmpx utmpx_entry;
if (utmp->ut_type != USER_PROCESS)
continue; /* that tty is free */
if (sel_user && strncmp(utmp->ut_name, sel_user, NMAX) != 0)
continue; /* we wanted only somebody else */
if ( equal( utmp->ut_line, "syscon" ) ||
equal( utmp->ut_line, "systty" ) )
continue;
#ifdef UTMPX_FILE
memcpy(utmpx_entry.ut_id, utmp->ut_id, sizeof(utmp->ut_id));
utmpx_entry.ut_type = utmp->ut_type;
setutxent();
utmpx = getutxid((struct utmpx *)&utmpx_entry);
if (utmpx != NULL) {
/*
* Make sure the entries match -- the utmpx one may
* be out-of-date.
*/
/* User names instead of of pids are compared for
* match.
*/
if (strncmp(utmp->ut_user, utmpx->ut_user,
MIN(sizeof(utmp->ut_user), sizeof(utmpx->ut_user))) ||
utmp->ut_time != utmpx->ut_xtime) {
utmpx = NULL;
} else {
/*
* ftpd puts hostname in ut_line -- use proper id
* from utx_line.
*/
strncpy(utmp->ut_line, utmpx->ut_line,
MIN(sizeof(utmp->ut_line), sizeof(utmpx->ut_line)));
}
}
#endif
gettty( utmp->ut_line );
idle = findidle( utmp->ut_line );
jobtime = 0;
proctime = 0;
strcpy(doing, "-"); /* default act: normally never prints */
curstart = -1;
for (i=0; i<np; i++) { /* for each process on this tty */
if (!(TTYEQ))
continue;
jobtime += pr[i].w_time + pr[i].w_ctime;
proctime += pr[i].w_time;
/*
* Meaning of debug fields following proc name is:
* (==> this proc is not a candidate.)
* *: proc pgrp == tty pgrp.
*/
if (debug)
printf("\t\t%d\t%s\n", pr[i].w_pid, pr[i].w_args);
if(pr[i].w_start>curstart){
curstart = pr[i].w_start;
strcpy(doing, lflag ? pr[i].w_args : pr[i].w_comm);
}
}
putline();
}
}
/* figure out the major/minor device # pair for this tty */
static void
gettty(char *devstr)
{
char ttybuf[20];
struct stat statbuf;
ttybuf[0] = 0;
strcpy(ttybuf, "/dev/");
strcat(ttybuf, devstr);
stat(ttybuf, &statbuf);
tty = statbuf.st_rdev;
uid = statbuf.st_uid;
}
/*
* putline: print out the accumulated line of info about one user.
*/
static void
putline(void)
{
register int tm;
int width = ttywidth - 1;
/* print login name of the user */
printf("%-*.*s ", NMAX, NMAX, utmp->ut_name);
width -= NMAX + 1;
/* print tty user is on */
if (lflag && !prfrom) {
/* long form: all (up to) LMAX chars */
printf("%-*.*s", LMAX, LMAX, utmp->ut_line);
width -= LMAX;
} else {
/* short form: 3 chars, skipping 'tty' if there */
if (utmp->ut_line[0]=='t' && utmp->ut_line[1]=='t' && utmp->ut_line[2]=='y')
printf("%-3.3s", &utmp->ut_line[3]);
else
printf("%-3.3s", utmp->ut_line);
width -= 3;
}
#ifdef UTMPX_FILE
if (prfrom) {
printf(" %-14.14s", utmpx ? utmpx->ut_host : " ");
width -= 15;
}
#endif
if (lflag) {
/* print when the user logged in */
prtat(&utmp->ut_time);
width -= 8;
}
/* print idle time */
if (idle >= 36 * 60)
printf(" %2ddays ", (idle + 12 * 60) / (24 * 60));
else {
printf(" ");
prttime(idle," ");
}
width -= 8;
if (lflag) {
/* print CPU time for all processes & children */
prttime(jobtime," ");
width -= 7;
/* print cpu time for interesting process */
prttime(proctime," ");
width -= 7;
}
/* what user is doing, either command tail or args */
/* Check here if the output is being sent to screen. If so
then print upto 80 characters.
*/
if (use_tty_flag == 0) {
printf (" %s\n", doing);
}
else
printf(" %-.*s\n", width-1, doing);
#ifdef UTMPX_FILE
if (wide_from && utmpx) {
printf("%*.s %-s\n", NMAX, " ", utmpx->ut_host);
}
#endif
fflush(stdout);
}
/* find & return number of minutes current tty has been idle */
static time_t
findidle( char *ttystr )
{
struct stat stbuf;
time_t lastaction, diff;
char ttyname[20];
strcpy(ttyname, "/dev/");
strncat(ttyname, ttystr, LMAX);
if (stat(ttyname, &stbuf) < 0)
return 0;
time(&now);
lastaction = stbuf.st_atime;
diff = now - lastaction;
diff = DIV60(diff);
if (diff < 0) diff = 0;
return(diff);
}
#define HR (60 * 60)
#define DAY (24 * HR)
#define MON (30 * DAY)
/*
* prttime prints a time in hours and minutes or minutes and seconds.
* The character string tail is printed at the end, obvious
* strings to pass are "", " ", or "am".
*/
static void
prttime(time_t tim, char *tail)
{
if (tim >= 60) {
printf("%3d:", tim/60);
tim %= 60;
printf("%02d", tim);
} else if (tim > 0)
printf(" %2d", tim);
else
printf(" ");
printf("%s", tail);
}
char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
/* prtat prints a 12 hour time given a pointer to a time of day */
static void
prtat(long *time)
{
struct tm *p;
register int hr, pm;
p = localtime(time);
hr = p->tm_hour;
pm = (hr > 11);
if (hr > 11)
hr -= 12;
if (hr == 0)
hr = 12;
if (now - *time <= 18 * HR)
prttime(hr * 60 + p->tm_min, pm ? "pm" : "am");
else if (now - *time <= 7 * DAY)
printf(" %s%2d%s", weekday[p->tm_wday], hr, pm ? "pm" : "am");
else
printf(" %2d%s%02d", p->tm_mday, month[p->tm_mon], p->tm_year%100);
}
/*
* readpr finds and reads in the array pr, containing the interesting
* parts of a process
*/
static void
readpr(void)
{
int procfd;
char procpath[100];
DIR *dirp;
struct dirent *dentp;
/*
* Determine which processes to print info about by searching
* the /proc/pinfo directory and looking at each process.
*/
if ((dirp = opendir(_PATH_PROCFSPI)) == NULL) {
(void) fprintf(stderr,
"Cannot open /proc/pinfo directory :%s\n",
strerror(errno));
exit(1);
}
pr = NULL;
np = 0; /* global define for the number of active process */
/*
* Basic idea - open the /proc directory and read all the files
* use the file name and then open using the ioctl for proc(4)
* get information instead of using syssgi call and other "old"
* methods of getting information.
*/
while (dentp = readdir(dirp)) {
if (dentp->d_name[0] == '.') /* skip . and .. */
continue;
(void) sprintf(procpath,"%s%s",_PATH_PROCFSPI,dentp->d_name);
if((procfd = open(procpath,O_RDONLY)) == -1)
continue;
if (ioctl(procfd, PIOCPSINFO, (char *) &pinfo) == -1) {
(void) close(procfd);
continue;
}
if (ioctl(procfd, PIOCCRED, (char *) &pcred) == -1) {
(void) close(procfd);
continue;
}
if (pinfo.pr_ttydev == PRNODEV){
(void) close(procfd);
continue;
}
/* decide if it's an interesting process */
if (pinfo.pr_state==0 || pinfo.pr_state==SZOMB || pinfo.pr_pgrp==0){
(void) close(procfd);
continue;
}
/* Check if is a valid pid */
if (pinfo.pr_pid < 0 || pinfo.pr_pid >= MAXPID){
(void) close(procfd);
continue;
}
/*
* Read from PIOCPSINFO
*/
pr = realloc(pr, (np + 1) * sizeof(*pr));
pr[np].w_pid = pinfo.pr_pid;
pr[np].w_tty = pinfo.pr_ttydev;
pr[np].w_start = pinfo.pr_start.tv_sec;
strcpy(pr[np].w_comm, pinfo.pr_fname);
strncpy(pr[np].w_args, pinfo.pr_psargs, ARGWIDTH);
pr[np].w_args[ARGWIDTH] = '\0';
pr[np].w_time = pinfo.pr_time.tv_sec;
pr[np].w_ctime = pinfo.pr_ctime.tv_sec;
/*
* Read the credentials structure to get the real
* user id.
*/
pr[np].w_uid = pcred.pr_ruid;
np++; /* increment for valid process information */
(void) close(procfd);
}
(void) closedir(dirp);
}
static long
getnamelist(void)
{
return(sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff);
}