Home | History | Annotate | Download | only in proc
      1 /*
      2  * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
      3  * Copyright (C) 1996 Charles L. Blake.
      4  * Copyright (C) 1998 Michael K. Johnson
      5  * Copyright 1998-2002 Albert Cahalan
      6  * May be distributed under the conditions of the
      7  * GNU Library General Public License; a copy is in COPYING
      8  */
      9 #ifdef HAVE_CONFIG_H
     10 #include "config.h"
     11 #endif
     12 #include "version.h"
     13 #include "readproc.h"
     14 #include "alloc.h"
     15 #include "pwcache.h"
     16 #include "devname.h"
     17 #include "procps.h"
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <errno.h>
     21 #include <stdarg.h>
     22 #include <string.h>
     23 #include <unistd.h>
     24 #include <signal.h>
     25 #include <fcntl.h>
     26 #include <sys/dir.h>
     27 #include <sys/types.h>
     28 #include <sys/stat.h>
     29 
     30 #ifdef FLASK_LINUX
     31 #include <fs_secure.h>
     32 #endif
     33 
     34 /* initiate a process table scan
     35  */
     36 PROCTAB *openproc(int flags, ...)
     37 {
     38 	va_list ap;
     39 	PROCTAB *PT = xmalloc(sizeof(PROCTAB));
     40 
     41 	if (flags & PROC_PID)
     42 		PT->procfs = NULL;
     43 	else if (!(PT->procfs = opendir("/proc")))
     44 		return NULL;
     45 	PT->flags = flags;
     46 	va_start(ap, flags);	/*  Init args list */
     47 	if (flags & PROC_PID)
     48 		PT->pids = va_arg(ap, pid_t *);
     49 	else if (flags & PROC_UID) {
     50 		PT->uids = va_arg(ap, uid_t *);
     51 		PT->nuid = va_arg(ap, int);
     52 	}
     53 	va_end(ap);		/*  Clean up args list */
     54 	return PT;
     55 }
     56 
     57 /* terminate a process table scan
     58  */
     59 void closeproc(PROCTAB * PT)
     60 {
     61 	if (PT) {
     62 		if (PT->procfs)
     63 			closedir(PT->procfs);
     64 		free(PT);
     65 	}
     66 }
     67 
     68 /* deallocate the space allocated by readproc if the passed rbuf was NULL
     69  */
     70 void freeproc(proc_t * p)
     71 {
     72 	if (!p)			/* in case p is NULL */
     73 		return;
     74 	/* ptrs are after strings to avoid copying memory when building them. */
     75 	/* so free is called on the address of the address of strvec[0]. */
     76 	if (p->cmdline)
     77 		free((void *)*p->cmdline);
     78 	if (p->environ)
     79 		free((void *)*p->environ);
     80 	free(p);
     81 }
     82 
     83 // 2.5.xx looks like:
     84 //
     85 // "State:\t%s\n"
     86 // "Tgid:\t%d\n"
     87 // "Pid:\t%d\n"
     88 // "PPid:\t%d\n"
     89 // "TracerPid:\t%d\n"
     90 
     91 static void status2proc(const char *S, proc_t * restrict P)
     92 {
     93 	char *tmp;
     94 	unsigned i;
     95 
     96 	// The cmd is escaped, with \\ and \n for backslash and newline.
     97 	// It certainly may contain "VmSize:" and similar crap.
     98 	if (unlikely(strncmp("Name:\t", S, 6)))
     99 		fprintf(stderr, "Internal error!\n");
    100 	S += 6;
    101 	i = 0;
    102 	while (i < sizeof P->cmd - 1) {
    103 		int c = *S++;
    104 		if (unlikely(c == '\n'))
    105 			break;
    106 		if (unlikely(c == '\0'))
    107 			return;	// should never happen
    108 		if (unlikely(c == '\\')) {
    109 			c = *S++;
    110 			if (c == '\n')
    111 				break;	// should never happen
    112 			if (!c)
    113 				break;	// should never happen
    114 			if (c == 'n')
    115 				c = '\n';	// else we assume it is '\\'
    116 		}
    117 		P->cmd[i++] = c;
    118 	}
    119 	P->cmd[i] = '\0';
    120 
    121 	tmp = strstr(S, "State:\t");
    122 	if (likely(tmp))
    123 		P->state = tmp[7];
    124 	else
    125 		fprintf(stderr, "Internal error!\n");
    126 
    127 	tmp = strstr(S, "PPid:");
    128 	if (likely(tmp))
    129 		sscanf(tmp, "PPid:\t%d\n", &P->ppid);
    130 	else
    131 		fprintf(stderr, "Internal error!\n");
    132 
    133 	tmp = strstr(S, "Uid:");
    134 	if (likely(tmp))
    135 		sscanf(tmp,
    136 		       "Uid:\t%d\t%d\t%d\t%d",
    137 		       &P->ruid, &P->euid, &P->suid, &P->fuid);
    138 	else
    139 		fprintf(stderr, "Internal error!\n");
    140 
    141 	tmp = strstr(S, "Gid:");
    142 	if (likely(tmp))
    143 		sscanf(tmp,
    144 		       "Gid:\t%d\t%d\t%d\t%d",
    145 		       &P->rgid, &P->egid, &P->sgid, &P->fgid);
    146 	else
    147 		fprintf(stderr, "Internal error!\n");
    148 
    149 	tmp = strstr(S, "VmSize:");
    150 	if (likely(tmp))
    151 		sscanf(tmp,
    152 		       "VmSize: %lu kB\n"
    153 		       "VmLck: %lu kB\n"
    154 		       "VmRSS: %lu kB\n"
    155 		       "VmData: %lu kB\n"
    156 		       "VmStk: %lu kB\n"
    157 		       "VmExe: %lu kB\n"
    158 		       "VmLib: %lu kB\n",
    159 		       &P->vm_size, &P->vm_lock, &P->vm_rss, &P->vm_data,
    160 		       &P->vm_stack, &P->vm_exe, &P->vm_lib);
    161 	else {			/* looks like an annoying kernel thread */
    162 
    163 		P->vm_size = 0;
    164 		P->vm_lock = 0;
    165 		P->vm_rss = 0;
    166 		P->vm_data = 0;
    167 		P->vm_stack = 0;
    168 		P->vm_exe = 0;
    169 		P->vm_lib = 0;
    170 	}
    171 
    172 	tmp = strstr(S, "SigPnd:");
    173 	if (likely(tmp))
    174 		sscanf(tmp,
    175 #ifdef SIGNAL_STRING
    176 		       "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
    177 		       P->signal, P->blocked, P->sigignore, P->sigcatch
    178 #else
    179 		       "SigPnd: %Lx SigBlk: %Lx SigIgn: %Lx %*s %Lx",
    180 		       &P->signal, &P->blocked, &P->sigignore, &P->sigcatch
    181 #endif
    182 		    );
    183 	else
    184 		fprintf(stderr, "Internal error!\n");
    185 }
    186 
    187 // Reads /proc/*/stat files, being careful not to trip over processes with
    188 // names like ":-) 1 2 3 4 5 6".
    189 static void stat2proc(const char *S, proc_t * restrict P)
    190 {
    191 	unsigned num;
    192 	char *tmp;
    193 
    194 	/* fill in default values for older kernels */
    195 	P->exit_signal = SIGCHLD;
    196 	P->processor = 0;
    197 	P->rtprio = -1;
    198 	P->sched = -1;
    199 
    200 	S = strchr(S, '(') + 1;
    201 	tmp = strrchr(S, ')');
    202 	num = tmp - S;
    203 	if (unlikely(num >= sizeof P->cmd))
    204 		num = sizeof P->cmd - 1;
    205 	memcpy(P->cmd, S, num);
    206 	P->cmd[num] = '\0';
    207 	S = tmp + 2;		// skip ") "
    208 
    209 	num = sscanf(S, "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu " "%Lu %Lu %Lu %Lu "	/* utime stime cutime cstime */
    210 		     "%ld %ld %ld %ld " "%Lu "	/* start_time */
    211 		     "%lu " "%ld " "%lu %lu %lu %lu %lu %lu " "%*s %*s %*s %*s "	/* discard, no RT signals & Linux 2.1 used hex */
    212 		     "%lu %lu %lu "
    213 		     "%d %d "
    214 		     "%lu %lu",
    215 		     &P->state,
    216 		     &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
    217 		     &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt,
    218 		     &P->cmaj_flt, &P->utime, &P->stime, &P->cutime, &P->cstime,
    219 		     &P->priority, &P->nice, &P->timeout, &P->it_real_value,
    220 		     &P->start_time, &P->vsize, &P->rss, &P->rss_rlim,
    221 		     &P->start_code, &P->end_code, &P->start_stack,
    222 		     &P->kstk_esp, &P->kstk_eip,
    223 		     /*     P->signal, P->blocked, P->sigignore, P->sigcatch,   *//* can't use */
    224 		     &P->wchan, &P->nswap, &P->cnswap,
    225 /* -- Linux 2.0.35 ends here -- */
    226 		     &P->exit_signal, &P->processor,	/* 2.2.1 ends with "exit_signal" */
    227 /* -- Linux 2.2.8 to 2.5.17 end here -- */
    228 		     &P->rtprio, &P->sched	/* both added to 2.5.18 */
    229 	    );
    230 }
    231 
    232 static void statm2proc(const char *s, proc_t * restrict P)
    233 {
    234 	int num;
    235 	num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
    236 		     &P->size, &P->resident, &P->share,
    237 		     &P->trs, &P->lrs, &P->drs, &P->dt);
    238 /*    fprintf(stderr, "statm2proc converted %d fields.\n",num); */
    239 }
    240 
    241 static int file2str(const char *directory, const char *what, char *ret, int cap)
    242 {
    243 	static char filename[80];
    244 	int fd, num_read;
    245 
    246 	sprintf(filename, "%s/%s", directory, what);
    247 	fd = open(filename, O_RDONLY, 0);
    248 	if (unlikely(fd == -1))
    249 		return -1;
    250 	num_read = read(fd, ret, cap - 1);
    251 	if (unlikely(num_read <= 0))
    252 		num_read = -1;
    253 	else
    254 		ret[num_read] = 0;
    255 	close(fd);
    256 	return num_read;
    257 }
    258 
    259 static char **file2strvec(const char *directory, const char *what)
    260 {
    261 	char buf[2048];		/* read buf bytes at a time */
    262 	char *p, *rbuf = 0, *endbuf, **q, **ret;
    263 	int fd, tot = 0, n, c, end_of_file = 0;
    264 	int align;
    265 
    266 	sprintf(buf, "%s/%s", directory, what);
    267 	fd = open(buf, O_RDONLY, 0);
    268 	if (fd == -1)
    269 		return NULL;
    270 
    271 	/* read whole file into a memory buffer, allocating as we go */
    272 	while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
    273 		if (n < (int)(sizeof buf - 1))
    274 			end_of_file = 1;
    275 		if (n == 0 && rbuf == 0)
    276 			return NULL;	/* process died between our open and read */
    277 		if (n < 0) {
    278 			free(rbuf);
    279 			return NULL;	/* read error */
    280 		}
    281 		if (end_of_file && buf[n - 1])	/* last read char not null */
    282 			buf[n++] = '\0';	/* so append null-terminator */
    283 		rbuf = xrealloc(rbuf, tot + n);	/* allocate more memory */
    284 		memcpy(rbuf + tot, buf, n);	/* copy buffer into it */
    285 		tot += n;	/* increment total byte ctr */
    286 		if (end_of_file)
    287 			break;
    288 	}
    289 	close(fd);
    290 	if (n <= 0 && !end_of_file) {
    291 		free(rbuf);
    292 		return NULL;	/* read error */
    293 	}
    294 	endbuf = rbuf + tot;	/* count space for pointers */
    295 	align =
    296 	    (sizeof(char *) - 1) -
    297 	    ((tot + sizeof(char *) - 1) & (sizeof(char *) - 1));
    298 	for (c = 0, p = rbuf; p < endbuf; p++)
    299 		if (!*p)
    300 			c += sizeof(char *);
    301 	c += sizeof(char *);	/* one extra for NULL term */
    302 
    303 	rbuf = xrealloc(rbuf, tot + c + align);	/* make room for ptrs AT END */
    304 	endbuf = rbuf + tot;	/* addr just past data buf */
    305 	q = ret = (char **)(endbuf + align);	/* ==> free(*ret) to dealloc */
    306 	*q++ = p = rbuf;	/* point ptrs to the strings */
    307 	endbuf--;		/* do not traverse final NUL */
    308 	while (++p < endbuf)
    309 		if (!*p)	/* NUL char implies that */
    310 			*q++ = p + 1;	/* next string -> next char */
    311 
    312 	*q = 0;			/* null ptr list terminator */
    313 	return ret;
    314 }
    315 
    316 // warning: interface may change
    317 int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid)
    318 {
    319 	char name[32];
    320 	int fd;
    321 	unsigned n = 0;
    322 	dst[0] = '\0';
    323 	snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
    324 	fd = open(name, O_RDONLY);
    325 	if (fd == -1)
    326 		return 0;
    327 	for (;;) {
    328 		ssize_t r = read(fd, dst + n, sz - n);
    329 		if (r == -1) {
    330 			if (errno == EINTR)
    331 				continue;
    332 			break;
    333 		}
    334 		n += r;
    335 		if (n == sz)
    336 			break;	// filled the buffer
    337 		if (r == 0)
    338 			break;	// EOF
    339 	}
    340 	if (n) {
    341 		int i;
    342 		if (n == sz)
    343 			n--;
    344 		dst[n] = '\0';
    345 		i = n;
    346 		while (i--) {
    347 			int c = dst[i];
    348 			if (c < ' ' || c > '~')
    349 				dst[i] = ' ';
    350 		}
    351 	}
    352 	return n;
    353 }
    354 
    355 /* These are some nice GNU C expression subscope "inline" functions.
    356  * The can be used with arbitrary types and evaluate their arguments
    357  * exactly once.
    358  */
    359 
    360 /* Test if item X of type T is present in the 0 terminated list L */
    361 #define XinL(T, X, L) ( {			\
    362 	    T  x = (X), *l = (L);		\
    363 	    while (*l && *l != x) l++;		\
    364 	    *l == x;				\
    365 	} )
    366 
    367 /* Test if item X of type T is present in the list L of length N */
    368 #define XinLN(T, X, L, N) ( {		\
    369 	    T x = (X), *l = (L);		\
    370 	    int i = 0, n = (N);			\
    371 	    while (i < n && l[i] != x) i++;	\
    372 	    i < n && l[i] == x;			\
    373 	} )
    374 
    375 /* readproc: return a pointer to a proc_t filled with requested info about the
    376  * next process available matching the restriction set.  If no more such
    377  * processes are available, return a null pointer (boolean false).  Use the
    378  * passed buffer instead of allocating space if it is non-NULL.  */
    379 
    380 /* This is optimized so that if a PID list is given, only those files are
    381  * searched for in /proc.  If other lists are given in addition to the PID list,
    382  * the same logic can follow through as for the no-PID list case.  This is
    383  * fairly complex, but it does try to not to do any unnecessary work.
    384  */
    385 proc_t *readproc(PROCTAB * PT, proc_t * p)
    386 {
    387 	static struct direct *ent;	/* dirent handle */
    388 	static struct stat sb;	/* stat buffer */
    389 	static char path[32], sbuf[1024];	/* bufs for stat,statm */
    390 #ifdef FLASK_LINUX
    391 	security_id_t secsid;
    392 #endif
    393 	pid_t pid;		// saved until we have a proc_t allocated for sure
    394 
    395 	/* loop until a proc matching restrictions is found or no more processes */
    396 	/* I know this could be a while loop -- this way is easier to indent ;-) */
    397 next_proc:			/* get next PID for consideration */
    398 
    399 /*printf("PT->flags is 0x%08x\n", PT->flags);*/
    400 #define flags (PT->flags)
    401 
    402 	if (flags & PROC_PID) {
    403 		pid = *(PT->pids)++;
    404 		if (unlikely(!pid))
    405 			return NULL;
    406 		snprintf(path, sizeof path, "/proc/%d", pid);
    407 	} else {		/* get next numeric /proc ent */
    408 		for (;;) {
    409 			ent = readdir(PT->procfs);
    410 			if (unlikely(unlikely(!ent) || unlikely(!ent->d_name)))
    411 				return NULL;
    412 			if (likely
    413 			    (likely(*ent->d_name > '0')
    414 			     && likely(*ent->d_name <= '9')))
    415 				break;
    416 		}
    417 		pid = strtoul(ent->d_name, NULL, 10);
    418 		memcpy(path, "/proc/", 6);
    419 		strcpy(path + 6, ent->d_name);	// trust /proc to not contain evil top-level entries
    420 //      snprintf(path, sizeof path, "/proc/%s", ent->d_name);
    421 	}
    422 #ifdef FLASK_LINUX
    423 	if (stat_secure(path, &sb, &secsid) == -1)	/* no such dirent (anymore) */
    424 #else
    425 	if (unlikely(stat(path, &sb) == -1))	/* no such dirent (anymore) */
    426 #endif
    427 		goto next_proc;
    428 
    429 	if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
    430 		goto next_proc;	/* not one of the requested uids */
    431 
    432 	if (!p)
    433 		p = xcalloc(p, sizeof *p);	/* passed buf or alloced mem */
    434 
    435 	p->euid = sb.st_uid;	/* need a way to get real uid */
    436 #ifdef FLASK_LINUX
    437 	p->secsid = secsid;
    438 #endif
    439 	p->pid = pid;
    440 
    441 	if (flags & PROC_FILLSTAT) {	/* read, parse /proc/#/stat */
    442 		if (unlikely(file2str(path, "stat", sbuf, sizeof sbuf) == -1))
    443 			goto next_proc;	/* error reading /proc/#/stat */
    444 		stat2proc(sbuf, p);	/* parse /proc/#/stat */
    445 	}
    446 
    447 	if (unlikely(flags & PROC_FILLMEM)) {	/* read, parse /proc/#/statm */
    448 		if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1))
    449 			statm2proc(sbuf, p);	/* ignore statm errors here */
    450 	}
    451 	/* statm fields just zero */
    452 	if (flags & PROC_FILLSTATUS) {	/* read, parse /proc/#/status */
    453 		if (likely(file2str(path, "status", sbuf, sizeof sbuf) != -1)) {
    454 			status2proc(sbuf, p);
    455 		}
    456 	}
    457 
    458 	/* some number->text resolving which is time consuming */
    459 	if (flags & PROC_FILLUSR) {
    460 		strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
    461 		if (flags & PROC_FILLSTATUS) {
    462 			strncpy(p->ruser, user_from_uid(p->ruid),
    463 				sizeof p->ruser);
    464 			strncpy(p->suser, user_from_uid(p->suid),
    465 				sizeof p->suser);
    466 			strncpy(p->fuser, user_from_uid(p->fuid),
    467 				sizeof p->fuser);
    468 		}
    469 	}
    470 
    471 	/* some number->text resolving which is time consuming */
    472 	if (flags & PROC_FILLGRP) {
    473 		strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
    474 		if (flags & PROC_FILLSTATUS) {
    475 			strncpy(p->rgroup, group_from_gid(p->rgid),
    476 				sizeof p->rgroup);
    477 			strncpy(p->sgroup, group_from_gid(p->sgid),
    478 				sizeof p->sgroup);
    479 			strncpy(p->fgroup, group_from_gid(p->fgid),
    480 				sizeof p->fgroup);
    481 		}
    482 	}
    483 
    484 	if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG))	/* read+parse /proc/#/cmdline */
    485 		p->cmdline = file2strvec(path, "cmdline");
    486 	else
    487 		p->cmdline = NULL;
    488 
    489 	if (unlikely(flags & PROC_FILLENV))	/* read+parse /proc/#/environ */
    490 		p->environ = file2strvec(path, "environ");
    491 	else
    492 		p->environ = NULL;
    493 
    494 	return p;
    495 }
    496 
    497 #undef flags
    498 
    499 /* ps_readproc: return a pointer to a proc_t filled with requested info about the
    500  * next process available matching the restriction set.  If no more such
    501  * processes are available, return a null pointer (boolean false).  Use the
    502  * passed buffer instead of allocating space if it is non-NULL.  */
    503 
    504 /* This is optimized so that if a PID list is given, only those files are
    505  * searched for in /proc.  If other lists are given in addition to the PID list,
    506  * the same logic can follow through as for the no-PID list case.  This is
    507  * fairly complex, but it does try to not to do any unnecessary work.
    508  */
    509 proc_t *ps_readproc(PROCTAB * PT, proc_t * p)
    510 {
    511 	static struct direct *ent;	/* dirent handle */
    512 	static struct stat sb;	/* stat buffer */
    513 	static char path[32], sbuf[1024];	/* bufs for stat,statm */
    514 #ifdef FLASK_LINUX
    515 	security_id_t secsid;
    516 #endif
    517 	pid_t pid;		// saved until we have a proc_t allocated for sure
    518 
    519 	/* loop until a proc matching restrictions is found or no more processes */
    520 	/* I know this could be a while loop -- this way is easier to indent ;-) */
    521 next_proc:			/* get next PID for consideration */
    522 
    523 /*printf("PT->flags is 0x%08x\n", PT->flags);*/
    524 #define flags (PT->flags)
    525 
    526 	for (;;) {
    527 		ent = readdir(PT->procfs);
    528 		if (unlikely(unlikely(!ent) || unlikely(!ent->d_name)))
    529 			return NULL;
    530 		if (likely
    531 		    (likely(*ent->d_name > '0') && likely(*ent->d_name <= '9')))
    532 			break;
    533 	}
    534 	pid = strtoul(ent->d_name, NULL, 10);
    535 	memcpy(path, "/proc/", 6);
    536 	strcpy(path + 6, ent->d_name);	// trust /proc to not contain evil top-level entries
    537 //  snprintf(path, sizeof path, "/proc/%s", ent->d_name);
    538 
    539 #ifdef FLASK_LINUX
    540 	if (stat_secure(path, &sb, &secsid) == -1)	/* no such dirent (anymore) */
    541 #else
    542 	if (stat(path, &sb) == -1)	/* no such dirent (anymore) */
    543 #endif
    544 		goto next_proc;
    545 
    546 	if (!p)
    547 		p = xcalloc(p, sizeof *p);	/* passed buf or alloced mem */
    548 
    549 	p->euid = sb.st_uid;	/* need a way to get real uid */
    550 #ifdef FLASK_LINUX
    551 	p->secsid = secsid;
    552 #endif
    553 	p->pid = pid;
    554 
    555 	if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1)
    556 		goto next_proc;	/* error reading /proc/#/stat */
    557 	stat2proc(sbuf, p);	/* parse /proc/#/stat */
    558 
    559 	if (flags & PROC_FILLMEM) {	/* read, parse /proc/#/statm */
    560 		if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1)
    561 			statm2proc(sbuf, p);	/* ignore statm errors here */
    562 	}
    563 
    564 	/* statm fields just zero */
    565 	/*  if (flags & PROC_FILLSTATUS) { */
    566 	/* read, parse /proc/#/status */
    567 	if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1) {
    568 		status2proc(sbuf, p);
    569 	}
    570 /*    }*/
    571 
    572 	/* some number->text resolving which is time consuming */
    573 	if (flags & PROC_FILLUSR) {
    574 		strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
    575 /*        if (flags & PROC_FILLSTATUS) { */
    576 		strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
    577 		strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
    578 		strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
    579 /*        }*/
    580 	}
    581 
    582 	/* some number->text resolving which is time consuming */
    583 	if (flags & PROC_FILLGRP) {
    584 		strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
    585 /*        if (flags & PROC_FILLSTATUS) { */
    586 		strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
    587 		strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
    588 		strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
    589 /*        }*/
    590 	}
    591 
    592 	if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG))	/* read+parse /proc/#/cmdline */
    593 		p->cmdline = file2strvec(path, "cmdline");
    594 	else
    595 		p->cmdline = NULL;
    596 
    597 	if (flags & PROC_FILLENV)	/* read+parse /proc/#/environ */
    598 		p->environ = file2strvec(path, "environ");
    599 	else
    600 		p->environ = NULL;
    601 
    602 	return p;
    603 }
    604 
    605 #undef flags
    606 
    607 void look_up_our_self(proc_t * p)
    608 {
    609 	static char path[32], sbuf[1024];	/* bufs for stat,statm */
    610 	sprintf(path, "/proc/%d", getpid());
    611 	file2str(path, "stat", sbuf, sizeof sbuf);
    612 	stat2proc(sbuf, p);	/* parse /proc/#/stat */
    613 	file2str(path, "statm", sbuf, sizeof sbuf);
    614 	statm2proc(sbuf, p);	/* ignore statm errors here */
    615 	file2str(path, "status", sbuf, sizeof sbuf);
    616 	status2proc(sbuf, p);
    617 }
    618 
    619 /* Convenient wrapper around openproc and readproc to slurp in the whole process
    620  * table subset satisfying the constraints of flags and the optional PID list.
    621  * Free allocated memory with freeproctab().  Access via tab[N]->member.  The
    622  * pointer list is NULL terminated.
    623  */
    624 proc_t **readproctab(int flags, ...)
    625 {
    626 	PROCTAB *PT = NULL;
    627 	proc_t **tab = NULL;
    628 	int n = 0;
    629 	va_list ap;
    630 
    631 	va_start(ap, flags);	/* pass through args to openproc */
    632 	if (flags & PROC_UID) {
    633 		/* temporary variables to ensure that va_arg() instances
    634 		 * are called in the right order
    635 		 */
    636 		uid_t *u;
    637 		int i;
    638 
    639 		u = va_arg(ap, uid_t *);
    640 		i = va_arg(ap, int);
    641 		PT = openproc(flags, u, i);
    642 	} else if (flags & PROC_PID)
    643 		PT = openproc(flags, va_arg(ap, void *));	/* assume ptr sizes same */
    644 	else
    645 		PT = openproc(flags);
    646 	va_end(ap);
    647 	do {			/* read table: */
    648 		tab = xrealloc(tab, (n + 1) * sizeof(proc_t *));	/* realloc as we go, using */
    649 		tab[n] = readproc(PT, NULL);	/* final null to terminate */
    650 	} while (tab[n++]);	/* stop when NULL reached */
    651 	closeproc(PT);
    652 	return tab;
    653 }
    654