From: bokonon@prism.gatech.EDU (Chris Cheyney)
Newsgroups: comp.unix.internals
Subject: Re: accessing system proc struct
Summary: System V useful process information dumper
Date: 16 Apr 92 18:00:58 GMT
Organization: The Zik Zak Corporation

In article <kum009INN875@smurf.smurf.sub.org> urlichs@smurf.sub.org
(Matthias Urlichs) writes:
>Any chance to get at the "real" argument vector?
>
>To do this, you'd probably have to hunt through /dev/(k)mem or /dev/swap.
>Ugly, but doable (hopefully). I also know that the program is free to modify
>that vector, but this is exactly what I want to do anyway.  ;-)

I've had this little hack for System V to read through proc and load up
needed information from the u block.  It's not exactly that pretty but
what it does is broken up enough to make the flow somewhat easy to
understand.

What it does is look up the address of v and proc and based on the
information found in these structures it dumps out information from
valid proc entries and searches for the u block for each of those
processes.

By the way, I'm only sure that this program works on SCO System V,
SCO Open Desktop, AT&T Conversant, and ISC 2.3 because those are
the systems I've tried it on.  Your mileage may vary.

Anyhow, here's the program.

--- cut here ---

/*
 * pstat.c
 * vi: set tabstop=4
 *
 * (c) Copyright 1991 Christopher S. Cheyney.
 * This program is hereby placed in the public domain.
 *
 * Displays listing of various tidbits in the
 * process structure for System V R.2 Unix.
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <nlist.h>
#include <string.h>
#include <pwd.h>
#include <sys/fs/s5dir.h>
#include <sys/param.h>
#include <sys/immu.h>
#include <sys/region.h>
#include <sys/proc.h>
#include <sys/var.h>
#include <sys/user.h>
#include <string.h>
#include <macros.h>
#include <errno.h>
#include <unistd.h>
#include <utmp.h>
#include <pwd.h>
#include <grp.h>


/* Structure for process information.
 */
struct st_pinfo {
	int inuse;
	int uid, pid, ppid, pgrp;
	char basecomm[PSCOMSIZ];
	char fullcomm[PSARGSZ];
};
struct st_pinfo *pdata;


/* openmem - open memory devs for read only
 */
int openmem(mem, kmem, swap)
int *mem, *kmem, *swap;
{
	if ((*kmem = open("/dev/kmem", O_RDONLY, 0)) < 0) {
		return(-1);
	}
	if ((*mem = open("/dev/mem", O_RDONLY, 0)) < 0) {
		return(-2);
	}
	if ((*swap = open("/dev/swap", O_RDONLY, 0)) < 0) {
		return(-3);
	}
	return(0);
}


/* rd_vblock - read the v block into a structure given its offset
 */
int rd_vblock(fdkmem, v_ptr, varbuf)
int *fdkmem;
caddr_t v_ptr;
struct var *varbuf;
{
	int bytes_io;

	if (lseek(*fdkmem, v_ptr, 0) == -1L) {
		return(-1);
	}

	bytes_io = read(*fdkmem, (char *) varbuf, sizeof(struct var));
	if (bytes_io < sizeof(struct var)) {
		return(-2);
	}

	return(0);
}


/* setids - a cute little setuid frontend to set the program
 *          user/group for accessing memory
 */
int setids(owner, group)
char *owner, *group;
{
	struct passwd *pw;
	struct group *gr;
	char   *do_setname, *do_setgrp;
	int    ret1, ret2;
	uid_t	setid;
	gid_t	setgroup;

	setgroup = setid = 0;

	pw = getpwnam(owner);
	gr = getgrnam(group);

	/* Get user ID from password file. */
	if (pw == NULL) {
		do_setname = "root";
		pw = getpwnam(do_setname);
		if (pw == NULL) {
			setid = 0;
		} else {
			setid = pw->pw_uid;
		}
	} else {
		do_setname = owner;
		setid = pw->pw_uid;
	}

	/* Get group ID from group file. */
	if (gr == NULL) {
		do_setgrp = "sys";
		gr = getgrnam(do_setgrp);
		if (gr == NULL) {
			setgroup = 0;
		} else {
			setgroup = gr->gr_gid;
		}
	} else {
		do_setgrp = owner;
		setgroup = gr->gr_gid;
	}
	ret1 = setgid(gr->gr_gid);
	ret2 = setuid(pw->pw_uid);

	if ((ret1 == 0) && (ret2 == 0)) {
		return(0);
	} else {
		return(-1);
	}
}


