Updated with Tizen:Base source codes
[external/procps.git] / proc / readproc.c
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-2003 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 // sometimes it's easier to do this manually, w/o gcc helping
31 #ifdef PROF
32 extern void __cyg_profile_func_enter(void*,void*);
33 #define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x)
34 #define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x)
35 #else
36 #define ENTER(x)
37 #define LEAVE(x)
38 #endif
39
40 // convert hex string to unsigned long long
41 static unsigned long long unhex(const char *restrict cp){
42     unsigned long long ull = 0;
43     for(;;){
44         char c = *cp++;
45         if(unlikely(c<0x30)) break;
46         ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ;
47     }
48     return ull;
49 }
50
51 static int task_dir_missing;
52
53 ///////////////////////////////////////////////////////////////////////////
54
55 typedef struct status_table_struct {
56     unsigned char name[7];        // /proc/*/status field name
57     unsigned char len;            // name length
58 #ifdef LABEL_OFFSET
59     long offset;                  // jump address offset
60 #else
61     void *addr;
62 #endif
63 } status_table_struct;
64
65 #ifdef LABEL_OFFSET
66 #define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)},
67 #else
68 #define F(x) {#x, sizeof(#x)-1, &&case_##x},
69 #endif
70 #define NUL  {"", 0, 0},
71
72 // Derived from:
73 // gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf
74 //
75 // Suggested method:
76 // Grep this file for "case_", then strip those down to the name.
77 // (leave the colon and newline) So "Pid:\n" and "Threads:\n"
78 // would be lines in the file. (no quote, no escape, etc.)
79 //
80 // Watch out for name size in the status_table_struct (grrr, expanding)
81 // and the number of entries (we mask with 63 for now). The table
82 // must be padded out to 64 entries, maybe 128 in the future.
83
84 static void status2proc(char *S, proc_t *restrict P, int is_proc){
85     long Threads = 0;
86     long Tgid = 0;
87     long Pid = 0;
88
89   static const unsigned char asso[] =
90     {
91       61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
92       61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
93       61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
94       61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
95       61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
96       61, 61, 61, 61, 61, 61, 61, 61, 15, 61,
97       61, 61, 61, 61, 61, 61, 30,  3,  5,  5,
98       61,  5, 61,  8, 61, 61,  3, 61, 10, 61,
99        6, 61, 13,  0, 30, 25,  0, 61, 61, 61,
100       61, 61, 61, 61, 61, 61, 61,  3, 61, 13,
101        0,  0, 61, 30, 61, 25, 61, 61, 61,  0,
102       61, 61, 61, 61,  5, 61,  0, 61, 61, 61,
103        0, 61, 61, 61, 61, 61, 61, 61
104     };
105
106     static const status_table_struct table[] = {
107       F(VmStk)
108       NUL NUL
109       F(State)
110       NUL
111       F(VmExe)
112       F(ShdPnd)
113       NUL
114       F(VmData)
115       NUL
116       F(Name)
117       NUL NUL
118       F(VmRSS)
119       NUL NUL
120       F(VmLck)
121       NUL NUL NUL
122       F(Gid)
123       F(Pid)
124       NUL NUL NUL
125       F(VmSize)
126       NUL NUL
127       F(VmLib)
128       NUL NUL
129       F(PPid)
130       NUL
131       F(SigCgt)
132       NUL
133       F(Threads)
134       F(SigPnd)
135       NUL
136       F(SigIgn)
137       NUL
138       F(Uid)
139       NUL NUL NUL NUL NUL NUL NUL NUL NUL
140       NUL NUL NUL NUL NUL
141       F(Tgid)
142       NUL NUL NUL NUL
143       F(SigBlk)
144       NUL NUL NUL
145     };
146
147 #undef F
148 #undef NUL
149
150 ENTER(0x220);
151
152     P->vm_size = 0;
153     P->vm_lock = 0;
154     P->vm_rss  = 0;
155     P->vm_data = 0;
156     P->vm_stack= 0;
157     P->vm_exe  = 0;
158     P->vm_lib  = 0;
159     P->nlwp    = 0;
160     P->signal[0] = '\0';  // so we can detect it as missing for very old kernels
161
162     goto base;
163
164     for(;;){
165         char *colon;
166         status_table_struct entry;
167
168         // advance to next line
169         S = strchr(S, '\n');
170         if(unlikely(!S)) break;  // if no newline
171         S++;
172
173         // examine a field name (hash and compare)
174     base:
175         if(unlikely(!*S)) break;
176         entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])];
177         colon = strchr(S, ':');
178         if(unlikely(!colon)) break;
179         if(unlikely(colon[1]!='\t')) break;
180         if(unlikely(colon-S != entry.len)) continue;
181         if(unlikely(memcmp(entry.name,S,colon-S))) continue;
182
183         S = colon+2; // past the '\t'
184
185 #ifdef LABEL_OFFSET
186         goto *(&&base + entry.offset);
187 #else
188         goto *entry.addr;
189 #endif
190
191     case_Name:{
192         unsigned u = 0;
193         while(u < sizeof P->cmd - 1u){
194             int c = *S++;
195             if(unlikely(c=='\n')) break;
196             if(unlikely(c=='\0')) break; // should never happen
197             if(unlikely(c=='\\')){
198                 c = *S++;
199                 if(c=='\n') break; // should never happen
200                 if(!c)      break; // should never happen
201                 if(c=='n') c='\n'; // else we assume it is '\\'
202             }
203             P->cmd[u++] = c;
204         }
205         P->cmd[u] = '\0';
206         S--;   // put back the '\n' or '\0'
207         continue;
208     }
209 #ifdef SIGNAL_STRING
210     case_ShdPnd:
211         memcpy(P->signal, S, 16);
212         P->signal[16] = '\0';
213         continue;
214     case_SigBlk:
215         memcpy(P->blocked, S, 16);
216         P->blocked[16] = '\0';
217         continue;
218     case_SigCgt:
219         memcpy(P->sigcatch, S, 16);
220         P->sigcatch[16] = '\0';
221         continue;
222     case_SigIgn:
223         memcpy(P->sigignore, S, 16);
224         P->sigignore[16] = '\0';
225         continue;
226     case_SigPnd:
227         memcpy(P->_sigpnd, S, 16);
228         P->_sigpnd[16] = '\0';
229         continue;
230 #else
231     case_ShdPnd:
232         P->signal = unhex(S);
233         continue;
234     case_SigBlk:
235         P->blocked = unhex(S);
236         continue;
237     case_SigCgt:
238         P->sigcatch = unhex(S);
239         continue;
240     case_SigIgn:
241         P->sigignore = unhex(S);
242         continue;
243     case_SigPnd:
244         P->_sigpnd = unhex(S);
245         continue;
246 #endif
247     case_State:
248         P->state = *S;
249         continue;
250     case_Tgid:
251         Tgid = strtol(S,&S,10);
252         continue;
253     case_Pid:
254         Pid = strtol(S,&S,10);
255         continue;
256     case_PPid:
257         P->ppid = strtol(S,&S,10);
258         continue;
259     case_Threads:
260         Threads = strtol(S,&S,10);
261         continue;
262     case_Uid:
263         P->ruid = strtol(S,&S,10);
264         P->euid = strtol(S,&S,10);
265         P->suid = strtol(S,&S,10);
266         P->fuid = strtol(S,&S,10);
267         continue;
268     case_Gid:
269         P->rgid = strtol(S,&S,10);
270         P->egid = strtol(S,&S,10);
271         P->sgid = strtol(S,&S,10);
272         P->fgid = strtol(S,&S,10);
273         continue;
274     case_VmData:
275         P->vm_data = strtol(S,&S,10);
276         continue;
277     case_VmExe:
278         P->vm_exe = strtol(S,&S,10);
279         continue;
280     case_VmLck:
281         P->vm_lock = strtol(S,&S,10);
282         continue;
283     case_VmLib:
284         P->vm_lib = strtol(S,&S,10);
285         continue;
286     case_VmRSS:
287         P->vm_rss = strtol(S,&S,10);
288         continue;
289     case_VmSize:
290         P->vm_size = strtol(S,&S,10);
291         continue;
292     case_VmStk:
293         P->vm_stack = strtol(S,&S,10);
294         continue;
295     }
296
297 #if 0
298     // recent kernels supply per-tgid pending signals
299     if(is_proc && *ShdPnd){
300         memcpy(P->signal, ShdPnd, 16);
301         P->signal[16] = '\0';
302     }
303 #endif
304
305     // recent kernels supply per-tgid pending signals
306 #ifdef SIGNAL_STRING
307     if(!is_proc || !P->signal[0]){
308         memcpy(P->signal, P->_sigpnd, 16);
309         P->signal[16] = '\0';
310     }
311 #else
312     if(!is_proc || !have_process_pending){
313         P->signal = P->_sigpnd;
314     }
315 #endif
316
317     // Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid"
318     // that is not initialized for built-in kernel tasks.
319     // Only 2.6.0 and above have "Threads" (nlwp) info.
320
321     if(Threads){
322        P->nlwp = Threads;
323        P->tgid = Tgid;     // the POSIX PID value
324        P->tid  = Pid;      // the thread ID
325     }else{
326        P->nlwp = 1;
327        P->tgid = Pid;
328        P->tid  = Pid;
329     }
330
331 LEAVE(0x220);
332 }
333
334 ///////////////////////////////////////////////////////////////////////
335
336 // Reads /proc/*/stat files, being careful not to trip over processes with
337 // names like ":-) 1 2 3 4 5 6".
338 static void stat2proc(const char* S, proc_t *restrict P) {
339     unsigned num;
340     char* tmp;
341
342 ENTER(0x160);
343
344     /* fill in default values for older kernels */
345     P->processor = 0;
346     P->rtprio = -1;
347     P->sched = -1;
348     P->nlwp = 0;
349
350     S = strchr(S, '(') + 1;
351     tmp = strrchr(S, ')');
352     num = tmp - S;
353     if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1;
354     memcpy(P->cmd, S, num);
355     P->cmd[num] = '\0';
356     S = tmp + 2;                 // skip ") "
357
358     num = sscanf(S,
359        "%c "
360        "%d %d %d %d %d "
361        "%lu %lu %lu %lu %lu "
362        "%Lu %Lu %Lu %Lu "  /* utime stime cutime cstime */
363        "%ld %ld "
364        "%d "
365        "%ld "
366        "%Lu "  /* start_time */
367        "%lu "
368        "%ld "
369        "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
370        "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
371        "%"KLF"u %*lu %*lu "
372        "%d %d "
373        "%lu %lu",
374        &P->state,
375        &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
376        &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
377        &P->utime, &P->stime, &P->cutime, &P->cstime,
378        &P->priority, &P->nice,
379        &P->nlwp,
380        &P->alarm,
381        &P->start_time,
382        &P->vsize,
383        &P->rss,
384        &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
385 /*     P->signal, P->blocked, P->sigignore, P->sigcatch,   */ /* can't use */
386        &P->wchan, /* &P->nswap, &P->cnswap, */  /* nswap and cnswap dead for 2.4.xx and up */
387 /* -- Linux 2.0.35 ends here -- */
388        &P->exit_signal, &P->processor,  /* 2.2.1 ends with "exit_signal" */
389 /* -- Linux 2.2.8 to 2.5.17 end here -- */
390        &P->rtprio, &P->sched  /* both added to 2.5.18 */
391     );
392
393     if(!P->nlwp){
394       P->nlwp = 1;
395     }
396
397 LEAVE(0x160);
398 }
399
400 /////////////////////////////////////////////////////////////////////////
401
402 static void statm2proc(const char* s, proc_t *restrict P) {
403     int num;
404     num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
405            &P->size, &P->resident, &P->share,
406            &P->trs, &P->lrs, &P->drs, &P->dt);
407 /*    fprintf(stderr, "statm2proc converted %d fields.\n",num); */
408 }
409
410 static int file2str(const char *directory, const char *what, char *ret, int cap) {
411     static char filename[80];
412     int fd, num_read;
413
414     sprintf(filename, "%s/%s", directory, what);
415     fd = open(filename, O_RDONLY, 0);
416     if(unlikely(fd==-1)) return -1;
417     num_read = read(fd, ret, cap - 1);
418     close(fd);
419     if(unlikely(num_read<=0)) return -1;
420     ret[num_read] = '\0';
421     return num_read;
422 }
423
424 static char** file2strvec(const char* directory, const char* what) {
425     char buf[2048];     /* read buf bytes at a time */
426     char *p, *rbuf = 0, *endbuf, **q, **ret;
427     int fd, tot = 0, n, c, end_of_file = 0;
428     int align;
429
430     sprintf(buf, "%s/%s", directory, what);
431     fd = open(buf, O_RDONLY, 0);
432     if(fd==-1) return NULL;
433
434     /* read whole file into a memory buffer, allocating as we go */
435     while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
436         if (n < (int)(sizeof buf - 1))
437             end_of_file = 1;
438         if (n == 0 && rbuf == 0)
439             return NULL;        /* process died between our open and read */
440         if (n < 0) {
441             if (rbuf)
442                 free(rbuf);
443             return NULL;        /* read error */
444         }
445         if (end_of_file && buf[n-1])            /* last read char not null */
446             buf[n++] = '\0';                    /* so append null-terminator */
447         rbuf = xrealloc(rbuf, tot + n);         /* allocate more memory */
448         memcpy(rbuf + tot, buf, n);             /* copy buffer into it */
449         tot += n;                               /* increment total byte ctr */
450         if (end_of_file)
451             break;
452     }
453     close(fd);
454     if (n <= 0 && !end_of_file) {
455         if (rbuf) free(rbuf);
456         return NULL;            /* read error */
457     }
458     endbuf = rbuf + tot;                        /* count space for pointers */
459     align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
460     for (c = 0, p = rbuf; p < endbuf; p++)
461         if (!*p)
462             c += sizeof(char*);
463     c += sizeof(char*);                         /* one extra for NULL term */
464
465     rbuf = xrealloc(rbuf, tot + c + align);     /* make room for ptrs AT END */
466     endbuf = rbuf + tot;                        /* addr just past data buf */
467     q = ret = (char**) (endbuf+align);          /* ==> free(*ret) to dealloc */
468     *q++ = p = rbuf;                            /* point ptrs to the strings */
469     endbuf--;                                   /* do not traverse final NUL */
470     while (++p < endbuf) 
471         if (!*p)                                /* NUL char implies that */
472             *q++ = p+1;                         /* next string -> next char */
473
474     *q = 0;                                     /* null ptr list terminator */
475     return ret;
476 }
477
478 // warning: interface may change
479 int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
480     char name[32];
481     int fd;
482     unsigned n = 0;
483     dst[0] = '\0';
484     snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
485     fd = open(name, O_RDONLY);
486     if(fd==-1) return 0;
487     for(;;){
488         ssize_t r = read(fd,dst+n,sz-n);
489         if(r==-1){
490             if(errno==EINTR) continue;
491             break;
492         }
493         n += r;
494         if(n==sz) break; // filled the buffer
495         if(r==0) break;  // EOF
496     }
497     close(fd);
498     if(n){
499         int i;
500         if(n==sz) n--;
501         dst[n] = '\0';
502         i=n;
503         while(i--){
504             int c = dst[i];
505             if(c<' ' || c>'~') dst[i]=' ';
506         }
507     }
508     return n;
509 }
510
511 /* These are some nice GNU C expression subscope "inline" functions.
512  * The can be used with arbitrary types and evaluate their arguments
513  * exactly once.
514  */
515
516 /* Test if item X of type T is present in the 0 terminated list L */
517 #   define XinL(T, X, L) ( {                    \
518             T  x = (X), *l = (L);               \
519             while (*l && *l != x) l++;          \
520             *l == x;                            \
521         } )
522
523 /* Test if item X of type T is present in the list L of length N */
524 #   define XinLN(T, X, L, N) ( {                \
525             T x = (X), *l = (L);                \
526             int i = 0, n = (N);                 \
527             while (i < n && l[i] != x) i++;     \
528             i < n && l[i] == x;                 \
529         } )
530
531 //////////////////////////////////////////////////////////////////////////////////
532 // This reads process info from /proc in the traditional way, for one process.
533 // The pid (tgid? tid?) is already in p, and a path to it in path, with some
534 // room to spare.
535 static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
536     static struct stat sb;              // stat() buffer
537     static char sbuf[1024];     // buffer for stat,statm
538     char *restrict const path = PT->path;
539     unsigned flags = PT->flags;
540
541     if (unlikely(stat(path, &sb) == -1))        /* no such dirent (anymore) */
542         goto next_proc;
543
544     if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
545         goto next_proc;                 /* not one of the requested uids */
546
547     p->euid = sb.st_uid;                        /* need a way to get real uid */
548     p->egid = sb.st_gid;                        /* need a way to get real gid */
549
550     if (flags & PROC_FILLSTAT) {         /* read, parse /proc/#/stat */
551         if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
552             goto next_proc;                     /* error reading /proc/#/stat */
553         stat2proc(sbuf, p);                             /* parse /proc/#/stat */
554     }
555
556     if (unlikely(flags & PROC_FILLMEM)) {       /* read, parse /proc/#/statm */
557         if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
558             statm2proc(sbuf, p);                /* ignore statm errors here */
559     }                                           /* statm fields just zero */
560
561     if (flags & PROC_FILLSTATUS) {         /* read, parse /proc/#/status */
562        if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
563            status2proc(sbuf, p, 1);
564        }
565     }
566
567     // if multithreaded, some values are crap
568     if(p->nlwp > 1){
569       p->wchan = (KLONG)~0ull;
570     }
571
572     /* some number->text resolving which is time consuming and kind of insane */
573     if (flags & PROC_FILLUSR){
574         memcpy(p->euser,   user_from_uid(p->euid), sizeof p->euser);
575         if(flags & PROC_FILLSTATUS) {
576             memcpy(p->ruser,   user_from_uid(p->ruid), sizeof p->ruser);
577             memcpy(p->suser,   user_from_uid(p->suid), sizeof p->suser);
578             memcpy(p->fuser,   user_from_uid(p->fuid), sizeof p->fuser);
579         }
580     }
581
582     /* some number->text resolving which is time consuming and kind of insane */
583     if (flags & PROC_FILLGRP){
584         memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
585         if(flags & PROC_FILLSTATUS) {
586             memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
587             memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
588             memcpy(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 (unlikely(flags & PROC_FILLENV))                 /* read+parse /proc/#/environ */
598         p->environ = file2strvec(path, "environ");
599     else
600         p->environ = NULL;
601     
602     return p;
603 next_proc:
604     return NULL;
605 }
606
607 //////////////////////////////////////////////////////////////////////////////////
608 // This reads /proc/*/task/* data, for one task.
609 // p is the POSIX process (task group summary) (not needed by THIS implementation)
610 // t is the POSIX thread (task group member, generally not the leader)
611 // path is a path to the task, with some room to spare.
612 static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
613     static struct stat sb;              // stat() buffer
614     static char sbuf[1024];     // buffer for stat,statm
615     unsigned flags = PT->flags;
616
617 //printf("hhh\n");
618     if (unlikely(stat(path, &sb) == -1))        /* no such dirent (anymore) */
619         goto next_task;
620
621 //    if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
622 //      goto next_task;                 /* not one of the requested uids */
623
624     t->euid = sb.st_uid;                        /* need a way to get real uid */
625     t->egid = sb.st_gid;                        /* need a way to get real gid */
626
627 //printf("iii\n");
628     if (flags & PROC_FILLSTAT) {         /* read, parse /proc/#/stat */
629         if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
630             goto next_task;                     /* error reading /proc/#/stat */
631         stat2proc(sbuf, t);                             /* parse /proc/#/stat */
632     }
633
634     if (unlikely(flags & PROC_FILLMEM)) {       /* read, parse /proc/#/statm */
635 #if 0
636         if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
637             statm2proc(sbuf, t);                /* ignore statm errors here */
638 #else
639         t->size     = p->size;
640         t->resident = p->resident;
641         t->share    = p->share;
642         t->trs      = p->trs;
643         t->lrs      = p->lrs;
644         t->drs      = p->drs;
645         t->dt       = p->dt;
646 #endif
647     }                                           /* statm fields just zero */
648
649     if (flags & PROC_FILLSTATUS) {         /* read, parse /proc/#/status */
650        if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
651            status2proc(sbuf, t, 0);
652        }
653     }
654
655     /* some number->text resolving which is time consuming */
656     if (flags & PROC_FILLUSR){
657         memcpy(t->euser,   user_from_uid(t->euid), sizeof t->euser);
658         if(flags & PROC_FILLSTATUS) {
659             memcpy(t->ruser,   user_from_uid(t->ruid), sizeof t->ruser);
660             memcpy(t->suser,   user_from_uid(t->suid), sizeof t->suser);
661             memcpy(t->fuser,   user_from_uid(t->fuid), sizeof t->fuser);
662         }
663     }
664
665     /* some number->text resolving which is time consuming */
666     if (flags & PROC_FILLGRP){
667         memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup);
668         if(flags & PROC_FILLSTATUS) {
669             memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup);
670             memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup);
671             memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup);
672         }
673     }
674
675 #if 0
676     if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG))       /* read+parse /proc/#/cmdline */
677         t->cmdline = file2strvec(path, "cmdline");
678     else
679         t->cmdline = NULL;
680
681     if (unlikely(flags & PROC_FILLENV))                 /* read+parse /proc/#/environ */
682         t->environ = file2strvec(path, "environ");
683     else
684         t->environ = NULL;
685 #else
686     t->cmdline = p->cmdline;  // better not free these until done with all threads!
687     t->environ = p->environ;
688 #endif
689
690     t->ppid = p->ppid;  // ought to put the per-task ppid somewhere
691
692     return t;
693 next_task:
694     return NULL;
695 }
696
697 //////////////////////////////////////////////////////////////////////////////////
698 // This finds processes in /proc in the traditional way.
699 // Return non-zero on success.
700 static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
701   static struct direct *ent;            /* dirent handle */
702   char *restrict const path = PT->path;
703   for (;;) {
704     ent = readdir(PT->procfs);
705     if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
706     if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
707   }
708   p->tgid = strtoul(ent->d_name, NULL, 10);
709   p->tid = p->tgid;
710   memcpy(path, "/proc/", 6);
711   strcpy(path+6, ent->d_name);  // trust /proc to not contain evil top-level entries
712   return 1;
713 }
714
715 //////////////////////////////////////////////////////////////////////////////////
716 // This finds tasks in /proc/*/task/ in the traditional way.
717 // Return non-zero on success.
718 static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
719   static struct direct *ent;            /* dirent handle */
720   if(PT->taskdir_user != p->tgid){
721     if(PT->taskdir){
722       closedir(PT->taskdir);
723     }
724     // use "path" as some tmp space
725     snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid);
726     PT->taskdir = opendir(path);
727     if(!PT->taskdir) return 0;
728     PT->taskdir_user = p->tgid;
729   }
730   for (;;) {
731     ent = readdir(PT->taskdir);
732     if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
733     if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
734   }
735   t->tid = strtoul(ent->d_name, NULL, 10);
736   t->tgid = p->tgid;
737   t->ppid = p->ppid;  // cover for kernel behavior? we want both actually...?
738   snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);
739   return 1;
740 }
741
742 //////////////////////////////////////////////////////////////////////////////////
743 // This "finds" processes in a list that was given to openproc().
744 // Return non-zero on success. (tgid was handy)
745 static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
746   char *restrict const path = PT->path;
747   pid_t tgid = *(PT->pids)++;
748   if(likely( tgid )){
749     snprintf(path, PROCPATHLEN, "/proc/%d", tgid);
750     p->tgid = tgid;
751     p->tid = tgid;  // they match for leaders
752   }
753   return tgid;
754 }
755
756 //////////////////////////////////////////////////////////////////////////////////
757 /* readproc: return a pointer to a proc_t filled with requested info about the
758  * next process available matching the restriction set.  If no more such
759  * processes are available, return a null pointer (boolean false).  Use the
760  * passed buffer instead of allocating space if it is non-NULL.  */
761
762 /* This is optimized so that if a PID list is given, only those files are
763  * searched for in /proc.  If other lists are given in addition to the PID list,
764  * the same logic can follow through as for the no-PID list case.  This is
765  * fairly complex, but it does try to not to do any unnecessary work.
766  */
767 proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
768   proc_t *ret;
769   proc_t *saved_p;
770
771   PT->did_fake=0;
772 //  if (PT->taskdir) {
773 //    closedir(PT->taskdir);
774 //    PT->taskdir = NULL;
775 //    PT->taskdir_user = -1;
776 //  }
777
778   saved_p = p;
779   if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
780
781   for(;;){
782     // fills in the path, plus p->tid and p->tgid
783     if (unlikely(! PT->finder(PT,p) )) goto out;
784
785     // go read the process data
786     ret = PT->reader(PT,p);
787     if(ret) return ret;
788   }
789
790 out:
791   if(!saved_p) free(p);
792   // FIXME: maybe set tid to -1 here, for "-" in display?
793   return NULL;
794 }
795
796 //////////////////////////////////////////////////////////////////////////////////
797 // readtask: return a pointer to a proc_t filled with requested info about the
798 // next task available.  If no more such tasks are available, return a null
799 // pointer (boolean false).  Use the passed buffer instead of allocating
800 // space if it is non-NULL.
801 proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) {
802   static char path[PROCPATHLEN];       // must hold /proc/2000222000/task/2000222000/cmdline
803   proc_t *ret;
804   proc_t *saved_t;
805
806   saved_t = t;
807   if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
808
809   // 1. got to fake a thread for old kernels
810   // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
811   if(task_dir_missing || p->nlwp < 2){
812     if(PT->did_fake) goto out;
813     PT->did_fake=1;
814     memcpy(t,p,sizeof(proc_t));
815     // use the per-task pending, not per-tgid pending
816 #ifdef SIGNAL_STRING
817         memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
818 #else
819         t->signal = t->_sigpnd;
820 #endif
821     return t;
822   }
823
824   for(;;){
825     // fills in the path, plus t->tid and t->tgid
826     if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out;  // simple_nexttid
827
828     // go read the task data
829     ret = PT->taskreader(PT,p,t,path);          // simple_readtask
830     if(ret) return ret;
831   }
832
833 out:
834   if(!saved_t) free(t);
835   return NULL;
836 }
837
838 //////////////////////////////////////////////////////////////////////////////////
839
840 // initiate a process table scan
841 PROCTAB* openproc(int flags, ...) {
842     va_list ap;
843     struct stat sbuf;
844     static int did_stat;
845     PROCTAB* PT = xmalloc(sizeof(PROCTAB));
846
847     if(!did_stat){
848       task_dir_missing = stat("/proc/self/task", &sbuf);
849       did_stat = 1;
850     }
851     PT->taskdir = NULL;
852     PT->taskdir_user = -1;
853     PT->taskfinder = simple_nexttid;
854     PT->taskreader = simple_readtask;
855
856     PT->reader = simple_readproc;
857     if (flags & PROC_PID){
858       PT->procfs = NULL;
859       PT->finder = listed_nextpid;
860     }else{
861       PT->procfs = opendir("/proc");
862       if(!PT->procfs) return NULL;
863       PT->finder = simple_nextpid;
864     }
865     PT->flags = flags;
866
867     va_start(ap, flags);                /*  Init args list */
868     if (flags & PROC_PID)
869         PT->pids = va_arg(ap, pid_t*);
870     else if (flags & PROC_UID) {
871         PT->uids = va_arg(ap, uid_t*);
872         PT->nuid = va_arg(ap, int);
873     }
874     va_end(ap);                         /*  Clean up args list */
875
876     return PT;
877 }
878
879 // terminate a process table scan
880 void closeproc(PROCTAB* PT) {
881     if (PT){
882         if (PT->procfs) closedir(PT->procfs);
883         if (PT->taskdir) closedir(PT->taskdir);
884         memset(PT,'#',sizeof(PROCTAB));
885         free(PT);
886     }
887 }
888
889 // deallocate the space allocated by readproc if the passed rbuf was NULL
890 void freeproc(proc_t* p) {
891     if (!p)     /* in case p is NULL */
892         return;
893     /* ptrs are after strings to avoid copying memory when building them. */
894     /* so free is called on the address of the address of strvec[0]. */
895     if (p->cmdline)
896         free((void*)*p->cmdline);
897     if (p->environ)
898         free((void*)*p->environ);
899     free(p);
900 }
901
902
903 //////////////////////////////////////////////////////////////////////////////////
904 void look_up_our_self(proc_t *p) {
905     char sbuf[1024];
906
907     if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){
908         fprintf(stderr, "Error, do this: mount -t proc none /proc\n");
909         _exit(47);
910     }
911     stat2proc(sbuf, p);    // parse /proc/self/stat
912 }
913
914 HIDDEN_ALIAS(readproc);
915 HIDDEN_ALIAS(readtask);
916
917 /* Convenient wrapper around openproc and readproc to slurp in the whole process
918  * table subset satisfying the constraints of flags and the optional PID list.
919  * Free allocated memory with exit().  Access via tab[N]->member.  The pointer
920  * list is NULL terminated.
921  */
922 proc_t** readproctab(int flags, ...) {
923     PROCTAB* PT = NULL;
924     proc_t** tab = NULL;
925     int n = 0;
926     va_list ap;
927
928     va_start(ap, flags);                /* pass through args to openproc */
929     if (flags & PROC_UID) {
930         /* temporary variables to ensure that va_arg() instances
931          * are called in the right order
932          */
933         uid_t* u;
934         int i;
935
936         u = va_arg(ap, uid_t*);
937         i = va_arg(ap, int);
938         PT = openproc(flags, u, i);
939     }
940     else if (flags & PROC_PID)
941         PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
942     else
943         PT = openproc(flags);
944     va_end(ap);
945     do {                                        /* read table: */
946         tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
947         tab[n] = readproc_direct(PT, NULL);     /* final null to terminate */
948     } while (tab[n++]);                           /* stop when NULL reached */
949     closeproc(PT);
950     return tab;
951 }
952
953 // Try again, this time with threads and selection.
954 proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) {
955     proc_t** ptab = NULL;
956     unsigned n_proc_alloc = 0;
957     unsigned n_proc = 0;
958
959     proc_t** ttab = NULL;
960     unsigned n_task_alloc = 0;
961     unsigned n_task = 0;
962
963     proc_t*  data = NULL;
964     unsigned n_alloc = 0;
965     unsigned long n_used = 0;
966
967     proc_data_t *pd;
968
969     for(;;){
970         proc_t *tmp;
971         if(n_alloc == n_used){
972           //proc_t *old = data;
973           n_alloc = n_alloc*5/4+30;  // grow by over 25%
974           data = realloc(data,sizeof(proc_t)*n_alloc);
975           //if(!data) return NULL;
976         }
977         if(n_proc_alloc == n_proc){
978           //proc_t **old = ptab;
979           n_proc_alloc = n_proc_alloc*5/4+30;  // grow by over 25%
980           ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc);
981           //if(!ptab) return NULL;
982         }
983         tmp = readproc_direct(PT, data+n_used);
984         if(!tmp) break;
985         if(!want_proc(tmp)) continue;
986         ptab[n_proc++] = (proc_t*)(n_used++);
987         if(!(  PT->flags & PROC_LOOSE_TASKS  )) continue;
988         for(;;){
989           proc_t *t;
990           if(n_alloc == n_used){
991             proc_t *old = data;
992             n_alloc = n_alloc*5/4+30;  // grow by over 25%
993             data = realloc(data,sizeof(proc_t)*n_alloc);
994             // have to move tmp too
995             tmp = data+(tmp-old);
996             //if(!data) return NULL;
997           }
998           if(n_task_alloc == n_task){
999             //proc_t **old = ttab;
1000             n_task_alloc = n_task_alloc*5/4+1;  // grow by over 25%
1001             ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc);
1002             //if(!ttab) return NULL;
1003           }
1004           t = readtask_direct(PT, tmp, data+n_used);
1005           if(!t) break;
1006           if(!want_task(t)) continue;
1007           ttab[n_task++] = (proc_t*)(n_used++);
1008         }
1009     }
1010
1011     pd = malloc(sizeof(proc_data_t));
1012     pd->proc = ptab;
1013     pd->task = ttab;
1014     pd->nproc = n_proc;
1015     pd->ntask = n_task;
1016     if(PT->flags & PROC_LOOSE_TASKS){
1017       pd->tab = ttab;
1018       pd->n   = n_task;
1019     }else{
1020       pd->tab = ptab;
1021       pd->n   = n_proc;
1022     }
1023     // change array indexes to pointers
1024     while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]);
1025     while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]);
1026
1027     return pd;
1028 }
1029
1030 /*
1031  * get_proc_stats - lookup a single tasks information and fill out a proc_t
1032  *
1033  * On failure, returns NULL.  On success, returns 'p' and 'p' is a valid
1034  * and filled out proc_t structure.
1035  */
1036 proc_t * get_proc_stats(pid_t pid, proc_t *p) {
1037         static char path[PATH_MAX], sbuf[1024];
1038         struct stat statbuf;
1039
1040         sprintf(path, "/proc/%d", pid);
1041         if (stat(path, &statbuf)) {
1042                 perror("stat");
1043                 return NULL;
1044         }
1045
1046         if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0)
1047                 stat2proc(sbuf, p);     /* parse /proc/#/stat */
1048         if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0)
1049                 statm2proc(sbuf, p);    /* ignore statm errors here */
1050         if (file2str(path, "status", sbuf, sizeof sbuf) >= 0)
1051                 status2proc(sbuf, p, 0 /*FIXME*/);
1052
1053         return p;
1054 }