* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)w.c 5.9 (Berkeley) %G%";
* w - print system status (who and what)
* This program is similar to the systat command on Tenex/Tops 10/20
* It needs read permission on /dev/mem, /dev/kmem, and /dev/drum.
#define NMAX sizeof(utmp.ut_name)
#define LMAX sizeof(utmp.ut_line)
#define HMAX sizeof(utmp.ut_host)
#define ARGWIDTH 33 /* # chars left on 80 col crt for args */
short w_pid
; /* proc.p_pid */
char w_flag
; /* proc.p_flag */
short w_size
; /* proc.p_size */
long w_seekaddr
; /* where to find args */
long w_lastpg
; /* disk address of stack */
int w_igintr
; /* INTR+3*QUIT, 0=die, 1=ign, 2=catch */
time_t w_time
; /* CPU time used by this process */
time_t w_ctime
; /* CPU time used by children */
dev_t w_tty
; /* tty device of process */
int w_uid
; /* uid of process */
char w_comm
[15]; /* user.u_comm, null terminated */
char w_args
[ARGWIDTH
+1]; /* args if interesting process */
int swap
; /* /dev/kmem, mem, and swap */
char doing
[520]; /* process attached to terminal */
time_t proctime
; /* cpu time of process in doing */
#define DIV60(t) ((t+30)/60) /* x/60 rounded */
#define TTYEQ (tty == pr[i].w_tty)
#define IGINT (1+3*1) /* ignoring both SIGINT & SIGQUIT */
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 */
int prfrom
= 1; /* true if not -f flag: print host from */
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
; /* time of last reboot & elapsed time since */
int np
; /* number of processes currently active */
register int curpid
, empty
;
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 */
for (i
=1; argv
[1][i
]; i
++) {
printf("Bad flag %s\n", argv
[1]);
if (!isalnum(argv
[1][0]) || argc
> 2) {
printf("Usage: %s [ -hlsfuw ] [ user ]\n", cp
);
if ((kmem
= open("/dev/kmem", 0)) < 0) {
fprintf(stderr
, "No kmem\n");
fprintf(stderr
, "No namelist\n");
if (firstchar
== 'u') /* uptime(1) */
nl
[X_BOOTTIME
+1].n_name
= "";
else { /* then read in procs, get window size */
if (ioctl(1, TIOCGWINSZ
, &win
) != -1 && win
.ws_col
> 70)
ut
= fopen("/etc/utmp","r");
* Print how long system has been up.
* (Found by looking for "boottime" in kernel)
lseek(kmem
, (long)nl
[X_BOOTTIME
].n_value
, 0);
read(kmem
, &boottime
, sizeof (boottime
));
uptime
= now
- boottime
.tv_sec
;
days
= uptime
/ (60*60*24);
printf(" %d day%s,", days
, days
>1?"s":"");
if (hrs
> 0 && mins
> 0) {
printf(" %2d:%02d,", hrs
, mins
);
printf(" %d hr%s,", hrs
, hrs
>1?"s":"");
printf(" %d min%s,", mins
, mins
>1?"s":"");
/* Print number of users logged in to system */
while (fread(&utmp
, sizeof(utmp
), 1, ut
)) {
if (utmp
.ut_name
[0] != '\0')
printf(" %d user%s", nusers
, nusers
>1?"s":"");
* Print 1, 5, and 15 minute load averages.
* (Found by looking in kernel for avenrun).
printf(", load average:");
lseek(kmem
, (long)nl
[X_AVENRUN
].n_value
, 0);
read(kmem
, avenrun
, sizeof(avenrun
));
for (i
= 0; i
< (sizeof(avenrun
)/sizeof(avenrun
[0])); i
++) {
printf(" %.2f", avenrun
[i
]);
if (firstchar
== 'u') /* if this was uptime(1), finished */
/* Headers for rest of output */
printf("User tty from login@ idle JCPU PCPU what\n");
printf("User tty login@ idle JCPU PCPU what\n");
printf("User tty from idle what\n");
printf("User tty idle what\n");
for (;;) { /* for each entry in utmp */
if (fread(&utmp
, sizeof(utmp
), 1, ut
) == NULL
) {
if (utmp
.ut_name
[0] == '\0')
continue; /* that tty is free */
if (sel_user
&& strcmpn(utmp
.ut_name
, sel_user
, NMAX
) != 0)
continue; /* we wanted only somebody else */
strcpy(doing
, "-"); /* default act: normally never prints */
for (i
=0; i
<np
; i
++) { /* for each process on this tty */
jobtime
+= pr
[i
].w_time
+ pr
[i
].w_ctime
;
proctime
+= pr
[i
].w_time
;
* Meaning of debug fields following proc name is:
* & by itself: ignoring both SIGINT and QUIT.
* (==> this proc is not a candidate.)
* & <i> <q>: i is SIGINT status, q is quit.
* 0 == DFL, 1 == IGN, 2 == caught.
* *: proc pgrp == tty pgrp.
printf("\t\t%d\t%s", pr
[i
].w_pid
, pr
[i
].w_args
);
if ((j
=pr
[i
].w_igintr
) > 0)
printf(" & %d %d", j
%3, j
/3);
if (empty
&& pr
[i
].w_igintr
!=IGINT
) {
if(pr
[i
].w_pid
>curpid
&& (pr
[i
].w_igintr
!=IGINT
|| empty
)){
strcpy(doing
, lflag
? pr
[i
].w_args
: pr
[i
].w_comm
);
if (doing
[0]==0 || doing
[0]=='-' && doing
[1]<=' ' || doing
[0] == '?') {
strcat(doing
, pr
[i
].w_comm
);
/* figure out the major/minor device # pair for this tty */
strcat(ttybuf
, utmp
.ut_line
);
* putline: print out the accumulated line of info about one user.
int width
= ttywidth
- 1;
/* print login name of the user */
printf("%-*.*s ", NMAX
, NMAX
, utmp
.ut_name
);
/* print tty user is on */
/* long form: all (up to) LMAX chars */
printf("%-*.*s", LMAX
, LMAX
, utmp
.ut_line
);
/* short form: 2 chars, skipping 'tty' if there */
if (utmp
.ut_line
[0]=='t' && utmp
.ut_line
[1]=='t' && utmp
.ut_line
[2]=='y')
printf("%-2.2s", &utmp
.ut_line
[3]);
printf("%-2.2s", utmp
.ut_line
);
printf(" %-14.14s", utmp
.ut_host
);
/* print when the user logged in */
printf("%2ddays ", (idle
+ 12 * 60) / (24 * 60));
/* print CPU time for all processes & children */
/* print cpu time for interesting process */
/* what user is doing, either command tail or args */
printf(" %-.*s\n", width
-1, doing
);
/* find & return number of minutes current tty has been idle */
strcpy(ttyname
, "/dev/");
strcatn(ttyname
, utmp
.ut_line
, LMAX
);
lastaction
= stbuf
.st_atime
;
* 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".
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 */
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");
printf(" %2d%s%2d", p
->tm_mday
, month
[p
->tm_mon
], p
->tm_year
);
* readpr finds and reads in the array pr, containing the interesting
* parts of the proc and user tables for each live process.
* We only accept procs whos controlling tty has a pgrp equal to the
* pgrp of the proc. This accurately defines the notion of the current
* process(s), but because of time skew, we always read in the tty struct
* after reading the proc, even though the same tty struct may have been
struct pte
*Usrptma
, *usrpt
, *pte
, apte
;
Usrptma
= (struct pte
*) nl
[X_USRPTMA
].n_value
;
usrpt
= (struct pte
*) nl
[X_USRPT
].n_value
;
if((mem
= open("/dev/mem", 0)) < 0) {
fprintf(stderr
, "No mem\n");
if ((swap
= open("/dev/drum", 0)) < 0) {
fprintf(stderr
, "No drum\n");
* read mem to find swap dev.
lseek(kmem
, (long)nl
[X_SWAPDEV
].n_value
, 0);
read(kmem
, &nl
[X_SWAPDEV
].n_value
, sizeof(nl
[X_SWAPDEV
].n_value
));
* Find base of and parameters of swap
lseek(kmem
, (long)nl
[X_NSWAP
].n_value
, 0);
read(kmem
, &nswap
, sizeof(nswap
));
lseek(kmem
, (long)nl
[X_DMMIN
].n_value
, 0);
read(kmem
, &dmmin
, sizeof(dmmin
));
lseek(kmem
, (long)nl
[X_DMMAX
].n_value
, 0);
read(kmem
, &dmmax
, sizeof(dmmax
));
lseek(kmem
, (long)nl
[X_NPROC
].n_value
, 0);
read(kmem
, &nproc
, sizeof(nproc
));
pr
= (struct pr
*)calloc(nproc
, sizeof (struct pr
));
lseek(kmem
, (long)nl
[X_PROC
].n_value
, 0);
read(kmem
, &aproc
, sizeof(aproc
));
for (pn
=0; pn
<nproc
; pn
++) {
lseek(kmem
, (int)(aproc
+ pn
), 0);
read(kmem
, &mproc
, sizeof mproc
);
/* decide if it's an interesting process */
if (mproc
.p_stat
==0 || mproc
.p_stat
==SZOMB
|| mproc
.p_stat
==SSTOP
|| mproc
.p_pgrp
==0)
/* find & read in the user structure */
if ((mproc
.p_flag
& SLOAD
) == 0) {
/* not in memory - get from swap device */
addr
= dtob(mproc
.p_swaddr
);
lseek(swap
, (long)addr
, 0);
if (read(swap
, &up
, sizeof(up
)) != sizeof(up
)) {
#define INTPPG (NBPG / sizeof (int))
struct pte pagetbl
[NBPG
/ sizeof (struct pte
)];
/* loaded, get each page from memory separately */
p0br
= (int)mproc
.p_p0br
;
pte
= &Usrptma
[btokmx(mproc
.p_p0br
) + szpt
-1];
lseek(kmem
, (long)pte
, 0);
if (read(kmem
, &apte
, sizeof(apte
)) != sizeof(apte
))
lseek(mem
, ctob(apte
.pg_pfnum
), 0);
if (read(mem
,pagetbl
,sizeof(pagetbl
)) != sizeof(pagetbl
))
for(cc
=0; cc
<UPAGES
; cc
++) { /* get u area */
int upage
= pagetbl
[NPTEPG
-UPAGES
+cc
].pg_pfnum
;
lseek(mem
,ctob(upage
),0);
if (read(mem
,((int *)&up
)+INTPPG
*cc
,NBPG
) != NBPG
)
szpt
= up
.u_pcb
.pcb_szpt
;
pr
[np
].w_seekaddr
= ctob(apte
.pg_pfnum
);
vstodb(0, CLSIZE
, &up
.u_smap
, &db
, 1);
pr
[np
].w_lastpg
= dtob(db
.db_base
);
/* only include a process whose tty has a pgrp which matchs its own */
lseek(kmem
, (long)up
.u_ttyp
, 0);
if (read(kmem
, &ttyent
, sizeof(ttyent
)) != sizeof(ttyent
))
if (ttyent
.t_pgrp
!= mproc
.p_pgrp
)
/* save the interesting parts */
pr
[np
].w_pid
= mproc
.p_pid
;
pr
[np
].w_flag
= mproc
.p_flag
;
pr
[np
].w_size
= mproc
.p_dsize
+ mproc
.p_ssize
;
pr
[np
].w_igintr
= (((int)up
.u_signal
[2]==1) +
2*((int)up
.u_signal
[2]>1) + 3*((int)up
.u_signal
[3]==1)) +
6*((int)up
.u_signal
[3]>1);
up
.u_ru
.ru_utime
.tv_sec
+ up
.u_ru
.ru_stime
.tv_sec
;
up
.u_cru
.ru_utime
.tv_sec
+ up
.u_cru
.ru_stime
.tv_sec
;
pr
[np
].w_tty
= up
.u_ttyd
;
pr
[np
].w_uid
= mproc
.p_uid
;
up
.u_comm
[14] = 0; /* Bug: This bombs next field. */
strcpy(pr
[np
].w_comm
, up
.u_comm
);
* Get args if there's a chance we'll print it.
* Cant just save pointer: getargs returns static place.
* Cant use strcpyn: that crock blank pads.
strcatn(pr
[np
].w_args
,getargs(&pr
[np
]),ARGWIDTH
);
if (pr
[np
].w_args
[0]==0 || pr
[np
].w_args
[0]=='-' && pr
[np
].w_args
[1]<=' ' || pr
[np
].w_args
[0] == '?') {
strcat(pr
[np
].w_args
, " (");
strcat(pr
[np
].w_args
, pr
[np
].w_comm
);
strcat(pr
[np
].w_args
, ")");
* getargs: given a pointer to a proc structure, this looks at the swap area
* and tries to reconstruct the arguments. This is straight out of ps.
static int abuf
[CLSIZE
*NBPG
/sizeof(int)];
struct pte pagetbl
[NPTEPG
];
if ((p
->w_flag
& SLOAD
) == 0) {
lseek(swap
, p
->w_lastpg
, 0);
if (read(swap
, abuf
, sizeof(abuf
)) != sizeof(abuf
))
if (read(mem
,pagetbl
,NBPG
) != NBPG
)
if (pagetbl
[NPTEPG
-CLSIZE
-UPAGES
].pg_fod
==0 && pagetbl
[NPTEPG
-CLSIZE
-UPAGES
].pg_pfnum
) {
lseek(mem
,ctob(pagetbl
[NPTEPG
-CLSIZE
-UPAGES
].pg_pfnum
),0);
if (read(mem
,abuf
,sizeof(abuf
)) != sizeof(abuf
))
lseek(swap
, p
->w_lastpg
, 0);
if (read(swap
, abuf
, sizeof(abuf
)) != sizeof(abuf
))
abuf
[sizeof(abuf
)/sizeof(abuf
[0])-1] = 0;
for (ip
= &abuf
[sizeof(abuf
)/sizeof(abuf
[0])-2]; ip
> abuf
;) {
/* Look from top for -1 or 0 as terminator flag. */
if (*--ip
== -1 || *ip
== 0) {
nbad
= 0; /* up to 5 funny chars as ?'s */
for (cp1
= cp
; cp1
< (char *)&abuf
[sizeof(abuf
)/sizeof(abuf
[0])]; cp1
++) {
if (c
==0) /* nulls between args => spaces */
else if (c
< ' ' || c
> 0176) {
} else if (c
=='=') { /* Oops - found an
while (cp1
>cp
&& *--cp1
!=' ')
while (*--cp1
==' ') /* strip trailing spaces */
* Given a base/size pair in virtual swap area,
* return a physical base/size pair which is the
* (largest) initial, physically contiguous block.
vstodb(vsbase
, vssize
, dmp
, dbp
, rev
)
register struct dblock
*dbp
;
register int blk
= dmmin
;
register swblk_t
*ip
= dmp
->dm_map
;
if (vsbase
< 0 || vsbase
+ vssize
> dmp
->dm_size
)
if (*ip
<= 0 || *ip
+ blk
> nswap
)
dbp
->db_size
= min(vssize
, blk
- vsbase
);
dbp
->db_base
= *ip
+ (rev
? blk
- (vsbase
+ dbp
->db_size
) : vsbase
);
/* printf("%s\n", cp); */