/* New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
 * Charles L. Blake.
 */

/* Basic data structure which holds all information we can get about a process.
 * (unless otherwise specified, fields are read from /proc/#/stat)
 */
typedef struct proc_s {
    char
    	user[10],	/* user name corresponding to owner of process */
    	cmd[40],	/* basename of executable file in call to exec(2) */
    	state,		/* single-char code for process state (S=sleeping) */
    	ttyc[4],	/* string representation of controlling tty device */
	**environ,	/* environment string vector (/proc/#/environ) */
	**cmdline;	/* command line string vector (/proc/#/cmdline) */
    int
    	uid,		/* user id */
    	pid,		/* process id */
    	ppid,		/* pid of parent process */
	pgrp,		/* process group id */
	session,	/* session id */
	tty,		/* full device number of controlling terminal */
	tpgid,		/* terminal process group id */
	utime,		/* user-mode CPU time accumulated by process */
	stime,		/* kernel-mode CPU time accumulated by process */
	cutime,		/* cumulative utime of process and child processes */
	cstime,		/* cumulative stime of process and child processes */
	priority,	/* kernel scheduling priority */
	nice,		/* Standard Unix nice level of process */
	start_time,	/* start time of process -- seconds since 1-1-70 */
	signal,		/* mask of pending signals */
	blocked,	/* mask of blocked signals */
	sigignore,	/* mask of ignored signals */
	sigcatch,	/* mask of caught  signals */
	size,		/* total 4k pages of memory (next 7 from /proc/#/statm)*/
	resident,	/* number of resident (non-swapped) pages (4k) */
	share,		/* number of pages of shared (mmap'd) memory */
	trs,		/* text resident size */
	lrs,		/* shared-lib resident size */
	drs,		/* data resident size */
	dt;		/* ? */
    unsigned
	flags,		/* kernel flags for the process */
	min_flt,	/* number of minor page faults since process start */
	maj_flt,	/* number of major page faults since process start */
	cmin_flt,	/* cumulative min_flt of process and child processes */
	cmaj_flt,	/* cumulative maj_flt of process and child processes */
	timeout,	/* ? */
	it_real_value,	/* ? */
	vsize,		/* Number of pages of virtual memory ... */
	rss,		/* Resident Set Size from /proc/#/stat */
	rss_rlim,	/* ? */
	start_code,	/* address of beginning of code segment */
	end_code,	/* address of end of code segment */
	start_stack,	/* address of the bottom of stack for the process */
	kstk_esp,	/* kernel stack pointer */
	kstk_eip,	/* kernel stack pointer */
	wchan;		/* address of kernel wait channel proc is sleeping in */
    struct proc_s **link; /* ptr list for building arbitrary linked structs */
} proc_t;

/* PROCTAB: data structure holding the persistent information readproc needs
 * from openproc().  This setup is intentionally similar to the dirent interface. */

#include <dirent.h>
#include <unistd.h>
typedef struct {
    DIR*	procfs;
    uid_t	uid;
    dev_t	tty;
    int		flags;
    pid_t*	PidList;
} PROCTAB;

/* initialize a PROCTAB structure holding needed persistent information */
PROCTAB* openproc(uid_t uid, dev_t tty, int flags, ... /* pid_t plist[], int len */);

/* retrieve the next process matching the criteria set by the openproc() */
proc_t* readproc(PROCTAB* PT, proc_t* return_buf);

/* deallocate the space allocated by readproc if the passed return_buf was NULL */
void freeproc(proc_t* p);

/* clean-up open files, etc from the openproc() */
void closeproc(PROCTAB* PT);

/* return a proc_t[] of all processes matching specified criteria. */
proc_t* readproctab(uid_t uid, dev_t tty, int flags, ... /*  pid_t plist[], int len  */);

/* openproc/readproctab:
 * return PROCTAB* / proc_t[] or NULL on error ((probably) "/proc" cannot be opened.)
 * Arguments:
 *     u is the user ID of procs to be read, -1 == all users
 *     tty is the devno of the controlling tty of procs to read
 *     flags is bit-wise OR of the following symbolic constants:
 */
#define PROC_FILLMEM     0x1   /* read statm into the appropriate the proc_t entries */
#define PROC_FILLCMDLINE 0x2   /* allocate and fill in the char** cmdline part of proc_t */
#define PROC_FILLENV     0x4   /* allocate and fill in the char** environ part of proc_t */
#define PROC_READALL     0x8   /* get info on procs of all users */
#define PROC_READNOTTY   0x10  /* get info on procs with no controlling tty */
#define PROC_READRUN     0x20  /* get info on procs with status 'R' == run-able by kernel */
#define PROC_READPIDLIST 0x40  /* arg 4 is a pid_t[] whose elements are pid's to read */
/* Note: to read a single pid, readproctab(-1, -1, PROC_READPIDLIST, &pid, 1) */

/* some utility functions that might be of general interest: */

/* convert a file of null terminated strings into an argv-style string vector which may be
 * de-allocated with a single free() on a dereference of the return value, e.g. free(*ret) */
char** file2strvec(char* directory, char* what);

/* parse /proc/#/stat  entries in string s into a proc_t */
void stat2proc(char* S, proc_t*);

/* parse /proc/#/statm entries in string s into a proc_t */
void statm2proc(char* s, proc_t*);

/* convert a memory buffer with nulls into a single string, replacing the nulls with sep */
void nulls2sep(char* str, int len, char sep);

/* slurp /proc/DIR/FILE into a single large string into the passed
   buffer.  return the number of bytes actually used. */
int file2str(char *dir, char *file, char *buf, int buf_size);
