File Coverage

OS.c
Criterion Covered Total %
statement 194 256 75.7
branch 104 202 51.4
condition n/a
subroutine n/a
pod n/a
total 298 458 65.0


line stmt bran cond sub pod time code
1             #ifndef _GNU_SOURCE
2             #define _GNU_SOURCE /* for canonicalize_file_name */
3             #endif
4              
5             #include /* is_digit */
6             #include /* opendir, readdir_r */
7             #include
8             #include /* BOOL */
9             #include /* *scanf family */
10             #include /* malloc family */
11             #include /* strchr */
12             #include /* time_t */
13             #include
14             #include
15             #include
16             #include /* statfs */
17             /* glibc only goodness */
18             #include /* glibc's handy obstacks */
19             /* pthreads */
20             #include /* pthread_once */
21              
22             #define obstack_chunk_alloc malloc
23             #define obstack_chunk_free free
24              
25             #include "os/Linux.h"
26              
27             /* NOTE: Before this was actually milliseconds even though it said microseconds, now it is correct. */
28             #define JIFFIES_TO_MICROSECONDS(x) (((x) * 1e6) / system_hertz)
29              
30             /* some static values that won't change, */
31             static pthread_once_t globals_init = PTHREAD_ONCE_INIT;
32              
33             static long long boot_time;
34             static unsigned page_size;
35             static unsigned long long system_memory;
36             static unsigned system_hertz;
37              
38             static bool init_failed = false;
39              
40              
41             /* get_string()
42             *
43             * Access strings in read only section
44             *
45             * @param elem String we want to retrive (look at enum strings)
46             * @return Address of string
47             */
48 106           inline static const char *get_string(int elem)
49             {
50 106           return strings + strings_index[elem];
51             }
52              
53             /* init_static_vars()
54             *
55             * Called by pthread_once to initialize global variables (system settings that don't change)
56             */
57 3           static void init_static_vars()
58             {
59             struct obstack mem_pool;
60              
61             char *file_text, *file_off;
62             off_t file_len;
63              
64             unsigned long long total_memory;
65              
66 3           boot_time = -1;
67 3           system_memory = -1;
68              
69 3           page_size = getpagesize();
70              
71             /* initilize our mem stack, tempoary memory */
72 3           obstack_init(&mem_pool);
73              
74             /* find hertz size, I'm hoping this is gotten from elf note AT_CLKTCK */
75 3           system_hertz = sysconf(_SC_CLK_TCK);
76              
77             /* find boot time */
78             /* read /proc/stat in */
79 3 50         if((file_text = read_file("stat", NULL, &file_len, &mem_pool)) == NULL) {
80 0           goto fail;
81             }
82              
83             /* look for the line that starts with btime
84             * NOTE: incrementing file_off after strchr is legal because file_text will
85             * be null terminated, so worst case after '\n' there will be '\0' and
86             * strncmp will fail or sscanf won't return 1
87             * Only increment on the first line */
88 60 50         for(file_off = file_text; file_off; file_off = strchr(file_off, '\n')) {
89 60 100         if(file_off != file_text) {
90 57           file_off++;
91             }
92              
93 60 100         if(strncmp(file_off, "btime", 5) == 0) {
94 3 50         if(sscanf(file_off, "btime %lld", &boot_time) == 1) {
95 3           break;
96             }
97             }
98             }
99              
100 3 50         obstack_free(&mem_pool, file_text);
    50          
101              
102             /* did we scrape the number of pages successfuly? */
103 3 50         if(boot_time == -1) {
104 0           goto fail;
105             }
106              
107             /* find total number of system pages */
108             /* read /proc/meminfo */
109 3 50         if((file_text = read_file("meminfo", NULL, &file_len, &mem_pool)) == NULL) {
110 0           goto fail;
111             }
112              
113             /* look for the line that starts with: MemTotal */
114 3 50         for(file_off = file_text; file_off; file_off = strchr(file_off, '\n')) {
115 3 50         if(file_off != file_text) {
116 0           file_off++;
117             }
118              
119 3 50         if(strncmp(file_off, "MemTotal:", 9) == 0) {
120 3 50         if(sscanf(file_off, "MemTotal: %llu", &system_memory) == 1) {
121 3           system_memory *= 1024; /* convert to bytes */
122 3           break;
123             }
124             }
125             }
126              
127 3 50         obstack_free(&mem_pool, file_text);
    50          
128              
129             /* did we scrape the number of pages successfuly? */
130 3 50         if(total_memory == -1) {
131 0           goto fail;
132             }
133              
134             /* initialize system hertz value */
135              
136             /* cleanup */
137 3 50         obstack_free(&mem_pool, NULL);
    0          
138 3           return;
139              
140             /* mark failure and cleanup allocated resources */
141             fail:
142 0 0         obstack_free(&mem_pool, NULL);
    0          
143 0           init_failed = true;
144             }
145              
146             /* OS_initialize()
147             *
148             * Called by XS parts whenever Proc::ProcessTable::new is called
149             *
150             * NOTE: There's a const char* -> char* conversion that's evil, but can't fix
151             * this without breaking the Proc::ProcessTable XS API.
152             */
153 3           char *OS_initialize()
154             {
155             struct statfs sfs;
156              
157             /* did we already try to initilize before and fail, if pthrad_once only
158             * let us flag a failure from the init function; behavor of longjmp is
159             * undefined, so that avaue is out out of the question */
160 3 50         if(init_failed) {
161 0           return (char *)get_string(STR_ERR_INIT);
162             }
163              
164             /* check if /proc is mounted, let this go before initializing globals (below),
165             * since the missing /proc message might me helpful */
166 3 50         if(statfs("/proc", &sfs) == -1) {
167 0           return (char *)get_string(STR_ERR_PROC_STATFS);
168             }
169              
170             /* one time initialization of some values that won't change */
171 3           pthread_once(&globals_init, init_static_vars);
172              
173 3           return NULL;
174             }
175              
176 583           inline static void field_enable(char *format_str, enum field field)
177             {
178 583           format_str[field] = tolower(format_str[field]);
179 583           }
180              
181 212           inline static void field_enable_range(char *format_str, enum field field1,
182             enum field field2)
183             {
184             int i;
185              
186 1696 100         for(i = field1; i <= field2; i++) {
187 1484           format_str[i] = tolower(format_str[i]);
188             }
189 212           }
190              
191             /* proc_pid_file()
192             *
193             * Build a path to the pid directory in proc '/proc/${pid}' with an optional
194             * relative path at the end. Put the resultant path on top of the obstack.
195             *
196             * @return Address of the create path string.
197             */
198 430           inline static char *proc_pid_file(const char *pid, const char *file,
199             struct obstack *mem_pool)
200             {
201             /* path to dir */
202 430           obstack_printf(mem_pool, "/proc/%s", pid);
203              
204             /* additional path (not just the dir) */
205 430 100         if(file) {
206 371           obstack_printf(mem_pool, "/%s", file);
207             }
208              
209 430 50         obstack_1grow(mem_pool, '\0');
210              
211 430 50         return (char *)obstack_finish(mem_pool);
    50          
212             }
213              
214             /* read_file()
215             *
216             * Reads the contents of a file using an obstack for memory. It can read files like
217             * /proc/stat or /proc/${pid}/stat.
218             *
219             * @param path String representing the part following /proc (usualy this is
220             * the process pid number)
221             * @param extra_path Path to the file to read in (relative to /proc/${path}/)
222             * @param len Pointer to the value where the length will be saved
223             *
224             * @return Pointer to a null terminate string allocated on the obstack, or
225             * NULL when it fails (doesn't clean up the obstack).
226             */
227 271           static char *read_file(const char *path, const char *extra_path,
228             off_t *len, struct obstack *mem_pool)
229             {
230 271           int fd, result = -1;
231             char *text, *file, *start;
232              
233             /* build the filename in our tempoary storage */
234 271           file = proc_pid_file(path, extra_path, mem_pool);
235              
236 271           fd = open(file, O_RDONLY);
237              
238             /* free tmp memory we allocated for the file contents, this way we are not
239             * poluting the obstack and the only thing left on it is the file */
240 271 50         obstack_free(mem_pool, file);
    50          
241              
242 271 50         if(fd == -1) {
243 0           return NULL;
244             }
245              
246             /* read file into our buffer */
247 883 100         for(*len = 0; result; *len += result) {
248 612 100         obstack_blank(mem_pool, 1024);
249 612           start = obstack_base(mem_pool) + *len;
250              
251 612 50         if((result = read(fd, start, 1024)) == -1) {
252 0 0         obstack_free(mem_pool, obstack_finish(mem_pool));
    0          
    0          
    0          
253 0           close(fd);
254 0           return NULL;
255             }
256             }
257              
258 271           start = obstack_base(mem_pool) + *len;
259 271           *start = '\0';
260              
261             /* finalize our text buffer */
262 271 50         text = obstack_finish(mem_pool);
    50          
263              
264             /* not bothering checking return value, because it's possible that the
265             * process went away */
266 271           close(fd);
267              
268 271           return text;
269             }
270              
271             /* get_user_info()
272             *
273             * Find the user/group id of the process
274             *
275             * @param pid String representing the pid
276             * @param prs Data structure where to put the scraped values
277             * @param mem_pool Obstack to use for temory storage
278             */
279 53           static void get_user_info(char *pid, char *format_str, struct procstat *prs,
280             struct obstack *mem_pool)
281             {
282             char * path_pid;
283             struct stat stat_pid;
284             int result;
285              
286             /* (temp) /proc/${pid} */
287 53           path_pid = proc_pid_file(pid, NULL, mem_pool);
288              
289 53           result = stat(path_pid, &stat_pid);
290              
291 53 50         obstack_free(mem_pool, path_pid);
    50          
292              
293 53 50         if(result == -1) {
294 0           return;
295             }
296              
297 53           prs->uid = stat_pid.st_uid;
298 53           prs->gid = stat_pid.st_gid;
299              
300 53           field_enable(format_str, F_UID);
301 53           field_enable(format_str, F_GID);
302             }
303              
304             /* get_proc_stat()
305             *
306             * Reads a processes stat file in the proc filesystem '/proc/${pid}/stat' and
307             * fills the procstat structure with the values.
308             *
309             * @param pid String representing the pid
310             * @param prs Data structure where to put the scraped values
311             * @param mem_pool Obstack to use for temory storage
312             */
313 53           static bool get_proc_stat(char *pid, char *format_str, struct procstat *prs,
314             struct obstack *mem_pool)
315             {
316             char *stat_text, *stat_cont, *close_paren, *open_paren;
317             int result;
318             off_t stat_len;
319             long dummy_l;
320             int dummy_i;
321              
322 53           bool read_ok = true;
323              
324 53 50         if((stat_text = read_file(pid, "stat", &stat_len, mem_pool)) == NULL) {
325 0           return false;
326             }
327              
328 53 50         if(sscanf(stat_text, "%d (", &prs->pid) != 1) {
329 0           goto done;
330             }
331              
332             /* replace the first ')' with a '\0', the contents look like this:
333             * pid (program_name) state ...
334             * if we don't find ')' then it's incorrectly formated */
335 53 50         if((close_paren = strrchr(stat_text, ')')) == NULL) {
336 0           read_ok = false;
337 0           goto done;
338             }
339 53           *close_paren = '\0';
340              
341 53 50         if((open_paren = strchr(stat_text, '(')) == NULL) {
342 0           read_ok = false;
343 0           goto done;
344             }
345              
346              
347 53           int comm_esize = sizeof(prs->comm) - 1;
348 53           int comm_len = close_paren - open_paren - 1;
349              
350 53 50         if(comm_len > comm_esize) {
351 0           comm_len = comm_esize;
352             }
353 53 50         if(comm_len > 0) {
354 53           strncpy(prs->comm, open_paren + 1, comm_esize);
355 53           prs->comm[comm_esize] = '\0';
356             }
357              
358              
359             /* address at which we pickup again, after the ')'
360             * NOTE: we don't bother checking bounds since strchr didn't return NULL
361             * thus the NULL terminator will be at least close_paren+1, which is ok */
362 53           stat_cont = close_paren + 1;
363              
364             /* scrape the remaining values */
365 53           result = sscanf(stat_cont,
366             " %c" /* state */
367             " %d" /* ppid */
368             " %d" /* pgrp */
369             " %d" /* sid */
370             " %d" /* tty */
371             " %d" /* tty_pgid (dummy) */
372             " %u" /* flags */
373             " %lu" /* minflt */
374             " %lu" /* cminflt */
375             " %lu" /* majflt */
376             " %lu" /* cmajflt */
377             " %llu" /* utime */
378             " %llu" /* stime */
379             " %llu" /* cutime */
380             " %lld" /* cstime */
381             " %ld" /* priority */
382             " %ld" /* nice (dummy) */
383             " %ld" /* num_threads (dummy) */
384             " %d" /* itrealvalue (dummy) */
385             " %llu" /* start_time */
386             " %lu" /* vsize */
387             " %ld" /* rss */
388             " %ld" /* rsslim */
389             " %lu" /* start_code (dummy) */
390             " %lu" /* end_code (dummy) */
391             " %lu" /* start_stack (dummy) */
392             " %lu" /* esp (dummy) */
393             " %lu" /* eip (dummy) */
394             " %lu" /* pending (dummy) */
395             " %lu" /* blocked (dummy) */
396             " %lu" /* sigign (dummy) */
397             " %lu" /* sigcatch (dummy) */
398             " %lu" /* wchan (obsolete) */
399             ,
400             &prs->state_c, /* %c */
401             &prs->ppid, &prs->pgrp, /* %d %d */
402             &prs->sid, /* %d */
403             &prs->tty, &dummy_i, /* tty, tty_pgid */
404             &prs->flags, /* %u */
405             &prs->minflt, &prs->cminflt,
406             &prs->majflt, &prs->cmajflt, /* %lu %lu %lu %lu */
407             &prs->utime, &prs->stime,
408             &prs->cutime, &prs->cstime,
409             &prs->priority,
410             &dummy_l, /* nice */
411             &dummy_l, /* num threads */
412             &dummy_i, /* timeout obsolete */
413             &prs->start_time,
414             &prs->vsize, &prs->rss,
415             &dummy_l,
416             &dummy_l, &dummy_l,
417             &dummy_l,
418             &dummy_l, &dummy_l,
419             &dummy_l, &dummy_l, &dummy_l, &dummy_l,
420             &prs->wchan);
421              
422             /* 33 items in scanf's list... It's all or nothing baby */
423 53 50         if(result != 33) {
424 0           read_ok = false;
425 0           goto done;
426             }
427              
428             /* enable fields; F_STATE is not the range */
429 53           field_enable_range(format_str, F_PID, F_WCHAN);
430              
431             done:
432 53 50         obstack_free(mem_pool, stat_text);
    50          
433 53           return read_ok;
434             }
435              
436 106           static void eval_link(char *pid, char *link_rel, enum field field, char **ptr,
437             char *format_str, struct obstack *mem_pool)
438             {
439             char *link_file, *link;
440              
441             /* path to the link file like. /proc/{pid}/{link_rel} */
442 106           link_file = proc_pid_file(pid, link_rel, mem_pool);
443              
444             /* It's okay to use canonicalize_file_name instead of readlink on linux
445             * for the cwd symlink, since on linux the links we care about will never
446             * be relative links (cwd, exec)
447             * Doing this because readlink works on static buffers */
448             /* canonicalize_file_name is no good for musl, use realpath instead */
449 106           link = realpath(link_file, NULL);
450              
451             /* we no longer need need the path to the link file */
452 106 50         obstack_free(mem_pool, link_file);
    50          
453              
454 106 50         if(link == NULL) {
455 0           return;
456             }
457              
458             /* copy the path onto our obstack, set the value (somewhere in pts)
459             * and free the results of canonicalize_file_name */
460 106           obstack_printf(mem_pool, "%s", link);
461 106 50         obstack_1grow(mem_pool, '\0');
462              
463 106 50         *ptr = (char *)obstack_finish(mem_pool);
    50          
464 106           free(link);
465              
466             /* enable whatever field we successfuly retrived */
467 106           field_enable(format_str, field);
468             }
469              
470 53           static void get_proc_cmndline(char *pid, char *format_str, struct procstat *prs,
471             struct obstack *mem_pool)
472             {
473             char *cmndline_text, *cur;
474             off_t cmndline_off;
475              
476 53 50         if((cmndline_text = read_file(pid, "cmdline", &cmndline_off, mem_pool)) == NULL) {
477 0           return;
478             }
479              
480             /* replace all '\0' with spaces (except for the last one */
481 7483 100         for(cur = cmndline_text; cur < cmndline_text + cmndline_off - 1; cur++) {
482 7430 100         if(*cur == '\0') {
483 342           *cur = ' ';
484             }
485             }
486              
487 53           prs->cmndline = cmndline_text;
488 53           field_enable(format_str, F_CMNDLINE);
489             }
490              
491 53           static void get_proc_cmdline(char *pid, char *format_str, struct procstat *prs,
492             struct obstack *mem_pool)
493             {
494             char *cmdline_text;
495             off_t cmdline_off;
496              
497 53 50         if((cmdline_text = read_file(pid, "cmdline", &cmdline_off, mem_pool)) == NULL) {
498 0           return;
499             }
500              
501 53           prs->cmdline = cmdline_text;
502 53           prs->cmdline_len = cmdline_off;
503 53           field_enable(format_str, F_CMDLINE);
504             }
505              
506 53           static void get_proc_environ(char *pid, char *format_str, struct procstat *prs,
507             struct obstack *mem_pool)
508             {
509             char *environ_text;
510             off_t environ_off;
511              
512 53 50         if((environ_text = read_file(pid, "environ", &environ_off, mem_pool)) == NULL) {
513 0           return;
514             }
515              
516 53           prs->environ = environ_text;
517 53           prs->environ_len = environ_off;
518 53           field_enable(format_str, F_ENVIRON);
519             }
520              
521 53           static void get_proc_status(char *pid, char *format_str, struct procstat *prs,
522             struct obstack *mem_pool)
523             {
524             char *status_text, *loc;
525             off_t status_len;
526             int dummy_i;
527              
528 53 50         if((status_text = read_file(pid, "status", &status_len, mem_pool)) == NULL) {
529 0           return;
530             }
531              
532 53           loc = status_text;
533              
534             /*
535             * get the euid, egid and so on out of /proc/$$/status
536             * where the 2 lines in which we are interested in are:
537             * [5] Uid: 500 500 500 500
538             * [6] Gid: 500 500 500 500
539             * added by scip
540             */
541              
542 530 50         for(loc = status_text; loc; loc = strchr(loc, '\n')) {
543             /* skip past the \n character */
544 530 100         if(loc != status_text) {
545 477           loc++;
546             }
547              
548 530 100         if(strncmp(loc, "Uid:", 4) == 0) {
549 53           sscanf(loc + 4, " %d %d %d %d", &dummy_i, &prs->euid, &prs->suid, &prs->fuid);
550 53           field_enable_range(format_str, F_EUID, F_FUID);
551 477 100         } else if(strncmp(loc, "Gid:", 4) == 0) {
552 53           sscanf(loc + 4, " %d %d %d %d", &dummy_i, &prs->egid, &prs->sgid, &prs->fgid);
553 53           field_enable_range(format_str, F_EGID, F_FGID);
554 424 100         } else if(strncmp(loc, "TracerPid:", 10) == 0) {
555 53           sscanf(loc + 10, " %d", &prs->tracer);
556 53           field_enable(format_str, F_TRACER);
557             }
558              
559             /* short circuit condition */
560 530 100         if(islower(format_str[F_EUID]) && islower(format_str[F_EGID]) && islower(format_str[F_TRACER])) {
    100          
    50          
561 53           goto done;
562             }
563             }
564              
565             done:
566 53 50         obstack_free(mem_pool, status_text);
    50          
567             }
568              
569             /* fixup_stat_values()
570             *
571             * Correct, calculate, covert values to user expected values.
572             *
573             * @param format_str String containing field index types
574             * @param prs Data structure to peform fixups on
575             */
576 53           static void fixup_stat_values(char *format_str, struct procstat *prs)
577             {
578             /* set the state pointer to the right (const) string */
579 53           switch(prs->state_c) {
580             case 'S':
581 48           prs->state = get_string(SLEEP);
582 48           break;
583              
584             case 'W':
585 0           prs->state = get_string(WAIT); /*Waking state. Could be mapped to WAKING, but would break backward compatibility */
586 0           break;
587              
588             case 'R':
589 5           prs->state = get_string(RUN);
590 5           break;
591              
592             case 'I':
593 0           prs->state = get_string(IDLE);
594 0           break;
595              
596             case 'Z':
597 0           prs->state = get_string(DEFUNCT);
598 0           break;
599              
600             case 'D':
601 0           prs->state = get_string(UWAIT);
602 0           break;
603              
604             case 'T':
605             case 'H': /* GNU Hurd HALTED state */
606 0           prs->state = get_string(STOP);
607 0           break;
608              
609             case 'x':
610 0           prs->state = get_string(DEAD);
611 0           break;
612              
613             case 'X':
614 0           prs->state = get_string(DEAD);
615 0           break;
616              
617             case 'K':
618 0           prs->state = get_string(WAKEKILL);
619 0           break;
620              
621             case 't':
622 0           prs->state = get_string(TRACINGSTOP);
623 0           break;
624              
625             case 'P':
626 0           prs->state = get_string(PARKED);
627 0           break;
628              
629             /* unknown state, state is already set to NULL */
630             default:
631 0           ppt_warn("Ran into unknown state (hex char: %x)", (int)prs->state_c);
632 0           goto skip_state_format;
633             }
634              
635 53           field_enable(format_str, F_STATE);
636              
637             skip_state_format:
638              
639 53           prs->start_time = (prs->start_time / system_hertz) + boot_time;
640              
641             /* fix time */
642              
643 53           prs->stime = JIFFIES_TO_MICROSECONDS(prs->stime);
644 53           prs->utime = JIFFIES_TO_MICROSECONDS(prs->utime);
645 53           prs->cstime = JIFFIES_TO_MICROSECONDS(prs->cstime);
646 53           prs->cutime = JIFFIES_TO_MICROSECONDS(prs->cutime);
647              
648             /* derived time values */
649 53           prs->time = prs->utime + prs->stime;
650 53           prs->ctime = prs->cutime + prs->cstime;
651              
652 53           field_enable_range(format_str, F_TIME, F_CTIME);
653              
654             /* fix rss to be in bytes (returned from kernel in pages) */
655 53           prs->rss *= page_size;
656 53           }
657              
658             /* calc_prec()
659             *
660             * calculate the two cpu/memory precentage values
661             */
662 53           static void calc_prec(char *format_str, struct procstat *prs, struct obstack *mem_pool)
663             {
664             int len;
665              
666             /* calculate pctcpu - NOTE: This assumes the cpu time is in microsecond units!
667             * multiplying by 1/1e6 puts all units back in seconds. Then multiply by 100.0f to get a percentage.
668             */
669              
670 53           float pctcpu = (100.0f * (prs->utime + prs->stime) * 1 / 1e6) / (time(NULL) - prs->start_time);
671              
672 53           len = snprintf(prs->pctcpu, LENGTH_PCTCPU, "%6.2f", pctcpu);
673 53 50         if(len >= LENGTH_PCTCPU) {
674 0           ppt_warn("percent cpu truncated from %d, set LENGTH_PCTCPU to at least: %d)", len, len + 1);
675             }
676              
677              
678 53           field_enable(format_str, F_PCTCPU);
679              
680             /* calculate pctmem */
681 53 50         if(system_memory > 0) {
682 53           sprintf(prs->pctmem, "%3.2f", (float)prs->rss / system_memory * 100.f);
683 53           field_enable(format_str, F_PCTMEM);
684             }
685 53           }
686              
687             /* is_pid()
688             *
689             *
690             * @return Boolean value.
691             */
692 363           inline static bool is_pid(const char *str)
693             {
694 502 100         for(; *str; str++) {
695 449 100         if(!isdigit(*str)) {
696 310           return false;
697             }
698             }
699              
700 53           return true;
701             }
702              
703 0           inline static bool pid_exists(const char *str, struct obstack *mem_pool)
704             {
705 0           char *pid_dir_path = NULL;
706             int result;
707              
708 0           obstack_printf(mem_pool, "/proc/%s", str);
709 0 0         obstack_1grow(mem_pool, '\0');
710 0 0         pid_dir_path = obstack_finish(mem_pool);
    0          
711              
712             /* directory exists? */
713 0           result = (access(pid_dir_path, F_OK) != -1);
714              
715 0 0         obstack_free(mem_pool, pid_dir_path);
    0          
716              
717 0           return result;
718             }
719              
720 5           void OS_get_table()
721             {
722             /* dir walker storage */
723             DIR * dir;
724             struct dirent *dir_ent, *dir_result;
725              
726             /* all our storage is going to be here */
727             struct obstack mem_pool;
728              
729             /* container for scraped process values */
730             struct procstat *prs;
731              
732             /* string containing our local copy of format_str, elements will be
733             * lower cased if we are able to figure them out */
734             char *format_str;
735              
736             /* initialize a small memory pool for this function */
737 5           obstack_init(&mem_pool);
738              
739             /* put the dirent on the obstack, since it's rather large */
740 5 50         dir_ent = obstack_alloc(&mem_pool, sizeof(struct dirent));
    50          
    50          
741              
742 5 50         if((dir = opendir("/proc")) == NULL) {
743 0           return;
744             }
745              
746             /* Iterate through all the process entries (numeric) under /proc */
747 368 50         while(readdir_r(dir, dir_ent, &dir_result) == 0 && dir_result) {
    100          
748             /* Only look at this file if it's a proc id; that is, all numbers */
749 363 100         if(!is_pid(dir_result->d_name)) {
750 310           continue;
751             }
752              
753             /* allocate container for storing process values */
754 53 50         prs = obstack_alloc(&mem_pool, sizeof(struct procstat));
    50          
    50          
755 53           bzero(prs, sizeof(struct procstat));
756              
757             /* initialize the format string */
758 53           obstack_printf(&mem_pool, "%s", get_string(STR_DEFAULT_FORMAT));
759 53 50         obstack_1grow(&mem_pool, '\0');
760 53 50         format_str = (char *)obstack_finish(&mem_pool);
    50          
761              
762             /* get process' uid/guid */
763 53           get_user_info(dir_result->d_name, format_str, prs, &mem_pool);
764              
765             /* scrape /proc/${pid}/stat */
766 53 50         if(get_proc_stat(dir_result->d_name, format_str, prs, &mem_pool) == false) {
767             /* did the pid directory go away mid flight? */
768 0 0         if(pid_exists(dir_result->d_name, &mem_pool) == false) {
769 0           continue;
770             }
771             }
772              
773             /* correct values (times) found in /proc/${pid}/stat */
774 53           fixup_stat_values(format_str, prs);
775              
776             /* get process' cmndline */
777 53           get_proc_cmndline(dir_result->d_name, format_str, prs, &mem_pool);
778              
779             /* get process' cmdline */
780 53           get_proc_cmdline(dir_result->d_name, format_str, prs, &mem_pool);
781              
782             /* get process' environ */
783 53           get_proc_environ(dir_result->d_name, format_str, prs, &mem_pool);
784              
785             /* get process' cwd & exec values from the symblink */
786 53           eval_link(dir_result->d_name, "cwd", F_CWD, &prs->cwd, format_str,
787             &mem_pool);
788 53           eval_link(dir_result->d_name, "exe", F_EXEC, &prs->exec, format_str,
789             &mem_pool);
790              
791             /* scrape from /proc/{$pid}/status */
792 53           get_proc_status(dir_result->d_name, format_str, prs, &mem_pool);
793              
794             /* calculate precent cpu & mem values */
795 53           calc_prec(format_str, prs, &mem_pool);
796              
797             /* Go ahead and bless into a perl object */
798             /* Linux.h defines const char* const* Fiels, but we cast it away, as bless_into_proc only understands char** */
799 53           bless_into_proc(format_str, (char **)field_names,
800             prs->uid,
801             prs->gid,
802             prs->pid,
803 53           prs->comm,
804             prs->ppid,
805             prs->pgrp,
806             prs->sid,
807             prs->tty,
808             prs->flags,
809             prs->minflt,
810             prs->cminflt,
811             prs->majflt,
812             prs->cmajflt,
813             prs->utime,
814             prs->stime,
815             prs->cutime,
816             prs->cstime,
817             prs->priority,
818             prs->start_time,
819             prs->vsize,
820             prs->rss,
821             prs->wchan,
822             prs->time,
823             prs->ctime,
824             prs->state,
825             prs->euid,
826             prs->suid,
827             prs->fuid,
828             prs->egid,
829             prs->sgid,
830             prs->fgid,
831 53           prs->pctcpu,
832 53           prs->pctmem,
833             prs->cmndline,
834             prs->exec,
835             prs->cwd,
836             prs->cmdline,
837             prs->cmdline_len,
838             prs->environ,
839             prs->environ_len,
840             prs->tracer
841             );
842              
843             /* we want a new prs, for the next itteration */
844 53 50         obstack_free(&mem_pool, prs);
    0          
845             }
846              
847 5           closedir(dir);
848              
849             /* free all our tempoary memory */
850 5 50         obstack_free(&mem_pool, NULL);
    0          
851             }