/* memperms - check permissions on memory files to make sure
 *            we can read them
 *
 * Even negative return codes indicate failure because of access
 * permissions, odd negative return codes indicate failure because
 * of the file not existing.
 *
 */
int memperms(mem, kmem, swap)
char *mem, *kmem, *swap;
{
	struct stat statbuf;	
	int statret;

	/* /dev/mem */
	statret = access(mem, R_OK);
	if (statret < 0) {
		if (errno == ENOENT) {
			return(-1);
		} else {
			return(-2);
		}
	}

	/* /dev/kmem */
	statret = access(kmem, R_OK);
	if (statret < 0) {
		if (errno == ENOENT) {
			return(-3);
		} else {
			return(-4);
		}
	}

	/* /dev/swap */
	statret = access(swap, R_OK);
	if (statret < 0) {
		if (errno == ENOENT) {
			return(-5);
		} else {
			return(-6);
		}
	}

	return(0);
}


/* get_ublock - given information from proc, load the
 *              ublock from either swap or mem
 */
int get_ublock(ublock, procp, fdmem, fdswap)
struct user *ublock;
struct proc *procp;
int *fdmem, *fdswap;
{
	caddr_t ublock_ptr = (caddr_t) ublock;
	int ublock_cnt     = sizeof(struct user);
	int ucount;				/* count of how many pages to read */
	int minbytes;			/* minimum bytes to read */
	paddr_t mptr;			/* memory data pointer */


	/* Determine whether the process is in memory or
	 * is swapped out.
	 */
	if (procp->p_flag & SULOAD) {		/* u page in mem */
		for (ucount = 0; ucount < procp->p_usize; ucount++)
		{
			/* Don't bother if not resident ....
			 */
			if (!procp->p_ubptbl[ucount].pgm.pg_pres) {
				return(-1);
			}

			/* Access where we're going to be for this u
			 * entry and calculate how big it is for the read.
			 */
			mptr = procp->p_ubptbl[ucount].pgm.pg_pfn * NBPP;
			minbytes = min(ublock_cnt, NBPP);


			/* Go to where we need to pick up the u entry and
			 * read in how many ever bytes we can get of it.
			 */
			if (lseek(*fdmem, mptr, 0) < 0L) {
				return(-2);
			}
			if (read(*fdmem, ublock_ptr, minbytes) != minbytes) {
				return(-3);
			}

			/* See how many bytes we've got left to read and not
			 * bother if we've filled it all in.
			 */
			ublock_ptr += NBPP;
			if ((ublock_cnt -= NBPP) <= 0) {
				break;
			}
		}
	} else {				/* u page in swap */
		/* Find location of entry in swap by looking it up in the
		 * disk block descriptor (entry/vector).
		 */
		mptr = procp->p_ubdbd.dbd_blkno * NBPSCTR;

		/* Go get the whole thing.
		 */
		if (lseek(*fdswap, mptr, 0) < 0L) {
			return(-4);
		}
		if (read(*fdswap, ublock_ptr, ublock_cnt) != ublock_cnt) {
			return(-5);
		}

	}	/* if (procp->p_flag & SULOAD) */


	/* Now let's make sure that we at least got the user id's
	 * to jive.
	 */
	if ((ublock->u_ruid == procp->p_uid) ||
		(ublock->u_ruid == procp->p_suid)) {
		return(0);
	} else {
		return(-6);
	}
}


/* buildproclist - build a list of interesting process information
 */
