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