allbery@ncoast.UUCP (Brandon S. Allbery) (08/02/87)
Herein are some programs I use under System V. They are: avenrun -- a daemon which calculates an approximate load average from the "runque" and "runocc" parameters in the kernel "sysinfo" structure. Thanks to Phil Budne for the original, which forged "rwho" packets for USG systems on BSD networks; this one puts its load average into an array of 3 (double)'s in a shm segment identified by ftok("/unix", 'a'). w -- The BSD "w" program, large as life. It works as expected, except that the "JCPU" is a kludge; USG doesn't keep per-session CPU time. csl -- A status line program. It displays things in "panels" which "rotate" every 5 seconds. The information displayed is: - sender and subject of mail in your mailbox, one message per panel - current program (if possible), login name, # of messages in mailbox, current date/time - full name and terminal (useful!), # non-daemons running - list of system users, similar to "who -q" - lines from $HOME/.messages, one per panel I've been using these for a few months without problems. Enjoy! ++Brandon #--------------------------------CUT HERE------------------------------------- #! /bin/sh # # This is a shell archive. Save this into a file, edit it # and delete all lines above this comment. Then give this # file to sh by executing the command "sh file". The files # will be extracted into the current directory owned by # you with default permissions. # # The files contained herein are: # # -rw-r--r-- 1 allbery System 1579 May 7 22:09 README.w # -rw-r--r-- 1 allbery System 8210 May 7 21:52 avenrun.c # -rw-r--r-- 1 allbery System 8313 May 7 21:52 csl.c # -rw-r--r-- 1 allbery System 7909 May 7 21:28 w.c # -rw-r--r-- 1 allbery System 1023 May 7 22:51 avenrun.1m # -rw-r--r-- 1 allbery System 3217 May 7 22:39 csl.1l # -rw-r--r-- 1 allbery System 3056 May 7 23:12 w.1l # echo 'x - README.w' if test -f README.w; then echo 'shar: not overwriting README.w'; else sed 's/^X//' << '________This_Is_The_END________' > README.w XThe following are some programs which give you some familiar BSD utilities Xin a System V environment. The programs are: X X w - Yes, "w", as big as life! Requires... X avenrun - A program to be invoked in /etc/rc which computes X a "load average" from information in the "sysinfo" X structure of the kernel. Based on a program by X Phil Budne which forges "rwhod" packets for mixed X networks; #define RWHOD for the original program. X csl - I have no idea what the BSD "sysline" does, but this X is my idea of a status line. The name stands for X "cyclic status line"; it doesn't even attempt to X fit everything into one line, instead it cycles X through a set of "panels". See the man page. X This also wants "avenrun" to be running. X XNote that "w" (and ONLY "w") can be compiled for System III or Xenix 3.0; Xthis is in fact the default, use -DSYS5 to get the full version. X XNo Makefile; the compile commands are trivial: X X cc -O -o w w.c -DSYS5 # omit the -DSYS5 for System III X cc -O -o avenrun avenrun.c # add -DRWHOD for rwhod forgery X cc -O -o csl csl.c X X"w" requires read permission on /dev/kmem, /dev/mem, and /dev/swap. "avenrun" Xrequires read permission on /dev/kmem (to read the sysinfo structure). "csl" Xcan run as a normal user program. On tdi2 we keep /dev/*mem and /dev/swap X-r--r----- root/sys and have "avenrun" and "w" setgid sys. X XIf someone wants to tell me what "sysline" under BSD does, I'd be interested Xin writing a real one. But "csl" is everything in one small package, so I Xwill probably continue to use it. X XEnjoy! X X++Brando ________This_Is_The_END________ if test `wc -l < README.w` -ne 37; then echo 'shar: README.w was damaged during transit (should have been 37 bytes)' fi fi ; : end of overwriting check echo 'x - avenrun.c' if test -f avenrun.c; then echo 'shar: not overwriting avenrun.c'; else sed 's/^X//' << '________This_Is_The_END________' > avenrun.c X/* X * ldavg.c -- compute load averages for System V X * Phil Budne @ Boston U / DSG X * X * Forges BSD 4.2 rwhod packets containing system load averages X * (#ifdef RWHOD for this, else a shm segment is used, ftok("/unix", 'a')) X */ X X# include <sys/types.h> /* system types */ X# include <sys/sysinfo.h> /* sysinfo structure */ X# include <sys/utsname.h> /* for uname(2) */ X# include <sys/stat.h> /* for stat(2) */ X# include <sys/param.h> /* for HZ */ X# include <stdio.h> X# include <nlist.h> X# include <time.h> X# include <math.h> X# include <utmp.h> X# include <fcntl.h> X#ifdef RWHOD X# include "rwhod.h" /* (from BSD) */ X#else SHM X# include <sys/ipc.h> X# include <sys/shm.h> X#endif RWHOD X X/* # define DEBUG /**/ X#ifdef RWHOD X# define UDP 1 X# define DSK 1 X# define PMUL 100 X X# if UDP X# include "netdb.h" Xunsigned short port = 513; Xunsigned long ipaddr; X# endif X#endif RWHOD X Xextern struct utmp *getutent(); X X# define SYSTEM "/unix" X# define KMEM "/dev/kmem" X Xstruct nlist nl[] = { X# define NL_SYSINFO 0 X { "_sysinfo" }, /* 0 */ X# define NL_LBOLT 1 X { "_lbolt" }, /* 1 */ X { 0 } X}; X X#ifdef RWHOD Xstruct whod proto; Xstruct utsname utsn; Xchar whopacket[100]; X#else SHM Xkey_t aven_key; Xint aven_shm; Xdouble *aven_seg; X#endif RWHOD Xint fd, memfd; X Xchar *system = SYSTEM; Xchar *kmem = KMEM; Xchar *argv0; X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X switch (fork()) { X case -1: X perror("fork"); X exit(1); X case 0: X break; X default: X exit(0); X } X argv0 = argv[0]; X#ifdef RWHOD X uname(&utsn); /* get system names */ X#endif RWHOD X setpgrp(); /* create own pgrp */ X init_nlist(); /* get name values, open kmem */ X init_packet(); /* initialize packet prototype */ X doit(); /* never returns */ X} /* main */ X Xinit_nlist() { X nlist(system, nl); /* get system values */ X X if(nl[NL_SYSINFO].n_value == 0) { X fprintf(stderr, "%s: can't find sysinf structure\n", argv0); X exit(1); X } /* no value */ X X if ((memfd = open(kmem, O_RDONLY)) < 0) { X fprintf(stderr, "%s: no mem\n", argv0); X exit(1); X } /* could not open kmem */ X X} /* init_nlist */ X X# define PERIOD 5 /* sample period (in seconds) */ X# define INTERVAL1 60 /* average interval 1 (in seconds) */ X# define INTERVAL2 (5*60) /* average interval 2 (in seconds) */ X# define INTERVAL3 (15*60) /* average interval 3 (in seconds) */ X# define PACKINTERVAL 30 /* interval for make_packet */ X Xdoit() { X struct sysinfo sinf; X int packt = 0; X long occ, que, nocc, nque, n, c; X double avg1, avg2, avg3, new; X double exp1, exp2, exp3; X X exp1 = exp( - ((double) PERIOD) / INTERVAL1 ); X exp2 = exp( - ((double) PERIOD) / INTERVAL2 ); X exp3 = exp( - ((double) PERIOD) / INTERVAL3 ); X X getsysinf(&sinf); /* prime the pump */ X occ = sinf.runocc; /* number of samples */ X que = sinf.runque; /* run queue summation */ X X avg1 = avg2 = avg3 = ((double) que) / occ; X X for( ; ; ) { X if( --packt < 0 ) { X#ifdef RWHOD X make_packet((int) (avg1 * PMUL), X (int) (avg2 * PMUL), X (int) (avg3 * PMUL)); X#else SHM X make_packet(avg1, avg2, avg3); X#endif RWHOD X packt = PACKINTERVAL / PERIOD; X } /* packet time */ X X/* printf("runque: %ld runocc: %ld\n", que, occ ); /**/ X X sleep(PERIOD); X getsysinf(&sinf); /* get new info */ X nocc = sinf.runocc; X nque = sinf.runque; X X n = nocc - occ; /* get number of times updated */ X if( n <= 0 ) continue; X c = nque - que - n; /* get number of runners w/o us */ X if( c < 0 ) c = 0; /* mumble! */ X X new = ((double) c ) / n; /* new instantaneous avg */ X X /************************************************/ X /* The following formula is used to achieve */ X /* exponential decay of old measurements: */ X /* avgN = avgN * expN + new * (1 - expN) */ X /* */ X /* However, the factorized forms below */ X /* require fewer floating point operations. */ X /************************************************/ X X avg1 = ((avg1 - new) * exp1) + new; X avg2 = ((avg2 - new) * exp2) + new; X avg3 = ((avg3 - new) * exp3) + new; X X occ = nocc; X que = nque; X X } /* for ever */ X} /* doit */ X Xgetsysinf(s) Xstruct sysinfo *s; X{ X l_lseek(memfd, (long)nl[NL_SYSINFO].n_value, 0); X r_read(memfd, (char *)s, sizeof(struct sysinfo)); X} X X/* lseek with error checking */ Xl_lseek(fd, offset, whence) Xint fd, whence; Xlong offset; X{ X if (lseek(fd, offset, whence) == -1) { X fprintf(stderr, "%s: error on lseek\n", argv0); X exit(1); X } X} X X/* read with error checking */ Xr_read (fd, buf, nbytes) Xint fd, nbytes; Xchar *buf; X{ X if (read(fd, buf, nbytes) != nbytes) { X fprintf(stderr, "%s: error on read\n", argv0); X exit(1); X } X} X Xinit_packet() { X#ifdef RWHOD X time_t boothz; X# if UDP X struct hostent *he; X X he = gethostbyname( "localnet" ); X if( he == NULL || he->h_addr == 0 ) { X fprintf(stderr, "no address: localnet\n"); X exit( 1 ); X } X ipaddr = he->h_addr; X# endif X# if DSK X sprintf(whopacket, "/usr/spool/rwho/whod.%s", utsn.nodename); X# endif X memset(&proto, '\0', sizeof proto); /* clear proto packet */ X X strncat(proto.wd_hostname, utsn.nodename, 9); /* at most 9, add null */ X proto.wd_vers = WHODVERSION; X proto.wd_type = WHODTYPE_STATUS; X X l_lseek(memfd, (long)nl[NL_LBOLT].n_value, 0); X r_read(memfd, (char *)&boothz, sizeof( boothz ) ); X proto.wd_boottime = time(0) - (boothz / HZ); X#else SHM X if ((aven_key = ftok(SYSTEM, 'a')) == (key_t) -1) { X perror(SYSTEM); X exit(1); X } X if ((aven_shm = shmget(aven_key, 3 * sizeof (double), IPC_CREAT|IPC_EXCL|0644)) < 0) { X perror("shmget"); X exit(1); X } X if ((int) (aven_seg = (double *) shmat(aven_shm, (char *) 0, 0)) == -1) { X perror("shmat"); X if (shmdt((char *) aven_seg) == -1) X perror("shmdt"); X if (shmctl(aven_shm, IPC_RMID, (struct shmid_ds *) 0) < 0) X perror("shmctl(IPC_RMID)"); X exit(1); X } X#endif RWHOD X X} /* init_packet */ X Xmake_packet(iavg1, iavg2, iavg3) X#ifdef RWHOD Xlong iavg1, iavg2, iavg3; X#else SHM Xdouble iavg1, iavg2, iavg3; X#endif RWHOD X{ X#ifdef RWHOD X static struct whod packet; /* local packet copy */ X register struct whoent *wep; /* pointer to packet whoent */ X register struct utmp *utp; /* return from getutent */ X int whof, cc; /* output file, char count */ X X packet = proto; /* copy proto packet */ X time(&packet.wd_sendtime); X time(&packet.wd_recvtime); /* forge this !! */ X packet.wd_loadav[0] = iavg1; X packet.wd_loadav[1] = iavg2; X packet.wd_loadav[2] = iavg3; X X setutent(); /* open utmp file */ X wep = &packet.wd_we[0]; /* get pointer to first user in pkt */ X X while( (utp = getutent()) != NULL ) { X if( (utp->ut_type == USER_PROCESS) && utp->ut_user[0]) { X strncpy(wep->we_utmp.out_line, utp->ut_id, 4); X wep->we_utmp.out_line[4] = '\0'; X X strncpy(wep->we_utmp.out_name, utp->ut_user, 8); X X wep->we_utmp.out_time = utp->ut_time; X X wep->we_idle = idletime(utp); X wep++; /* bump packet pointer */ X } /* user process */ X } /* while */ X endutent(); X X# if DSK X whof = creat(whopacket, 0644); /* open packt file */ X if( whof >= 0 ) { X cc = (char *)wep - (char *)&packet; X if( write(whof, (char *)&packet, cc) != cc ) X perror("write failed"); X close(whof); X } /* file opened */ X else perror(whopacket); X# endif X# if UDP X cc = (char *)wep - (char *)&packet; X udpsend( (char *)&packet, cc, ipaddr, port, port, 1); X# endif X# ifdef DEBUG X fprintf(stderr, "wrote packet (%d)\n", cc); X fflush(stderr); X# endif X#else SHM X aven_seg[0] = iavg1; X aven_seg[1] = iavg2; X aven_seg[2] = iavg3; X#endif RWHOD X} /* make_packet */ X X#ifdef RWHOD Xidletime(up) Xstruct utmp *up; X{ X register int i; X register char *cp, *dp; X char ttyname[10]; X struct stat buf; X time_t now; X X cp = "/dev/"; X dp = ttyname; X X while( *cp != '\0' ) /* copy "/dev/" */ X *dp++ = *cp++; X X cp = up->ut_line; /* get line name */ X if( *cp == 's' ) /* starts with an 's'? (sxtnnn) */ X *dp++ = 'v'; /* insert a 'v' */ X X for( i = 0; i < 8; i++ ) /* copy line name */ X if( (*dp++ = *cp++) == '\0' ) break; /* or until null */ X X if( stat(ttyname, &buf) != 0 ) /* get file status */ X return( 0 ); X X time(&now); /* get current time */ X i = now - buf.st_atime; /* get differnce from last acces */ X return( i ); /* return idle time */ X} /* idletime */ X#endif RWHOD ________This_Is_The_END________ if test `wc -l < avenrun.c` -ne 342; then echo 'shar: avenrun.c was damaged during transit (should have been 342 bytes)' fi fi ; : end of overwriting check echo 'x - csl.c' if test -f csl.c; then echo 'shar: not overwriting csl.c'; else sed 's/^X//' << '________This_Is_The_END________' > csl.c X#include <curses.h> X#include <a.out.h> X#include <term.h> X#include <time.h> X#include <fcntl.h> X#include <varargs.h> X#include <pwd.h> X#include <signal.h> X#include <sys/types.h> X#include <sys/ipc.h> X#include <sys/shm.h> X#include <sys/param.h> X#include <sys/dir.h> X#include <sys/proc.h> X#include <sys/user.h> X#include <sys/var.h> X#include <sys/inode.h> X#include <sys/file.h> X#include <sys/stat.h> X#include <sys/sysmacros.h> X#include <utmp.h> X X#define DISPLAY 5 /* seconds between panels */ X Xchar SObuf[BUFSIZ], lock[1024], messages[1024], fullname[64], logname[64]; Xdouble *avenrun; Xint kmem, nproc, mem, swap, mail; Xdaddr_t swplo; X Xextern char *getenv(), *curcmd(), *ttyname(), *strrchr(), *itoa(), *fname(), *strchr(); Xextern int cleanup(); Xextern void setpwent(); Xextern struct passwd *getpwnam(); Xextern long time(); Xextern void setutent(); Xextern struct utmp *getutent(); X Xstruct nlist k[] = { X {"_proc"}, X {"_v"}, X {"_swplo"}, X {0}, X}; X Xchar *weekday[] = { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", X}; X Xmain() { X int cnt, nmsg, avend_id, aprogs, panel; X char *cp, *cmd; X key_t avend_k; X struct tm *now; X long instant; X char msg[1024], subj[1024], from[128], mailf[128]; X char *fp, *bp, *logtty; X struct utmp *user; X X setupterm((char *) 0, fileno(stdout), (int *) 0); X if (!has_status_line) { X fprintf(stderr, "csl: terminal doesn't have a status line\n"); X exit(1); X } X switch (fork()) { X case 0: X break; X case -1: X perror("csl"); X exit(1); X default: X exit(0); X } X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X signal(SIGHUP, cleanup); X freopen("/dev/null", "r", stdin); X if (nlist("/unix", k) != 0) { X perror("/unix"); X exit(2); X } X if ((kmem = open("/dev/kmem", O_RDONLY)) == -1) { X perror("/dev/kmem"); X exit(1); X } X if ((mem = open("/dev/mem", O_RDONLY)) == -1) { X perror("/dev/mem"); X exit(1); X } X if ((swap = open("/dev/swap", O_RDONLY)) == -1) { X perror("/dev/swap"); X exit(1); X } X if ((cp = getenv("LOGNAME")) == (char *) 0) X strcpy(logname, "daemon"); X else X strcpy(logname, cp); X fname(fullname); X getnproc(); X if ((cp = getenv("HOME")) == (char *) 0) { X strcpy(lock, "./.syslinelock"); X strcpy(messages, "./.messages"); X } X else { X sprintf(lock, "%s/.syslinelock", cp); X sprintf(messages, "%s/.messages", cp); X } X setbuf(stdout, SObuf); X if ((avend_k = ftok("/unix", 'a')) == (key_t) -1) { X perror("/unix"); X exit(1); X } X if ((avend_id = shmget(avend_k, 3 * sizeof (double), 0644)) < 0) X avenrun = (double *) 0; X else if ((int) (avenrun = (double *) shmat(avend_id, (char *) 0, SHM_RDONLY)) == -1) { X perror("shmat"); X exit(1); X } X lseek(kmem, k[3].n_value, 0); X read(kmem, &swplo, sizeof swplo); X logtty = strrchr(ttyname(fileno(stdout)), '/') + 1; X sprintf(mailf, "/usr/mail/%s", logname); X for (;;) { X if (access(lock, 0) == 0) { X putp(dis_status_line); X fflush(stdout); X while (access(lock, 0) == 0) X sleep(30); X } X panel = 1; X from[0] = '\0'; X subj[0] = '\0'; X if ((mail = open(mailf, O_RDONLY)) == -1) X nmsg = 0; X else { X cnt = 0; X while (rgets(mail, msg, sizeof msg) != -1) { X if (strncmp(msg, "From ", 5) == 0) { X int mcnt; X X if (from[0] != '\0') X showpanel(panel++, "New mail from %s: %s", from, (subj[0]? subj: "[No subject]")); X subj[0] = '\0'; X cnt++; X for (mcnt = 5; msg[mcnt] != '\0'; mcnt++) X if (msg[mcnt] == ' ') X break; X msg[mcnt++] = '\0'; X if ((fp = strrchr(msg + 5, '!')) == (char *) 0) X strcpy(from, msg + 5); X else { X *fp = '\0'; X if ((bp = strrchr(msg + 5, '!')) == (char *) 0) X bp = msg + 5; X *fp = '!'; X strcpy(from, bp); X } X subj[0] = '\0'; X } X else if (strncmp(msg, "Subject: ", 9) == 0) X strcpy(subj, msg + 9); X } X close(mail); X if (from[0] != '\0') X showpanel(panel++, "New mail from %s: %s", from, (subj[0]? subj: "[No subject]")); X nmsg = cnt; X } X cmd = curcmd(&aprogs); X time(&instant); X now = localtime(&instant); X if (avenrun != (double *) 0) X showpanel(panel++, " %s // %s // %4.2f %4.2f %4.2f // %s msgs // %3s %02d/%02d %02d:%02d ", cmd, logname, avenrun[0], avenrun[1], avenrun[2], (nmsg == 0? "No": itoa(nmsg)), weekday[now->tm_wday], now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min); X else X showpanel(panel++, " %s // %s // %s msgs // %3s %02d/%02d %02d:%02d //", cmd, logname, (nmsg == 0? "No": itoa(nmsg)), weekday[now->tm_wday], now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min); X showpanel(panel++, " ``%s'' on %s // %d active programs", fullname, logtty, aprogs); X if ((mail = open(messages, O_RDONLY)) != -1) { X while (rgets(mail, msg, sizeof msg) != -1) X showpanel(panel++, "%s", msg); X close(mail); X } X setutent(); X cnt = 0; X msg[0] = '\0'; X while ((user = getutent()) != (struct utmp *) 0) { X if (user->ut_type != USER_PROCESS) X continue; X sprintf(subj, "%.8s", user->ut_user); X if (msg[0] != '\0') X strcat(msg, ", "); X strcat(msg, subj); X cnt++; X } X showpanel(panel++, " %d users: %s", cnt, msg); X } X} X Xcleanup() { X if (avenrun != (double *) 0 && shmdt(avenrun) < 0) { X perror("shmdt"); X exit(1); X } X reset_shell_mode(); X exit(0); X} X Xgetnproc() { X struct var v; X X lseek(kmem, k[1].n_value, 0); X read(kmem, &v, sizeof v); X nproc = v.v_proc; X} X Xchar *curcmd(progp) Xint *progp; { X struct proc p; X struct user cmd; X struct file f; X struct inode i; X static struct stat idev; X static int pgrp, mypid; X static char cmdname[DIRSIZ + 1]; X long stime; X int cnt; X static int didit = 0; X X if (didit == 0) { X stat("/dev/null", &idev); X pgrp = getpgrp(); X mypid = getpid(); X didit = 1; X } X *progp = 0; X stime = 0L; X strcpy(cmdname, "???"); X lseek(kmem, k[0].n_value, 0); X for (cnt = 0; cnt < nproc; cnt++) { X read(kmem, &p, sizeof p); X if (p.p_stat != SRUN && p.p_stat != SSLEEP) X continue; X if (p.p_pid == mypid) X continue; X if (p.p_pgrp != pgrp) X continue; X if (p.p_stat == SSLEEP && (long) p.p_wchan >= k[0].n_value && (long) p.p_wchan < k[0].n_value + nproc * sizeof p) X continue; /* sleeping on a child */ X (*progp)++; X if (p.p_flag & SLOAD) { X lseek(mem, (long) (NBPC * p.p_addr), 0); X read(mem, &cmd, sizeof cmd); X } X else { X lseek(swap, (swplo + p.p_swaddr + ctod(p.p_swsize) - ctod(USIZE)) << 9, 0); X read(swap, &cmd, sizeof cmd); X } X if (cmd.u_ttyp == 0) X continue; X/* AAUGH! USG VAR csh'es close stdin!!! That's absolutely insane!!! X * if (p.p_pid != p.p_ppid && cmd.u_ofile[0] == 0) X * continue; X */ X if (cmd.u_ofile[0] != 0) { X lseek(kmem, cmd.u_ofile[0], 0); X read(kmem, &f, sizeof f); X lseek(kmem, f.f_inode, 0); X read(kmem, &i, sizeof i); X lseek(kmem, k[0].n_value + cnt * sizeof p, 0); X if (i.i_dev == idev.st_dev && i.i_number == idev.st_ino && cmd.u_signal[SIGINT] == (long) SIG_IGN) X continue; /* ignore background processes */ X } X if (cmd.u_start < stime) X continue; X stime = cmd.u_start; X strncpy(cmdname, cmd.u_comm, DIRSIZ); X } X return cmdname; X} X Xrgets(fd, buf, maxlen) Xchar *buf; Xunsigned maxlen; { X unsigned cnt; X X *buf = '\0'; X for (cnt = 0; cnt < maxlen - 1 && read(fd, buf, 1) > 0 && *buf != '\n'; cnt++, buf++) X ; X if (cnt == 0 && *buf != '\n') X return -1; X *buf = '\0'; X return cnt; X} X Xchar *itoa(n) { X static char ibuf[20]; X X sprintf(ibuf, "%d", n); X return ibuf; X} X X/* Does the lint declaration below actually *do* anything? */ X/*VARARGS PRINTFLIKE2*/ Xshowpanel(va_alist) Xva_dcl { X va_list args; X int panel, cnt; X static char buf[5120]; X register char *cp; X X va_start(args); X panel = va_arg(args, int); X cp = va_arg(args, char *); X sprintf(buf, "%2.2d>", panel); X vsprintf(buf + 3, cp, args); X putp(tparm(to_status_line, 0)); X for (cp = buf, cnt = 0; cnt < width_status_line && *cp != '\0'; cp++, cnt++) X putchar(*cp); X while (cnt++ < width_status_line) X putchar(' '); X putp(from_status_line); X fflush(stdout); X sleep(DISPLAY); X} X X/* X * If you use RJE fullnames :0000-Foo Z. Bar(0000):, change this. X * If you aren't running RJE, why bother with it? X */ X Xchar *fname(buf) Xchar *buf; { X static char fbuf[128]; X struct passwd *me; X char *cp; X X setpwent(); X me = getpwnam(logname); X endpwent(); X if (me == (struct passwd *) 0) X strcpy(fbuf, logname); X else { X strcpy(fbuf, me->pw_gecos); X if ((cp = strchr(fbuf, ',')) != (char *) 0) X *cp = '\0'; X } X if (buf == (char *) 0) X return fbuf; X strcpy(buf, fbuf); X return buf; X} ________This_Is_The_END________ if test `wc -l < csl.c` -ne 348; then echo 'shar: csl.c was damaged during transit (should have been 348 bytes)' fi fi ; : end of overwriting check echo 'x - w.c' if test -f w.c; then echo 'shar: not overwriting w.c'; else sed 's/^X//' << '________This_Is_The_END________' > w.c X/* X * %W% %E% %U% ncoast!bsa %Z% X * %Z% Copyright (C) 1985 by Brandon S. Allbery, All Rights Reserved %Z% X */ X X#ifndef lint Xstatic char _SccsId[] = "%W% %E% %U% ncoast!bsa %Z%"; Xstatic char _CopyRt[] = "%Z% Copyright (C) 1985 by Brandon S. Allbery %Z%"; X#endif lint X X#include <stdio.h> X#include <time.h> X#include <signal.h> X#include <a.out.h> X#ifdef SYS5 X#include <sys/types.h> X#endif X#include <sys/param.h> X#include <sys/var.h> X#include <sys/proc.h> X#include <sys/dir.h> X#include <sys/user.h> X#include <sys/stat.h> X#ifdef SYS5 X#include <sys/ipc.h> X#include <sys/shm.h> X#include <sys/sysmacros.h> X#endif X#include <utmp.h> X X#define UTMP "/etc/utmp" X#ifndef KERNEL X#define KERNEL "/unix" X#endif X#define KMEM "/dev/kmem" X#define PMEM "/dev/mem" X#define SMEM "/dev/swap" X X#define MIN (60) X#define HOUR (MIN * 60) X#define DAY (HOUR * 24) X#define WEEK (DAY * 7) X#define MONTH (DAY * 30) X XFILE *kfd; XFILE *mfd; XFILE *sfd; XFILE *utmp; Xlong nproc; Xchar _SObuf[BUFSIZ]; Xdaddr_t swplo; Xshort mypid; X Xstruct nlist kernel[] = { X {"_v"}, X {"_proc"}, X {"_swplo"}, X {NULL}, X}; X Xchar *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X}; X Xstruct tm *localtime(); Xchar *vtime(); Xchar *uptime(); Xchar *itoa(); X Xmain() { X struct utmp user; X short ucnt; X long now; X struct var vars; X#ifdef SYS5 X int shm_id; X key_t shm_k; X double *avenrun; X#endif X X setbuf(stdout, _SObuf); X mypid = getpid(); X time(&now); X if (nlist(KERNEL, kernel) == -1) { X perror(KERNEL); X exit(2); X } X if ((utmp = fopen(UTMP, "r")) == NULL) { X perror(UTMP); X exit(1); X } X ucnt = 0; X while (fread(&user, sizeof user, 1, utmp) > 0) { X#ifdef SYS5 X if (user.ut_type != USER_PROCESS) X#else X if (user.ut_name[0] == '\0') X#endif X continue; X ucnt++; X } X if (kfd == NULL) { X if ((kfd = fopen(KMEM, "r")) == NULL) { X perror(KMEM); X exit(3); X } X } X fseek(kfd, kernel[0].n_value, 0); X fread(&vars, sizeof vars, 1, kfd); X nproc = vars.v_proc; X fseek(kfd, kernel[2].n_value, 0); X fread(&swplo, sizeof swplo, 1, kfd); X if (mfd == NULL) { X if ((mfd = fopen(PMEM, "r")) == NULL) { X perror(PMEM); X exit(4); X } X } X if (sfd == NULL) { X if ((sfd = fopen(SMEM, "r")) == NULL) { X perror(SMEM); X exit(5); X } X } X#ifdef SYS5 X if ((shm_k = ftok(KERNEL, 'a')) == (key_t) -1) { X perror(KERNEL); X exit(2); X } X if ((shm_id = shmget(shm_k, 3 * sizeof *avenrun, 0644)) < 0) { X perror("shmget()"); X exit(2); X } X if ((int) (avenrun = (double *) shmat(shm_id, (char *) 0, SHM_RDONLY)) == -1) { X perror("shmat()"); X exit(2); X } X printf(" %s up%s, %d users, load average: %.1f, %.1f, %.1f\nUser tty login@ idle JCPU PCPU what\n", vtime(&now), uptime(), ucnt, avenrun[0], avenrun[1], avenrun[2]); X if (shmdt((char *) avenrun) == -1) { X perror("shmdt()"); X exit(2); X } X#else X printf(" %s up%s, %d users, load average: not available under sys3\nUser tty login@ idle JCPU PCPU what\n", vtime(&now), uptime(), ucnt); X#endif X rewind(utmp); X while (fread(&user, sizeof user, 1, utmp) > 0) { X#ifdef SYS5 X if (user.ut_type != USER_PROCESS) X#else X if (user.ut_name[0] == '\0') X#endif X continue; X show(&user); X } X fclose(utmp); X exit(0); X} X Xchar *uptime() { X static char timebuf[128]; X#ifdef SYS5 X struct utmp bootdata; X#else X struct proc swapper; X struct user bootproc; X#endif X long oldpos, now; X short cnt, ocnt; X X#ifdef SYS5 X oldpos = fseek(utmp, 0L, 0); X do { X fread(&bootdata, sizeof bootdata, 1, utmp); X } while (bootdata.ut_type != BOOT_TIME); X fseek(utmp, oldpos, 0); X#else X oldpos = fseek(kfd, 0L, 1); X fseek(kfd, kernel[1].n_value, 0); X fread(&swapper, sizeof swapper, 1, kfd); X fseek(kfd, oldpos, 0); X oldpos = fseek(mfd, 0L, 1); X fseek(mfd, ctob(swapper.p_addr), 0); X fread(&bootproc, sizeof bootproc, 1, mfd); X fseek(mfd, 0L, 1); X#endif X time(&now); X#ifdef SYS5 X now -= bootdata.ut_time; X#else X now -= bootproc.u_start; X#endif X if (now < 0L) X return " with strange clock time"; X timebuf[0] = '\0'; X ocnt = 0; X cnt = 0; X while (now >= MONTH) { X cnt++; X now -= MONTH; X } X if (cnt > 0) { X strcat(timebuf, itoa(cnt)); X strcat(timebuf, " mon"); X if (cnt > 1) X strcat(timebuf, "s"); X if (++ocnt == 2) X return timebuf; X } X cnt = 0; X while (now >= WEEK) { X cnt++; X now -= WEEK; X } X if (cnt > 0) { X strcat(timebuf, itoa(cnt)); X strcat(timebuf, " wk"); X if (cnt > 1) X strcat(timebuf, "s"); X if (++ocnt == 2) X return timebuf; X } X cnt = 0; X while (now >= DAY) { X cnt++; X now -= DAY; X } X if (cnt > 0) { X strcat(timebuf, itoa(cnt)); X strcat(timebuf, " day"); X if (cnt > 1) X strcat(timebuf, "s"); X if (++ocnt == 2) X return timebuf; X } X cnt = 0; X while (now >= HOUR) { X cnt++; X now -= HOUR; X } X if (cnt > 0) { X strcat(timebuf, itoa(cnt)); X strcat(timebuf, " hr"); X if (cnt > 1) X strcat(timebuf, "s"); X } X return timebuf; X} X Xchar *itoa(n) Xint n; { X static char buf[20]; X X sprintf(buf, " %d", n); X return buf; X} X Xshow(uinfo) Xstruct utmp *uinfo; { X struct stat sbuf; X struct proc proc; X struct user prog; X char ttydev[16]; X long now, cnt, offset, jcpu; X short mpid, isswap; X FILE *ufd; X X strcpy(ttydev, "/dev/"); X strncpy(&ttydev[5], uinfo->ut_line, 8); X ttydev[13] = '\0'; X if (stat(ttydev, &sbuf) != 0) { X perror(ttydev); X return; X } X time(&now); X now -= sbuf.st_atime; X printf("%-8.8s %-8.8s %7s ", uinfo->ut_name, uinfo->ut_line, vtime(&uinfo->ut_time)); X if (now > DAY) X printf("%4dd", now / DAY); X else if (now > HOUR) X printf("%2d:%02d", now / HOUR, (now % HOUR) / 60); X else if (now > MIN) X printf("%5d", now / MIN); X else X printf(" "); X putchar(' '); X fseek(kfd, kernel[1].n_value, 0); X mpid = -1; X jcpu = 0; X for (cnt = 0; cnt < nproc; cnt++) { X fread(&proc, sizeof proc, 1, kfd); X if (proc.p_stat == 0 || proc.p_stat == SZOMB || proc.p_stat == SWAIT) X continue; X if (proc.p_flag & SLOAD) { X fseek(mfd, ctob(proc.p_addr), 0); X fread(&prog, sizeof prog, 1, mfd); X isswap = 0; X } X else { X#ifdef SYS5 X fseek(sfd, (swplo + proc.p_swaddr + ctod(proc.p_swsize) - ctod(USIZE)) << 9, 0); X#else X fseek(sfd, (long) (swplo + proc.p_addr) << 9, 0); X#endif X fread(&prog, sizeof prog, 1, sfd); X isswap = 1; X } X if (prog.u_ttyp == NULL) X continue; X if (sbuf.st_rdev != prog.u_ttyd) X continue; X jcpu += prog.u_utime + prog.u_stime; X if (proc.p_pid == mypid) X continue; X if (proc.p_pid > mpid) X mpid = proc.p_pid; X } X if (mpid == -1) X printf("[can't stat]"); X else { X fseek(kfd, kernel[1].n_value, 0); X for (cnt = 0; cnt < nproc; cnt++) { X fread(&proc, sizeof proc, 1, kfd); X if (proc.p_pid == mpid) X break; X } X if (proc.p_pid != mpid) X printf("[interstice]"); X else { X if (proc.p_flag & SLOAD) { X fseek(mfd, ctob(proc.p_addr), 0); X fread(&prog, sizeof prog, 1, mfd); X } X else { X#ifdef SYS5 X fseek(sfd, (swplo + proc.p_swaddr + ctod(proc.p_swsize) - ctod(USIZE)) << 9, 0); X#else X fseek(sfd, (long) (swplo + proc.p_addr) << 9, 0); X#endif X fread(&prog, sizeof prog, 1, sfd); X } X printf("%3d:%02d %3d:%02d ", jcpu / MIN, jcpu % MIN, (prog.u_utime + prog.u_stime) / MIN, (prog.u_utime + prog.u_stime) % MIN); X prog.u_procp = &proc; X pcmd(&prog); X } X } X putchar('\n'); X} X Xchar *vtime(when) Xlong *when; { X struct tm then, now; X static char buf[20]; X short hour, min, ampm; X long clock; X X time(&clock); X now = *localtime(&clock); X then = *localtime(when); X if (then.tm_mon != now.tm_mon || then.tm_mday != now.tm_mday) { X sprintf(buf, "%s %2d", months[then.tm_mon], then.tm_mday); X return buf; X } X min = then.tm_min; X if (then.tm_hour == 0) { X ampm = 'a'; X hour = 12; X } X else if (then.tm_hour > 0 && then.tm_hour < 12) { X ampm = 'a'; X hour = then.tm_hour; X } X else if (then.tm_hour == 12) { X ampm = 'p'; X hour = 12; X } X else { X ampm = 'p'; X hour = then.tm_hour - 12; X } X sprintf(buf, "%d:%02d%cm", hour, min, ampm); X return buf; X} X Xpcmd(uinfo) Xstruct user *uinfo; { X /* someday look up the user's command line */ X printf("%-.14s", uinfo->u_comm); X} ________This_Is_The_END________ if test `wc -l < w.c` -ne 390; then echo 'shar: w.c was damaged during transit (should have been 390 bytes)' fi fi ; : end of overwriting check echo 'x - avenrun.1m' if test -f avenrun.1m; then echo 'shar: not overwriting avenrun.1m'; else sed 's/^X//' << '________This_Is_The_END________' > avenrun.1m X.TH AVENRUN 1M X.SH NAME Xavenrun \- compute load averages X.SH SYNOPSIS X.B avenrun X.SH DESCRIPTION X.B XAvenrun Xis a program to generate load average numbers on systems running AT&T's Xversion of UNIX(R). (UNIX is a registered trademark of AT&T.) It does Xits job by creating a shared memory segment whose key is the value of X.I ftok(\"/unix\", 'a') Xwhich contains three (double)'s. It then updates this structure with the X1-minute, 5-minute, and 15-minute load averages, as calculated from the Xsysinfo structure of the kernel (the "runque" and "runocc" entries). X.P XThe load average is only an approximation of the actual number, which is Xthe average number of processes on the run queue. It is about as close as Xyou can get without actually computing the load average in the kernel as Xprocesses are placed on the run queue. X.SH FILES X.ta \w'/dev/kmem 'u X/unix system namelist X.br X/dev/kmem kernel memory, for the sysinfo structure X.DT X.SH SEE ALSO Xstdipc(3), shmop(2), sa(4). X.SH BUGS XIt really should be in the kernel. ________This_Is_The_END________ if test `wc -l < avenrun.1m` -ne 30; then echo 'shar: avenrun.1m was damaged during transit (should have been 30 bytes)' fi fi ; : end of overwriting check echo 'x - csl.1l' if test -f csl.1l; then echo 'shar: not overwriting csl.1l'; else sed 's/^X//' << '________This_Is_The_END________' > csl.1l X.TH CSL local X.SH NAME Xcsl \- cyclic status line X.SH SYNOPSIS X.B csl X.SH DESCRIPTION X.B Csl Xis a program for displaying interesting information on the status line Xof a terminal which possesses one. The information is shown in "panels" Xwhich are displayed for five seconds before going to the next panel. XAfter the last panel is displayed, the first panel will be redisplayed. X.PP XThe panels display information in the following order: X.IP X- mail messages, one per panel; sender and subject X.IP X- current program, login name, load average, current time X.IP X- full name, terminal, number of active programs X.IP X- lines from $HOME/.messages, one per panel X.IP X- current users on the system X.PP XAn example of some successive panels: X.nf X X 01>From bobw: Re: x10.bas X 02>From root: [No subject] X 03> jove // brandon // 0.76 1.02 0.95 // Thu 05/08 16:56 X 04> "Brandon Allbery" on tty2 // 3 active programs X 05>START THE MONTHLY REPORTS!!! X X.fi X.PP X.B Csl Xlooks for terminfo definitions "hsl", "tsl", "fsl", "wsl", and "dsl". XIt does not make use of the parameterization feature except to position Xto the first column of the status line, so non-addressable status lines X(such as on the Wyse 50 and Wyse 75) can be used. X.P XIf the file X.I $HOME/.syslinelock Xis found to exist at the beginning of a cycle, X.B csl Xwaits until the file disappears before performing any more displays. XThis is useful when X.B csl Xupsets the display from a particular program, or on terminals which Xdo not accept keyboard commands while the status line is being loaded X(e.g. the Wyse 75) when rapid keyboard input is required. X.SH FILES X.ta \w'$HOME/.syslinelock 'u X$HOME/.syslinelock Disables status line X.br X$HOME/.messages Scanned for messages to display X.br X/usr/mail/$LOGNAME Scanned for new mail X.br X/etc/passwd The user's full name is stored here X.br X/etc/utmp Current users, and the current user's terminal X.DT X.SH NOTES X.B Csl Xwill refuse to run on a terminal whose terminfo description does not Xinclude the boolean attribute "hsl". See terminfo(4) for information Xabout the status line attributes of terminfo. X.PP XThe full name of a user is expected to be the (only) entry in the pw_gecos Xfield of /etc/passwd for that user (see X.B getpwent(3) . XRJE format (of the form X"0000-user(0000)") is not understood. X.SH SEE ALSO Xterminfo(4), avenrun(1m). X.SH BUGS X.B Csl Xshould scan for $HOME/.syslinelock after each panel, rather than at Xthe beginning of a cycle. X.PP XThings can change while X.I csl Xis displaying a panel; in general, changes are not seen until the next Xcycle. X.PP XFull names are not always easy to figure out under System V. Some sites Xuse the RJE format, others use the BSD format. X.B Csl Xuses BSD because it's straightforward, the full name is the pw_gecos field Xup to a comma. X.PP XBecause VAR versions of X.B csh(1) Xinsist on closing their fd 0, X.B csl Xmust admit such programs as being running in the foreground. This causes Xvarious lies about the name of the current process. X.PP XOn systems running MH, the mail checking routines will fail; they Xexpect "normal" UUCP mailboxes, not MMDF maildrops. Perhaps the Xbest fix for this is to run X.B ims(local) Xinstead. X.PP XIt isn't X.B statline(1) . X(This may be a feature.) ________This_Is_The_END________ if test `wc -l < csl.1l` -ne 108; then echo 'shar: csl.1l was damaged during transit (should have been 108 bytes)' fi fi ; : end of overwriting check echo 'x - w.1l' if test -f w.1l; then echo 'shar: not overwriting w.1l'; else sed 's/^X//' << '________This_Is_The_END________' > w.1l X.TH W 1 local X.SH NAME Xw \- display users and processes X.SH SYNOPSIS X.B w X.SH DESCRIPTION X.B W Xis a program which displays an "intelligent" listing of the current users, Xwhat they're doing, and how active they are. There are two basic kinds Xof information displayed: system information and per-user information. XAn example is shown below. X.nf X X 10:55pm up 2 wks 2 days, 4 users, load average: 1.2, 1.0, 0.8 XUser tty login@ idle JCPU PCPU what Xrhg tty6 10:36pm 12:20 12:20 csh Xrobertd tty7 10:21pm 9:21 9:21 csh Xbobw tty13 10:26pm 160:35 149:43 rn Xallbery tty15 9:47pm 50:22 0:18 sh X X.fi XThe first line displays the current time, how long the system has been up, Xthe number of users, and the load average (if available; not under System XIII). The other lines display for each user, the user's login name, the Xterminal the user is on, the time the user logged in, how long the user has Xbeen idle, the CPU time used by the current program and total CPU for the Xlogin session, and the current program. X.SH FILES X.ta \w'/dev/kmem 'u X/dev/kmem System memory (the process table) X.br X/dev/mem In-core program images X.br X/dev/swap Swapped program images X.br X/dev Searches for terminals X.br X/etc/utmp Current system users and boot time X.DT X.SH NOTES X.B W Xdisplays the process name as shown by X.B ps(1) Xwithout the -f argument. X.SH SEE ALSO Xavenrun(1m), ps(1), who(1). X.SH BUGS XJCPU and current process are both kludges. The former is really only the XCPU of running programs in the terminal session, as System V does not retain Xuser and system times for all programs in a sessionl; the latter attempts to Xdisregard background processes, but it is nearly impossible to successfully Xdetermine if a program is in the background or not. This is exacerbated by Xthe fact that VAR csh(1)'s, when available, look suspiciously like Xbackground processes because they close their standard input. X.PP XIf the user block is demand paged, X.B w Xwon't find it; I don't have access to a demand-paged system. X.PP X.B who -u Xand X.B w Xhave different ideas on what constitutes idle time; one uses time of last Xinput, the other the time of last output. X.PP XThe boot time isn't guaranteed to be correct if you don't have a real time Xclock with battery backup. There is also a kludge which will not affect Xnormal systems, but which deals with a tendency for Plexus systems to Xforward-date the system clock by one month. X.PP XIt is possible that reading the summarized child's system and user times Xwould produce a better approximation of JCPU. This is only likely, however, Xif the times are updated recursively. X.PP XThings can change while X.B w Xis running; this occasionally causes the current program to be printed as X"[can't stat]" or as "[interstice]". X.PP XIf you want "w" to work correctly, get 4.2BSD. X.PP XIt should really take options for the system namelist, memory, and Xswap files, as well as the key of the shared memory partition; it should Xalso recover gracefully if avenrun(1m) isn't running. ________This_Is_The_END________ if test `wc -l < w.1l` -ne 84; then echo 'shar: w.1l was damaged during transit (should have been 84 bytes)' fi fi ; : end of overwriting check exit 0 -- Brandon S. Allbery, moderator of comp.sources.misc and comp.binaries.ibm.pc {{harvard,mit-eddie}!necntc,well!hoptoad,sun!cwruecmp!hal}!ncoast!allbery ARPA: necntc!ncoast!allbery@harvard.harvard.edu Fido: 157/502 MCI: BALLBERY <<ncoast Public Access UNIX: +1 216 781 6201 24hrs. 300/1200/2400 baud>>