home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / A / PS / PROCPS-0.000 / PROCPS-0 / procps-0.97 / pstree.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-16  |  11.2 KB  |  493 lines

  1. /* pstree.c  -  display process tree. Written by Werner Almesberger */
  2.  
  3.  
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <ctype.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>
  10. #include <getopt.h>
  11. #include <pwd.h>
  12. #include <dirent.h>
  13. #include <termios.h>
  14. #include <termcap.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <sys/ioctl.h>
  18. #include <linux/sched.h>
  19.  
  20.  
  21. #define COMM_LEN     sizeof(dummy.comm)
  22. #ifndef MAX_DEPTH
  23. #define MAX_DEPTH    100
  24. #endif
  25. #define PROC_BASE    "/proc"
  26.  
  27.  
  28. static struct task_struct dummy;
  29.  
  30.  
  31. typedef struct _proc {
  32.     char comm[COMM_LEN+1];
  33.     char **argv;    /* only used : argv[0] is 1st arg; undef if argc < 1 */
  34.     int argc;        /* with -a   : number of arguments, -1 if swapped    */
  35.     pid_t pid;
  36.     uid_t uid;
  37.     int highlight;
  38.     struct _child *children;
  39.     struct _proc *parent;
  40.     struct _proc *next;
  41. } PROC;
  42.  
  43. typedef struct _child {
  44.     PROC *child;
  45.     struct _child *next;
  46. }  CHILD;
  47.  
  48.  
  49. static PROC *list = NULL;
  50. static int width[MAX_DEPTH],more[MAX_DEPTH];
  51. static int print_args = 0,compact = 1,user_change = 0,pids = 0,trunc = 1;
  52. static output_width = 132;
  53. static int cur_x = 1;
  54. static char last_char = 0;
  55. static int dumped = 0; /* used by dump_by_user */
  56.  
  57.  
  58. static void out_char(char c)
  59. {
  60.     if (cur_x < output_width || !trunc) putchar(c);
  61.     if (cur_x == output_width && trunc)
  62.     if (last_char) putchar('+');
  63.     else {
  64.         last_char = c;
  65.         return;
  66.     }
  67.     cur_x++;
  68. }
  69.  
  70.  
  71. static void out_string(char *str)
  72. {
  73.     while (*str) out_char(*str++);
  74. }
  75.  
  76.  
  77. static int out_int(int x) /* non-negative integers only */
  78. {
  79.     int digits,div;
  80.  
  81.     digits = 0;
  82.     for (div = 1; x/div; div *= 10) digits++;
  83.     if (!digits) digits = 1;
  84.     for (div /= 10; div; div /= 10) out_char('0'+(x/div) % 10);
  85.     return digits;
  86. }
  87.  
  88.  
  89. static void out_newline(void)
  90. {
  91.     if (last_char && cur_x == output_width) putchar(last_char);
  92.     last_char = 0;
  93.     putchar('\n');
  94.     cur_x = 1;
  95. }
  96.  
  97.  
  98. static PROC *find_proc(pid_t pid)
  99. {
  100.     PROC *walk;
  101.  
  102.     for (walk = list; walk; walk = walk->next)
  103.     if (walk->pid == pid) break;
  104.     return walk;
  105. }
  106.  
  107.  
  108. static PROC *new_proc(char *comm,pid_t pid,uid_t uid)
  109. {
  110.     PROC *new;
  111.  
  112.     if (!(new = malloc(sizeof(PROC)))) {
  113.     perror("malloc");
  114.     exit(1);
  115.     }
  116.     strcpy(new->comm,comm);
  117.     new->pid = pid;
  118.     new->uid = uid;
  119.     new->highlight = 0;
  120.     new->children = NULL;
  121.     new->parent = NULL;
  122.     new->next = list;
  123.     return list = new;
  124. }
  125.  
  126.  
  127. static void add_child(PROC *parent,PROC *child)
  128. {
  129.     CHILD *new,**walk;
  130.     int cmp;
  131.  
  132.     if (!(new = malloc(sizeof(CHILD)))) {
  133.     perror("malloc");
  134.     exit(1);
  135.     }
  136.     new->child = child;
  137.     for (walk = &parent->children; *walk; walk = &(*walk)->next)
  138.     if ((cmp = strcmp((*walk)->child->comm,child->comm)) > 0) break;
  139.     else if (!cmp && (*walk)->child->uid > child->uid) break;
  140.     new->next = *walk;
  141.     *walk = new;
  142. }
  143.  
  144.  
  145. static void set_args(PROC *this,char *args,int size)
  146. {
  147.     char *start;
  148.     int i;
  149.  
  150.     if (!size) {
  151.     this->argc = -1;
  152.     return;
  153.     }
  154.     this->argc = 0;
  155.     for (i = 0; i < size-1; i++)
  156.     if (!args[i]) this->argc++;
  157.     if (!this->argc) return;
  158.     if (!(this->argv = malloc(sizeof(char *)*this->argc))) {
  159.     perror("malloc");
  160.     exit(1);
  161.     }
  162.     start = strchr(args,0)+1;
  163.     size -= start-args;
  164.     if (!(this->argv[0] = malloc(size))) {
  165.     perror("malloc");
  166.     exit(1);
  167.     }
  168.     start = memcpy(this->argv[0],start,size);
  169.     for (i = 1; i < this->argc; i++) this->argv[i] = start = strchr(start,0)+1;
  170. }
  171.  
  172.  
  173. static void add_proc(char *comm,pid_t pid,pid_t ppid,uid_t uid,char *args,
  174.   int size)
  175. {
  176.     PROC *this,*parent;
  177.  
  178.     if (!(this = find_proc(pid))) this = new_proc(comm,pid,uid);
  179.     else {
  180.     strcpy(this->comm,comm);
  181.     this->uid = uid;
  182.     }
  183.     if (args) set_args(this,args,size);
  184.     if (!(parent = find_proc(ppid))) parent = new_proc("?",ppid,0);
  185.     add_child(parent,this);
  186.     this->parent = parent;
  187. }
  188.  
  189.  
  190. static int tree_equal(PROC *a,PROC *b)
  191. {
  192.     CHILD *walk_a,*walk_b;
  193.  
  194.     if (strcmp(a->comm,b->comm)) return 0;
  195.     if (user_change && a->uid != b->uid) return 0;
  196.     for (walk_a = a->children, walk_b = b->children; walk_a && walk_b;
  197.       walk_a = walk_a->next, walk_b = walk_b->next)
  198.     if (!tree_equal(walk_a->child,walk_b->child)) return 0;
  199.     return !(walk_a || walk_b);
  200. }
  201.  
  202.  
  203. static void dump_tree(PROC *current,int level,int rep,int leaf,int last,
  204.   uid_t prev_uid,int closing)
  205. {
  206.     CHILD *walk,*next,**scan;
  207.     struct passwd *pw;
  208.     int lvl,i,add,offset,len,swapped,info,count,comm_len,first;
  209.     char *tmp,*here;
  210.     char comm_tmp[5];
  211.  
  212.     if (!current) return;
  213.     if (level >= MAX_DEPTH-1) {
  214.     fprintf(stderr,"MAX_DEPTH not big enough.\n");
  215.     exit(1);
  216.     }
  217.     if (!leaf)
  218.     for (lvl = 0; lvl < level; lvl++) {
  219.         for (i = width[lvl]+1; i; i--) out_char(' ');
  220.         out_string(lvl == level-1 ? last ? "`-" : "|-" : more[lvl+1] ?
  221.           "| " : "  ");
  222.     }
  223.     if (rep < 2) add = 0;
  224.     else {
  225.     add = out_int(rep)+2;
  226.     out_string("*[");
  227.     }
  228.     if (current->highlight && (tmp = tgetstr("md",NULL))) printf("%s",tmp);
  229.     if (swapped = print_args && current->argc < 0) out_char('(');
  230.     comm_len = 0;
  231.     for (here = current->comm; *here; here++)
  232.     if (*here == '\\') {
  233.         out_string("\\\\");
  234.         comm_len += 2;
  235.     }
  236.     else if (*here > ' ' && *here <= '~') {
  237.         out_char(*here);
  238.         comm_len++;
  239.         }
  240.         else {
  241.         sprintf(comm_tmp,"\\%03o",(unsigned char) *here);
  242.         out_string(comm_tmp);
  243.         comm_len += 4;
  244.         }
  245.     offset = cur_x;
  246.     info = pids || (user_change && prev_uid != current->uid);
  247.     if (info) out_char(swapped ? ',' : '(');
  248.     if (pids) (void) out_int(current->pid);
  249.     if (user_change && prev_uid != current->uid) {
  250.     if (pids) out_char(',');
  251.     if ((pw = getpwuid(current->uid))) out_string(pw->pw_name);
  252.     else (void) out_int(current->uid);
  253.     }
  254.     if (info || swapped) out_char(')');
  255.     if (current->highlight && (tmp = tgetstr("me",NULL))) printf("%s",tmp);
  256.     if (print_args) {
  257.     for (i = 0; i < current->argc; i++) {
  258.         out_char(' ');
  259.         len = 0;
  260.         for (here = current->argv[i]; *here; here++)
  261.         len += *here > ' ' && *here <= '~' ? 1 : 4;
  262.         if (cur_x+len <= output_width-(i == current->argc-1 ? 0 : 4))
  263.         for (here = current->argv[i]; *here; here++)
  264.             if (*here > ' ' && *here <= '~') out_char(*here);
  265.             else {
  266.             sprintf(comm_tmp,"\\%03o",(unsigned char) *here);
  267.             out_string(comm_tmp);
  268.             }
  269.         else {
  270.         out_string("...");
  271.         break;
  272.         }
  273.     }
  274.     }
  275.     if (print_args || !current->children) {
  276.     while (closing--) out_char(']');
  277.     out_newline();
  278.     if (print_args) {
  279.         more[level] = !last;
  280.         width[level] = swapped+(comm_len > 1 ? 0 : -1);
  281.         for (walk = current->children; walk; walk = walk->next)
  282.         dump_tree(walk->child,level+1,1,0,!walk->next,current->uid,0);
  283.     }
  284.     }
  285.     else {
  286.     more[level] = !last;
  287.     width[level] = comm_len+cur_x-offset+add;
  288.     if (cur_x >= output_width && trunc) out_string("---+");
  289.     else {
  290.         first = 1;
  291.         for (walk = current->children; walk; walk = next) {
  292.         count = 0;
  293.         next = walk->next;
  294.         if (compact) {
  295.             scan = &walk->next;
  296.             while (*scan)
  297.             if (!tree_equal(walk->child,(*scan)->child)) 
  298.                 scan = &(*scan)->next;
  299.             else {
  300.                 if (next == *scan) next = (*scan)->next;
  301.                 count++;
  302.                 *scan = (*scan)->next;
  303.             }
  304.         }
  305.         if (first) {
  306.             out_string(next ? "-+-" : "---");
  307.             first = 0;
  308.         }
  309.         dump_tree(walk->child,level+1,count+1,walk == current->children,
  310.           !next,current->uid,closing+(count ? 1 : 0));
  311.         }
  312.     }
  313.     }
  314. }
  315.  
  316.  
  317. static void dump_by_user(PROC *current,uid_t uid)
  318. {
  319.     CHILD *walk;
  320.  
  321.     if (current->uid == uid) {
  322.     if (dumped) putchar('\n');
  323.     dump_tree(current,0,1,1,1,uid,0);
  324.     dumped = 1;
  325.     return;
  326.     }
  327.     for (walk = current->children; walk; walk = walk->next)
  328.     dump_by_user(walk->child,uid);
  329. }
  330.  
  331.  
  332. static void read_proc(void)
  333. {
  334.     DIR *dir;
  335.     struct dirent *de;
  336.     FILE *file;
  337.     struct stat st;
  338.     char path[PATH_MAX+1],comm[COMM_LEN+1];
  339.     char *buffer;
  340.     pid_t pid,ppid;
  341.     int fd,size;
  342.     int empty,dummy;
  343.  
  344.     if (!print_args) buffer = NULL;
  345.     else if (!(buffer = malloc(output_width+1))) {
  346.         perror("malloc");
  347.         exit(1);
  348.     }
  349.     if (!(dir = opendir(PROC_BASE))) {
  350.     perror(PROC_BASE);
  351.     exit(1);
  352.     }
  353.     empty = 1;
  354.     while (de = readdir(dir))
  355.     if (pid = atoi(de->d_name)) {
  356.         sprintf(path,"%s/%d/stat",PROC_BASE,pid);
  357.         if (file = fopen(path,"r")) {
  358.         empty = 0;
  359.         if (fstat(fileno(file),&st) < 0) {
  360.             perror(path);
  361.             exit(1);
  362.         }
  363.         if (fscanf(file,"%d (%[^)]) %c %d",&dummy,comm,(char *) &dummy,
  364.           &ppid) == 4) {
  365.             if (!print_args) add_proc(comm,pid,ppid,st.st_uid,NULL,0);
  366.             else {
  367.             sprintf(path,"%s/%d/cmdline",PROC_BASE,pid);
  368.             if ((fd = open(path,O_RDONLY)) < 0) {
  369.                 perror(path);
  370.                 exit(1);
  371.             }
  372.             if ((size = read(fd,buffer,output_width)) < 0) {
  373.                 perror(path);
  374.                 exit(1);
  375.             }
  376.             (void) close(fd);
  377.             if (size) buffer[size++] = 0;
  378.             add_proc(comm,pid,ppid,st.st_uid,buffer,size);
  379.             }
  380.         }
  381.         (void) fclose(file);
  382.         }
  383.     }
  384.     (void) closedir(dir);
  385.     if (print_args) free(buffer);
  386.     if (empty) {
  387.     fprintf(stderr,PROC_BASE " is empty (not mounted ?)\n");
  388.     exit(1);
  389.     }
  390. }
  391.  
  392.  
  393. #if 0
  394.  
  395. /* Could use output of  ps achlx | awk '{ print $3,$4,$2,$13 }'  */
  396.  
  397. static void read_stdin(void)
  398. {
  399.     char comm[PATH_MAX+1];
  400.     char *cmd;
  401.     int pid,ppid,uid;
  402.  
  403.     while (scanf("%d %d %d %s\n",&pid,&ppid,&uid,comm) == 4) {
  404.     if (cmd = strrchr(comm,'/')) cmd++;
  405.     else cmd = comm;
  406.     if (*cmd == '-') cmd++;
  407.     add_proc(cmd,pid,ppid,uid,NULL,0);
  408.     }
  409. }
  410.  
  411. #endif
  412.  
  413.  
  414. static void usage(char *name)
  415. {
  416.     fprintf(stderr,"usage: %s [ -a ] [ -c ] [ -h ] [ -l ] [ -p ] [ -u ] "
  417.       "[ pid | user]\n\n",name);
  418.     fprintf(stderr,"    -a     show command line arguments\n");
  419.     fprintf(stderr,"    -c     don't compact identical subtrees\n");
  420.     fprintf(stderr,"    -h     highlight current process and its ancestors\n");
  421.     fprintf(stderr,"    -l     don't truncate long lines\n");
  422.     fprintf(stderr,"    -p     show PIDs; implies -c\n");
  423.     fprintf(stderr,"    -u     show uid transitions\n");
  424.     fprintf(stderr,"    pid    start at pid, default 1 (init)\n");
  425.     fprintf(stderr,"    user   show only trees rooted at processes of that "
  426.       "user\n\n");
  427.     exit(1);
  428. }
  429.  
  430.  
  431. int main(int argc,char **argv)
  432. {
  433.     PROC *current;
  434.     struct winsize winsz;
  435.     struct passwd *pw;
  436.     pid_t pid,highlight;
  437.     char termcap_area[1024];
  438.     int c;
  439.  
  440.     if (ioctl(1,TIOCGWINSZ,&winsz) >= 0)
  441.     if (winsz.ws_col) output_width = winsz.ws_col;
  442.     pid = 1;
  443.     highlight = 0;
  444.     pw = NULL;
  445.     while ((c = getopt(argc,argv,"achplu")) != EOF)
  446.     switch (c) {
  447.         case 'a':
  448.         print_args = 1;
  449.         break;
  450.         case 'c':
  451.         compact = 0;
  452.         break;
  453.         case 'h':
  454.         if (getenv("TERM") && tgetent(termcap_area,getenv("TERM")) > 0)
  455.             highlight = getpid();
  456.         break;
  457.         case 'l':
  458.         trunc = 0;
  459.         break;
  460.         case 'p':
  461.         pids = 1;
  462.         compact = 0;
  463.         break;
  464.         case 'u':
  465.         user_change = 1;
  466.         break;
  467.         default:
  468.         usage(argv[0]);
  469.     }
  470.     if (optind == argc-1)
  471.     if (isdigit(*argv[optind])) {
  472.         if (!(pid = atoi(argv[optind++]))) usage(argv[0]);
  473.     }
  474.     else if (!(pw = getpwnam(argv[optind++]))) {
  475.         fprintf(stderr,"No such user name: %s\n",argv[optind-1]);
  476.         return 1;
  477.         }
  478.     if (optind != argc) usage(argv[0]);
  479.     read_proc();
  480.     for (current = find_proc(highlight); current; current = current->parent)
  481.     current->highlight = 1;
  482.     if (!pw)
  483.     dump_tree(find_proc(pid),0,1,1,1,0,0);
  484.     else {
  485.     dump_by_user(find_proc(1),pw->pw_uid);
  486.     if (!dumped) {
  487.         fprintf(stderr,"No processes found.\n");
  488.         return 1;
  489.     }
  490.     }
  491.     return 0;
  492. }
  493.