Updated with Tizen:Base source codes
[external/procps.git] / ps / display.c
1 /*
2  * Copyright 1998-2003 by Albert Cahalan; all rights resered.
3  * This file may be used subject to the terms and conditions of the
4  * GNU Library General Public License Version 2, or any later version
5  * at your option, as published by the Free Software Foundation.
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU Library General Public License for more details.
10  */                                                                     
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16
17 #if (__GNU_LIBRARY__ >= 6)
18 # include <locale.h>
19 #endif
20
21 /* username lookups */
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <grp.h>
25
26 /* major/minor number */
27 #include <sys/sysmacros.h>
28
29 #include <signal.h>   /* catch signals */
30
31 #include "common.h"
32 #include "../proc/wchan.h"
33 #include "../proc/version.h"
34 #include "../proc/readproc.h"
35 #include "../proc/sysinfo.h"
36 #include "../proc/sig.h"
37
38 #ifndef SIGCHLD
39 #define SIGCHLD SIGCLD
40 #endif
41
42 /* just reports a crash */
43 static void signal_handler(int signo){
44   if(signo==SIGPIPE) _exit(0);  /* "ps | head" will cause this */
45   /* fprintf() is not reentrant, but we _exit() anyway */
46   fprintf(stderr,
47     "\n\n"
48     "Signal %d (%s) caught by ps (%s).\n"
49     "Please send bug reports to <feedback@lists.sf.net> or <albert@users.sf.net>\n",
50     signo,
51     signal_number_to_name(signo),
52     procps_version
53   );
54   _exit(signo+128);
55 }
56
57 /////////////////////////////////////////////////////////////////////////////////////
58 #undef DEBUG
59 #ifdef DEBUG
60 void init_stack_trace(char *prog_name);
61
62 #include <ctype.h>
63
64 void hex_dump(void *vp){
65   char *charlist;
66   int i = 0;
67   int line = 45;
68   char *cp = (char *)vp;
69
70   while(line--){
71       printf("%8lx  ", (unsigned long)cp);
72       charlist = cp;
73       cp += 16;
74       for(i=0; i<16; i++){
75         if((charlist[i]>31) && (charlist[i]<127)){
76           printf("%c", charlist[i]);
77         }else{
78           printf(".");
79         }
80       }
81       printf(" ");
82       for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i])));
83       printf("\n");
84       i=0;
85   }
86 }
87
88 static void show_tgid(char *s, int n, sel_union *data){
89   printf("%s  ", s);
90   while(--n){
91     printf("%d,", data[n].tgid);
92   }
93   printf("%d\n", data[0].tgid);
94 }
95
96 static void show_uid(char *s, int n, sel_union *data){
97   struct passwd *pw_data;
98   printf("%s  ", s);
99   while(--n){
100     pw_data = getpwuid(data[n].uid);
101     if(pw_data) printf("%s,", pw_data->pw_name);
102     else        printf("%d,", data[n].uid);
103   }
104   pw_data = getpwuid(data[n].uid);
105   if(pw_data) printf("%s\n", pw_data->pw_name);
106   else        printf("%d\n", data[n].uid);
107 }
108
109 static void show_gid(char *s, int n, sel_union *data){
110   struct group *gr_data;
111   printf("%s  ", s);
112   while(--n){
113     gr_data = getgrgid(data[n].gid);
114     if(gr_data) printf("%s,", gr_data->gr_name);
115     else        printf("%d,", data[n].gid);
116   }
117   gr_data = getgrgid(data[n].gid);
118   if(gr_data) printf("%s\n", gr_data->gr_name);
119   else        printf("%d\n", data[n].gid);
120 }
121
122 static void show_tty(char *s, int n, sel_union *data){
123   printf("%s  ", s);
124   while(--n){
125     printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty));
126   }
127   printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty));
128 }
129
130 static void show_cmd(char *s, int n, sel_union *data){
131   printf("%s  ", s);
132   while(--n){
133     printf("%.8s,", data[n].cmd);
134   }
135   printf("%.8s\n", data[0].cmd);
136 }
137
138 static void arg_show(void){
139   selection_node *walk = selection_list;
140   while(walk){
141     switch(walk->typecode){
142     case SEL_RUID: show_uid("RUID", walk->n, walk->u); break;
143     case SEL_EUID: show_uid("EUID", walk->n, walk->u); break;
144     case SEL_SUID: show_uid("SUID", walk->n, walk->u); break;
145     case SEL_FUID: show_uid("FUID", walk->n, walk->u); break;
146     case SEL_RGID: show_gid("RGID", walk->n, walk->u); break;
147     case SEL_EGID: show_gid("EGID", walk->n, walk->u); break;
148     case SEL_SGID: show_gid("SGID", walk->n, walk->u); break;
149     case SEL_FGID: show_gid("FGID", walk->n, walk->u); break;
150     case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break;
151     case SEL_PID : show_pid("PID ", walk->n, walk->u); break;
152     case SEL_PPID: show_pid("PPID", walk->n, walk->u); break;
153     case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break;
154     case SEL_SESS: show_pid("SESS", walk->n, walk->u); break;
155     case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break;
156     default: printf("Garbage typecode value!\n");
157     }
158     walk = walk->next;
159   }
160 }
161
162 #endif
163 //////////////////////////////////////////////////////////////////////////
164
165
166 /***** check the header */
167 /* Unix98: must not print empty header */
168 static void check_headers(void){
169   format_node *walk = format_list;
170   int head_normal = 0;
171   if(header_type==HEAD_MULTI){
172     header_gap = screen_rows-1;  /* true BSD */
173     return;
174   }
175   if(header_type==HEAD_NONE){
176     lines_to_next_header = -1;  /* old Linux */
177     return;
178   }
179   while(walk){
180     if(!*(walk->name)){
181       walk = walk->next;
182       continue;
183     }
184     if(walk->pr){
185       head_normal++;
186       walk = walk->next;
187       continue;
188     }
189     walk = walk->next;
190   }
191   if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */
192 }
193
194 /***** check sort needs */
195 /* see what files need to be read, etc. */
196 static unsigned check_sort_needs(sort_node *walk){
197   unsigned needs = 0;
198   while(walk){
199     needs |= walk->need;
200     walk = walk->next;
201   }
202   return needs;
203 }
204
205 /***** check needs */
206 /* see what files need to be read, etc. */
207 static unsigned collect_format_needs(format_node *walk){
208   unsigned needs = 0;
209   while(walk){
210     needs |= walk->need;
211     walk = walk->next;
212   }
213   return needs;
214 }
215
216 static format_node *proc_format_list;
217 static format_node *task_format_list;
218
219 static unsigned needs_for_threads;
220 static unsigned needs_for_sort;
221 static unsigned proc_format_needs;
222 static unsigned task_format_needs;
223
224 #define needs_for_format (proc_format_needs|task_format_needs)
225
226 #define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM)
227
228 /***** munge lists and determine openproc() flags */
229 static void lists_and_needs(void){
230   check_headers();
231
232   // only care about the difference when showing both
233   if(thread_flags & TF_show_both){
234     format_node pfn, tfn; // junk, to handle special case at begin of list
235     format_node *walk = format_list;
236     format_node *p_end = &pfn;
237     format_node *t_end = &tfn;
238     while(walk){
239       format_node *new = malloc(sizeof(format_node));
240       memcpy(new,walk,sizeof(format_node));
241       p_end->next = walk;
242       t_end->next = new;
243       p_end       = walk;
244       t_end       = new;
245       switch(walk->flags & CF_PRINT_MASK){
246       case CF_PRINT_THREAD_ONLY:
247         p_end->pr   = pr_nop;
248         p_end->need = 0;
249         break;
250       case CF_PRINT_PROCESS_ONLY:
251         t_end->pr   = pr_nop;
252         t_end->need = 0;
253         break;
254       default:
255         fprintf(stderr, "please report this bug\n");
256         // FALL THROUGH
257       case CF_PRINT_AS_NEEDED:
258       case CF_PRINT_EVERY_TIME:
259         break;
260       }
261       walk = walk->next;
262     }
263     t_end->next = NULL;
264     p_end->next = NULL;
265     proc_format_list = pfn.next;
266     task_format_list = tfn.next;
267   }else{
268     proc_format_list = format_list;
269     task_format_list = format_list;
270   }
271
272   proc_format_needs = collect_format_needs(proc_format_list);
273   task_format_needs = collect_format_needs(task_format_list);
274
275   needs_for_sort = check_sort_needs(sort_list);
276
277   // move process-only flags to the process
278   proc_format_needs |= (task_format_needs &~ PROC_ONLY_FLAGS);
279   task_format_needs &= ~PROC_ONLY_FLAGS;
280
281   if(bsd_c_option){
282     proc_format_needs &= ~PROC_FILLARG;
283     needs_for_sort    &= ~PROC_FILLARG;
284   }
285   if(!unix_f_option){
286     proc_format_needs &= ~PROC_FILLCOM;
287     needs_for_sort    &= ~PROC_FILLCOM;
288   }
289   // convert ARG to COM as a standard
290   if(proc_format_needs & PROC_FILLARG){
291     proc_format_needs |= PROC_FILLCOM;
292     proc_format_needs &= ~PROC_FILLARG;
293   }
294   if(bsd_e_option){
295     if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV;
296   }
297   
298   /* FIXME  broken filthy hack -- got to unify some stuff here */
299   if( ( (proc_format_needs|task_format_needs|needs_for_sort) & PROC_FILLWCHAN) && !wchan_is_number)
300     if (open_psdb(namelist_file)) wchan_is_number = 1;
301
302   if(thread_flags&TF_loose_tasks) needs_for_threads |= PROC_LOOSE_TASKS;
303 }
304
305 //////////////////////////////////////////////////////////////////////////
306
307 /***** fill in %CPU; not in libproc because of include_dead_children */
308 /* Note: for sorting, not display, so 0..0x7fffffff would be OK */
309 static int want_this_proc_pcpu(proc_t *buf){
310   unsigned long long used_jiffies;
311   unsigned long pcpu = 0;
312   unsigned long long avail_jiffies;
313
314   if(!want_this_proc(buf)) return 0;
315
316   used_jiffies = buf->utime + buf->stime;
317   if(include_dead_children) used_jiffies += (buf->cutime + buf->cstime);
318
319   avail_jiffies = seconds_since_boot * Hertz - buf->start_time;
320   if(avail_jiffies) pcpu = (used_jiffies << 24) / avail_jiffies;
321
322   buf->pcpu = pcpu;  // fits in an int, summing children on 128 CPUs
323
324   return 1;
325 }
326
327 /***** just display */
328 static void simple_spew(void){
329   proc_t buf;
330   PROCTAB* ptp;
331   ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
332   if(!ptp) {
333     fprintf(stderr, "Error: can not access /proc.\n");
334     exit(1);
335   }
336   memset(&buf, '#', sizeof(proc_t));
337   switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){
338   case TF_show_proc:                   // normal non-thread output
339     while(readproc(ptp,&buf)){
340       if(want_this_proc(&buf)){
341         show_one_proc(&buf, proc_format_list);
342       }
343       if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
344       if(buf.environ) free((void*)*buf.environ); // ought to reuse
345     }
346     break;
347   case TF_show_proc|TF_loose_tasks:    // H option
348     while(readproc(ptp,&buf)){
349       proc_t buf2;
350       // must still have the process allocated
351       while(readtask(ptp,&buf,&buf2)){
352         if(!want_this_proc(&buf)) continue;
353         show_one_proc(&buf2, task_format_list);
354       }
355       if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
356       if(buf.environ) free((void*)*buf.environ); // ought to reuse
357     }
358     break;
359   case TF_show_proc|TF_show_task:      // m and -m options
360     while(readproc(ptp,&buf)){
361       if(want_this_proc(&buf)){
362         proc_t buf2;
363         show_one_proc(&buf, proc_format_list);
364         // must still have the process allocated
365         while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
366       }
367       if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
368       if(buf.environ) free((void*)*buf.environ); // ought to reuse
369     }
370     break;
371   case TF_show_task:                   // -L and -T options
372     while(readproc(ptp,&buf)){
373       if(want_this_proc(&buf)){
374         proc_t buf2;
375         // must still have the process allocated
376         while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
377       }
378       if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
379       if(buf.environ) free((void*)*buf.environ); // ought to reuse
380     }
381     break;
382   }
383   closeproc(ptp);
384 }
385
386 /***** forest output requires sorting by ppid; add start_time by default */
387 static void prep_forest_sort(void){
388   sort_node *tmp_list = sort_list;
389   const format_struct *incoming;
390
391   if(!sort_list) {     /* assume start time order */
392     incoming = search_format_array("start_time");
393     if(!incoming) fprintf(stderr, "Could not find start_time!\n");
394     tmp_list = malloc(sizeof(sort_node));
395     tmp_list->reverse = 0;
396     tmp_list->typecode = '?'; /* what was this for? */
397     tmp_list->sr = incoming->sr;
398     tmp_list->need = incoming->need;
399     tmp_list->next = sort_list;
400     sort_list = tmp_list;
401   }
402   /* this is required for the forest option */
403   incoming = search_format_array("ppid");
404   if(!incoming) fprintf(stderr, "Could not find ppid!\n");
405   tmp_list = malloc(sizeof(sort_node));
406   tmp_list->reverse = 0;
407   tmp_list->typecode = '?'; /* what was this for? */
408   tmp_list->sr = incoming->sr;
409   tmp_list->need = incoming->need;
410   tmp_list->next = sort_list;
411   sort_list = tmp_list;
412 }
413
414 /* we rely on the POSIX requirement for zeroed memory */
415 //static proc_t *processes[98*1024];  // FIXME
416 static proc_t **processes;
417
418 /***** compare function for qsort */
419 static int compare_two_procs(const void *a, const void *b){
420   sort_node *tmp_list = sort_list;
421   while(tmp_list){
422     int result;
423     result = (*tmp_list->sr)(*(const proc_t *const*)a, *(const proc_t *const*)b);
424     if(result) return (tmp_list->reverse) ? -result : result;
425     tmp_list = tmp_list->next;
426   }
427   return 0; /* no conclusion */
428 }
429
430 /***** show pre-sorted array of process pointers */
431 static void show_proc_array(PROCTAB *restrict ptp, int n){
432   proc_t **p = processes;
433   while(n--){
434     if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list);
435     if(thread_flags & TF_show_task){
436       proc_t buf2;
437       // must still have the process allocated
438       while(readtask(ptp,*p,&buf2)) show_one_proc(&buf2, task_format_list);
439       // must not attempt to free cmdline and environ
440     }
441     /* no point freeing any of this -- won't need more mem */
442 //    if((*p)->cmdline) free((void*)*(*p)->cmdline);
443 //    if((*p)->environ) free((void*)*(*p)->environ);
444 //    memset(*p, '%', sizeof(proc_t)); /* debug */
445 //    free(*p);
446     p++;
447   }
448 }
449
450 /***** show tree */
451 /* this needs some optimization work */
452 #define ADOPTED(x) 1
453 static void show_tree(const int self, const int n, const int level, const int have_sibling){
454   int i = 0;
455   if(level){
456     /* add prefix of "+" or "L" */
457     if(have_sibling) forest_prefix[level-1] = '+';
458     else             forest_prefix[level-1] = 'L';
459     forest_prefix[level] = '\0';
460   }
461   show_one_proc(processes[self],format_list);  /* first show self */
462   /* no point freeing any of this -- won't need more mem */
463 //  if(processes[self]->cmdline) free((void*)*processes[self]->cmdline);
464 //  if(processes[self]->environ) free((void*)*processes[self]->environ);
465   for(;;){  /* look for children */
466     if(i >= n) return; /* no children */
467     if(processes[i]->ppid == processes[self]->XXXID) break;
468     i++;
469   }
470   if(level){
471     /* change our prefix to "|" or " " for the children */
472     if(have_sibling) forest_prefix[level-1] = '|';
473     else             forest_prefix[level-1] = ' ';
474     forest_prefix[level] = '\0';
475   }
476   for(;;){
477     int self_pid;
478     int more_children = 1;
479     if(i >= n) break; /* over the edge */
480     self_pid=processes[self]->XXXID;
481     if(i+1 >= n)
482       more_children = 0;
483     else
484       if(processes[i+1]->ppid != self_pid) more_children = 0;
485     if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u')
486       show_tree(i++, n, level,   more_children);
487     else
488       show_tree(i++, n, level+1, more_children);
489     if(!more_children) break;
490   }
491   /* chop prefix that children added -- do we need this? */
492   forest_prefix[level] = '\0';
493 //  memset(processes[self], '$', sizeof(proc_t));  /* debug */
494 }
495
496 /***** show forest */
497 static void show_forest(const int n){
498   int i = n;
499   int j;
500   while(i--){   /* cover whole array looking for trees */
501     j = n;
502     while(j--){   /* search for parent: if none, i is a tree! */
503       if(processes[j]->XXXID == processes[i]->ppid) goto not_root;
504     }
505     show_tree(i,n,0,0);
506 not_root:
507     ;
508   }
509   /* don't free the array because it takes time and ps will exit anyway */
510 }
511
512 static int want_this_proc_nop(proc_t *dummy){
513   (void)dummy;
514   return 1;
515 }
516
517 /***** sorted or forest */
518 static void fancy_spew(void){
519   proc_data_t *pd = NULL;
520   PROCTAB *restrict ptp;
521   int n = 0;  /* number of processes & index into array */
522
523   ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
524   if(!ptp) {
525     fprintf(stderr, "Error: can not access /proc.\n");
526     exit(1);
527   }
528
529   if(thread_flags & TF_loose_tasks){
530     pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp);
531   }else{
532     pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp);
533   }
534   n = pd->n;
535   processes = pd->tab;
536
537   if(!n) return;  /* no processes */
538   if(forest_type) prep_forest_sort();
539   qsort(processes, n, sizeof(proc_t*), compare_two_procs);
540   if(forest_type) show_forest(n);
541   else show_proc_array(ptp,n);
542   closeproc(ptp);
543 }
544
545
546 /***** no comment */
547 int main(int argc, char *argv[]){
548 #if (__GNU_LIBRARY__ >= 6)
549   setlocale (LC_CTYPE, "");
550 #endif
551
552 #ifdef DEBUG
553   init_stack_trace(argv[0]);
554 #else
555   do {
556     struct sigaction sa;
557     int i = 32;
558     memset(&sa, 0, sizeof(sa));
559     sa.sa_handler = signal_handler;
560     sigfillset(&sa.sa_mask);
561     while(i--) switch(i){
562     default:
563       sigaction(i,&sa,NULL);
564     case 0:
565     case SIGINT:   /* ^C */
566     case SIGTSTP:  /* ^Z */
567     case SIGTTOU:  /* see stty(1) man page */
568     case SIGQUIT:  /* ^\ */
569     case SIGPROF:  /* profiling */
570     case SIGKILL:  /* can not catch */
571     case SIGSTOP:  /* can not catch */
572     case SIGWINCH: /* don't care if window size changes */
573       ;
574     }
575   } while (0);
576 #endif
577
578   reset_global();  /* must be before parser */
579   arg_parse(argc,argv);
580
581 /*  arg_show(); */
582   trace("screen is %ux%u\n",screen_cols,screen_rows);
583 /*  printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */
584   trace("======= ps output follows =======\n");
585
586   init_output(); /* must be between parser and output */
587
588   lists_and_needs();
589
590   if(forest_type || sort_list) fancy_spew(); /* sort or forest */
591   else simple_spew(); /* no sort, no forest */
592   show_one_proc((proc_t *)-1,format_list); /* no output yet? */
593   return 0;
594 }