home *** CD-ROM | disk | FTP | other *** search
- /* Loosely derived from a simple hack posted to c.o.l
- * -Michael K. Johnson, johnsonm@sunsite.unc.edu
- *
- * Some pieces from Branko Lankester's kmem ps, copyright 1992 Branko Lankester
- *
- * Modified 1994/05/25 Michael Shields <mjshield@nyx.cs.du.edu>
- * xrealloc() added and xmalloc() cleaned up.
- * Made get_process() take a pid_t.
- * Added get_processes(). Split off do_get_process().
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/dir.h>
- #include <regex.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <pwd.h>
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include "psdata.h"
- #include "ps.h"
-
- /* parse_stat() takes great pains to be able to handle truly arbitrary
- executable file basenames -- i.e. it's oddly segmented for a *reason* */
-
- void parse_stat(char* S, struct ps_proc* P) {
- int n = 33;
- char* tmp = S + strlen(S);
-
- /* Split S into "PID (cmd)" and "<rest of entries>" */
- while (n--) /* tmp -> spc following PID (cmd) */
- while (*--tmp != ' ') /* need to do in reverse for arbitrary cmd's */
- ;
- *tmp='\0';
- /* Now we can parse these two strings separately */
- sscanf(S, "%d %40c", &P->pid, P->cmd);
- sscanf(tmp+1, "%c %d %d %d %d %d %u %u %u %u %u %d %d %d %d %d %d %u %u "
- "%d %u %u %u %u %u %u %u %u %d %d %d %d %u",
- &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->counter,
- &P->priority, &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);
-
- #ifdef TTY_FULL_DEVNO /* temporary kludge until other tty majors exist */
- if (P->tty)
- P->tty = MINOR(P->tty);
- else
- P->tty = -1;
- #endif
- }
-
- void parse_stat_m(char* s, struct ps_proc* P) {
- sscanf(s, "%d %d %d %d %d %d %d",
- &P->statm.size, &P->statm.resident, &P->statm.share, &P->statm.trs,
- &P->statm.lrs, &P->statm.drs, &P->statm.dt);
- }
-
- /* note: mycpy eqivalent to nulls2spc(ret, file2str(..., ret, ...)) */
- void nulls2spc(char* str, int len) {
- int i;
- for (i=0; i < len; i++)
- if (str[i]==0)
- str[i]=' ';
- }
-
- int file2str(char *directory, char *ret, char *what, int cap)
- {
- static char filename[80];
- int fd, num_read;
-
- sprintf(filename, "/proc/%s/%s", directory, what);
- if ( (fd = open(filename, O_RDONLY, 0)) == -1 )
- return -1;
- if ( (num_read = read(fd, ret, cap-1)) == -1 )
- return -1;
- ret[num_read] = 0;
- close(fd);
- return num_read;
- }
-
- struct ps_proc_head *take_snapshot(char a, char u, char x, char m, char r,
- uid_t uid, int ctty)
- {
- DIR *proc;
- static struct direct *ent;
- static char filename[80];
- static char stat_str[4096];
- struct ps_proc_head *ph = NULL;
- struct ps_proc *this = NULL, *that = NULL;
- struct stat sb;
- int cmd_len;
-
- if ((proc = opendir("/proc")) == NULL) {
- perror("opendir /proc");
- exit(1);
- }
- /*re_comp("^[0-9]*$");*/
-
- ph = (struct ps_proc_head *) xcalloc(ph, sizeof(struct ps_proc_head));
- /* initializes ph->head and ph->count to zero ;-) */
- ph->head = (struct ps_proc *) xcalloc(ph->head, sizeof(struct ps_proc));
- this = ph->head;
-
- while(ent = readdir(proc)) {
- /* if(!re_exec(ent->d_name)) continue; */
- if(*ent->d_name < '0' || *ent->d_name > '9') continue;
- sprintf(filename, "/proc/%s", ent->d_name);
- stat(filename, &sb);
- if(!a && (sb.st_uid != uid)) continue;
- this->uid = sb.st_uid;
- cmd_len = file2str(ent->d_name, this->cmdline, "cmdline", sizeof(this->cmdline));
- if ( (file2str(ent->d_name, stat_str, "stat", sizeof(stat_str))) == -1 )
- continue;
- parse_stat(stat_str,this);
- nulls2spc(this->cmdline, cmd_len);
- if ((ctty && (ctty != this->tty))
- || (r && this->state != 'R' && this->state != 'D')
- || (!x && (this->tty == -1))) {
- this->pid = 0;
- continue;
- }
- /* 0 normally passed, which is never the value given as the tty from the
- proc filesystem, so this only happens if a specific tty was passed. */
- if(m) {
- if((file2str(ent->d_name, stat_str, "statm", sizeof(stat_str))) == -1)
- continue;
- parse_stat_m(stat_str,this);
- }
- if (this->state == 'Z') strcat(this->cmd," <zombie>");
- dev_to_tty(this->ttyc, this->tty);
- if(u) strncpy(this->user, user_from_uid(this->uid), 9);
-
- /* update the linked list and increase the count */
- if(this->pid) {
- that = this;
- this->next = (struct ps_proc *) xcalloc(this->next,
- sizeof(struct ps_proc));
- this = this->next;
- ph->count++;
- }
- } /* end of the while loop */
- closedir(proc);
- if (ph->count != 0)
- if(!this->pid) { /* I beleive this will always be true, because it will try
- one more readdir, and there will be a hanging entry...
- But I make it conditional to be safe */
- that->next = (struct ps_proc *) NULL;
- free (this);
- } else this->next = (struct ps_proc *) NULL;
- return ph;
- }
-
- /*
- * Fill a struct with information about the given PID. M means to fill
- * the statm field. The structure is freshly allocated. If this fails,
- * it will return NULL.
- */
- static struct ps_proc *do_get_process(pid_t pid, int m)
- {
- static char stat_str[256];
- static char filename[80];
- char *fn = filename;
- struct ps_proc *ret = NULL;
- struct stat sb;
- int cmd_len;
-
- ret = (struct ps_proc *) xcalloc(ret, sizeof(struct ps_proc));
-
- sprintf(fn, "/proc/%u", pid);
- stat(fn, &sb);
- ret->uid = sb.st_uid;
- fn += 6; /* cut "/proc/" out of fn cheaply */
- cmd_len = file2str(fn, ret->cmdline, "cmdline", sizeof(ret->cmdline));
- if((file2str(fn, stat_str, "stat", sizeof(stat_str))) == -1)
- { free(ret); return NULL; }
- parse_stat(stat_str,ret);
- nulls2spc(ret->cmdline, cmd_len);
- if(m) {
- if((file2str(fn, stat_str, "statm", sizeof(stat_str))) == -1)
- { free(ret); return NULL; }
- parse_stat_m(stat_str, ret);
- }
- if (ret->state == 'Z') strcat(ret->cmd," <zombie>");
- dev_to_tty(ret->ttyc, ret->tty);
- strncpy(ret->user, user_from_uid(ret->uid), 9);
-
- return ret;
- }
-
- struct ps_proc_head *get_process(pid_t pid, int m)
- {
- struct ps_proc_head *ph = NULL;
-
- ph = xmalloc(sizeof(struct ps_proc_head));
- ph->head = do_get_process(pid, m);
- ph->count = ph->head ? 1 : 0;
- return ph;
- }
-
- /*
- * Return the status of the given processes; PIDS is a zero-terminated
- * list of pids. M is same as for get_process().
- */
- struct ps_proc_head *get_processes(pid_t *pids, int m)
- {
- struct ps_proc_head *ph;
- struct ps_proc *this_process;
- struct ps_proc **next_of_last;
-
- if (!pids)
- /* Shouldn't happen. */
- return(NULL);
-
- ph = xmalloc(sizeof(struct ps_proc_head));
- ph->count = 0;
- next_of_last = &ph->head;
-
- while (*pids) {
- this_process = do_get_process(*pids, m);
- if (this_process) {
- *next_of_last = this_process;
- next_of_last = &this_process->next;
- ph->count++;
- }
- pids++;
- }
-
- return ph;
- }
-
- struct ps_proc_head *refresh_snapshot(struct ps_proc_head *ph,
- char a, char u, char x, char m, char r,
- uid_t uid, int ctty)
- {
- DIR *proc;
- static struct direct *ent;
- static char filename[80];
- static char stat_str[4096];
- struct ps_proc *this = NULL, *that = NULL;
- struct stat sb;
- int cmd_len;
-
- if ((proc = opendir("/proc")) == NULL) {
- perror("opendir /proc");
- exit(1);
- }
- /*re_comp("^[0-9]*$"); */
-
- ph->count = 0;
- this = ph->head;
-
- while((ent = readdir(proc))) { /* Extra parens to make gcc -Wall happy... */
- /* if(!re_exec(ent->d_name)) continue; */
- if(*ent->d_name < '0' || *ent->d_name > '9') continue;
- sprintf(filename, "/proc/%s", ent->d_name);
- stat(filename, &sb);
- if(!a && (sb.st_uid != uid)) continue;
- this->uid = sb.st_uid;
- cmd_len = file2str(ent->d_name, this->cmdline, "cmdline", sizeof(this->cmdline));
- if((file2str(ent->d_name, stat_str, "stat", sizeof(stat_str))) == -1)
- continue;
- parse_stat(stat_str,this);
- nulls2spc(this->cmdline, cmd_len);
- if ((ctty && (ctty != this->tty))
- || (r && this->state != 'R' && this->state != 'D')
- || (!x && (this->tty == -1))) {
- this->pid = 0;
- continue;
- }
- /* 0 normally passed, which is never the value given as the tty from the
- proc filesystem, so this only happens if a specific tty was passed. */
- if(m) {
- if((file2str(ent->d_name, stat_str, "statm", sizeof(stat_str))) == -1)
- continue;
- parse_stat_m(stat_str,this);
- }
- if (this->state == 'Z') strcat(this->cmd," <zombie>");
- dev_to_tty(this->ttyc, this->tty);
- if(u) strncpy(this->user, user_from_uid(this->uid), 9);
-
- /* update the linked list and increase the count */
- if(this->pid) {
- that = this;
- if(!this->next)
- this->next = (struct ps_proc *) xcalloc(this->next,
- sizeof(struct ps_proc));
- this = this->next;
- this->pid = 0;
- ph->count++;
- }
- } /* end of the while loop */
- closedir(proc);
- if(!this->pid) { /* if the last slot was not used */
- if(that->next)
- free_psproc(that->next);
- that->next = (struct ps_proc *) NULL;
- } else {
- if (this->next)
- free_psproc(this->next);
- this->next = (struct ps_proc *) NULL;
- }
- return ph;
- }
-
- void free_psproc(struct ps_proc * this) {
-
- struct ps_proc *that;
-
- for(; this != NULL; this = that) {
- that = this->next;
- free(this);
- }
- }
-
- /* The next few functions are modified versions of functions from
- various files in the kmem ps. They are not as complete at error
- checking, but that's life. Thanks, Branko. I had to change them
- not to look at /dev/kmem, and to make my life simpler for a while,
- error checking came out. It's a stupid move, and I'll regret it,
- but quite a bit of it depends on kmem reading, so I just chopped
- it out...
- */
-
- struct tbl_s vars, fncs;
- struct psdb_hdr db_hdr;
- int psdb = -1;
-
- 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);
- }
-
- int open_psdb(void)
- {
-
- if ((psdb = open(PSDATABASE, O_RDONLY)) == -1)
- return -1;
- if (read(psdb, (char *) &db_hdr, sizeof(db_hdr)) != sizeof(db_hdr))
- return -1;
-
- if (strncmp(db_hdr.magic, PS_MAGIC, sizeof(db_hdr.magic))) {
- fprintf(stderr, "invalid psdatabase\n");
- return -1;
- }
-
- return(0);
- }
-
- void close_psdb(void)
- {
- if (psdb != -1)
- close(psdb);
- psdb = -1;
- }
-
- 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);
- }
-
- /*
- * misc stuff needed
- */
-
- char * find_func(unsigned long address)
- {
- int n;
- struct sym_s *p;
- char *s;
-
- 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 int 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);
- }
-