summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Baulig <martin@src.gnome.org>1998-07-18 16:15:56 +0000
committerMartin Baulig <martin@src.gnome.org>1998-07-18 16:15:56 +0000
commit5e9d34c91a58103012dbc41bd3e2165f76ee99f0 (patch)
tree746aa07e44fb1aec0f5406638bb24986547f38bf
parent0e086aef2513d7263ba21f6835f74945fa9915e2 (diff)
downloadlibgtop-5e9d34c91a58103012dbc41bd3e2165f76ee99f0.tar.gz
Initial revision
-rw-r--r--libproc/Makefile111
-rw-r--r--libproc/alloc.c49
-rw-r--r--libproc/compare.c307
-rw-r--r--libproc/devname.c269
-rw-r--r--libproc/devname.h10
-rw-r--r--libproc/ksym.c252
-rw-r--r--libproc/output.c52
-rw-r--r--libproc/ps.h30
-rw-r--r--libproc/psdata.h103
-rw-r--r--libproc/pwcache.c37
-rw-r--r--libproc/readproc.c395
-rw-r--r--libproc/readproc.h168
-rw-r--r--libproc/signals.c65
-rw-r--r--libproc/signals.h12
-rw-r--r--libproc/signames.h32
-rw-r--r--libproc/status.c20
-rw-r--r--libproc/sysinfo.c176
-rw-r--r--libproc/sysinfo.h17
-rw-r--r--libproc/tree.h14
-rw-r--r--libproc/version.c39
-rw-r--r--libproc/version.h24
-rw-r--r--libproc/whattime.c89
-rw-r--r--libproc/whattime.h9
23 files changed, 2280 insertions, 0 deletions
diff --git a/libproc/Makefile b/libproc/Makefile
new file mode 100644
index 00000000..6d72070b
--- /dev/null
+++ b/libproc/Makefile
@@ -0,0 +1,111 @@
+# Auto-adaptive C library Makefile adapted for libproc, Chuck Blake.
+# Assumptions are basically that all the .c files in the CWD are modules
+# for the library and that all .h files are the interface to the library.
+
+# PROJECT SPECIFIC MACROS
+NAME = proc
+
+# INSTALLATION OPTIONS
+TOPDIR = /usr
+HDRDIR = $(TOPDIR)/include/$(NAME)# where to put .h files
+LIBDIR = $(TOPDIR)/lib# where to put library files
+SHLIBDIR = /lib# where to put shared library files
+HDROWN = -o root -g root # owner of header files
+LIBOWN = -o root -g root # owner of library files
+INSTALL = install
+
+# COMPILATION OPTIONS
+CC = gcc -O2 -D_GNU_SOURCE #-ggdb # easy to command-line override
+CFLAGS = -I.. -Wall
+
+# ----------------------------------------------------------------#
+# The rest is the auto-magic section -- highly GNU make dependent #
+# You should never need to edit this. #
+# ----------------------------------------------------------------#
+
+VC_SUF = ,v
+VC_PFX = RCS/
+RCSFILES = $(patsubst $(VC_PFX)%$(VC_SUF),%,$(wildcard $(VC_PFX)*$(VC_SUF)))
+
+# We take the union of RCS files and other files in CWD so that new files do
+# not need to alter this makefile. 'sort' removes duplicates. This allows the
+# convenience of compiling and testing new files before the initial check-in.
+
+SRC = $(sort $(wildcard *.c) $(filter %.c,$(RCSFILES)))
+HDR = $(sort $(wildcard *.h) $(filter %.h,$(RCSFILES)))
+
+OBJ = $(SRC:.c=.o)
+SONAME = lib$(NAME).so.$(LIBVERSION)
+
+ifeq ($(SHARED),1)
+CFLAGS += -fpic
+all: lib$(NAME).a $(SONAME)
+else
+all: lib$(NAME).a
+endif
+
+lib$(NAME).a: $(OBJ)
+ $(AR) rcs $@ $^
+
+$(SONAME): $(OBJ)
+ gcc -Wl,-shared -Wl,-soname,$(SONAME) -o $@ $^ -lc
+ ln -sf $(SONAME) lib$(NAME).so
+
+# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT
+
+.depend:
+ $(strip $(CC) $(CFLAGS) -MM -MG $(SRC) > .depend)
+include .depend
+
+# INSTALLATION
+
+install: all
+ if ! [ -d $(HDRDIR) ] ; then mkdir $(HDRDIR) ; fi
+ $(INSTALL) $(HDROWN) $(HDR) $(TOPDIR)/include/$(NAME)
+ $(INSTALL) $(LIBOWN) lib$(NAME).a $(LIBDIR)
+ifeq ($(SHARED),1)
+ $(INSTALL) $(LIBOWN) $(SONAME) $(SHLIBDIR)
+ ln -sf $(SHLIBDIR)/$(SONAME) $(SHLIBDIR)/lib$(NAME).so
+ ldconfig
+endif
+
+# VARIOUS SHORT CUT TARGETS
+.PHONY: all install dep clean distclean checkout checkclean
+
+dep: .depend
+
+clean:
+ $(RM) lib$(NAME).* *.o
+
+distclean: clean
+ $(RM) .depend signames.h
+
+checkout:
+ $(CO) $(RCSFILES)
+
+checkclean:
+ $(RM) $(RCSFILES)
+
+# CUSTOM c -> o rule so that command-line has minimal whitespace
+
+%.o : %.c
+ $(strip $(CC) $(CFLAGS) -c $<)
+
+# PROJECT SPECIFIC DEPENDENCIES/BUILD RULES
+
+
+version.o: version.c version.h
+ifdef MINORVERSION
+ $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c version.c)
+else
+ $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -c version.c)
+endif
+
+signals.o : signames.h
+
+signames.h ../proc/signames.h : /usr/include/signal.h
+ /lib/cpp -dM </usr/include/signal.h | \
+ tr -s '\t ' ' ' | sort -n +2 | sed \
+ 's:#define SIG\([A-Z]\+[0-9]*\) \([0-9]\+\) *\(\|/\*.*\)$$:{\
+\2,"\1" },:p;d' > signames.h
+
diff --git a/libproc/alloc.c b/libproc/alloc.c
new file mode 100644
index 00000000..92fba2c5
--- /dev/null
+++ b/libproc/alloc.c
@@ -0,0 +1,49 @@
+/***********************************************************************\
+* Copyright (C) 1992 by Michael K. Johnson, johnsonm@sunsite.unc.edu *
+* *
+* This file is placed under the conditions of the GNU public *
+* license, version 2, or any later version. See file COPYING *
+* for information on distribution conditions. *
+\***********************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+
+void *xcalloc(void *pointer, int size) {
+ void * ret;
+ if (pointer)
+ free(pointer);
+ if (!(ret = calloc(1, size))) {
+ fprintf(stderr, "xcalloc: allocation error, size = %d\n", size);
+ exit(1);
+ } else {
+ return ret;
+ }
+}
+
+void *xmalloc(unsigned int size) {
+ void *p;
+
+ if (size == 0)
+ ++size;
+ p = malloc(size);
+ if (!p) {
+ fprintf(stderr, "xmalloc: malloc(%d) failed", size);
+ perror(NULL);
+ exit(1);
+ }
+ return(p);
+}
+
+void *xrealloc(void *oldp, unsigned int size) {
+ void *p;
+
+ if (size == 0)
+ ++size;
+ p = realloc(oldp, size);
+ if (!p) {
+ fprintf(stderr, "xrealloc: realloc(%d) failed", size);
+ perror(NULL);
+ exit(1);
+ }
+ return(p);
+}
diff --git a/libproc/compare.c b/libproc/compare.c
new file mode 100644
index 00000000..cfbdebd6
--- /dev/null
+++ b/libproc/compare.c
@@ -0,0 +1,307 @@
+/*
+ *
+ * Copyright 1994 Charles Blake and Michael K. Johnson
+ * This file is a part of procps, which is distributable
+ * under the conditions of the GNU Public License. See the
+ * file COPYING for details.
+ *
+ */
+
+#include <string.h> /* for strcmp */
+#include <stdio.h> /* for parse error output */
+#include "proc/readproc.h" /* for proc_t */
+#include "proc/tree.h" /* for struct tree_node */
+
+/*
+ This module was written by Charles Blake for procps.
+
+mult_lvl_cmp:
+ slick general purpose multi-level compare function I invented.
+sort_depth:
+ the number of levels of functions *to use*. This means many more levels
+ can be defined than mult_lvl_cmp tres out. If this is 1 then mult_lvl_cmp
+ is just a trivial wrapper around (*sort_function[0]).
+sort_direction:
+ multiplicative factor for the output of cmp_whatever.
+ 1 ==> default order, -1 ==> reverse order, 0 ==> forced equality
+ The 0 bit is the neat part. Since a value of zero is the code for equality
+ multiplying the output of cmp_foo(a,b) forces a==b to be true. This is a
+ convenient way to turn sorting off in middle levels of a multi-level sort.
+ If time is a problem, reforming the whole sort_function array to not include
+ these unsorted middle levels will be faster since then cmp_foo won't even
+ be called. It might simplify some code depending upon how you organize it.
+sort_function[]:
+ array of function pointers that points to our family of comparison functions
+ (I have named them cmp_* but mult_lvl_cmp doesn't care what they're named).
+ This may be declared and initialized like so:
+ int (*sort_function[])(void* a, void* b)={&cmp_foo, &cmp_bar, &cmp_hiho};
+ You could also use my command line '-O' parser below.
+
+Note that we only descend levels until the order is determined. If we descend
+all levels, that means that the items are equal at all levels, so we return 0.
+Otherwise we return whatever the level's cmp_foo function would have returned.
+This allows whatever default behavior you want for cmp_foo. sort_direction[]
+reverses this default behavior, but mult_lvl_cmp doesn't decide that ascending
+or descending is the default. That is the job of your cmp_foo's.
+*/
+
+/* the only reason these are global is because qsort(3) likes it that way.
+ It's also a little more efficient if mult_lvl_cmp() is called many times.
+*/
+
+typedef int (*cmp_t)(void*,void*); /* for function pointer casts */
+
+int sort_depth = 0;
+int sort_direction[10]; /* storage for 10 levels, but 4 would be plenty!*/
+int (*sort_function[10])(void* a, void* b);
+
+int mult_lvl_cmp(void* a, void* b) {
+ int i, cmp_val;
+ for(i = 0; i < sort_depth; i++) {
+ cmp_val = sort_direction[i] * (*sort_function[i])(a,b);
+ if (cmp_val != 0)
+ return cmp_val;
+ }
+ return 0;
+}
+
+int node_mult_lvl_cmp(void* a, void* b) {
+ int i, cmp_val;
+ for(i = 0; i < sort_depth; i++) {
+ cmp_val = sort_direction[i] * (*sort_function[i])(&(((struct tree_node *)a)->proc),&(((struct tree_node *)b)->proc));
+ if (cmp_val != 0)
+ return cmp_val;
+ }
+ return 0;
+}
+
+/* qsort(3) compliant comparison functions for all members of the ps_proc
+ structure (in the same order in which they appear in the proc_t declaration)
+ return is {-1,0,1} as {a<b, a==b, a>b}
+ default ordering is ascending for all members. (flip 1,-1 to reverse)
+*/
+/* pre-processor macros to cut down on source size (and typing!)
+ Note the use of the string concatenation operator ##
+*/
+#define CMP_STR(NAME) \
+int cmp_ ## NAME(proc_t** P, proc_t** Q) { \
+ return strcmp((*P)-> ## NAME, (*Q)-> ## NAME); \
+}
+
+#define CMP_INT(NAME) \
+int cmp_ ## NAME (proc_t** P, proc_t** Q) { \
+ if ((*P)-> ## NAME < (*Q)-> ## NAME) return -1; \
+ if ((*P)-> ## NAME > (*Q)-> ## NAME) return 1; \
+ return 0; \
+}
+
+/* Define the (46!) cmp_ functions with the above macros for every element
+ of proc_t. If the binary gets too big, we could nuke inessentials.
+*/
+
+/* CMP_STR(cmdline) */
+CMP_STR(user)
+CMP_STR(cmd)
+/* CMP_INT(state) */
+/* CMP_STR(ttyc) */
+CMP_INT(uid)
+CMP_INT(pid)
+CMP_INT(ppid)
+CMP_INT(pgrp)
+CMP_INT(session)
+CMP_INT(tty)
+CMP_INT(tpgid)
+CMP_INT(utime)
+CMP_INT(stime)
+CMP_INT(cutime)
+CMP_INT(cstime)
+/* CMP_INT(priority) */
+CMP_INT(nice)
+CMP_INT(start_time)
+/* CMP_INT(signal) */
+/* CMP_INT(blocked) */
+/* CMP_INT(sigignore) */
+/* CMP_INT(sigcatch) */
+CMP_INT(flags)
+CMP_INT(min_flt)
+CMP_INT(cmin_flt)
+CMP_INT(maj_flt)
+CMP_INT(cmaj_flt)
+/* CMP_INT(timeout) */
+CMP_INT(vsize)
+CMP_INT(rss)
+/* CMP_INT(rss_rlim) */
+/* CMP_INT(start_code) */
+/* CMP_INT(end_code) */
+/* CMP_INT(start_stack) */
+/* CMP_INT(kstk_esp) */
+/* CMP_INT(kstk_eip) */
+/* CMP_INT(wchan) */
+CMP_INT(pcpu)
+CMP_INT(size)
+CMP_INT(resident)
+CMP_INT(share)
+/* CMP_INT(trs) */
+/* CMP_INT(lrs) */
+/* CMP_INT(drs) */
+/* CMP_INT(dt) */
+
+/* define user interface to sort keys. Fairly self-explanatory. */
+
+struct cmp_fun_struct {
+ char letter; /* single option-letter for key */
+ char name[15]; /* long option name for key */
+ int (*fun)(proc_t**, proc_t**); /* pointer to cmp_key */
+} cmp[] = {
+/* { '?', "cmdline", &cmp_cmdline }, */
+ { 'u', "user", &cmp_user },
+ { 'c', "cmd", &cmp_cmd },
+/* { '?', "state", &cmp_state }, */
+/* { '?', "ttyc", &cmp_ttyc }, */
+ { 'U', "uid", &cmp_uid },
+ { 'p', "pid", &cmp_pid },
+ { 'P', "ppid", &cmp_ppid },
+ { 'g', "pgrp", &cmp_pgrp },
+ { 'o', "session", &cmp_session },
+ { 't', "tty", &cmp_tty },
+ { 'G', "tpgid", &cmp_tpgid },
+ { 'k', "utime", &cmp_utime },
+ { 'K', "stime", &cmp_stime },
+ { 'j', "cutime", &cmp_cutime },
+ { 'J', "cstime", &cmp_cstime },
+/* { '?', "counter", &cmp_counter }, */
+ { 'y', "priority", &cmp_nice },
+ { 'T', "start_time", &cmp_start_time },
+/* { '?', "signal", &cmp_signal }, */
+/* { '?', "blocked", &cmp_blocked }, */
+/* { '?', "sigignore", &cmp_sigignore }, */
+/* { '?', "sigcatch", &cmp_sigcatch }, */
+ { 'f', "flags", &cmp_flags },
+ { 'm', "min_flt", &cmp_min_flt },
+ { 'n', "cmin_flt", &cmp_cmin_flt },
+ { 'M', "maj_flt", &cmp_maj_flt },
+ { 'N', "cmaj_flt", &cmp_cmaj_flt },
+/* { 'C', "timeout", &cmp_timeout }, */
+ { 'v', "vsize", &cmp_vsize },
+ { 'r', "rss", &cmp_rss },
+/* { '?', "rss_rlim", &cmp_rss_rlim }, */
+/* { '?', "start_code", &cmp_start_code }, */
+/* { '?', "end_code", &cmp_end_code }, */
+/* { '?', "start_stack", &cmp_start_stack }, */
+/* { '?', "kstk_esp", &cmp_kstk_esp }, */
+/* { '?', "kstk_eip", &cmp_kstk_eip }, */
+/* { '?', "wchan", &cmp_wchan }, */
+ { 'C', "pcpu", &cmp_pcpu },
+ { 's', "size", &cmp_size },
+ { 'R', "resident", &cmp_resident },
+ { 'S', "share", &cmp_share },
+/* { '?', "trs", &cmp_trs }, */
+/* { '?', "lrs", &cmp_lrs }, */
+/* { '?', "drs", &cmp_drs }, */
+/* { '?', "dt", &cmp_dt }, */
+ { '\0',"terminator", NULL }
+};
+
+void dump_keys(void) {
+ int i;
+ for(i=0; cmp[i].letter; i++)
+ fprintf(stderr, "%s-O%c , --sort:%-15.15s%s",
+ i%2?"":" ",
+ cmp[i].letter, cmp[i].name,
+ i%2?"\n":"");
+ if (i%2)
+ fprintf(stderr, "\n");
+}
+
+/* command line option parsing. Assign sort_{depth,direction[],function[]}
+ based upon a string of the form:
+ [+-]a[+-]b[+-]c...
+ with a,b,c,... being letter flags corresponding to a particular sort
+ key and the optional '-' specifying a reverse sort on that key. + doesn't
+ mean anything, but it keeps things looking balanced...
+*/
+int parse_sort_opt(char* opt) {
+ int i, next_dir=1;
+ for(; *opt ; ++opt) {
+ if (*opt == '-' || *opt == '+') {
+ if (*opt == '-')
+ next_dir = -1;
+ opt++;
+ continue;
+ }
+ for (i = 0; cmp[i].letter; i++)
+ if (*opt == cmp[i].letter)
+ break;
+ if (!cmp[i].letter) {
+ fprintf(stderr,
+ "ps: no such sort key -- %c. Possibilities are:\n", *opt);
+ dump_keys();
+ return -1;
+ } else {
+#ifdef DEBUG
+ fprintf(stderr,
+ "sort level %d: key %s, direction % d\n",
+ sort_depth, cmp[i].name, next_dir);
+#endif
+ sort_function[sort_depth] = (cmp_t)cmp[i].fun;
+ sort_direction[sort_depth++] = next_dir;
+ next_dir = 1;
+ }
+ }
+ return 0;
+}
+
+int parse_long_sort(char* opt) {
+ char* comma;
+ int i, more_keys, next_dir=1;
+ do {
+ if (*opt == '-' || *opt == '+') {
+ if (*opt == '-')
+ next_dir = -1;
+ more_keys = 1;
+ opt++;
+ continue;
+ }
+ more_keys = ((comma=index(opt,',')) != NULL);
+ /* keys are ',' delimited */
+ if (more_keys)
+ *comma='\0'; /* terminate for strcmp() */
+ for(i = 0; cmp[i].letter; ++i)
+ if (strcmp(opt, cmp[i].name) == 0)
+ break;
+ if (!cmp[i].letter) {
+ fprintf(stderr,
+ "ps: no such sort key -- %s. Possibilities are:\n", opt);
+ dump_keys();
+ return -1;
+ } else {
+#ifdef DEBUG
+ fprintf(stderr,
+ "sort level %d: key %s, direction % d\n",
+ sort_depth, cmp[i].name, next_dir);
+#endif
+ sort_function[sort_depth] = (cmp_t)cmp[i].fun;
+ sort_direction[sort_depth++] = next_dir;
+ next_dir = 1;
+ }
+ opt = comma + 1; /* do next loop on next key, if more keys, else done*/
+ } while (more_keys);
+ return 0;
+}
+
+void reset_sort_options (void)
+{
+ int i;
+
+ sort_depth=0;
+ for (i=0;i<10;i++){
+ sort_direction[i]=0;
+ sort_function[i]=(cmp_t)NULL;
+ }
+}
+
+void register_sort_function (int dir, cmp_t func)
+{
+ sort_function[sort_depth] = func;
+ sort_direction[sort_depth++] = dir;
+}
diff --git a/libproc/devname.c b/libproc/devname.c
new file mode 100644
index 00000000..f81af2dc
--- /dev/null
+++ b/libproc/devname.c
@@ -0,0 +1,269 @@
+/* device name <-> number map system optimized for rapid, constant time lookup.
+ Copyright Charles Blake, 1996, see COPYING for details.
+*/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define __KERNEL__
+#include <linux/kdev_t.h>
+#undef __KERNEL__
+
+#define DEVDIR "/dev"
+#define DEVTAB "psdevtab"
+static char *devtab_paths[] = {
+ "/etc/" DEVTAB,
+ "%s/." DEVTAB,
+ NULL
+};
+#define DEVINITIALMODE 0664
+#define DEV_MAX_PATH (5+256)
+#define DEV_NAME_MAX 8
+
+static dev_t major[] = { 2, 3, 4, 5, 19, 20, 22, 23, 24, 25, 32, 33,
+ 46, 47, 48, 49 };
+#define Nminor 256
+#define Nmajor (sizeof(major)/sizeof(dev_t))
+#define Ndev (Nmajor*Nminor)
+#define Ndevtab (Ndev*DEV_NAME_MAX)
+
+static char* devtab; /* the memory buffer holding all the name strings */
+
+/* This macro evaluates to the address into the table of the string of
+ DEV_NAME_MAX chars for the device with major m, minor n. */
+#define TAB(m,n) (devtab + (m)*(Nminor*DEV_NAME_MAX) + (n)*DEV_NAME_MAX)
+
+static int devtab_initialized = 0;
+
+static char* name_to_path(char* name); /* forward declarations */
+static int init_devtab (void);
+
+/* Device Name -> Number Map
+ many-to-one: -1 on failed match.
+*/
+dev_t name_to_dev(char* name) {
+ static struct stat sbuf;
+ return (stat(name_to_path(name), &sbuf) < 0) ? -1 : sbuf.st_rdev;
+}
+
+/* find m in a[] assuming a is sorted into ascending order */
+/* in-line linear search placeholder until more majors warrant binary search */
+static __inline__ int lookup(dev_t m, dev_t* a, int n) {
+ int k;
+ for(k=0; k < n && a[k] != m; k++)
+ ;
+ return (k < n) ? k : -1;
+}
+
+/* Device Number -> Name Map
+ one-to-many: first directory order match in DEVDIR, "" on failed match.
+*/
+char* dev_to_name(dev_t num) {
+ static char rval[DEV_NAME_MAX+1];
+ dev_t m = MAJOR(num), n = MINOR(num), tmp;
+ if (!devtab_initialized && !init_devtab())
+ return "";
+ if ((tmp = lookup(m, major, Nmajor)) == (dev_t)-1)
+ return "";
+ strncpy(rval, TAB(tmp,n), DEV_NAME_MAX);
+ rval[DEV_NAME_MAX] = '\0';
+ return rval;
+}
+
+static int dev_to_devtab(int);
+
+static int init_devtab(void) {
+ static struct stat sbuf, lbuf;
+ static int fd;
+ char **fmt, path[64], *HOME = getenv("HOME") ? getenv("HOME") : "";
+ for (fmt = devtab_paths; *fmt; fmt++) {
+ snprintf(path, sizeof path, *fmt, HOME);
+ lbuf.st_ino = 0; /* initialize for test later */
+ if (lstat(path, &lbuf) >= 0 && S_ISLNK(lbuf.st_mode))
+ /* don't follow symlinks */
+ continue;
+ if ( (fd = open(path, O_RDONLY)) < 0 /* open DEVTAB file */
+ || fstat(fd, &sbuf) < 0 /* fstat it */
+ || (lbuf.st_ino && (sbuf.st_ino != lbuf.st_ino)) /* race */
+ || sbuf.st_nlink > 1 /* hardlink attack */
+ || sbuf.st_size != Ndevtab /* make sure it's the right size */
+ || (devtab = mmap(0, Ndevtab, PROT_READ, MAP_SHARED, fd, 0)) == (caddr_t) -1
+ || close(fd) == -1)
+ { /* could not open for read, attempt to fix/create */
+ int oumsk = umask(0);
+ if (devtab)
+ munmap(devtab, Ndevtab);
+ if (((fd = open(path, O_RDWR|O_TRUNC|O_CREAT, DEVINITIALMODE)) == -1 &&
+ (unlink(path), fd = open(path, O_RDWR|O_TRUNC|O_CREAT, DEVINITIALMODE)) == -1)
+ || !dev_to_devtab(fd)) {
+ close(fd); /* either both opens failed or the constructor failed */
+ unlink(path); /* in case we created but could not fill a file */
+ umask(oumsk);
+ continue;
+ } else {
+ devtab_initialized = 1;
+ close(fd);
+ umask(oumsk);
+ return 1;
+ }
+ }
+ else
+ return devtab_initialized = 1;
+ }
+ return devtab_initialized;
+}
+
+/* stat every file in DEVDIR saving its basename in devtab[] if it has
+ a MAJOR(st_rdev) in our list of majors. return 0 on error otherwise 1. */
+static int dev_to_devtab(int fd) {
+ static struct stat sbuf;
+ int i;
+ dev_t m;
+ struct dirent* ent;
+ DIR* dev;
+
+ if (!(dev = opendir(DEVDIR))) {
+ fprintf(stderr, "%s: %s\nCannot generate device number -> name mapping.\n",
+ DEVDIR, sys_errlist[errno]);
+ return 0;
+ }
+ if (!(devtab = malloc(Ndevtab))) {
+ fprintf(stderr, "%s: could not allocate memory\n", sys_errlist[errno]);
+ return 0;
+ }
+ memset((void*)devtab, 0, Ndevtab);
+ while ((ent = readdir(dev))) { /* loop over all dirents in DEVDIR */
+ if (lstat(name_to_path(ent->d_name), &sbuf) < 0
+ || !S_ISCHR(sbuf.st_mode)) /* only look for char special devs */
+ continue; /* due to overloading of majors */
+ m = MAJOR(sbuf.st_rdev); /* copy name to appropriate spot */
+ if ((i = lookup(m, major, Nmajor)) != -1)
+ strncpy(TAB(i,MINOR(sbuf.st_rdev)), ent->d_name, DEV_NAME_MAX);
+ }
+ closedir(dev);
+ if (write(fd, devtab, Ndevtab) != Ndevtab) /* probably no disk space */
+ return 0;
+ return 1;
+}
+
+static char path[DEV_MAX_PATH];
+
+static char* name_to_path(char* name) {
+ static char* Path;
+ if (!Path) {
+ strcpy(path, DEVDIR); /* copy DEVDIR */
+ path[sizeof(DEVDIR) - 1] = '/'; /* change NUL to '/' */
+ Path = path + sizeof(DEVDIR); /* put Path at start of basename */
+ }
+ strncpy(Path, name, DEV_MAX_PATH - sizeof(DEVDIR));
+ return path;
+}
+
+#ifdef TEST_DEVNAME
+int main(int argc, char** argv) { /* usage: cmd [<major> <minor>|<name>] */
+ dev_t tmp;
+ if (argc < 2) {
+ printf("%s: [ maj min... | name ... ]\n", argv[0]);
+ return 0;
+ }
+ if (argv[1][0] >= '0' && argv[1][0] <= '9')
+ for(argv++ ; argv[0] && argv[1] ; argv+=2)
+ printf("%s\n", dev_to_name(MKDEV( atoi(argv[0]), atoi(argv[1]) )));
+ else
+ for(argv++ ; *argv ; argv++) {
+ tmp = name_to_dev(*argv);
+ printf("%d, %d\n", MAJOR(tmp), MINOR(tmp));
+ }
+ return 0;
+}
+#endif
+/*
+Using this program on over 700 files in /dev to perform number->name resolution
+took well under 300 microsecs per device number pair on a Pentium 90. It is
+somewhat tough to time because once the 3 pages have been mapped in, the time is
+almost zero. For things like top, this method may even be faster in the long
+run. Those interested can gprof it for me. This system has the virtue of being
+nearly perfectly adaptable to individual systems, self updating when /dev
+changes and pretty darn fast when it hasn't. It will be slow for users without
+perms to change the psdevtab file, though. So this is what I decided was
+reasonable. If the process does not have perms to create or update
+/etc/psdevtab and it is out of date, we try /tmp/psdevtab. If /tmp/psdevtab is
+either out of date or unreadable (malicious user creates it and chmods it),
+$HOME/.psdevtab is used. This secondarily allows for per-user naming of ttys,
+but is really so that at most one user sees only a single delay per /dev
+modification.
+
+To do the timings I did something like this with zsh:
+ a=(`ls -l *(%^@/) | awk '{print $5 $6}' | sed 's/,/ /'`);
+ time ./test $a
+
+Finally, for lack of a better file for these to be in, I have collected the
+old algorithmic device number <-> device name mappings.
+ Let m = major device number and n = minor device number satisfy:
+ devno = m<<8 + n , m = devno>>8 , n = devno && 0x00FF, and let
+ char L[32]="pqrstuvwxyzABCDEFGHIJKLMNOPQRSTU", H[16]="01234567890abcdef";
+ DEVICE NUMBERS SPECIAL FILE NAMES
+ OLD SYSTEM (64 pseudoterminal devices):
+ m=4:
+ n=0..63: tty + itoa_dec(n+1)
+ n=128..191: pty + L[(n-128)/16] + H[(n-128)%16]
+ n=192..255: tty + L[(n-192)/16] + H[(n-192)%16]
+ NEW SYSTEM (256/512 pseudoterminal devices):
+ m=2, n: pty + L[n/16] + H[n%16]
+ m=3, n: tty + L[n/16] + H[n%16]
+ m=4, n: tty + itoa_dec(n+1)
+ m=49, n: pty + L[16+n/16] + H[n%16]
+ m=50, n: tty + L[16+n/16] + H[n%16]
+ (THE SAME IN EITHER SYSTEM)
+ CALL-UNIX AND CONTROLLING TERMINAL DEVICES
+ m=5:
+ n=0: tty
+ n=64..128: cua + {'0' + (n-64)}
+ CYCLADES MULTIPORT:
+ m=19, n: ttyC + itoa_hex(n)
+ m=20, n: cub + itoa_hex(n) */
+
+/* Re-implementation of old interface with the new generic functions. */
+
+/* This does exactly the same thing as name_to_dev only now a full "ttyXX"
+ specification will work as well.
+*/
+int tty_to_dev(char *tty) {
+ static char pref_name_1[32] = "tty", *pnam1 = pref_name_1 + 3,
+ pref_name_2[32] = "cu", *pnam2 = pref_name_2 + 2;
+ dev_t num;
+ if ((num = name_to_dev(tty)) != (dev_t) -1) /* try tty straight up */
+ return num;
+ strncpy(pnam1, tty, 32 - 3); /* try with "tty" prepended */
+ if ((num = name_to_dev(pref_name_1)) != (dev_t) -1)
+ return num;
+ strncpy(pnam2, tty, 32 - 2); /* try with "cu" prepended */
+ if ((num = name_to_dev(pref_name_2)) != (dev_t) -1)
+ return num;
+ return -1; /* no match */
+}
+
+/* new abstraction that can maybe be generalized a little better. */
+char* abbrev_of_tty(char *tty) {
+ static char temp[32]; /* return buf: good only until next call */
+ char *pos = strpbrk(tty, "yu"); /* end of (presumed) prefices: tty*, cu* */
+ temp[0] = 0;
+ if (tty && tty[0] && pos && pos[0] && pos[1])
+ sprintf(temp, "%*.*s", 3, 3, pos + 1);
+ else
+ strncpy(temp, " ? ", 31);
+ return temp;
+}
+
+/* Do in-place modification of the 4-buffer `tty' based upon `dev' */
+void dev_to_tty(char *tty, int dev) {
+ char* new = abbrev_of_tty(dev_to_name(dev));
+ strncpy(tty, new, 4);
+}
diff --git a/libproc/devname.h b/libproc/devname.h
new file mode 100644
index 00000000..e1371f45
--- /dev/null
+++ b/libproc/devname.h
@@ -0,0 +1,10 @@
+
+#include <sys/types.h>
+
+dev_t name_to_dev(char* name);
+char* dev_to_name(dev_t num);
+
+dev_t tty_to_dev(char *tty);
+void dev_to_tty(char *tty, int dev);
+
+char* abbrev_of_tty(char *tty);
diff --git a/libproc/ksym.c b/libproc/ksym.c
new file mode 100644
index 00000000..b3ae3284
--- /dev/null
+++ b/libproc/ksym.c
@@ -0,0 +1,252 @@
+/* kernel address -> symbol with next lower address. Charles Blake, 1996.
+ * Written to obviate the need for psdatabase initialization based upon kernel
+ * binary formats, etc.
+ *
+ * The basic algorithm is an approximate (intervals split vaguely 50-50) binary
+ * search taking advantage of the fact the System.map is already sorted in
+ * ascending order by the kernel makefile. It needs to assume an average symbol
+ * record length to avoid scanning the entire symbol table, but in practice the
+ * search time does not seem to be especially sensitive to this choice.
+ *
+ * The search could be an exact binary search if the lines of System.map were
+ * padded with blanks to the right. awk '{printf "%8s%2s %-21.21s\n",$1,$2,$3}'
+ * would do the trick for this but either makes the file large or truncates
+ * symbols. The approximate method seems to be plenty fast enough, costing
+ * only about as much as one extra fstat() or so per process.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include "proc/psdata.h"
+#include "proc/ps.h"
+#include "proc/version.h"
+
+#define MAX_ADDR_SZ 32
+static char *sysmap, *sysmap_last, sysmap_fmt[10];
+static int sysmap_len, sysmap_mean = 32, sysmap_addrsz;
+
+/* scan backward in a string no further than address beg looking for c */
+static char *strchrrev(char *a, char *beg, char c) {
+ if (a)
+ while (--a > beg && *a != c) ;
+ return a;
+}
+
+/* return ptr to the beg of approximately the i-th record */
+static char *addr_str(int i) {
+ char *guess = sysmap + sysmap_mean * i;
+ if (!i) return sysmap;
+ if (guess - sysmap > sysmap_len - 2) guess = sysmap + sysmap_len - 2;
+ for ( ; *guess != '\n' && guess > sysmap; guess--)
+ ;
+ return guess + 1;
+
+}
+
+/* return ptr to symbol string (\n terminated) given beg of record ptr */
+static char *sym_put(char *buf, int len, char *addrptr) {
+ char *s;
+ while (*addrptr++ != ' ') ;
+ while (*addrptr++ != ' ') ;
+ strncpy(buf, addrptr, len);
+ for (s = buf; s < buf + len; s++)
+ if (*s == '\n')
+ *s = '\0';
+ buf[len - 1] = '\0';
+ return buf;
+}
+
+/* Try to open and mmap a single symbol table file and initialize globals */
+int sysmap_mmap(char *path) {
+ int fd;
+ struct stat sbuf;
+ char *p;
+ if (sysmap) /* do nothing if already mapped */
+ return 1;
+ if ((fd = open(path, O_RDONLY)) < 0
+ || fstat(fd, &sbuf) < 0
+ || (sysmap = mmap(0, sbuf.st_size,
+ PROT_READ, MAP_SHARED,
+ fd, 0)) == (caddr_t) -1)
+ {
+ close(fd);
+ sysmap = NULL;
+ return 0;
+ }
+ sysmap_len = sbuf.st_size;
+ sysmap_last = strchrrev(sysmap + sysmap_len - 2, sysmap, '\n') + 1;
+
+ /* Now check first line of sysmap for hex numbers in first column. Note:
+ 0x/0X prefixes are disallowed, but easily addable. Capitalization is
+ irrelevant because strncasecmp(3) is used below instead of strncmp. */
+ for (p = sysmap; *p != ' '
+ && ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'F') ||
+ (*p >= 'a' && *p <= 'f'))
+ && p < sysmap + MAX_ADDR_SZ;
+ p++) /* no-op */ ;
+ if (*p != ' ') { /* uh-oh: cannot understand format */
+ fprintf(stderr, "warning: %s not parseable as a System.map.\n", path);
+ munmap(sysmap, sysmap_len);
+ sysmap = NULL;
+ close(fd);
+ return 0;
+ }
+ sysmap_addrsz = p - sysmap;
+ snprintf(sysmap_fmt, sizeof sysmap_fmt, "%%0%dlx", sysmap_addrsz);
+ close(fd);
+ return 1;
+}
+
+/* kernel address -> name resolver.
+ returned value is only good until the next call to the function.
+ */
+char *sysmap_symbol(unsigned long address) {
+ static char rval[128], *pc, addr[MAX_ADDR_SZ];
+ int i, p, n = sysmap_len / (double)sysmap_mean;
+
+ sprintf(addr, sysmap_fmt, address);
+ p = 0; pc = sysmap;
+ while (n) {
+ i = p + (n >> 1);
+ if (strncasecmp(addr, pc = addr_str(i), sysmap_addrsz) > 0)
+ p = i + 1;
+ n >>= 1;
+ }
+ if (pc == sysmap_last) /* scan forward but not past end */
+ return sym_put(rval, sizeof rval, pc);
+ while (strncasecmp(addr, pc, sysmap_addrsz) > 0)
+ pc = strchr(pc, '\n') + 1;
+ if (pc == sysmap) /* scan backward but not past beg */
+ return sym_put(rval, sizeof rval, pc);
+ while (strncasecmp(addr, pc, sysmap_addrsz) < 0)
+ pc = strchrrev(pc - 1, sysmap, '\n') + 1;
+ return sym_put(rval, sizeof rval, pc);
+}
+
+/* extern struct nlist *namelist; */
+struct tbl_s vars, fncs;
+struct psdb_hdr db_hdr;
+int psdb = -1;
+
+int open_psdb(void) {
+ static char *sysmap_paths[] = {
+ "/boot/System.map-%s",
+ "/boot/System.map",
+ "/lib/modules/%s/System.map",
+ NULL
+ };
+ static char *psdb_paths[] = {
+ "/etc/psdatabase",
+ "/boot/psdatabase-%s",
+ "/boot/psdatabase",
+ "/lib/modules/%s/psdatabase",
+ NULL
+ };
+ char **fmt, *env, path[64];
+ struct utsname uts;
+ uname(&uts);
+ if ((env = getenv("PS_SYSMAP")) && sysmap_mmap(env))
+ return 0;
+ for (fmt = sysmap_paths; *fmt; fmt++) {
+ snprintf(path, sizeof path, *fmt, uts.release);
+ if (sysmap_mmap(path))
+ return 0;
+ }
+ for (fmt = psdb_paths; *fmt; fmt++) {
+ snprintf(path, sizeof path, *fmt, uts.release);
+ if ((psdb = open(path, O_RDONLY)) != -1 &&
+ read(psdb, (char*)&db_hdr, sizeof db_hdr) == sizeof db_hdr &&
+ strncmp(db_hdr.magic, procps_version, sizeof(db_hdr.magic)) == 0)
+ /* && version_cmp(kernel,psdatabase) */
+ return 0;
+ if (psdb != -1)
+ fprintf(stderr,
+ "psdatabase has magic no. %*s instead of %*s\n",
+ (int) sizeof db_hdr.magic, db_hdr.magic,
+ (int) sizeof db_hdr.magic, procps_version);
+ close(psdb);
+ }
+ return -1;
+}
+
+void close_psdb(void) {
+ if (sysmap)
+ munmap(sysmap, sysmap_len);
+ else if (psdb != -1)
+ close(psdb);
+ psdb = -1;
+ sysmap = NULL;
+}
+
+int read_tbl(struct dbtbl_s *dbtbl, struct tbl_s *tbl) {
+ lseek(psdb, dbtbl->off, SEEK_SET);
+ tbl->tbl = (struct sym_s *) xmalloc(dbtbl->size);
+ if (read(psdb, (char *) tbl->tbl, dbtbl->size) != dbtbl->size) {
+ perror(PSDATABASE);
+ exit(1);
+ }
+ tbl->nsym = dbtbl->nsym;
+ tbl->strings = (char *) (tbl->tbl + tbl->nsym);
+ return 0;
+}
+
+char * find_func(unsigned long address) {
+ int n;
+ struct sym_s *p;
+ char *s;
+
+ if (sysmap)
+ return sysmap_symbol(address);
+ if (psdb == -1)
+ return "(no psdb)";
+ if (fncs.tbl == NULL)
+ read_tbl(&db_hdr.fncs, &fncs);
+ p = fncs.tbl;
+ n = fncs.nsym;
+ while (n) {
+ int i = n / 2;
+ if (p[i].addr < address) {
+ p = &p[i+1];
+ if (p->addr > address) {
+ --p;
+ break;
+ }
+ --n;
+ }
+ n /= 2;
+ }
+ s = p->name + fncs.strings;
+ return *s=='_' ? s+1 : s;
+}
+
+char * wchan(unsigned long address) {
+ static char zero = 0;
+ char *p;
+
+ if (address) {
+ p = find_func(address);
+ if (strncmp(p, "sys_", 4) == 0)
+ p += 4;
+ while (*p == '_' && *p)
+ ++p;
+ } else /* 0 address means not in kernel space */
+ p = &zero;
+ return p;
+}
+
+#ifdef SYSMAP_TEST
+int main(int ac, char** av) {
+ if (ac < 3) {printf("%s System.map lines hexaddr ...\n",av[0]); return 1;}
+ if (!sysmap_mmap(av[1])) return 1;
+ if ((sysmap_mean = atoi(av[2])) <= 0) return 1;
+ for (av += 3; *av; av++)
+ printf("%s %s\n", *av, sysmap_symbol(strtoul(*av, NULL, 16)));
+ return 0;
+}
+#endif
diff --git a/libproc/output.c b/libproc/output.c
new file mode 100644
index 00000000..66e4e407
--- /dev/null
+++ b/libproc/output.c
@@ -0,0 +1,52 @@
+/*
+ Some output conversion routines for libproc
+ Copyright (C) 1996, Charles Blake. See COPYING for details.
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+/* output a string, converting unprintables to octal as we go, and stopping after
+ processing max chars of output (accounting for expansion due to octal rep).
+*/
+unsigned print_str(FILE* file, char *s, unsigned max) {
+ int i;
+ for (i=0; s[i] && i < max; i++)
+ if (isprint(s[i]) || s[i] == ' ')
+ fputc(s[i], file);
+ else {
+ if (max - i > 3) {
+ fprintf(file, "\\%03o", s[i]);
+ i += 3; /* 4 printed, but i counts one */
+ } else
+ return max - i;
+ }
+ return max - i;
+}
+
+/* output an argv style NULL-terminated string list, converting unprintables
+ to octal as we go, separating items of the list by 'sep' and stopping after
+ processing max chars of output (accounting for expansion due to octal rep).
+*/
+unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max) {
+ int i, n, seplen = strlen(sep);
+ for (n=0; *strs && n < max; strs++) {
+ for (i=0; strs[0][i] && n+i < max; i++)
+ if (isprint(strs[0][i]) || strs[0][i] == ' ')
+ fputc(strs[0][i], file);
+ else {
+ if (max-(n+i) > 3) {
+ fprintf(file, "\\%03o", strs[0][i]);
+ n += 3; /* 4 printed, but i counts one */
+ } else
+ return max - n;
+ }
+ n += i;
+ if (n + seplen < max) {
+ fputs(sep, file);
+ n += seplen;
+ } else
+ return max - n;
+ }
+ return max - n;
+}
diff --git a/libproc/ps.h b/libproc/ps.h
new file mode 100644
index 00000000..aa065cbf
--- /dev/null
+++ b/libproc/ps.h
@@ -0,0 +1,30 @@
+/* The shadow of the original with only common prototypes now. */
+#include <stdio.h>
+#include <sys/types.h>
+
+/* get definition of HZ */
+#include <asm/param.h>
+
+/* get page info */
+#include <asm/page.h>
+
+char *wchan(unsigned long);
+char *find_func(unsigned long address);
+void *xcalloc(void *pointer, int size);
+void *xmalloc(unsigned int size);
+void *xrealloc(void *oldp, unsigned int size);
+
+int mult_lvl_cmp(void* a, void* b);
+int node_mult_lvl_cmp(void* a, void* b);
+void dump_keys(void);
+
+char *user_from_uid(int uid);
+
+int open_sysmap(void);
+int open_psdb(void);
+void close_psdb(void);
+void make_fnctbl(void);
+
+unsigned print_str (FILE* file, char *s, unsigned max);
+unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max);
+unsigned snprint_strlist(char *buf, int max, char **strs, char *sep);
diff --git a/libproc/psdata.h b/libproc/psdata.h
new file mode 100644
index 00000000..a6c480e8
--- /dev/null
+++ b/libproc/psdata.h
@@ -0,0 +1,103 @@
+/*
+ * psdata.h
+ *
+ * Jeffrey A. Uphoff <juphoff@nrao.edu>, 1995, 1996.
+ * Michael K. Johnson.
+ * Bruno Lankester.
+ * (And others I'm sure...)
+ *
+ */
+
+/*
+ * Capabilities are for reading system images and producing maps for
+ * WCHAN output.
+ *
+ * AOUT_CAPABLE and ELF_CAPABLE may have 32-bit word size limitations
+ * and have only been tested by the maintainer on Intel systems. They
+ * are retained in the source tree in case they are useful; they are
+ * intended to be generally deprecated.
+ *
+ * BFD_CAPABLE should work on any system with BFD.
+ *
+ * Set the capabilities in the top-level Makefile.
+ */
+
+#if defined(ELF_CAPABLE)
+# define ELF_OBJECT 1
+# define ELF_FUNC 2
+#endif
+
+#include <sys/types.h>
+#include <linux/utsname.h>
+
+#define PSDATABASE "/etc/psdatabase"
+
+struct dbtbl_s {
+ off_t off; /* offset in psdatabase */
+ int nsym; /* # symbols */
+ int size; /* size of array + strings */
+};
+
+/*
+ * header of psdatabase
+ */
+struct psdb_hdr {
+ /* Current procps package version goes here. kmemps doesn't like this. */
+ char magic[32];
+ /*
+ * These are not functional--they only reside in the database for
+ * informational purposes (i.e. if you want to look at the raw
+ * database and see what kernel it's for).
+ */
+ char uts_release[__NEW_UTS_LEN];
+ char uts_version[__NEW_UTS_LEN];
+ /*
+ * Again, this is not functional, it's just there for information: it
+ * shows the path to the uncompressed kernel image that was used to
+ * generate this database.
+ */
+ char sys_path[128];
+ /* List of all functions. */
+ struct dbtbl_s fncs;
+ /*
+ * This is currently only used to look up system_utsname while
+ * psupdate is building the database--it really should be phased out!
+ */
+ /* List of all bss and data symbols. */
+ struct dbtbl_s vars;
+ /*
+ * The list of tty names that kmemps likes/uses in no longer present
+ * in the procps psdatabase--it was never being built by procps'
+ * psupdate anyway, so I removed the entry from the database header.
+ */
+};
+
+struct sym_s {
+ unsigned long addr; /* core address in kernel */
+ int name; /* offset from strings ptr */
+};
+
+struct tbl_s {
+ struct sym_s *tbl;
+ int nsym;
+ char *strings; /* ptr to start of strings */
+};
+
+extern struct psdb_hdr db_hdr;
+extern struct tbl_s fncs, vars;
+
+int read_tbl (struct dbtbl_s *, struct tbl_s *);
+void *xmalloc (unsigned int);
+void *xrealloc (void *, unsigned int);
+
+#define MLSEEK(FD, WHERE, WHENCE, ERROR)\
+if (lseek ((FD), (WHERE), (WHENCE)) == -1) {\
+ perror ((ERROR));\
+ exit (errno);\
+}
+
+#define MREAD(FD, WHAT, SIZE, ERROR)\
+if (read ((FD), (WHAT), (SIZE)) != (SIZE)) {\
+ perror ((ERROR));\
+ exit (errno);\
+}
diff --git a/libproc/pwcache.c b/libproc/pwcache.c
new file mode 100644
index 00000000..2d50054c
--- /dev/null
+++ b/libproc/pwcache.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include "proc/ps.h"
+
+#define HASHSIZE 16 /* power of 2 */
+#define HASH(x) ((x) & (HASHSIZE - 1))
+
+
+static struct pwbuf {
+ int uid;
+ char name[12];
+ struct pwbuf *next;
+} *pwhash[HASHSIZE];
+
+char *user_from_uid(int uid)
+{
+ struct pwbuf **p;
+ struct passwd *pw;
+
+ p = &pwhash[HASH(uid)];
+ while (*p) {
+ if ((*p)->uid == uid)
+ return((*p)->name);
+ p = &(*p)->next;
+ }
+ *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf));
+ (*p)->uid = uid;
+ if ((pw = getpwuid(uid)) == NULL)
+ sprintf((*p)->name, "#%d", uid);
+ else
+ sprintf((*p)->name, "%-.8s", pw->pw_name);
+ (*p)->next = NULL;
+ return((*p)->name);
+}
+
+void bad_user_access_length() { }
diff --git a/libproc/readproc.c b/libproc/readproc.c
new file mode 100644
index 00000000..31836b0c
--- /dev/null
+++ b/libproc/readproc.c
@@ -0,0 +1,395 @@
+/*
+ * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+ * Copyright(C) 1996. Charles L. Blake.
+ */
+#include "proc/version.h"
+#include "proc/readproc.h"
+#include "proc/devname.h"
+#include "proc/ps.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 = 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);
+}
+
+/* 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) {
+ 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 (%39c", &P->pid, P->cmd);
+ sscanf(tmp + 2, /* skip space after ')' too */
+ "%c %d %d %d %d %d %lu %lu %lu %lu %lu %ld %ld %ld %ld %d "
+ "%d %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %LX %LX %LX %LX %lu",
+ &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, &P->wchan);
+ 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) {
+ sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
+ &P->size, &P->resident, &P->share,
+ &P->trs, &P->lrs, &P->drs, &P->dt);
+}
+
+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 ) return -1;
+ 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.
+ */
+#define flags (PT->flags)
+proc_t* readproc(PROCTAB* PT, proc_t* rbuf) {
+ static struct direct *ent; /* dirent handle */
+ static struct stat sb; /* stat buffer */
+ static char path[32], sbuf[256]; /* bufs for stat,statm */
+ int allocated = 0, matched = 0; /* flags */
+ proc_t *p = NULL;
+
+ /* loop until a proc matching restrictions is found or no more processes */
+ /* I know this could be a while loop -- this way is easier to indent ;-) */
+next_proc: /* get next PID for consideration */
+ if (Do(PID)) {
+ if (!*PT->pids) /* set to next item in pids */
+ return NULL;
+ sprintf(path, "/proc/%d", *(PT->pids)++);
+ matched = 1;
+ } else { /* get next numeric /proc ent */
+ while ((ent = readdir(PT->procfs)) &&
+ (*ent->d_name < '0' || *ent->d_name > '9'))
+ ;
+ if (!ent || !ent->d_name)
+ return NULL;
+ sprintf(path, "/proc/%s", ent->d_name);
+ }
+ if (stat(path, &sb) == -1) /* no such dirent (anymore) */
+ goto next_proc;
+ if (Do(UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
+ goto next_proc; /* not one of the requested uids */
+
+ if (!allocated) { /* assign mem for return buf */
+ p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */
+ allocated = 1; /* remember space is set up */
+ }
+ p->uid = sb.st_uid; /* need a way to get real uid */
+
+ if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1)
+ goto next_proc; /* error reading /proc/#/stat */
+ stat2proc(sbuf, p); /* parse /proc/#/stat */
+
+ if (!matched && Do(TTY) && !XinL(dev_t, p->tty, PT->ttys))
+ goto next_proc; /* not one of the requested ttys */
+
+ if (!matched && Do(ANYTTY) && p->tty == -1)
+ goto next_proc; /* no controlling terminal */
+
+ if (!matched && Do(STAT) && !strchr(PT->stats,p->state))
+ goto next_proc; /* not one of the requested states */
+
+ if (Do(FILLMEM)) { /* read, parse /proc/#/statm */
+ if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 )
+ statm2proc(sbuf, p); /* ignore statm errors here */
+ } /* statm fields just zero */
+
+ /* some number->text resolving which is time consuming */
+ if (Do(FILLTTY))
+ dev_to_tty(p->ttyc, p->tty);
+ if (Do(FILLUSR))
+ strncpy(p->user, user_from_uid(p->uid), sizeof p->user);
+
+ if (Do(FILLCMD)) /* read+parse /proc/#/cmdline */
+ p->cmdline = file2strvec(path, "cmdline");
+ if (Do(FILLENV)) /* read+parse /proc/#/environ */
+ p->environ = file2strvec(path, "environ");
+
+ if (p->state == 'Z') /* fixup cmd for zombies */
+ strncat(p->cmd," <zombie>", sizeof p->cmd);
+
+ return p;
+}
+#undef flags
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * tree subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with freeproctree(). The tree structure is a classic
+ * left-list children + right-list siblings. The algorithm is a two-pass of the
+ * process table. Since most process trees will have children with strictly
+ * increasing PIDs, most of the structure will be picked up in the first pass.
+ * The second loop then cleans up any nodes which turn out to have preceeded
+ * their parent in /proc order.
+ */
+
+/* Traverse tree 't' breadth-first looking for a process with pid p */
+proc_t* LookupPID(proc_t* t, pid_t p) {
+ proc_t* tmp = NULL;
+ if (!t)
+ return NULL;
+ if (t->pid == p) /* look here/terminate recursion */
+ return t;
+ if ((tmp = LookupPID(t->l, p))) /* recurse over children */
+ return tmp;
+ for (; t; t=t->r) /* recurse over siblings */
+ if ((tmp = LookupPID(tmp, p)))
+ return tmp;
+ return NULL;
+}
+
+proc_t* readproctree(int flags, ...) {
+ static proc_t tree;
+ PROCTAB* PT = NULL;
+ proc_t *node, *tmp=NULL, *tmp2=NULL;
+ va_list ap;
+
+ /* pass through apropriate arguments to openproc */
+ va_start(ap, flags);
+ if (Do(UID)) {
+ /* 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);
+ }
+ else if (Do(PID) || Do(TTY) || Do(STAT))
+ PT = openproc(flags, va_arg(ap, void*));
+ else
+ PT = openproc(flags);
+ va_end(ap);
+
+ /* first pass: build tree, putting orphans on the first level */
+ tree.l = tree.r = NULL;
+ while ((node = readproc(PT,0)))
+ if ((tmp = LookupPID(&tree, node->ppid))) {
+ node->r = tmp->l->r; /* node --> left list of parent */
+ tmp->l->r = node;
+ } else {
+ node->r = tree.r; /* node --> right list of 'tree' */
+ tree.r = node;
+ }
+ /* second pass: scan tree for PPIDs of level-1 nodes moving links as necessary */
+ for (node = &tree; node; node = node->r)
+ if ((tmp = LookupPID(&tree, node->r->ppid))) {
+ tmp2 = node->r; /* unlink from right list of 'tree' */
+ node->r = node->r->r;
+ tmp2->r = tmp->l->r; /* insert as child of found node */
+ tmp->l->r = node;
+ }
+ closeproc(PT);
+ return &tree;
+}
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * table subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with freeproctab(). Access via tab[N]->member. The
+ * pointer list is NULL terminated.
+ */
+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 */
+ if (Do(UID)) {
+ /* 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);
+ }
+ else if (Do(PID) || Do(TTY) || Do(STAT))
+ 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 */
+ tab[n] = readproc(PT, NULL); /* final null to terminate */
+ } while (tab[n++]); /* stop when NULL reached */
+ closeproc(PT);
+ return tab;
+}
+
+/* deallocate a table of pointers to proc structures
+ */
+void freeproctab(proc_t** tab) {
+ proc_t** p;
+ for(p = tab; *p; p++)
+ freeproc(*p);
+ free(tab);
+}
diff --git a/libproc/readproc.h b/libproc/readproc.h
new file mode 100644
index 00000000..b93f0198
--- /dev/null
+++ b/libproc/readproc.h
@@ -0,0 +1,168 @@
+/*
+ * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+ * Copyright(C) 1996. Charles L. Blake.
+ */
+
+/* Basic data structure which holds all information we can get about a process.
+ * (unless otherwise specified, fields are read from /proc/#/stat)
+ */
+typedef struct proc_s {
+ char
+ user[10], /* user name corresponding to owner of process */
+ cmd[40], /* basename of executable file in call to exec(2) */
+ state, /* single-char code for process state (S=sleeping) */
+ ttyc[5], /* string representation of controlling tty device */
+ **environ, /* environment string vector (/proc/#/environ) */
+ **cmdline; /* command line string vector (/proc/#/cmdline) */
+ int
+ uid, /* user id */
+ pid, /* process id */
+ ppid, /* pid of parent process */
+ pgrp, /* process group id */
+ session, /* session id */
+ tty, /* full device number of controlling terminal */
+ tpgid, /* terminal process group id */
+ priority, /* kernel scheduling priority */
+ nice; /* standard unix nice level of process */
+ long long
+ signal, /* mask of pending signals */
+ blocked, /* mask of blocked signals */
+ sigignore, /* mask of ignored signals */
+ sigcatch; /* mask of caught signals */
+ long
+ start_time, /* start time of process -- seconds since 1-1-70 */
+ utime, /* user-mode CPU time accumulated by process */
+ stime, /* kernel-mode CPU time accumulated by process */
+ cutime, /* cumulative utime of process and reaped children */
+ cstime, /* cumulative stime of process and reaped children */
+ /* the next 7 members come from /proc/#/statm */
+ size, /* total # of pages of memory */
+ resident, /* number of resident set (non-swapped) pages (4k) */
+ share, /* number of pages of shared (mmap'd) memory */
+ trs, /* text resident set size */
+ lrs, /* shared-lib resident set size */
+ drs, /* data resident set size */
+ dt; /* dirty pages */
+ unsigned
+ pcpu; /* %CPU usage (is not filled in by readproc!!!) */
+ unsigned long
+ vsize, /* number of pages of virtual memory ... */
+ rss, /* resident set size from /proc/#/stat */
+ rss_rlim, /* resident set size ... ? */
+ timeout, /* ? */
+ it_real_value, /* ? */
+ flags, /* kernel flags for the process */
+ min_flt, /* number of minor page faults since process start */
+ maj_flt, /* number of major page faults since process start */
+ cmin_flt, /* cumulative min_flt of process and child processes */
+ cmaj_flt, /* cumulative maj_flt of process and child processes */
+ start_code, /* address of beginning of code segment */
+ end_code, /* address of end of code segment */
+ start_stack, /* address of the bottom of stack for the process */
+ kstk_esp, /* kernel stack pointer */
+ kstk_eip, /* kernel stack pointer */
+ wchan; /* address of kernel wait channel proc is sleeping in */
+ struct proc_s *l, /* ptrs for building arbitrary linked structs */
+ *r; /* (i.e. singly/doubly-linked lists and trees */
+} proc_t;
+
+/* PROCTAB: data structure holding the persistent information readproc needs
+ * from openproc(). The setup is intentionally similar to the dirent interface
+ * and other system table interfaces (utmp+wtmp come to mind).
+ */
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+typedef struct {
+ DIR* procfs;
+ int flags;
+ pid_t* pids; /* pids of the procs */
+ dev_t* ttys; /* devnos of the cttys */
+ uid_t* uids; /* uids of procs */
+ int nuid; /* cannot really sentinel-terminate unsigned short[] */
+ char* stats; /* status chars (actually output into /proc//stat) */
+} PROCTAB;
+
+/* initialize a PROCTAB structure holding needed call-to-call persistent data
+ */
+PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * table subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with freeproctab(). Access via tab[N]->member. The
+ * pointer list is NULL terminated.
+ */
+proc_t** readproctab(int flags, ... /* same as openproc */ );
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * tree subset satisfying the constraints of flags and the optional PID list.
+ */
+proc_t* readproctree(int flags, ... /* same as openproc */ );
+
+/* clean-up open files, etc from the openproc()
+ */
+void closeproc(PROCTAB* PT);
+
+/* retrieve the next process matching the criteria set by the openproc()
+ */
+proc_t* readproc(PROCTAB* PT, proc_t* return_buf);
+
+/* deallocate space allocated by readproc
+ */
+void freeproc(proc_t* p);
+
+/* deallocate space allocated by readproctab
+ */
+void freeproctab(proc_t** p);
+
+/* openproc/readproctab:
+ *
+ * Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be
+ * opened.) By default readproc will consider all processes as valid to parse
+ * and return, but not actually fill in the cmdline, environ, and /proc/#/statm
+ * derived memory fields.
+ *
+ * `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The
+ * "fill" options will cause more of the proc_t to be filled in. The "filter"
+ * options all use the second argument as the pointer to a list of objects:
+ * process status', process id's, user id's, and tty device numbers. The third
+ * argument is the length of the list (currently only used for lists of user
+ * id's since unsigned short[] supports no convenient termination sentinel.)
+ */
+#define PROC_FILLMEM 0x1 /* read statm into the appropriate proc_t entries */
+#define PROC_FILLCMD 0x2 /* alloc and fill in `cmdline' part of proc_t */
+#define PROC_FILLENV 0x4 /* alloc and fill in `environ' part of proc_t */
+#define PROC_FILLTTY 0x8 /* resolve device number -> tty name via psdevtab */
+#define PROC_FILLUSR 0x10 /* resolve user id number -> user name via passwd */
+ /* consider only processes with one of the passed: */
+#define PROC_PID 0x100 /* process id numbers ( 0 terminated) */
+#define PROC_TTY 0x200 /* ctty device nos. ( 0 terminated) */
+#define PROC_UID 0x400 /* user id numbers ( length needed ) */
+#define PROC_STAT 0x800 /* status fields ('\0' terminated) */
+#define PROC_ANYTTY 0x1000 /* proc must have a controlling terminal */
+
+/* utility functions which may be of general interest: */
+
+/* slurp /proc/DIR/FILE into a single large string into the passed
+ buffer. return the number of bytes actually used. used for stat,statm
+ */
+int file2str(char *dir, char *file, char *buf, int buf_size);
+
+/* convert a file of null terminated strings into an argv-style string vector
+ * which may be de-allocated with a single free() on a dereference of the return
+ * value, e.g. free(*ret). used for cmdline, environ.
+ */
+char** file2strvec(char* directory, char* what);
+
+/* parse /proc/#/stat entries in string s into a proc_t
+ */
+void stat2proc(char* S, proc_t*);
+
+/* parse /proc/#/statm entries in string s into a proc_t
+ */
+void statm2proc(char* s, proc_t*);
+
+/* convert a memory buffer with nulls into a single string,
+ replacing the nulls with sep. No longer used.
+ */
+void nulls2sep(char* str, int len, char sep);
diff --git a/libproc/signals.c b/libproc/signals.c
new file mode 100644
index 00000000..5a1e8aa0
--- /dev/null
+++ b/libproc/signals.c
@@ -0,0 +1,65 @@
+/* signals.c - signal name handling */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "proc/signals.h"
+
+
+typedef struct {
+ int number;
+ char *name;
+} SIGNAME;
+
+
+static SIGNAME signals[] = {
+#include "signames.h" /* should be in same dir as this file */
+ { 0,NULL }};
+
+
+void list_signals(void)
+{
+ SIGNAME *walk;
+ int col;
+
+ col = 0;
+ for (walk = signals; walk->name; walk++) {
+ if (col+strlen(walk->name)+1 > 80) {
+ putchar('\n');
+ col = 0;
+ }
+ printf("%s%s",col ? " " : "",walk->name);
+ col += strlen(walk->name)+1;
+ }
+ putchar('\n');
+}
+
+
+int get_signal(char *name,char *cmd)
+{
+ SIGNAME *walk;
+
+ if (isdigit(*name))
+ return atoi(name);
+ for (walk = signals; walk->name; walk++)
+ if (!strcmp(walk->name,name)) break;
+ if (walk->name) return walk->number;
+ fprintf(stderr,"%s: unknown signal; %s -l lists signals.\n",name,cmd);
+ exit(1);
+}
+
+/* get_signal2 is by Michael Shields. 1994/04/25. */
+int get_signal2(char *name)
+{
+ SIGNAME *walk;
+
+ if (!name)
+ return(-1);
+ if (isdigit(*name))
+ return atoi(name);
+ for (walk = signals; walk->name; walk++)
+ if (!strcmp(walk->name,name))
+ return(walk->number);
+ return(-1);
+}
diff --git a/libproc/signals.h b/libproc/signals.h
new file mode 100644
index 00000000..c793fae0
--- /dev/null
+++ b/libproc/signals.h
@@ -0,0 +1,12 @@
+/* signals.h - signal name handling */
+
+void list_signals(void);
+
+/* Lists all known signal names on standard output. */
+
+int get_signal(char *name,char *cmd);
+int get_signal2(char *name);
+
+/* Returns the signal number of NAME. If no such signal exists, an error
+ message is displayed and the program is terminated. CMD is the name of the
+ application. */
diff --git a/libproc/signames.h b/libproc/signames.h
new file mode 100644
index 00000000..edcd6a38
--- /dev/null
+++ b/libproc/signames.h
@@ -0,0 +1,32 @@
+{ 1,"HUP" },
+{ 2,"INT" },
+{ 3,"QUIT" },
+{ 4,"ILL" },
+{ 5,"TRAP" },
+{ 6,"ABRT" },
+{ 6,"IOT" },
+{ 7,"BUS" },
+{ 8,"FPE" },
+{ 9,"KILL" },
+{ 10,"USR1" },
+{ 11,"SEGV" },
+{ 12,"USR2" },
+{ 13,"PIPE" },
+{ 14,"ALRM" },
+{ 15,"TERM" },
+{ 16,"STKFLT" },
+{ 17,"CHLD" },
+{ 18,"CONT" },
+{ 19,"STOP" },
+{ 20,"TSTP" },
+{ 21,"TTIN" },
+{ 22,"TTOU" },
+{ 23,"URG" },
+{ 24,"XCPU" },
+{ 25,"XFSZ" },
+{ 26,"VTALRM" },
+{ 27,"PROF" },
+{ 28,"WINCH" },
+{ 29,"IO" },
+{ 30,"PWR" },
+{ 31,"UNUSED" },
diff --git a/libproc/status.c b/libproc/status.c
new file mode 100644
index 00000000..92758196
--- /dev/null
+++ b/libproc/status.c
@@ -0,0 +1,20 @@
+#include "proc/ps.h"
+#include "proc/readproc.h"
+
+char * status(proc_t* task) {
+ static char buf[4] = " ";
+
+ buf[0] = task->state;
+ if (task->rss == 0 && task->state != 'Z')
+ buf[1] = 'W';
+ else
+ buf[1] = ' ';
+ if (task->nice < 0)
+ buf[2] = '<';
+ else if (task->nice > 0)
+ buf[2] = 'N';
+ else
+ buf[2] = ' ';
+
+ return(buf);
+}
diff --git a/libproc/sysinfo.c b/libproc/sysinfo.c
new file mode 100644
index 00000000..8a5e1a7f
--- /dev/null
+++ b/libproc/sysinfo.c
@@ -0,0 +1,176 @@
+/* File for parsing top-level /proc entities. */
+#include "proc/sysinfo.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include "proc/version.h"
+
+#define BAD_OPEN_MESSAGE \
+"Error: /proc must be mounted\n" \
+" To mount /proc at boot you need an /etc/fstab line like:\n" \
+" /proc /proc proc defaults\n" \
+" In the meantime, mount /proc /proc -t proc\n"
+
+#define UPTIME_FILE "/proc/uptime"
+#define LOADAVG_FILE "/proc/loadavg"
+#define MEMINFO_FILE "/proc/meminfo"
+
+static char buf[1024];
+
+/* This macro opens FILE only if necessary and seeks to 0 so that successive
+ calls to the functions are more efficient. It also reads the current
+ contents of the file into the global buf.
+*/
+#define FILE_TO_BUF(FILE) { \
+ static int n, fd = -1; \
+ if (fd == -1 && (fd = open(FILE, O_RDONLY)) == -1) { \
+ fprintf(stderr, BAD_OPEN_MESSAGE); \
+ close(fd); \
+ return 0; \
+ } \
+ lseek(fd, 0L, SEEK_SET); \
+ if ((n = read(fd, buf, sizeof buf - 1)) < 0) { \
+ perror(FILE); \
+ close(fd); \
+ fd = -1; \
+ return 0; \
+ } \
+ buf[n] = '\0'; \
+}
+
+#define SET_IF_DESIRED(x,y) if (x) *(x) = (y) /* evals 'x' twice */
+
+int uptime(double *uptime_secs, double *idle_secs) {
+ double up=0, idle=0;
+
+ FILE_TO_BUF(UPTIME_FILE)
+ if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
+ fprintf(stderr, "bad data in " UPTIME_FILE "\n");
+ return 0;
+ }
+ SET_IF_DESIRED(uptime_secs, up);
+ SET_IF_DESIRED(idle_secs, idle);
+ return up; /* assume never be zero seconds in practice */
+}
+
+int loadavg(double *av1, double *av5, double *av15) {
+ double avg_1=0, avg_5=0, avg_15=0;
+
+ FILE_TO_BUF(LOADAVG_FILE)
+ if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
+ fprintf(stderr, "bad data in " LOADAVG_FILE "\n");
+ exit(1);
+ }
+ SET_IF_DESIRED(av1, avg_1);
+ SET_IF_DESIRED(av5, avg_5);
+ SET_IF_DESIRED(av15, avg_15);
+ return 1;
+}
+
+/* The following /proc/meminfo parsing routine assumes the following format:
+ [ <label> ... ] # header lines
+ [ <label> ] <num> [ <num> ... ] # table rows
+ [ repeats of above line ]
+
+ Any lines with fewer <num>s than <label>s get trailing <num>s set to zero.
+ The return value is a NULL terminated unsigned** which is the table of
+ numbers without labels. Convenient enumeration constants for the major and
+ minor dimensions are available in the header file. Note that this version
+ requires that labels do not contain digits. It is readily extensible to
+ labels which do not *begin* with digits, though.
+*/
+
+#define MAX_ROW 3 /* these are a little liberal for flexibility */
+#define MAX_COL 7
+
+unsigned** meminfo(void) {
+ static unsigned *row[MAX_ROW + 1]; /* row pointers */
+ static unsigned num[MAX_ROW * MAX_COL]; /* number storage */
+ char *p;
+ char fieldbuf[12]; /* bigger than any field name or size in kb */
+ int i, j, k, l;
+
+ set_linux_version();
+ FILE_TO_BUF(MEMINFO_FILE)
+ if (!row[0]) /* init ptrs 1st time through */
+ for (i=0; i < MAX_ROW; i++) /* std column major order: */
+ row[i] = num + MAX_COL*i; /* A[i][j] = A + COLS*i + j */
+ p = buf;
+ for (i=0; i < MAX_ROW; i++) /* zero unassigned fields */
+ for (j=0; j < MAX_COL; j++)
+ row[i][j] = 0;
+ if (linux_version_code < LINUX_VERSION(2,0,0)) {
+ for (i=0; i < MAX_ROW && *p; i++) { /* loop over rows */
+ while(*p && !isdigit(*p)) p++; /* skip chars until a digit */
+ for (j=0; j < MAX_COL && *p; j++) { /* scanf column-by-column */
+ l = sscanf(p, "%u%n", row[i] + j, &k);
+ p += k; /* step over used buffer */
+ if (*p == '\n' || l < 1) /* end of line/buffer */
+ break;
+ }
+ }
+ }
+ else {
+ while(*p) {
+ sscanf(p,"%11s%n",fieldbuf,&k);
+ if(!strcmp(fieldbuf,"MemTotal:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_main][meminfo_total]));
+ row[meminfo_main][meminfo_total]<<=10;
+ while(*p++ != '\n');
+ }
+ else if(!strcmp(fieldbuf,"MemFree:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_main][meminfo_free]));
+ row[meminfo_main][meminfo_free]<<=10;
+ while(*p++ != '\n');
+ }
+ else if(!strcmp(fieldbuf,"MemShared:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_main][meminfo_shared]));
+ row[meminfo_main][meminfo_shared]<<=10;
+ while(*p++ != '\n');
+ }
+ else if(!strcmp(fieldbuf,"Buffers:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_main][meminfo_buffers]));
+ row[meminfo_main][meminfo_buffers]<<=10;
+ while(*p++ != '\n');
+ }
+ else if(!strcmp(fieldbuf,"Cached:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_main][meminfo_cached]));
+ row[meminfo_main][meminfo_cached]<<=10;
+ while(*p++ != '\n');
+ }
+ else if(!strcmp(fieldbuf,"SwapTotal:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_swap][meminfo_total]));
+ row[meminfo_swap][meminfo_total]<<=10;
+ while(*p++ != '\n');
+ }
+ else if(!strcmp(fieldbuf,"SwapFree:")) {
+ p+=k;
+ sscanf(p," %d",&(row[meminfo_swap][meminfo_free]));
+ row[meminfo_swap][meminfo_free]<<=10;
+ while(*p++ != '\n');
+ }
+ else
+ while(*p++ != '\n'); /* ignore lines we don't understand */
+ }
+ row[meminfo_swap][meminfo_used]=row[meminfo_swap][meminfo_total]-row[meminfo_swap][meminfo_free];
+ row[meminfo_main][meminfo_used]=row[meminfo_main][meminfo_total]-row[meminfo_main][meminfo_free];
+ }
+ return row; /* NULL return ==> error */
+}
+
+/* shorthand for read_table("/proc/meminfo")[meminfo_main][meminfo_total] */
+unsigned read_total_main(void) {
+ unsigned** mem;
+ return (mem = meminfo()) ? mem[meminfo_main][meminfo_total] : -1;
+}
diff --git a/libproc/sysinfo.h b/libproc/sysinfo.h
new file mode 100644
index 00000000..d78770fd
--- /dev/null
+++ b/libproc/sysinfo.h
@@ -0,0 +1,17 @@
+#ifndef SYSINFO_H
+#define SYSINFO_H
+
+int loadavg(double *av1, double *av5, double *av15);
+int uptime (double *uptime_secs, double *idle_secs);
+unsigned** meminfo(void);
+
+enum meminfo_row { meminfo_main = 0,
+ meminfo_swap };
+
+enum meminfo_col { meminfo_total = 0, meminfo_used, meminfo_free,
+ meminfo_shared, meminfo_buffers, meminfo_cached
+};
+
+unsigned read_total_main(void);
+
+#endif /* SYSINFO_H */
diff --git a/libproc/tree.h b/libproc/tree.h
new file mode 100644
index 00000000..b370732e
--- /dev/null
+++ b/libproc/tree.h
@@ -0,0 +1,14 @@
+struct tree_node {
+ proc_t *proc;
+ pid_t pid;
+ pid_t ppid;
+ char *line;
+ char *cmd;
+ char **cmdline;
+ char **environ;
+ int children;
+ int maxchildren;
+ int *child;
+ int have_parent;
+};
+
diff --git a/libproc/version.c b/libproc/version.c
new file mode 100644
index 00000000..e8f152de
--- /dev/null
+++ b/libproc/version.c
@@ -0,0 +1,39 @@
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Ammended by cblake to only export the function symbol.
+ */
+#include <stdio.h>
+
+#ifdef MINORVERSION
+char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION;
+#else
+char procps_version[] = "procps version " VERSION "." SUBVERSION;
+#endif
+
+void display_version(void) {
+ fprintf(stdout, "%s\n", procps_version);
+}
+
+/* Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ */
+#include <sys/utsname.h>
+
+#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
+
+int linux_version_code = 0;
+
+void set_linux_version(void) {
+ static struct utsname uts;
+ int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
+
+ if (linux_version_code) return;
+ if (uname(&uts) == -1) /* failure most likely implies impending death */
+ exit(1);
+ if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3)
+ fprintf(stderr, /* *very* unlikely to happen by accident */
+ "Non-standard uts for running kernel:\n"
+ "release %s=%d.%d.%d gives version code %d\n",
+ uts.release, x, y, z, LINUX_VERSION(x,y,z));
+ linux_version_code = LINUX_VERSION(x, y, z);
+}
diff --git a/libproc/version.h b/libproc/version.h
new file mode 100644
index 00000000..0d388e99
--- /dev/null
+++ b/libproc/version.h
@@ -0,0 +1,24 @@
+#ifndef PROC_VERSION_H
+#define PROC_VERSION_H
+
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ */
+
+extern void display_version(void); /* display suite version */
+extern char procps_version[]; /* global buf for suite version */
+
+extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE
+ in /usr/include/linux/version.h */
+extern void set_linux_version(void); /* set linux_version_code */
+
+
+/* Convenience macros for composing/decomposing version codes */
+#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x) ((x) & 0xFF0000) /* Dare we hope for a */
+#define LINUX_VERSION_MINOR(x) ((x) & 0x00FF00) /* Linux 256.0.0? ;-) */
+#define LINUX_VERSION_PATCH(x) ((x) & 0x0000FF)
+
+#endif /* PROC_VERSION_H */
diff --git a/libproc/whattime.c b/libproc/whattime.c
new file mode 100644
index 00000000..031ce0e5
--- /dev/null
+++ b/libproc/whattime.c
@@ -0,0 +1,89 @@
+/* This is a trivial uptime program. I hereby release this program
+ * into the public domain. I disclaim any responsibility for this
+ * program --- use it at your own risk. (as if there were any.. ;-)
+ * -michaelkjohnson (johnsonm@sunsite.unc.edu)
+ *
+ * Modified by Larry Greenfield to give a more traditional output,
+ * count users, etc. (greenfie@gauss.rutgers.edu)
+ *
+ * Modified by mkj again to fix a few tiny buglies.
+ *
+ * Modified by J. Cowley to add printing the uptime message to a
+ * string (for top) and to optimize file handling. 19 Mar 1993.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include "proc/whattime.h"
+#include "proc/sysinfo.h"
+
+static char buf[128];
+double av[3];
+
+char *sprint_uptime(void) {
+ struct utmp *utmpstruct;
+ int upminutes, uphours, updays;
+ int pos;
+ struct tm *realtime;
+ time_t realseconds;
+ int numuser;
+ double uptime_secs, idle_secs;
+
+/* first get the current time */
+
+ time(&realseconds);
+ realtime = localtime(&realseconds);
+ pos = sprintf(buf, " %2d:%02d%s ",
+ realtime->tm_hour%12 ? realtime->tm_hour%12 : 12,
+ realtime->tm_min, realtime->tm_hour > 11 ? "pm" : "am");
+
+/* read and calculate the amount of uptime */
+
+ uptime(&uptime_secs, &idle_secs);
+
+ updays = (int) uptime_secs / (60*60*24);
+ strcat (buf, "up ");
+ pos += 3;
+ if (updays)
+ pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : "");
+ upminutes = (int) uptime_secs / 60;
+ uphours = upminutes / 60;
+ uphours = uphours % 24;
+ upminutes = upminutes % 60;
+ if(uphours)
+ pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes);
+ else
+ pos += sprintf(buf + pos, "%d min, ", upminutes);
+
+/* count the number of users */
+
+ numuser = 0;
+ setutent();
+ while ((utmpstruct = getutent())) {
+ if ((utmpstruct->ut_type == USER_PROCESS) &&
+ (utmpstruct->ut_name[0] != '\0'))
+ numuser++;
+ }
+ endutent();
+
+ pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s");
+
+ loadavg(&av[0], &av[1], &av[2]);
+
+ pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f",
+ av[0], av[1], av[2]);
+
+ return buf;
+}
+
+void print_uptime(void)
+{
+ printf("%s\n", sprint_uptime());
+}
diff --git a/libproc/whattime.h b/libproc/whattime.h
new file mode 100644
index 00000000..46a1d821
--- /dev/null
+++ b/libproc/whattime.h
@@ -0,0 +1,9 @@
+/* whattime.h --- see whattime.c for explanation */
+
+#ifndef __WHATTIME_H
+#define __WHATTIME_H
+
+void print_uptime(void);
+char *sprint_uptime(void);
+
+#endif