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  */
openproc(int flags,...)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  */
closeproc(PROCTAB * PT)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  */
freeproc(proc_t * p)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 
status2proc(const char * S,proc_t * restrict P)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".
stat2proc(const char * S,proc_t * restrict P)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 
statm2proc(const char * s,proc_t * restrict P)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 
file2str(const char * directory,const char * what,char * ret,int cap)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 
file2strvec(const char * directory,const char * what)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
read_cmdline(char * restrict const dst,unsigned sz,unsigned pid)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  */
readproc(PROCTAB * PT,proc_t * p)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  */
ps_readproc(PROCTAB * PT,proc_t * p)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 
look_up_our_self(proc_t * p)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  */
readproctab(int flags,...)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