In shell we can get a simple process list with:
1 2 3 4 5 6 7 | $ for P in $( seq 1 32168); do [ -d "/proc/$P" ] || continue echo "$P: $(cat /proc/$P/cmdline 2>/dev/null |sed 's/\x00/ /g')" done 1: init [2] 1423: - bash [...] |
Well, simply download procps and patch it to iterate over all PIDs instead of reading entries of /proc with opendir/readdir. By the way, the maximum PID is given by the kernel at the following location:
1 2 | $ cat /proc/sys/kernel/pid_max 32768 |
So here is my small patch, just a few lines:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | $ diff -ur procps-3.2.8{,.mine} --- procps-3.2.8/proc/readproc.c 2006-06-16 08:18:13.000000000 +0000 +++ procps-3.2.8.mine/proc/readproc.c 2010-08-28 04:25:03.000000000 +0000 @@ -27,6 +27,10 @@ #include <sys/types.h> #include <sys/stat.h> +/* global variables to read /proc entries blindly if proc is not readable */ +int current_pid; +int max_pid; + // sometimes it's easier to do this manually, w/o gcc helping #ifdef PROF extern void __cyg_profile_func_enter(void*,void*); @@ -700,15 +704,25 @@ static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) { static struct direct *ent; /* dirent handle */ char *restrict const path = PT->path; - for (;;) { - ent = readdir(PT->procfs); - if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; - if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + if (PT->procfs) { + for (;;) { + ent = readdir(PT->procfs); + if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; + if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + } + p->tgid = strtoul(ent->d_name, NULL, 10); + p->tid = p->tgid; + memcpy(path, "/proc/", 6); + strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries + } + else { + for (;;) { + if (unlikely(++current_pid > max_pid)) return 0; + snprintf(path, PROCPATHLEN, "/proc/%i", current_pid); + if (unlikely(!access(path,F_OK))) break; + } + p->tid = p->tgid = current_pid; } - p->tgid = strtoul(ent->d_name, NULL, 10); - p->tid = p->tgid; - memcpy(path, "/proc/", 6); - strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries return 1; } @@ -859,7 +873,17 @@ PT->finder = listed_nextpid; }else{ PT->procfs = opendir("/proc"); - if(!PT->procfs) return NULL; + if(!PT->procfs) { + FILE *fp; + char maxpid[32]; + if ((fp = fopen("/proc/sys/kernel/pid_max", "r")) > 0) { + fread(maxpid,sizeof(maxpid),1,fp); + max_pid = atoi(maxpid); + fclose(fp); + } + else max_pid = 32768; /* assume it would be the case */ + current_pid = 0; /* start at 0, first iteration will increment */ + } PT->finder = simple_nextpid; } PT->flags = flags; |
For testing and portability we compile ps as a static binary:
1 2 3 4 5 6 7 8 9 | $ make SHARED=0 CFLAGS=-static ps /ps [...] $ file ps /ps ps /ps : ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU /Linux 2.6.8, not stripped $ ps /ps PID TTY TIME CMD 3479 pts /8 00:00:03 bash 19787 pts /8 00:00:00 ps |
When /proc is not readable, this ps now works! And good to know, iterating over all pids does not make things much slower.
>And good to know, iterating over all pids does not make things much slower.
ReplyDeleteWhy do you say that?
With /proc +r:
ReplyDelete$ time ps
[...]
real 0m0.008s
With /proc -r:
$ time ps
real 0m0.088s
Still acceptable :)
Oh okie, for a moment I underestimated ps(1)'s code :)
ReplyDeleteThanks for taking the time to reply :)