📄 readproc.c
字号:
/* * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) * Copyright (C) 1996 Charles L. Blake. * Copyright (C) 1998 Michael K. Johnson * May be distributed under the conditions of the * GNU Library General Public License; a copy is in COPYING */#include "proc/version.h"#include "proc/readproc.h"#include "proc/devname.h"#include "proc/procps.h"#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <sys/dir.h>#include <sys/types.h>#include <sys/stat.h>#define Do(x) (flags & PROC_ ## x) /* convenient shorthand *//* initiate a process table scan */PROCTAB* openproc(int flags, ...) { va_list ap; PROCTAB* PT = xmalloc(sizeof(PROCTAB)); if (Do(PID)) PT->procfs = NULL; else if (!(PT->procfs = opendir("/proc"))) return NULL; PT->flags = flags; va_start(ap, flags); /* Init args list */ if (Do(PID)) PT->pids = va_arg(ap, pid_t*); else if (Do(TTY)) PT->ttys = va_arg(ap, dev_t*); else if (Do(UID)) { PT->uids = va_arg(ap, uid_t*); PT->nuid = va_arg(ap, int); } else if (Do(STAT)) PT->stats = va_arg(ap, char*); va_end(ap); /* Clean up args list */ if (Do(ANYTTY) && Do(TTY)) PT->flags = PT->flags & ~PROC_TTY; /* turn off TTY flag */ return PT;}/* terminate a process table scan */void closeproc(PROCTAB* PT) { if (PT->procfs) closedir(PT->procfs); if (PT) 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); free(p);}void status2proc (char* S, proc_t* P, int fill) { char* tmp; if (fill == 1) { memset(P->cmd, 0, sizeof P->cmd); sscanf (S, "Name:\t%15c", P->cmd); tmp = strchr(P->cmd,'\n'); *tmp='\0'; tmp = strstr (S,"State"); sscanf (tmp, "State:\t%c", &P->state); } tmp = strstr (S,"Pid:"); if(tmp) sscanf (tmp, "Pid:\t%d\n" "PPid:\t%d\n", &P->pid, &P->ppid ); else fprintf(stderr, "Internal error!\n"); tmp = strstr (S,"Uid:"); if(tmp) sscanf (tmp, "Uid:\t%d\t%d\t%d\t%d", &P->ruid, &P->euid, &P->suid, &P->fuid ); else fprintf(stderr, "Internal error!\n"); tmp = strstr (S,"Gid:"); if(tmp) sscanf (tmp, "Gid:\t%d\t%d\t%d\t%d", &P->rgid, &P->egid, &P->sgid, &P->fgid ); else fprintf(stderr, "Internal error!\n"); tmp = strstr (S,"VmSize:"); if(tmp) sscanf (tmp, "VmSize: %lu kB\n" "VmLck: %lu kB\n" "VmRSS: %lu kB\n" "VmData: %lu kB\n" "VmStk: %lu kB\n" "VmExe: %lu kB\n" "VmLib: %lu kB\n", &P->vm_size, &P->vm_lock, &P->vm_rss, &P->vm_data, &P->vm_stack, &P->vm_exe, &P->vm_lib ); else /* looks like an annoying kernel thread */ { 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; } tmp = strstr (S,"SigPnd:"); if(tmp) sscanf (tmp,#ifdef SIGNAL_STRING "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s", P->signal, P->blocked, P->sigignore, P->sigcatch#else "SigPnd: %Lx SigBlk: %Lx SigIgn: %Lx %*s %Lx", &P->signal, &P->blocked, &P->sigignore, &P->sigcatch#endif ); else fprintf(stderr, "Internal error!\n");}/* stat2proc() makes sure it can handle arbitrary executable file basenames for `cmd', i.e. those with embedded whitespace or embedded ')'s. Such names confuse %s (see scanf(3)), so the string is split and %39c is used instead. (except for embedded ')' "(%[^)]c)" would work.*/void stat2proc(char* S, proc_t* P) { int num; char* tmp = strrchr(S, ')'); /* split into "PID (cmd" and "<rest>" */ *tmp = '\0'; /* replace trailing ')' with NUL */ /* parse these two strings separately, skipping the leading "(". */ memset(P->cmd, 0, sizeof P->cmd); /* clear even though *P xcalloc'd ?! */ sscanf(S, "%d (%15c", &P->pid, P->cmd); /* comm[16] in kernel */ num = sscanf(tmp + 2, /* skip space after ')' too */ "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu %lu %lu " "%ld %ld %ld %ld %ld %ld " "%lu %lu " "%ld " "%lu %lu %lu %lu %lu %lu " "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ "%lu %lu %lu %*d %d", &P->state, &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, &P->utime, &P->stime, &P->cutime, &P->cstime, &P->priority, &P->nice, &P->timeout, &P->it_real_value, &P->start_time, &P->vsize, &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 */ &P->wchan, &P->nswap, &P->cnswap /* , &P->exit_signal */, &P->lproc);/* TODO: add &P->exit_signal support here, perhaps to identify Linux threads */ /* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ if (P->tty == 0) P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 */ if (linux_version_code < LINUX_VERSION(1,3,39)) { P->priority = 2*15 - P->priority; /* map old meanings to new */ P->nice = 15 - P->nice; } if (linux_version_code < LINUX_VERSION(1,1,30) && P->tty != -1) P->tty = 4*0x100 + P->tty; /* when tty wasn't full devno */}void statm2proc(char* s, proc_t* P) { 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); */}void nulls2sep(char* str, int len, char sep) { int i; for (i = 0; i < len; i++) if (str[i] == 0) str[i] = sep;}int file2str(char *directory, char *what, char *ret, int cap) { static char filename[80]; int fd, num_read; sprintf(filename, "%s/%s", directory, what); if ( (fd = open(filename, O_RDONLY, 0)) == -1 ) return -1; if ( (num_read = read(fd, ret, cap - 1)) <= 0 ) num_read = -1; else ret[num_read] = 0; close(fd); return num_read;}char** file2strvec(char* directory, char* what) { 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); if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return NULL; /* read whole file into a memory buffer, allocating as we go */ while ((n = read(fd, buf, sizeof buf - 1)) > 0) { if (n < sizeof buf - 1) 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;}/* 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; \ } )/* readproc: return a pointer to a proc_t filled with requested info about the * 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. * Unfortunately, the reverse filtering option in which any PID *except* the * ones listed is pursued. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -