procps/proc/readproc.c

1101 lines
32 KiB
C
Raw Normal View History

2002-02-02 04:17:29 +05:30
/*
* New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
* Copyright (C) 1996 Charles L. Blake.
* Copyright (C) 1998 Michael K. Johnson
2003-09-18 07:48:43 +05:30
* Copyright 1998-2003 Albert Cahalan
2002-02-02 04:17:29 +05:30
* May be distributed under the conditions of the
* GNU Library General Public License; a copy is in COPYING
*/
2002-09-27 19:18:00 +05:30
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "version.h"
#include "readproc.h"
2002-12-09 12:30:07 +05:30
#include "alloc.h"
#include "pwcache.h"
#include "devname.h"
#include "procps.h"
2002-02-02 04:17:29 +05:30
#include <stdio.h>
#include <stdlib.h>
2002-12-03 14:37:59 +05:30
#include <errno.h>
2002-02-02 04:17:29 +05:30
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/stat.h>
2003-09-18 03:28:32 +05:30
// sometimes it's easier to do this manually, w/o gcc helping
2003-02-17 06:27:15 +05:30
#ifdef PROF
extern void __cyg_profile_func_enter(void*,void*);
#define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x)
#define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x)
#else
#define ENTER(x)
#define LEAVE(x)
#endif
// convert hex string to unsigned long long
static unsigned long long unhex(const char *restrict cp){
unsigned long long ull = 0;
for(;;){
char c = *cp++;
if(unlikely(c<0x30)) break;
ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ;
}
return ull;
}
2003-09-20 13:59:55 +05:30
static int task_dir_missing;
2003-02-17 06:27:15 +05:30
///////////////////////////////////////////////////////////////////////////
2002-02-02 04:17:29 +05:30
2003-02-17 06:27:15 +05:30
typedef struct status_table_struct {
2003-12-13 22:21:40 +05:30
unsigned char name[7]; // /proc/*/status field name
unsigned char len; // name length
2003-02-17 06:27:15 +05:30
#ifdef LABEL_OFFSET
long offset; // jump address offset
#else
void *addr;
#endif
} status_table_struct;
#ifdef LABEL_OFFSET
2003-05-30 08:43:32 +05:30
#define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)},
2003-02-17 06:27:15 +05:30
#else
#define F(x) {#x, sizeof(#x)-1, &&case_##x},
#endif
#define NUL {"", 0, 0},
2002-12-03 14:37:59 +05:30
2003-02-17 06:27:15 +05:30
// Derived from:
// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf
2003-12-13 22:21:40 +05:30
//
// Suggested method:
// Grep this file for "case_", then strip those down to the name.
// (leave the colon and newline) So "Pid:\n" and "Threads:\n"
// would be lines in the file. (no quote, no escape, etc.)
//
// In the status_table_struct watch out for name size (grrr, expanding)
2003-12-13 22:21:40 +05:30
// and the number of entries (we mask with 63 for now). The table
// must be padded out to 64 entries, maybe 128 in the future.
2002-02-02 04:17:29 +05:30
2003-09-28 08:15:05 +05:30
static void status2proc(char *S, proc_t *restrict P, int is_proc){
2003-12-13 22:21:40 +05:30
long Threads = 0;
long Tgid = 0;
long Pid = 0;
// 128 entries because we trust the kernel to use ASCII names
2003-12-13 22:21:40 +05:30
static const unsigned char asso[] =
{
64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 28, 64,
64, 64, 64, 64, 64, 64, 8, 25, 23, 25,
6, 25, 0, 3, 64, 64, 3, 64, 25, 64,
20, 1, 1, 5, 0, 30, 0, 0, 64, 64,
64, 64, 64, 64, 64, 64, 64, 3, 64, 0,
0, 18, 64, 10, 64, 10, 64, 64, 64, 20,
64, 20, 0, 64, 25, 64, 3, 15, 64, 0,
30, 64, 64, 64, 64, 64, 64, 64
2003-02-17 06:27:15 +05:30
};
2003-12-13 22:21:40 +05:30
2003-02-17 06:27:15 +05:30
static const status_table_struct table[] = {
F(VmHWM)
2003-12-13 22:21:40 +05:30
NUL NUL
F(VmLck)
NUL
F(VmSwap)
F(VmRSS)
NUL
F(VmStk)
NUL
F(Tgid)
2003-12-13 22:21:40 +05:30
F(State)
NUL
F(VmLib)
NUL
F(VmSize)
F(SigQ)
2003-12-13 22:21:40 +05:30
NUL
F(SigIgn)
NUL
F(VmPTE)
F(FDSize)
NUL
F(SigBlk)
NUL
F(ShdPnd)
2003-12-13 22:21:40 +05:30
F(VmData)
NUL
F(CapInh)
NUL
F(PPid)
2003-12-13 22:21:40 +05:30
NUL NUL
F(CapBnd)
NUL
F(SigPnd)
2003-12-13 22:21:40 +05:30
NUL NUL
F(VmPeak)
2003-12-13 22:21:40 +05:30
NUL
F(SigCgt)
NUL NUL
2003-12-13 22:21:40 +05:30
F(Threads)
NUL
F(CapPrm)
NUL NUL
F(Pid)
2003-12-13 22:21:40 +05:30
NUL
F(CapEff)
NUL NUL
F(Gid)
NUL
F(VmExe)
NUL NUL
2003-12-13 22:21:40 +05:30
F(Uid)
NUL
F(Groups)
NUL NUL
F(Name)
2003-02-17 06:27:15 +05:30
};
2002-02-02 04:17:29 +05:30
2003-02-17 06:27:15 +05:30
#undef F
#undef NUL
2002-02-02 04:17:29 +05:30
2003-02-17 06:27:15 +05:30
ENTER(0x220);
P->vm_size = 0;
P->vm_lock = 0;
P->vm_rss = 0;
P->vm_data = 0;
P->vm_stack= 0;
P->vm_exe = 0;
P->vm_lib = 0;
2003-12-13 22:21:40 +05:30
P->nlwp = 0;
P->signal[0] = '\0'; // so we can detect it as missing for very old kernels
2002-02-02 04:17:29 +05:30
2003-02-17 06:27:15 +05:30
goto base;
2003-02-12 13:45:53 +05:30
2003-02-17 06:27:15 +05:30
for(;;){
char *colon;
status_table_struct entry;
// advance to next line
S = strchr(S, '\n');
if(unlikely(!S)) break; // if no newline
S++;
// examine a field name (hash and compare)
base:
if(unlikely(!*S)) break;
entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])];
colon = strchr(S, ':');
if(unlikely(!colon)) break;
if(unlikely(colon[1]!='\t')) break;
if(unlikely(colon-S != entry.len)) continue;
if(unlikely(memcmp(entry.name,S,colon-S))) continue;
S = colon+2; // past the '\t'
#ifdef LABEL_OFFSET
goto *(&&base + entry.offset);
2002-02-02 04:17:29 +05:30
#else
2003-02-17 06:27:15 +05:30
goto *entry.addr;
2002-02-02 04:17:29 +05:30
#endif
2003-02-17 06:27:15 +05:30
case_Name:{
2003-02-18 09:21:03 +05:30
unsigned u = 0;
while(u < sizeof P->cmd - 1u){
2003-02-17 06:27:15 +05:30
int c = *S++;
if(unlikely(c=='\n')) break;
2003-02-18 09:21:03 +05:30
if(unlikely(c=='\0')) break; // should never happen
2003-02-17 06:27:15 +05:30
if(unlikely(c=='\\')){
c = *S++;
if(c=='\n') break; // should never happen
if(!c) break; // should never happen
if(c=='n') c='\n'; // else we assume it is '\\'
}
2003-02-18 09:21:03 +05:30
P->cmd[u++] = c;
2003-02-17 06:27:15 +05:30
}
2003-02-18 09:21:03 +05:30
P->cmd[u] = '\0';
S--; // put back the '\n' or '\0'
2003-02-17 06:27:15 +05:30
continue;
}
#ifdef SIGNAL_STRING
2003-02-17 06:27:15 +05:30
case_ShdPnd:
memcpy(P->signal, S, 16);
P->signal[16] = '\0';
2003-02-17 06:27:15 +05:30
continue;
case_SigBlk:
memcpy(P->blocked, S, 16);
P->blocked[16] = '\0';
continue;
case_SigCgt:
memcpy(P->sigcatch, S, 16);
P->sigcatch[16] = '\0';
continue;
case_SigIgn:
memcpy(P->sigignore, S, 16);
P->sigignore[16] = '\0';
continue;
case_SigPnd:
memcpy(P->_sigpnd, S, 16);
P->_sigpnd[16] = '\0';
2003-02-17 06:27:15 +05:30
continue;
#else
case_ShdPnd:
P->signal = unhex(S);
continue;
case_SigBlk:
P->blocked = unhex(S);
continue;
case_SigCgt:
P->sigcatch = unhex(S);
continue;
case_SigIgn:
P->sigignore = unhex(S);
continue;
case_SigPnd:
P->_sigpnd = unhex(S);
continue;
#endif
2003-02-17 06:27:15 +05:30
case_State:
P->state = *S;
continue;
case_Tgid:
2003-12-13 22:21:40 +05:30
Tgid = strtol(S,&S,10);
continue;
case_Pid:
Pid = strtol(S,&S,10);
continue;
case_PPid:
P->ppid = strtol(S,&S,10);
continue;
case_Threads:
Threads = strtol(S,&S,10);
2003-02-17 06:27:15 +05:30
continue;
case_Uid:
P->ruid = strtol(S,&S,10);
P->euid = strtol(S,&S,10);
P->suid = strtol(S,&S,10);
P->fuid = strtol(S,&S,10);
continue;
2003-12-13 22:21:40 +05:30
case_Gid:
P->rgid = strtol(S,&S,10);
P->egid = strtol(S,&S,10);
P->sgid = strtol(S,&S,10);
P->fgid = strtol(S,&S,10);
continue;
2003-02-17 06:27:15 +05:30
case_VmData:
P->vm_data = strtol(S,&S,10);
continue;
case_VmExe:
P->vm_exe = strtol(S,&S,10);
continue;
case_VmLck:
P->vm_lock = strtol(S,&S,10);
continue;
case_VmLib:
P->vm_lib = strtol(S,&S,10);
continue;
case_VmRSS:
P->vm_rss = strtol(S,&S,10);
continue;
case_VmSize:
P->vm_size = strtol(S,&S,10);
continue;
case_VmStk:
P->vm_stack = strtol(S,&S,10);
continue;
case_VmSwap: // Linux 2.6.34
P->vm_swap = strtol(S,&S,10);
continue;
case_CapBnd:
case_CapEff:
case_CapInh:
case_CapPrm:
case_FDSize:
case_Groups:
case_SigQ:
case_VmHWM: // 2005, peak VmRSS unless VmRSS is bigger
case_VmPTE:
case_VmPeak: // 2005, peak VmSize unless VmSize is bigger
continue;
2003-02-17 06:27:15 +05:30
}
2003-09-28 08:15:05 +05:30
#if 0
2003-09-28 08:15:05 +05:30
// recent kernels supply per-tgid pending signals
if(is_proc && *ShdPnd){
memcpy(P->signal, ShdPnd, 16);
P->signal[16] = '\0';
}
#endif
// recent kernels supply per-tgid pending signals
#ifdef SIGNAL_STRING
if(!is_proc || !P->signal[0]){
memcpy(P->signal, P->_sigpnd, 16);
P->signal[16] = '\0';
}
#else
if(!is_proc || !have_process_pending){
P->signal = P->_sigpnd;
}
#endif
2003-09-28 08:15:05 +05:30
2003-12-13 22:21:40 +05:30
// Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid"
// that is not initialized for built-in kernel tasks.
// Only 2.6.0 and above have "Threads" (nlwp) info.
if(Threads){
P->nlwp = Threads;
P->tgid = Tgid; // the POSIX PID value
P->tid = Pid; // the thread ID
}else{
P->nlwp = 1;
P->tgid = Pid;
P->tid = Pid;
}
2003-02-17 06:27:15 +05:30
LEAVE(0x220);
}
///////////////////////////////////////////////////////////////////////
2002-02-02 04:17:29 +05:30
2002-12-21 16:04:50 +05:30
// Reads /proc/*/stat files, being careful not to trip over processes with
// names like ":-) 1 2 3 4 5 6".
2002-11-25 15:46:33 +05:30
static void stat2proc(const char* S, proc_t *restrict P) {
2002-12-03 14:48:27 +05:30
unsigned num;
2002-12-03 14:37:59 +05:30
char* tmp;
2003-02-17 06:27:15 +05:30
ENTER(0x160);
2002-02-02 04:17:29 +05:30
/* fill in default values for older kernels */
P->processor = 0;
2002-10-03 15:11:57 +05:30
P->rtprio = -1;
P->sched = -1;
2003-12-13 22:21:40 +05:30
P->nlwp = 0;
2002-12-03 14:37:59 +05:30
2002-12-21 11:52:00 +05:30
S = strchr(S, '(') + 1;
tmp = strrchr(S, ')');
2002-12-03 14:37:59 +05:30
num = tmp - S;
2002-12-07 14:04:03 +05:30
if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1;
2002-12-03 14:37:59 +05:30
memcpy(P->cmd, S, num);
P->cmd[num] = '\0';
S = tmp + 2; // skip ") "
num = sscanf(S,
2002-02-02 04:17:29 +05:30
"%c "
"%d %d %d %d %d "
2002-05-28 09:48:55 +05:30
"%lu %lu %lu %lu %lu "
"%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */
2003-10-16 09:00:41 +05:30
"%ld %ld "
"%d "
"%ld "
2002-05-28 09:48:55 +05:30
"%Lu " /* start_time */
"%lu "
2002-02-02 04:17:29 +05:30
"%ld "
2003-01-16 13:33:40 +05:30
"%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
2002-02-02 04:17:29 +05:30
"%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
2004-04-13 09:26:52 +05:30
"%"KLF"u %*lu %*lu "
2002-10-03 15:11:57 +05:30
"%d %d "
"%lu %lu",
2002-02-02 04:17:29 +05:30
&P->state,
&P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
2002-05-28 09:48:55 +05:30
&P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
&P->utime, &P->stime, &P->cutime, &P->cstime,
2003-10-16 09:00:41 +05:30
&P->priority, &P->nice,
&P->nlwp,
&P->alarm,
2002-05-28 09:48:55 +05:30
&P->start_time,
&P->vsize,
2002-02-02 04:17:29 +05:30
&P->rss,
&P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */
2004-04-13 09:26:52 +05:30
&P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */
2002-02-02 04:17:29 +05:30
/* -- Linux 2.0.35 ends here -- */
2002-10-03 15:11:57 +05:30
&P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */
/* -- Linux 2.2.8 to 2.5.17 end here -- */
&P->rtprio, &P->sched /* both added to 2.5.18 */
2002-02-02 04:17:29 +05:30
);
2003-12-13 22:21:40 +05:30
if(!P->nlwp){
P->nlwp = 1;
}
2003-02-17 06:27:15 +05:30
LEAVE(0x160);
2002-02-02 04:17:29 +05:30
}
2003-02-17 06:27:15 +05:30
/////////////////////////////////////////////////////////////////////////
2002-11-25 15:46:33 +05:30
static void statm2proc(const char* s, proc_t *restrict P) {
2002-02-02 04:17:29 +05:30
int num;
num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
&P->size, &P->resident, &P->share,
&P->trs, &P->lrs, &P->drs, &P->dt);
/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */
}
2002-10-12 09:55:57 +05:30
static int file2str(const char *directory, const char *what, char *ret, int cap) {
2002-02-02 04:17:29 +05:30
static char filename[80];
int fd, num_read;
sprintf(filename, "%s/%s", directory, what);
2002-12-03 14:37:59 +05:30
fd = open(filename, O_RDONLY, 0);
if(unlikely(fd==-1)) return -1;
num_read = read(fd, ret, cap - 1);
2002-02-02 04:17:29 +05:30
close(fd);
2003-02-17 06:27:15 +05:30
if(unlikely(num_read<=0)) return -1;
ret[num_read] = '\0';
2002-02-02 04:17:29 +05:30
return num_read;
}
2002-10-12 09:55:57 +05:30
static char** file2strvec(const char* directory, const char* what) {
2002-02-02 04:17:29 +05:30
char buf[2048]; /* read buf bytes at a time */
char *p, *rbuf = 0, *endbuf, **q, **ret;
int fd, tot = 0, n, c, end_of_file = 0;
int align;
sprintf(buf, "%s/%s", directory, what);
2002-12-03 14:37:59 +05:30
fd = open(buf, O_RDONLY, 0);
if(fd==-1) return NULL;
2002-02-02 04:17:29 +05:30
/* read whole file into a memory buffer, allocating as we go */
while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
2002-10-14 02:02:09 +05:30
if (n < (int)(sizeof buf - 1))
2002-02-02 04:17:29 +05:30
end_of_file = 1;
if (n == 0 && rbuf == 0)
return NULL; /* process died between our open and read */
if (n < 0) {
if (rbuf)
free(rbuf);
return NULL; /* read error */
}
if (end_of_file && buf[n-1]) /* last read char not null */
buf[n++] = '\0'; /* so append null-terminator */
rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */
memcpy(rbuf + tot, buf, n); /* copy buffer into it */
tot += n; /* increment total byte ctr */
if (end_of_file)
break;
}
close(fd);
if (n <= 0 && !end_of_file) {
if (rbuf) free(rbuf);
return NULL; /* read error */
}
endbuf = rbuf + tot; /* count space for pointers */
align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
for (c = 0, p = rbuf; p < endbuf; p++)
if (!*p)
c += sizeof(char*);
c += sizeof(char*); /* one extra for NULL term */
rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */
endbuf = rbuf + tot; /* addr just past data buf */
q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
*q++ = p = rbuf; /* point ptrs to the strings */
endbuf--; /* do not traverse final NUL */
while (++p < endbuf)
if (!*p) /* NUL char implies that */
*q++ = p+1; /* next string -> next char */
*q = 0; /* null ptr list terminator */
return ret;
}
2002-12-03 14:37:59 +05:30
// warning: interface may change
int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
char name[32];
int fd;
2002-12-03 14:48:27 +05:30
unsigned n = 0;
dst[0] = '\0';
2002-12-03 14:37:59 +05:30
snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
fd = open(name, O_RDONLY);
2002-12-03 14:48:27 +05:30
if(fd==-1) return 0;
2002-12-03 14:37:59 +05:30
for(;;){
ssize_t r = read(fd,dst+n,sz-n);
if(r==-1){
if(errno==EINTR) continue;
break;
}
n += r;
if(n==sz) break; // filled the buffer
if(r==0) break; // EOF
}
close(fd);
2002-12-03 14:37:59 +05:30
if(n){
int i;
if(n==sz) n--;
dst[n] = '\0';
i=n;
while(i--){
2003-02-17 06:27:15 +05:30
int c = dst[i];
if(c<' ' || c>'~') dst[i]=' ';
2002-12-03 14:37:59 +05:30
}
}
return n;
}
2002-02-02 04:17:29 +05:30
/* These are some nice GNU C expression subscope "inline" functions.
* The can be used with arbitrary types and evaluate their arguments
* exactly once.
*/
/* Test if item X of type T is present in the 0 terminated list L */
# define XinL(T, X, L) ( { \
T x = (X), *l = (L); \
while (*l && *l != x) l++; \
*l == x; \
} )
/* Test if item X of type T is present in the list L of length N */
# define XinLN(T, X, L, N) ( { \
T x = (X), *l = (L); \
int i = 0, n = (N); \
while (i < n && l[i] != x) i++; \
i < n && l[i] == x; \
} )
2003-09-18 03:28:32 +05:30
//////////////////////////////////////////////////////////////////////////////////
// This reads process info from /proc in the traditional way, for one process.
// The pid (tgid? tid?) is already in p, and a path to it in path, with some
// room to spare.
2003-09-20 13:59:55 +05:30
static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
2003-09-18 03:28:32 +05:30
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm
2003-09-20 13:59:55 +05:30
char *restrict const path = PT->path;
2003-09-18 03:28:32 +05:30
unsigned flags = PT->flags;
2002-02-02 04:17:29 +05:30
2002-12-09 05:23:05 +05:30
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
2002-02-02 04:17:29 +05:30
goto next_proc;
2002-09-27 19:18:00 +05:30
2002-10-22 11:42:12 +05:30
if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
2002-02-02 04:17:29 +05:30
goto next_proc; /* not one of the requested uids */
2002-12-21 16:57:47 +05:30
p->euid = sb.st_uid; /* need a way to get real uid */
2003-06-30 09:00:35 +05:30
p->egid = sb.st_gid; /* need a way to get real gid */
2002-09-27 19:18:00 +05:30
2002-12-09 00:21:56 +05:30
if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
2002-12-09 05:23:05 +05:30
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
2002-12-09 00:21:56 +05:30
goto next_proc; /* error reading /proc/#/stat */
stat2proc(sbuf, p); /* parse /proc/#/stat */
}
2002-02-02 04:17:29 +05:30
2003-09-18 03:28:32 +05:30
if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
2002-12-09 05:23:05 +05:30
if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
2002-02-02 04:17:29 +05:30
statm2proc(sbuf, p); /* ignore statm errors here */
} /* statm fields just zero */
2002-10-22 11:42:12 +05:30
if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
2002-12-09 05:23:05 +05:30
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
2003-09-28 08:15:05 +05:30
status2proc(sbuf, p, 1);
2002-02-02 04:17:29 +05:30
}
}
// if multithreaded, some values are crap
if(p->nlwp > 1){
p->wchan = (KLONG)~0ull;
}
/* some number->text resolving which is time consuming and kind of insane */
2002-10-22 11:42:12 +05:30
if (flags & PROC_FILLUSR){
2004-07-21 05:01:12 +05:30
memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
2002-10-22 11:42:12 +05:30
if(flags & PROC_FILLSTATUS) {
2004-07-21 05:01:12 +05:30
memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
}
}
/* some number->text resolving which is time consuming and kind of insane */
2002-10-22 11:42:12 +05:30
if (flags & PROC_FILLGRP){
2004-07-21 05:01:12 +05:30
memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
2002-10-22 11:42:12 +05:30
if(flags & PROC_FILLSTATUS) {
2004-07-21 05:01:12 +05:30
memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
2002-02-02 04:17:29 +05:30
}
}
2002-10-22 11:42:12 +05:30
if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
2002-02-02 04:17:29 +05:30
p->cmdline = file2strvec(path, "cmdline");
else
p->cmdline = NULL;
2002-12-09 05:23:05 +05:30
if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
2002-02-02 04:17:29 +05:30
p->environ = file2strvec(path, "environ");
else
p->environ = NULL;
if(linux_version_code>=LINUX_VERSION(2,6,24) && (flags & PROC_FILLCGROUP)) {
p->cgroup = file2strvec(path, "cgroup"); /* read /proc/#/cgroup */
if(p->cgroup && *p->cgroup) {
int i = strlen(*p->cgroup);
if( (*p->cgroup)[i-1]=='\n' )
(*p->cgroup)[i-1] = ' '; //little hack to remove trailing \n
}
}
else
p->cgroup = NULL;
2002-02-02 04:17:29 +05:30
return p;
2003-09-18 03:28:32 +05:30
next_proc:
return NULL;
}
2003-09-20 13:59:55 +05:30
//////////////////////////////////////////////////////////////////////////////////
// This reads /proc/*/task/* data, for one task.
// p is the POSIX process (task group summary) (not needed by THIS implementation)
// t is the POSIX thread (task group member, generally not the leader)
// path is a path to the task, with some room to spare.
static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm
unsigned flags = PT->flags;
2003-10-20 05:07:47 +05:30
//printf("hhh\n");
2003-09-20 13:59:55 +05:30
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_task;
// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
// goto next_task; /* not one of the requested uids */
t->euid = sb.st_uid; /* need a way to get real uid */
t->egid = sb.st_gid; /* need a way to get real gid */
2003-10-20 05:07:47 +05:30
//printf("iii\n");
2003-09-20 13:59:55 +05:30
if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
goto next_task; /* error reading /proc/#/stat */
stat2proc(sbuf, t); /* parse /proc/#/stat */
}
if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
2003-09-28 08:15:05 +05:30
#if 0
2003-09-20 13:59:55 +05:30
if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
statm2proc(sbuf, t); /* ignore statm errors here */
2003-09-28 08:15:05 +05:30
#else
t->size = p->size;
t->resident = p->resident;
t->share = p->share;
t->trs = p->trs;
t->lrs = p->lrs;
t->drs = p->drs;
t->dt = p->dt;
#endif
2003-09-20 13:59:55 +05:30
} /* statm fields just zero */
if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
2003-09-28 08:15:05 +05:30
status2proc(sbuf, t, 0);
2003-09-20 13:59:55 +05:30
}
}
/* some number->text resolving which is time consuming */
if (flags & PROC_FILLUSR){
2004-07-21 05:01:12 +05:30
memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser);
2003-09-20 13:59:55 +05:30
if(flags & PROC_FILLSTATUS) {
2004-07-21 05:01:12 +05:30
memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser);
memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser);
memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser);
2003-09-20 13:59:55 +05:30
}
}
/* some number->text resolving which is time consuming */
if (flags & PROC_FILLGRP){
2004-07-21 05:01:12 +05:30
memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup);
2003-09-20 13:59:55 +05:30
if(flags & PROC_FILLSTATUS) {
2004-07-21 05:01:12 +05:30
memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup);
memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup);
memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup);
2003-09-20 13:59:55 +05:30
}
}
#if 0
if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
t->cmdline = file2strvec(path, "cmdline");
else
t->cmdline = NULL;
if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
t->environ = file2strvec(path, "environ");
else
t->environ = NULL;
#else
t->cmdline = p->cmdline; // better not free these until done with all threads!
t->environ = p->environ;
#endif
t->cgroup = p->cgroup;
2003-09-29 09:39:52 +05:30
t->ppid = p->ppid; // ought to put the per-task ppid somewhere
2003-09-20 13:59:55 +05:30
return t;
next_task:
return NULL;
}
2003-09-18 03:28:32 +05:30
//////////////////////////////////////////////////////////////////////////////////
// This finds processes in /proc in the traditional way.
// Return non-zero on success.
2003-09-20 13:59:55 +05:30
static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
2003-09-18 03:28:32 +05:30
static struct direct *ent; /* dirent handle */
2003-09-20 13:59:55 +05:30
char *restrict const path = PT->path;
2003-09-18 03:28:32 +05:30
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;
}
2003-09-20 13:59:55 +05:30
p->tgid = strtoul(ent->d_name, NULL, 10);
p->tid = p->tgid;
2003-09-18 03:28:32 +05:30
memcpy(path, "/proc/", 6);
strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries
return 1;
2002-02-02 04:17:29 +05:30
}
2003-09-20 13:59:55 +05:30
//////////////////////////////////////////////////////////////////////////////////
// This finds tasks in /proc/*/task/ in the traditional way.
// Return non-zero on success.
static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
static struct direct *ent; /* dirent handle */
2003-10-20 05:07:47 +05:30
if(PT->taskdir_user != p->tgid){
if(PT->taskdir){
closedir(PT->taskdir);
}
2003-09-20 13:59:55 +05:30
// use "path" as some tmp space
2003-10-21 06:21:36 +05:30
snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid);
2003-09-20 13:59:55 +05:30
PT->taskdir = opendir(path);
if(!PT->taskdir) return 0;
2003-10-20 05:07:47 +05:30
PT->taskdir_user = p->tgid;
2003-09-20 13:59:55 +05:30
}
for (;;) {
ent = readdir(PT->taskdir);
if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
}
t->tid = strtoul(ent->d_name, NULL, 10);
t->tgid = p->tgid;
t->ppid = p->ppid; // cover for kernel behavior? we want both actually...?
2003-10-21 06:21:36 +05:30
snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);
2003-09-20 13:59:55 +05:30
return 1;
}
2003-09-18 03:28:32 +05:30
//////////////////////////////////////////////////////////////////////////////////
// This "finds" processes in a list that was given to openproc().
2003-09-20 13:59:55 +05:30
// Return non-zero on success. (tgid was handy)
static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
char *restrict const path = PT->path;
pid_t tgid = *(PT->pids)++;
if(likely( tgid )){
snprintf(path, PROCPATHLEN, "/proc/%d", tgid);
p->tgid = tgid;
p->tid = tgid; // they match for leaders
2003-09-18 03:28:32 +05:30
}
2003-09-20 13:59:55 +05:30
return tgid;
2003-09-18 03:28:32 +05:30
}
//////////////////////////////////////////////////////////////////////////////////
/* readproc: return a pointer to a proc_t filled with requested info about the
2002-02-02 04:17:29 +05:30
* next process available matching the restriction set. If no more such
* processes are available, return a null pointer (boolean false). Use the
* passed buffer instead of allocating space if it is non-NULL. */
/* This is optimized so that if a PID list is given, only those files are
* searched for in /proc. If other lists are given in addition to the PID list,
* the same logic can follow through as for the no-PID list case. This is
* fairly complex, but it does try to not to do any unnecessary work.
*/
2003-09-18 03:28:32 +05:30
proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
proc_t *ret;
proc_t *saved_p;
PT->did_fake=0;
2003-10-20 05:07:47 +05:30
// if (PT->taskdir) {
// closedir(PT->taskdir);
// PT->taskdir = NULL;
// PT->taskdir_user = -1;
// }
2003-09-20 13:59:55 +05:30
2003-09-18 03:28:32 +05:30
saved_p = p;
if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
for(;;){
2003-09-20 13:59:55 +05:30
// fills in the path, plus p->tid and p->tgid
if (unlikely(! PT->finder(PT,p) )) goto out;
2003-09-18 03:28:32 +05:30
// go read the process data
2003-09-20 13:59:55 +05:30
ret = PT->reader(PT,p);
2003-09-18 03:28:32 +05:30
if(ret) return ret;
}
out:
if(!saved_p) free(p);
2003-09-20 13:59:55 +05:30
// FIXME: maybe set tid to -1 here, for "-" in display?
return NULL;
}
//////////////////////////////////////////////////////////////////////////////////
// readtask: return a pointer to a proc_t filled with requested info about the
// next task available. If no more such tasks are available, return a null
// pointer (boolean false). Use the passed buffer instead of allocating
// space if it is non-NULL.
proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) {
static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline
proc_t *ret;
proc_t *saved_t;
saved_t = t;
if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
2003-12-13 22:21:40 +05:30
// 1. got to fake a thread for old kernels
// 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
2003-12-13 22:21:40 +05:30
if(task_dir_missing || p->nlwp < 2){
2003-09-20 13:59:55 +05:30
if(PT->did_fake) goto out;
PT->did_fake=1;
memcpy(t,p,sizeof(proc_t));
// use the per-task pending, not per-tgid pending
#ifdef SIGNAL_STRING
memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
#else
t->signal = t->_sigpnd;
#endif
2003-09-20 13:59:55 +05:30
return t;
}
for(;;){
// fills in the path, plus t->tid and t->tgid
if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid
// go read the task data
ret = PT->taskreader(PT,p,t,path); // simple_readtask
if(ret) return ret;
}
out:
if(!saved_t) free(t);
2003-09-18 03:28:32 +05:30
return NULL;
}
2002-02-02 04:17:29 +05:30
2003-09-18 03:28:32 +05:30
//////////////////////////////////////////////////////////////////////////////////
2002-02-02 04:17:29 +05:30
2003-09-18 03:28:32 +05:30
// initiate a process table scan
PROCTAB* openproc(int flags, ...) {
va_list ap;
2003-09-20 13:59:55 +05:30
struct stat sbuf;
static int did_stat;
2003-09-18 03:28:32 +05:30
PROCTAB* PT = xmalloc(sizeof(PROCTAB));
2003-09-20 13:59:55 +05:30
if(!did_stat){
task_dir_missing = stat("/proc/self/task", &sbuf);
did_stat = 1;
}
PT->taskdir = NULL;
2003-10-20 05:07:47 +05:30
PT->taskdir_user = -1;
2003-09-20 13:59:55 +05:30
PT->taskfinder = simple_nexttid;
PT->taskreader = simple_readtask;
2003-09-18 03:28:32 +05:30
PT->reader = simple_readproc;
if (flags & PROC_PID){
PT->procfs = NULL;
PT->finder = listed_nextpid;
}else{
PT->procfs = opendir("/proc");
if(!PT->procfs) return NULL;
PT->finder = simple_nextpid;
2002-12-21 11:52:00 +05:30
}
2003-09-18 03:28:32 +05:30
PT->flags = flags;
2002-02-02 04:17:29 +05:30
2003-09-18 03:28:32 +05:30
va_start(ap, flags); /* Init args list */
if (flags & PROC_PID)
PT->pids = va_arg(ap, pid_t*);
else if (flags & PROC_UID) {
PT->uids = va_arg(ap, uid_t*);
PT->nuid = va_arg(ap, int);
}
2003-09-18 03:28:32 +05:30
va_end(ap); /* Clean up args list */
2002-02-02 04:17:29 +05:30
2003-09-18 03:28:32 +05:30
return PT;
}
2003-09-18 03:28:32 +05:30
// terminate a process table scan
void closeproc(PROCTAB* PT) {
if (PT){
if (PT->procfs) closedir(PT->procfs);
2003-09-20 13:59:55 +05:30
if (PT->taskdir) closedir(PT->taskdir);
2003-10-20 05:07:47 +05:30
memset(PT,'#',sizeof(PROCTAB));
2003-09-18 03:28:32 +05:30
free(PT);
}
}
// deallocate the space allocated by readproc if the passed rbuf was NULL
void freeproc(proc_t* p) {
if (!p) /* in case p is NULL */
return;
/* ptrs are after strings to avoid copying memory when building them. */
/* so free is called on the address of the address of strvec[0]. */
if (p->cmdline)
free((void*)*p->cmdline);
if (p->environ)
free((void*)*p->environ);
if (p->cgroup)
free((void*)*p->cgroup);
2003-09-18 03:28:32 +05:30
free(p);
2002-02-02 04:17:29 +05:30
}
2003-09-18 03:28:32 +05:30
//////////////////////////////////////////////////////////////////////////////////
2002-02-02 04:17:29 +05:30
void look_up_our_self(proc_t *p) {
2003-05-31 20:48:13 +05:30
char sbuf[1024];
if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){
fprintf(stderr, "Error, do this: mount -t proc none /proc\n");
_exit(47);
}
stat2proc(sbuf, p); // parse /proc/self/stat
2002-02-02 04:17:29 +05:30
}
2003-01-15 16:22:39 +05:30
HIDDEN_ALIAS(readproc);
2003-10-16 09:00:41 +05:30
HIDDEN_ALIAS(readtask);
2002-02-02 04:17:29 +05:30
/* Convenient wrapper around openproc and readproc to slurp in the whole process
* table subset satisfying the constraints of flags and the optional PID list.
2003-07-03 10:50:19 +05:30
* Free allocated memory with exit(). Access via tab[N]->member. The pointer
* list is NULL terminated.
2002-02-02 04:17:29 +05:30
*/
proc_t** readproctab(int flags, ...) {
PROCTAB* PT = NULL;
proc_t** tab = NULL;
int n = 0;
va_list ap;
va_start(ap, flags); /* pass through args to openproc */
2002-10-22 11:42:12 +05:30
if (flags & PROC_UID) {
2002-02-02 04:17:29 +05:30
/* temporary variables to ensure that va_arg() instances
* are called in the right order
*/
uid_t* u;
int i;
u = va_arg(ap, uid_t*);
i = va_arg(ap, int);
PT = openproc(flags, u, i);
}
2002-10-22 11:42:12 +05:30
else if (flags & PROC_PID)
2002-02-02 04:17:29 +05:30
PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
else
PT = openproc(flags);
va_end(ap);
do { /* read table: */
tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
2003-01-15 16:22:39 +05:30
tab[n] = readproc_direct(PT, NULL); /* final null to terminate */
2002-02-02 04:17:29 +05:30
} while (tab[n++]); /* stop when NULL reached */
closeproc(PT);
return tab;
}
2003-10-16 09:00:41 +05:30
// Try again, this time with threads and selection.
2003-10-20 05:07:47 +05:30
proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) {
2003-10-16 09:00:41 +05:30
proc_t** ptab = NULL;
unsigned n_proc_alloc = 0;
unsigned n_proc = 0;
2003-10-20 05:07:47 +05:30
proc_t** ttab = NULL;
2003-10-16 09:00:41 +05:30
unsigned n_task_alloc = 0;
2003-10-20 05:07:47 +05:30
unsigned n_task = 0;
2003-10-16 09:00:41 +05:30
2003-10-20 05:07:47 +05:30
proc_t* data = NULL;
unsigned n_alloc = 0;
unsigned long n_used = 0;
2003-10-16 09:00:41 +05:30
2003-10-20 05:07:47 +05:30
proc_data_t *pd;
2003-10-16 09:00:41 +05:30
for(;;){
proc_t *tmp;
if(n_alloc == n_used){
//proc_t *old = data;
n_alloc = n_alloc*5/4+30; // grow by over 25%
data = realloc(data,sizeof(proc_t)*n_alloc);
//if(!data) return NULL;
}
if(n_proc_alloc == n_proc){
//proc_t **old = ptab;
n_proc_alloc = n_proc_alloc*5/4+30; // grow by over 25%
ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc);
//if(!ptab) return NULL;
}
tmp = readproc_direct(PT, data+n_used);
if(!tmp) break;
if(!want_proc(tmp)) continue;
2003-10-20 05:07:47 +05:30
ptab[n_proc++] = (proc_t*)(n_used++);
2003-10-16 09:00:41 +05:30
if(!( PT->flags & PROC_LOOSE_TASKS )) continue;
for(;;){
proc_t *t;
if(n_alloc == n_used){
proc_t *old = data;
2003-10-16 09:00:41 +05:30
n_alloc = n_alloc*5/4+30; // grow by over 25%
data = realloc(data,sizeof(proc_t)*n_alloc);
// have to move tmp too
tmp = data+(tmp-old);
2003-10-16 09:00:41 +05:30
//if(!data) return NULL;
}
if(n_task_alloc == n_task){
//proc_t **old = ttab;
n_task_alloc = n_task_alloc*5/4+1; // grow by over 25%
ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc);
//if(!ttab) return NULL;
}
t = readtask_direct(PT, tmp, data+n_used);
if(!t) break;
if(!want_task(t)) continue;
2003-10-20 05:07:47 +05:30
ttab[n_task++] = (proc_t*)(n_used++);
2003-10-16 09:00:41 +05:30
}
}
pd = malloc(sizeof(proc_data_t));
pd->proc = ptab;
pd->task = ttab;
pd->nproc = n_proc;
pd->ntask = n_task;
2003-10-20 05:07:47 +05:30
if(PT->flags & PROC_LOOSE_TASKS){
2003-10-16 09:00:41 +05:30
pd->tab = ttab;
pd->n = n_task;
}else{
pd->tab = ptab;
pd->n = n_proc;
}
2003-10-20 05:07:47 +05:30
// change array indexes to pointers
while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]);
while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]);
2003-10-16 09:00:41 +05:30
return pd;
}
2004-01-27 01:31:56 +05:30
/*
* get_proc_stats - lookup a single tasks information and fill out a proc_t
*
* On failure, returns NULL. On success, returns 'p' and 'p' is a valid
* and filled out proc_t structure.
*/
2004-11-05 02:20:59 +05:30
proc_t * get_proc_stats(pid_t pid, proc_t *p) {
2004-01-27 01:31:56 +05:30
static char path[PATH_MAX], sbuf[1024];
struct stat statbuf;
sprintf(path, "/proc/%d", pid);
if (stat(path, &statbuf)) {
perror("stat");
return NULL;
}
if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0)
stat2proc(sbuf, p); /* parse /proc/#/stat */
if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0)
statm2proc(sbuf, p); /* ignore statm errors here */
if (file2str(path, "status", sbuf, sizeof sbuf) >= 0)
status2proc(sbuf, p, 0 /*FIXME*/);
return p;
}