[net.sources] utree -- print user process tree

brandon@tdi2.UUCP (Brandon Allbery) (11/06/86)

Quoted from <388@cullvax.UUCP> ["ps and systat"], by news@cullvax.UUCP (Mail and USENET News System maintenance account)...
+---------------
| Has anyone out there written a "system status" program (like ps) that
| presents things in a better format, like (1) process id, (2) state,
| (3) user, (4) command, (5) tty, and (6) shows the tree structure of
| the selected processes.  I've written a post-processor for ps that
| shows the tree structure, but the ps options that I have to use (-xal,
| or some such) only list the first 14 or so characters of the command,
| and don't list the user name.  Is there a better way, or do I have to
| do it myself?
+---------------

Well, here's part of it...  It's written for System V, but should be convert-
able for BSD, etc. (but don't ask me how!).  One warning:  the constant 9 is
coded in a few places dealing with disk addresses on swap.  This is because
of what appears to be a bug in the Plexus kernel:  BSHIFT is 10, but the
actual shift used to read swap is 9.

Sample output:

% utree
USER     TTY        PID COMMAND
brandon  tty2      3927 csh
                   5121   UNIFY
                   5122     ENTER
                   5124     csh
                   6308       rn
                   6312         sh
                   6313           sh
                   6332             emacs
                   6339               sh
                   6340                 utree

lori     tty8      4477 sh
                   4504   UNIFY
                   4509     ENTER
                   6333     unisys

joni     tty23     6218 sh
                   6246   UNIFY
                   6249     ENTER

matt     tty15     6105 sh
                   6113   runcobol

glenn    tty17     6025 sh
                   6056   runcobol

jeff     tty14     5665 sh
                   6016   UNIFY
                   6019     ENTER
                   6080     runcobol

scott    tty18     6063 csh
                   6074   UNIFY
                   6295     unisys
                   6076     ENTER

lori     tty10     4930 sh
                   4939   UNIFY
                   4941     ENTER

gregs    tty24     4557 sh
                   6115   UNIFY
                   6118     ENTER
                   6268     runcobol

bobw     tty25     1650 csh

karen    tty20     3577 sh
                   4641   runcobol

lorib    tty19     4596 sh
                   5975   runcobol
                   6336     unisys

scott    tty16     4938 csh
                   4951   UNIFY
                   4954     ENTER

brian    tty21     4986 UNIFY
                   6238   ENTER
                   6239   unisys

bill     tty26     4780 sh
                   4791   UNIFY
                   5900     unisys
                   4793     ENTER

rich     tty30     3633 sh
                   6138   sh
                   6142     cu
                   6143       cu
%

++Brandon

------------------- CUT HERE -------------- NOT A SHAR -----------------------
#include <stdio.h>
#include <fcntl.h>
#include <a.out.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/var.h>
#include <sys/dir.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/sysmacros.h>
#include <utmp.h>

struct nlist k[] = {
#define VAR		0
	{"_v"},
#define PROC		1
	{"_proc"},
#define SWPLO		2
	{"_swplo"},
	{0},
};

char swapname[128] = "/dev/swap";
char kmemname[128] = "/dev/kmem";
char memname[128] = "/dev/mem";
char kernel[128] = "/unix";

extern void setutent(), exit();
extern struct utmp *getutent();

main(argc, argv)
char **argv; {
	int mem, swap, kmem;
	daddr_t swplo;
	short opt, initial;
	extern int optind;
	extern char *optarg;
	struct utmp *user;
	struct var var;
	
	while ((opt = getopt(argc, argv, "s:k:m:n:")) != EOF)
		switch (opt) {
		case 's':
			strcpy(swapname, optarg);
			break;
		case 'k':
			strcpy(kmemname, optarg);
			break;
		case 'm':
			strcpy(memname, optarg);
			break;
		case 'n':
			strcpy(kernel, optarg);
			break;
		default:
			fprintf(stderr, "usage: %s [-k kmemname] [-s swapname] [-m memname] [-n namelist]\n", argv[0]);
			exit(1);
		}
	if (nlist(kernel, k) != 0) {
		perror(kernel);
		exit(2);
	}
	if ((kmem = open(kmemname, O_RDONLY)) == -1) {
		perror(kmemname);
		exit(3);
	}
	if ((mem = open(memname, O_RDONLY)) == -1) {
		perror(memname);
		exit(4);
	}
	if ((swap = open(swapname, O_RDONLY)) == -1) {
		perror(swapname);
		exit(5);
	}
	if (lseek(kmem, k[VAR].n_value, 0) == -1L) {
		perror(kmemname);
		exit(6);
	}
	if (read(kmem, (char *) &var, sizeof var) == -1) {
		perror(kmemname);
		exit(7);
	}
	if (lseek(kmem, k[SWPLO].n_value, 0) == -1L) {
		perror(kmemname);
		exit(8);
	}
	if (read(kmem, (char *) &swplo, sizeof swplo) == -1) {
		perror(kmemname);
		exit(9);
	}
	initial = 1;
	while ((user = getutent()) != (struct utmp *) 0) {
		if (user->ut_type != USER_PROCESS)
			continue;
		if (initial) {
			initial = 0;
			printf("USER     TTY        PID COMMAND\n");
		}
		else
			putchar('\n');
		printf("%-8.*s %-8s ", sizeof user->ut_user, user->ut_user, user->ut_line);
		ptree(kmem, mem, swap, user->ut_pid, swplo, k[PROC].n_value, 0, var.v_proc);
	}
	exit(0);
	/*NOTREACHED*/
}

ptree(kmem, mem, swap, pid, swplo, procaddr, depth, nproc)
daddr_t swplo;
ushort pid;
long procaddr, nproc; {
	struct proc process;
	long cnt, here;
	
	if (lseek(kmem, procaddr, 0) == -1L) {
		perror(kmemname);
		exit(10);
	}
	for (cnt = 0; cnt < nproc; cnt++) {
		if (read(kmem, &process, sizeof process) == -1) {
			perror(kmemname);
			exit(11);
		}
		if (process.p_stat == 0)
			continue;
		if ((depth == 0? process.p_pid: process.p_ppid) == pid) {
			if (depth != 0)
				printf("                  ");
			emit(&process, mem, swap, swplo, depth);
			if ((here = lseek(kmem, 0L, 1)) == -1L) {
				perror(kmemname);
				exit(12);
			}
			ptree(kmem, mem, swap, process.p_pid, swplo, procaddr, depth + 1, nproc);
			if (lseek(kmem, here, 0) == -1L) {
				perror(kmemname);
				exit(13);
			}
		}
	}
}

emit(process, mem, swap, swplo, depth)
struct proc *process;
long swplo; {
	struct user command;
	
	if (process->p_flag & SLOAD) {
		if (lseek(mem, ctob(process->p_addr), 0) == -1L) {
			perror(memname);
			exit(14);
		}
		if (read(mem, (char *) &command, sizeof command) == -1) {
			perror(memname);
			exit(15);
		}
	}
	else {
		if (lseek(swap, (swplo + process->p_swaddr + ctod(process->p_swsize) - ctod(USIZE)) << 9, 0) == -1L) {
			perror(swapname);
			exit(16);
		}
		if (read(swap, (char *) &command, sizeof command) == -1) {
			perror(swapname);
			exit(17);
		}
	}
	printf("%5d %*s%.*s\n", process->p_pid, depth * 2, "", DIRSIZ, command.u_comm);
}
================================== CUT HERE ===================================
-- 
 /-----------//***/  Brandon S. Allbery		cbosgd!cwruecmp!ncoast!allbery
/---   -----//***/   Tridelta Industries, Inc.  ihnp4!tft2!tdi2!brandon
  /  //---, /---/    7350 Corporate Blvd.      		       !ncoast!allbery
 /  //   / /   /     Mentor, OH 44060
/--//---' /---/      +1 216 255 1080		HOME: +1 216 974 9210