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