buildproclist(argc, argv)
int argc;
char **argv;
{
	struct nlist symmap[3];
	int fdkmem, i, kern_ok, fdswap, fdmem, mem_ok, bytes_io;
	caddr_t proc_ptr, v_ptr;
	struct proc procbuf;
	struct var varbuf;
	struct user ublock;
	int prblksiz, pindex;


	/* Make sure that the memory files are accessible.
	 */
	mem_ok = memperms("/dev/mem", "/dev/kmem", "/dev/swap");
	if (mem_ok < 0) {
		if ((mem_ok < 0) && (mem_ok % 2)) {
			fprintf(stderr,"%s: memory image not found\n", argv[0]);
		} else {
			fprintf(stderr,"%s: cannot access memory image\n", argv[0]);
		}
		return(1);
	}
	

	/* Find out the locations of v and proc.
	 */
	memset(symmap, (char) 0, sizeof(symmap));
	symmap[0].n_name = "v";
	symmap[1].n_name = "proc";

	nlist("/unix", symmap);


	/* Determine whether the locations returned by nlist
	 * are valid.
	 */
	if ((symmap[0].n_sclass == 0) && (symmap[0].n_value == 0L)) {
		fprintf(stderr,"%s: cannot fetch var address\n", argv[0]);
		return(1);
	}
	if ((symmap[1].n_sclass == 0) && (symmap[1].n_value == 0L)) {
		fprintf(stderr,"%s: cannot fetch proc address\n", argv[0]);
		return(1);
	}
		

	/* Stash the v and proc addresses for future use.
	 */
	proc_ptr = (caddr_t) symmap[1].n_value;
	v_ptr    = (caddr_t) symmap[0].n_value;


	/* Open the memory files.
	 */
	mem_ok = openmem(&fdmem, &fdkmem, &fdswap);
	if (mem_ok < 0) {
		fprintf(stderr,"%s: cannot access memory\n", argv[0]);
		return(1);
	}


	/* Fetch a copy of the v block.
	 */
	mem_ok = rd_vblock(&fdkmem, v_ptr, &varbuf);
	if (mem_ok < 0) {
		fprintf(stderr, "%s: cannot seek to var structure\n", argv[0]);
		return(1);
	}


	/* Based on the v block information, build a list large enough
	 * to hold all the process data that we want.
	 */
	prblksiz = ((int) (varbuf.v_proc) + 1) * sizeof(struct st_pinfo);
	pdata = (struct st_pinfo *) malloc(prblksiz);
	if (pdata == (struct st_pinfo *) NULL) {
		return(1);					/* buffer space no workee */
	}
	memset(pdata, (char) 0, prblksiz);
	pindex = 0;


	/* Go seek to the proc structure.
	 */
	if (lseek(fdkmem, proc_ptr, 0) == -1L) {
		fprintf(stderr,"%s: cannot seek to proc structure\n", argv[0]);
		return(1);
	}


	/* Process each proc entry and its corresponding
	 * u block entry.
	 */
	for (i=0; i < varbuf.v_proc; ++i) {
		bytes_io = read(fdkmem, (char *) &procbuf, sizeof(struct proc));
		if (bytes_io < sizeof(struct proc)) {
			fprintf(stderr, "%s: cannot read proc entry %d\n", argv[0], i);
		} else {
			if (procbuf.p_flag > 0) {
				mem_ok = get_ublock(&ublock, &procbuf, &fdmem, &fdswap);

				pdata[pindex].inuse = 1;
				pdata[pindex].uid   = (int) procbuf.p_uid;
				pdata[pindex].pid   = (int) procbuf.p_pid;
				pdata[pindex].ppid  = (int) procbuf.p_ppid;
				pdata[pindex].pgrp  = (int) procbuf.p_pgrp;

				if (mem_ok >= 0) {
					strncpy(pdata[pindex].basecomm, ublock.u_comm,
							PSCOMSIZ);
					strncpy(pdata[pindex].fullcomm, ublock.u_psargs,
							PSARGSZ);
				}
				++pindex;
			}
		}
	}

	/* Close the memory files.
	 */
	close(fdkmem);
	close(fdmem);
	close(fdswap);

	return(0);
}


/* main - dump out a brief list of process information
 */
main(argc, argv)
int argc;
char **argv;
{
	int proc_ok, setuid_ok;
	int index_cnt;

	setuid_ok = setids("root", "root");
	if (setuid_ok < 0) {
		fprintf(stderr, "%s: cannot set id to superuser\n", argv[0]);
		exit(1);
	}
	proc_ok = buildproclist(argc, argv);
	if (proc_ok < 0) {
		fprintf(stderr,"%s: cannot load process data\n", argv[0]);
		exit(1);
	}
	fprintf(stdout,"   PID      PPID     PGRP     UID       COMMAND\n");

	for (index_cnt = 0; pdata[index_cnt].inuse > 0; ++index_cnt) {
		fprintf(stdout,"  %-06.6d   %-06.6d   %-06.6d   %-06.6d",
				pdata[index_cnt].pid, pdata[index_cnt].ppid,
				pdata[index_cnt].pgrp, pdata[index_cnt].uid);
		if (pdata[index_cnt].basecomm[0]) {
			fprintf(stdout,"   [%s]", pdata[index_cnt].basecomm);
			if (pdata[index_cnt].fullcomm[0]) {
				fprintf(stdout," %s", pdata[index_cnt].fullcomm);
			}
		}
		fprintf(stdout,"\n");
	}
	exit(0);
}


/*
 * End of pstat.c.
 */

--- cut here ---

Hope this helps.

chris
---
Chris Cheyney                      | "Where there is a way, there is a
P.O. Box 723353, Atlanta GA 30339  |  will to subvert it."
Internet: bokonon@hydra.gatech.edu |____________________________________
UUCP:  {backbones}!emory!arg!chris |