home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / A / PS / PROCPS-0.000 / PROCPS-0 / procps-0.97 / snap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-25  |  11.8 KB  |  449 lines

  1. /* Loosely derived from a simple hack posted to c.o.l
  2.  * -Michael K. Johnson, johnsonm@sunsite.unc.edu
  3.  *
  4.  * Some pieces from Branko Lankester's kmem ps, copyright 1992 Branko Lankester
  5.  *
  6.  * Modified 1994/05/25 Michael Shields <mjshield@nyx.cs.du.edu>
  7.  * xrealloc() added and xmalloc() cleaned up.
  8.  * Made get_process() take a pid_t.
  9.  * Added get_processes().  Split off do_get_process().
  10.  */
  11.  
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <sys/dir.h>
  15. #include <regex.h>
  16. #include <sys/stat.h>
  17. #include <sys/types.h>
  18. #include <pwd.h>
  19. #include <string.h>
  20. #include <unistd.h>
  21. #include <fcntl.h>
  22. #include "psdata.h"
  23. #include "ps.h"
  24.  
  25. /* parse_stat() takes great pains to be able to handle truly arbitrary 
  26.    executable file basenames -- i.e. it's oddly segmented for a *reason* */
  27.  
  28. void parse_stat(char* S, struct ps_proc* P) {
  29.     int n = 33;
  30.     char* tmp = S + strlen(S);
  31.  
  32.     /* Split S into "PID (cmd)" and "<rest of entries>" */
  33.     while (n--)               /* tmp -> spc following PID (cmd) */
  34.     while (*--tmp != ' ') /* need to do in reverse for arbitrary cmd's */
  35.         ;
  36.     *tmp='\0';
  37.     /* Now we can parse these two strings separately */
  38.     sscanf(S, "%d %40c", &P->pid, P->cmd);
  39.     sscanf(tmp+1, "%c %d %d %d %d %d %u %u %u %u %u %d %d %d %d %d %d %u %u "
  40.                   "%d %u %u %u %u %u %u %u %u %d %d %d %d %u",
  41.            &P->state, &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
  42.            &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
  43.            &P->utime, &P->stime, &P->cutime, &P->cstime, &P->counter,
  44.            &P->priority, &P->timeout, &P->it_real_value, &P->start_time,
  45.            &P->vsize, &P->rss, &P->rss_rlim, &P->start_code, &P->end_code,
  46.            &P->start_stack, &P->kstk_esp, &P->kstk_eip, &P->signal,
  47.            &P->blocked, &P->sigignore, &P->sigcatch, &P->wchan);
  48.  
  49. #ifdef TTY_FULL_DEVNO       /* temporary kludge until other tty majors exist */
  50.     if (P->tty)
  51.         P->tty = MINOR(P->tty);
  52.     else
  53.         P->tty = -1;
  54. #endif
  55. }
  56.  
  57. void parse_stat_m(char* s, struct ps_proc* P) {
  58.     sscanf(s, "%d %d %d %d %d %d %d",
  59.            &P->statm.size, &P->statm.resident, &P->statm.share, &P->statm.trs,
  60.        &P->statm.lrs, &P->statm.drs, &P->statm.dt);
  61. }
  62.  
  63. /* note: mycpy eqivalent to nulls2spc(ret, file2str(..., ret, ...)) */
  64. void nulls2spc(char* str, int len) {
  65.     int i;
  66.     for (i=0; i < len; i++)
  67.     if (str[i]==0)
  68.         str[i]=' ';
  69. }
  70.  
  71. int file2str(char *directory, char *ret, char *what, int cap)
  72. {
  73.   static char filename[80];
  74.   int fd, num_read;
  75.  
  76.   sprintf(filename, "/proc/%s/%s", directory, what);
  77.   if ( (fd = open(filename, O_RDONLY, 0)) == -1 )
  78.      return -1;
  79.   if ( (num_read = read(fd, ret, cap-1)) == -1 )
  80.       return -1;
  81.   ret[num_read] = 0;
  82.   close(fd);
  83.   return num_read;
  84. }
  85.  
  86. struct ps_proc_head *take_snapshot(char a, char u, char x, char m, char r,
  87.                    uid_t uid, int ctty)
  88. {
  89.   DIR *proc;
  90.   static struct direct *ent;
  91.   static char filename[80];
  92.   static char stat_str[4096];
  93.   struct ps_proc_head *ph = NULL;
  94.   struct ps_proc *this = NULL, *that = NULL;
  95.   struct stat sb;
  96.   int cmd_len;
  97.  
  98.   if ((proc = opendir("/proc")) == NULL) {
  99.       perror("opendir /proc");
  100.       exit(1);
  101.   }
  102. /*re_comp("^[0-9]*$");*/
  103.  
  104.   ph = (struct ps_proc_head *) xcalloc(ph, sizeof(struct ps_proc_head));
  105.   /* initializes ph->head and ph->count to zero ;-) */
  106.   ph->head = (struct ps_proc *) xcalloc(ph->head, sizeof(struct ps_proc));
  107.   this = ph->head;
  108.  
  109.   while(ent = readdir(proc)) {
  110. /*  if(!re_exec(ent->d_name)) continue; */
  111.     if(*ent->d_name < '0' || *ent->d_name > '9') continue;
  112.     sprintf(filename, "/proc/%s", ent->d_name);
  113.     stat(filename, &sb);
  114.     if(!a && (sb.st_uid != uid)) continue;
  115.     this->uid = sb.st_uid;
  116.     cmd_len = file2str(ent->d_name, this->cmdline, "cmdline", sizeof(this->cmdline));
  117.     if ( (file2str(ent->d_name, stat_str, "stat", sizeof(stat_str))) == -1 )
  118.     continue;
  119.     parse_stat(stat_str,this);
  120.     nulls2spc(this->cmdline, cmd_len);
  121.     if ((ctty && (ctty != this->tty))
  122.     || (r && this->state != 'R' && this->state != 'D')
  123.     || (!x && (this->tty == -1))) {
  124.       this->pid = 0;
  125.       continue;
  126.     }
  127.     /* 0 normally passed, which is never the value given as the tty from the
  128.        proc filesystem, so this only happens if a specific tty was passed. */
  129.     if(m) {
  130.       if((file2str(ent->d_name, stat_str, "statm", sizeof(stat_str))) == -1)
  131.       continue;
  132.       parse_stat_m(stat_str,this);
  133.     }
  134.     if (this->state == 'Z') strcat(this->cmd," <zombie>");
  135.     dev_to_tty(this->ttyc, this->tty);
  136.     if(u) strncpy(this->user, user_from_uid(this->uid), 9);
  137.  
  138.     /* update the linked list and increase the count */
  139.     if(this->pid) {
  140.       that = this;
  141.       this->next = (struct ps_proc *) xcalloc(this->next,
  142.                           sizeof(struct ps_proc));
  143.       this = this->next;
  144.       ph->count++;
  145.     }
  146.   } /* end of the while loop */
  147.   closedir(proc);
  148.   if (ph->count != 0)
  149.      if(!this->pid) { /* I beleive this will always be true, because it will try
  150.                          one more readdir, and there will be a hanging entry... 
  151.                          But I make it conditional to be safe */
  152.        that->next = (struct ps_proc *) NULL;
  153.        free (this);
  154.      } else this->next = (struct ps_proc *) NULL;
  155.   return ph;
  156. }
  157.  
  158. /*
  159.  * Fill a struct with information about the given PID.  M means to fill
  160.  * the statm field.  The structure is freshly allocated.  If this fails,
  161.  * it will return NULL.
  162.  */
  163. static struct ps_proc *do_get_process(pid_t pid, int m)
  164. {
  165.   static char stat_str[256];
  166.   static char filename[80];
  167.   char *fn = filename;
  168.   struct ps_proc *ret = NULL;
  169.   struct stat sb;
  170.   int cmd_len;
  171.  
  172.   ret = (struct ps_proc *) xcalloc(ret, sizeof(struct ps_proc));
  173.  
  174.   sprintf(fn, "/proc/%u", pid);
  175.   stat(fn, &sb);
  176.   ret->uid = sb.st_uid;
  177.   fn += 6; /* cut "/proc/" out of fn cheaply */
  178.   cmd_len = file2str(fn, ret->cmdline, "cmdline", sizeof(ret->cmdline));
  179.   if((file2str(fn, stat_str, "stat", sizeof(stat_str))) == -1)
  180.     { free(ret); return NULL; }
  181.   parse_stat(stat_str,ret);
  182.   nulls2spc(ret->cmdline, cmd_len);
  183.   if(m) {
  184.     if((file2str(fn, stat_str, "statm", sizeof(stat_str))) == -1)
  185.       { free(ret); return NULL; }
  186.     parse_stat_m(stat_str, ret);
  187.   }
  188.   if (ret->state == 'Z') strcat(ret->cmd," <zombie>");
  189.   dev_to_tty(ret->ttyc, ret->tty);
  190.   strncpy(ret->user, user_from_uid(ret->uid), 9);
  191.  
  192.   return ret;
  193. }
  194.  
  195. struct ps_proc_head *get_process(pid_t pid, int m)
  196. {
  197.   struct ps_proc_head *ph = NULL;
  198.  
  199.   ph = xmalloc(sizeof(struct ps_proc_head));
  200.   ph->head = do_get_process(pid, m);
  201.   ph->count = ph->head ? 1 : 0;
  202.   return ph;
  203. }
  204.  
  205. /*
  206.  * Return the status of the given processes; PIDS is a zero-terminated
  207.  * list of pids.  M is same as for get_process().
  208.  */
  209. struct ps_proc_head *get_processes(pid_t *pids, int m)
  210. {
  211.     struct ps_proc_head *ph;
  212.     struct ps_proc *this_process;
  213.     struct ps_proc **next_of_last;
  214.  
  215.     if (!pids)
  216.         /* Shouldn't happen. */
  217.         return(NULL);
  218.  
  219.     ph = xmalloc(sizeof(struct ps_proc_head));
  220.     ph->count = 0;
  221.     next_of_last = &ph->head;
  222.  
  223.     while (*pids) {
  224.         this_process = do_get_process(*pids, m);
  225.         if (this_process) {
  226.             *next_of_last = this_process;
  227.             next_of_last = &this_process->next;
  228.             ph->count++;
  229.         }
  230.         pids++;
  231.     }
  232.  
  233.     return ph;
  234. }
  235.  
  236. struct ps_proc_head *refresh_snapshot(struct ps_proc_head *ph,
  237.                       char a, char u, char x, char m, char r,
  238.                       uid_t uid, int ctty)
  239. {
  240.   DIR *proc;
  241.   static struct direct *ent;
  242.   static char filename[80];
  243.   static char stat_str[4096];
  244.   struct ps_proc *this = NULL, *that = NULL;
  245.   struct stat sb;
  246.   int cmd_len;
  247.  
  248.   if ((proc = opendir("/proc")) == NULL) {
  249.       perror("opendir /proc");
  250.       exit(1);
  251.   }
  252. /*re_comp("^[0-9]*$"); */
  253.  
  254.   ph->count = 0;
  255.   this = ph->head;
  256.  
  257.   while((ent = readdir(proc))) { /* Extra parens to make gcc -Wall happy... */
  258. /*  if(!re_exec(ent->d_name)) continue; */
  259.     if(*ent->d_name < '0' || *ent->d_name > '9') continue;
  260.     sprintf(filename, "/proc/%s", ent->d_name);
  261.     stat(filename, &sb);
  262.     if(!a && (sb.st_uid != uid)) continue;
  263.     this->uid = sb.st_uid;
  264.     cmd_len = file2str(ent->d_name, this->cmdline, "cmdline", sizeof(this->cmdline));
  265.     if((file2str(ent->d_name, stat_str, "stat", sizeof(stat_str))) == -1)
  266.     continue;
  267.     parse_stat(stat_str,this);
  268.     nulls2spc(this->cmdline, cmd_len);
  269.     if ((ctty && (ctty != this->tty))
  270.     || (r && this->state != 'R' && this->state != 'D')
  271.     || (!x && (this->tty == -1))) {
  272.       this->pid = 0;
  273.       continue;
  274.     }
  275.     /* 0 normally passed, which is never the value given as the tty from the
  276.        proc filesystem, so this only happens if a specific tty was passed. */
  277.     if(m) {
  278.       if((file2str(ent->d_name, stat_str, "statm", sizeof(stat_str))) == -1)
  279.       continue;
  280.       parse_stat_m(stat_str,this);
  281.     }
  282.     if (this->state == 'Z') strcat(this->cmd," <zombie>");
  283.     dev_to_tty(this->ttyc, this->tty);
  284.     if(u) strncpy(this->user, user_from_uid(this->uid), 9);
  285.  
  286.     /* update the linked list and increase the count */
  287.     if(this->pid) {
  288.       that = this;
  289.       if(!this->next)
  290.     this->next = (struct ps_proc *) xcalloc(this->next,
  291.                         sizeof(struct ps_proc));
  292.       this = this->next;
  293.       this->pid = 0;
  294.       ph->count++;
  295.     }
  296.   } /* end of the while loop */
  297.   closedir(proc);
  298.   if(!this->pid) { /* if the last slot was not used */
  299.     if(that->next)
  300.       free_psproc(that->next);
  301.     that->next = (struct ps_proc *) NULL;
  302.   } else {
  303.     if (this->next)
  304.       free_psproc(this->next);
  305.     this->next = (struct ps_proc *) NULL;
  306.   }
  307.   return ph;
  308. }
  309.  
  310. void free_psproc(struct ps_proc * this) {
  311.  
  312.   struct ps_proc *that;
  313.  
  314.   for(; this != NULL; this = that) {
  315.     that = this->next;
  316.     free(this);
  317.   }
  318. }
  319.  
  320. /* The next few functions are modified versions of functions from
  321.    various files in the kmem ps.  They are not as complete at error
  322.    checking, but that's life.  Thanks, Branko.  I had to change them
  323.    not to look at /dev/kmem, and to make my life simpler for a while,
  324.    error checking came out.  It's a stupid move, and I'll regret it,
  325.    but quite a bit of it depends on kmem reading, so I just chopped
  326.    it out...
  327. */
  328.  
  329. struct tbl_s vars, fncs;
  330. struct psdb_hdr db_hdr;
  331. int psdb = -1;
  332.  
  333. void *xmalloc(unsigned int size)
  334. {
  335.     void *p;
  336.  
  337.     if (size == 0)
  338.         ++size;
  339.  
  340.     p = malloc(size);
  341.     if (!p) {
  342.     fprintf(stderr, "xmalloc: malloc(%d) failed", size);
  343.     perror(NULL);
  344.     exit(1);
  345.     }
  346.     return(p);
  347. }
  348.  
  349. void *xrealloc(void *oldp, unsigned int size)
  350. {
  351.     void *p;
  352.  
  353.     if (size == 0)
  354.         ++size;
  355.  
  356.     p = realloc(oldp, size);
  357.     if (!p) {
  358.     fprintf(stderr, "xrealloc: realloc(%d) failed", size);
  359.     perror(NULL);
  360.     exit(1);
  361.     }
  362.     return(p);
  363. }
  364.  
  365. int open_psdb(void)
  366. {
  367.  
  368.     if ((psdb = open(PSDATABASE, O_RDONLY)) == -1)
  369.     return -1;
  370.     if (read(psdb, (char *) &db_hdr, sizeof(db_hdr)) != sizeof(db_hdr))
  371.     return -1;
  372.     
  373.     if (strncmp(db_hdr.magic, PS_MAGIC, sizeof(db_hdr.magic))) {
  374.     fprintf(stderr, "invalid psdatabase\n");
  375.     return -1;
  376.     }
  377.     
  378.     return(0);
  379. }
  380.  
  381. void close_psdb(void)
  382. {
  383.     if (psdb != -1)
  384.     close(psdb);
  385.     psdb = -1;
  386. }
  387.  
  388. int read_tbl(struct dbtbl_s *dbtbl, struct tbl_s *tbl)
  389. {
  390.   lseek(psdb, dbtbl->off, SEEK_SET);
  391.   tbl->tbl = (struct sym_s *) xmalloc(dbtbl->size);
  392.   if (read(psdb, (char *) tbl->tbl, dbtbl->size) != dbtbl->size) {
  393.     perror(PSDATABASE);
  394.     exit(1);
  395.   }
  396.   tbl->nsym = dbtbl->nsym;
  397.   tbl->strings = (char *) (tbl->tbl + tbl->nsym);
  398.   return(0);
  399. }
  400.  
  401. /*
  402.  * misc stuff needed
  403.  */
  404.  
  405. char * find_func(unsigned long address)
  406. {
  407.   int n;
  408.   struct sym_s *p;
  409.   char *s;
  410.  
  411.   if (fncs.tbl == NULL)
  412.     read_tbl(&db_hdr.fncs, &fncs);
  413.  
  414.   p = fncs.tbl;
  415.   n = fncs.nsym;
  416.   while (n) {
  417.     int i = n / 2;
  418.     if (p[i].addr < address) {
  419.       p = &p[i+1];
  420.       if (p->addr > address) {
  421.     --p;
  422.     break;
  423.       }
  424.       --n;
  425.     }
  426.     n /= 2;
  427.   }
  428.   s = p->name + fncs.strings;
  429.   return(*s == '_' ? s+1 : s);
  430. }
  431.  
  432. char * wchan(unsigned int address)
  433. {
  434.   static char zero = 0;
  435.   char *p;
  436.  
  437.   if (address) {
  438.     p = find_func(address);
  439.     
  440.     if (strncmp(p, "sys_", 4) == 0)
  441.       p += 4;
  442.     while (*p == '_' && *p)
  443.       ++p;
  444.   } else { /* 0 address means not in kernel space */
  445.     p = &zero;
  446.   }
  447.   return(p);
  448. }
  449.