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.
17 #if (__GNU_LIBRARY__ >= 6)
21 /* username lookups */
22 #include <sys/types.h>
26 /* major/minor number */
27 #include <sys/sysmacros.h>
29 #include <signal.h> /* catch signals */
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"
39 #define SIGCHLD SIGCLD
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 */
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",
51 signal_number_to_name(signo),
57 /////////////////////////////////////////////////////////////////////////////////////
60 void init_stack_trace(char *prog_name);
64 void hex_dump(void *vp){
68 char *cp = (char *)vp;
71 printf("%8lx ", (unsigned long)cp);
75 if((charlist[i]>31) && (charlist[i]<127)){
76 printf("%c", charlist[i]);
82 for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i])));
88 static void show_tgid(char *s, int n, sel_union *data){
91 printf("%d,", data[n].tgid);
93 printf("%d\n", data[0].tgid);
96 static void show_uid(char *s, int n, sel_union *data){
97 struct passwd *pw_data;
100 pw_data = getpwuid(data[n].uid);
101 if(pw_data) printf("%s,", pw_data->pw_name);
102 else printf("%d,", data[n].uid);
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);
109 static void show_gid(char *s, int n, sel_union *data){
110 struct group *gr_data;
113 gr_data = getgrgid(data[n].gid);
114 if(gr_data) printf("%s,", gr_data->gr_name);
115 else printf("%d,", data[n].gid);
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);
122 static void show_tty(char *s, int n, sel_union *data){
125 printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty));
127 printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty));
130 static void show_cmd(char *s, int n, sel_union *data){
133 printf("%.8s,", data[n].cmd);
135 printf("%.8s\n", data[0].cmd);
138 static void arg_show(void){
139 selection_node *walk = selection_list;
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");
163 //////////////////////////////////////////////////////////////////////////
166 /***** check the header */
167 /* Unix98: must not print empty header */
168 static void check_headers(void){
169 format_node *walk = format_list;
171 if(header_type==HEAD_MULTI){
172 header_gap = screen_rows-1; /* true BSD */
175 if(header_type==HEAD_NONE){
176 lines_to_next_header = -1; /* old Linux */
191 if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */
194 /***** check sort needs */
195 /* see what files need to be read, etc. */
196 static unsigned check_sort_needs(sort_node *walk){
205 /***** check needs */
206 /* see what files need to be read, etc. */
207 static unsigned collect_format_needs(format_node *walk){
216 static format_node *proc_format_list;
217 static format_node *task_format_list;
219 static unsigned needs_for_threads;
220 static unsigned needs_for_sort;
221 static unsigned proc_format_needs;
222 static unsigned task_format_needs;
224 #define needs_for_format (proc_format_needs|task_format_needs)
226 #define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM)
228 /***** munge lists and determine openproc() flags */
229 static void lists_and_needs(void){
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;
239 format_node *new = malloc(sizeof(format_node));
240 memcpy(new,walk,sizeof(format_node));
245 switch(walk->flags & CF_PRINT_MASK){
246 case CF_PRINT_THREAD_ONLY:
250 case CF_PRINT_PROCESS_ONLY:
255 fprintf(stderr, "please report this bug\n");
257 case CF_PRINT_AS_NEEDED:
258 case CF_PRINT_EVERY_TIME:
265 proc_format_list = pfn.next;
266 task_format_list = tfn.next;
268 proc_format_list = format_list;
269 task_format_list = format_list;
272 proc_format_needs = collect_format_needs(proc_format_list);
273 task_format_needs = collect_format_needs(task_format_list);
275 needs_for_sort = check_sort_needs(sort_list);
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;
282 proc_format_needs &= ~PROC_FILLARG;
283 needs_for_sort &= ~PROC_FILLARG;
286 proc_format_needs &= ~PROC_FILLCOM;
287 needs_for_sort &= ~PROC_FILLCOM;
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;
295 if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV;
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;
302 if(thread_flags&TF_loose_tasks) needs_for_threads |= PROC_LOOSE_TASKS;
305 //////////////////////////////////////////////////////////////////////////
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;
314 if(!want_this_proc(buf)) return 0;
316 used_jiffies = buf->utime + buf->stime;
317 if(include_dead_children) used_jiffies += (buf->cutime + buf->cstime);
319 avail_jiffies = seconds_since_boot * Hertz - buf->start_time;
320 if(avail_jiffies) pcpu = (used_jiffies << 24) / avail_jiffies;
322 buf->pcpu = pcpu; // fits in an int, summing children on 128 CPUs
327 /***** just display */
328 static void simple_spew(void){
331 ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
333 fprintf(stderr, "Error: can not access /proc.\n");
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);
343 if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
344 if(buf.environ) free((void*)*buf.environ); // ought to reuse
347 case TF_show_proc|TF_loose_tasks: // H option
348 while(readproc(ptp,&buf)){
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);
355 if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
356 if(buf.environ) free((void*)*buf.environ); // ought to reuse
359 case TF_show_proc|TF_show_task: // m and -m options
360 while(readproc(ptp,&buf)){
361 if(want_this_proc(&buf)){
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);
367 if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
368 if(buf.environ) free((void*)*buf.environ); // ought to reuse
371 case TF_show_task: // -L and -T options
372 while(readproc(ptp,&buf)){
373 if(want_this_proc(&buf)){
375 // must still have the process allocated
376 while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
378 if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
379 if(buf.environ) free((void*)*buf.environ); // ought to reuse
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;
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;
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;
414 /* we rely on the POSIX requirement for zeroed memory */
415 //static proc_t *processes[98*1024]; // FIXME
416 static proc_t **processes;
418 /***** compare function for qsort */
419 static int compare_two_procs(const void *a, const void *b){
420 sort_node *tmp_list = sort_list;
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;
427 return 0; /* no conclusion */
430 /***** show pre-sorted array of process pointers */
431 static void show_proc_array(PROCTAB *restrict ptp, int n){
432 proc_t **p = processes;
434 if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list);
435 if(thread_flags & TF_show_task){
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
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 */
451 /* this needs some optimization work */
453 static void show_tree(const int self, const int n, const int level, const int have_sibling){
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';
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;
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';
478 int more_children = 1;
479 if(i >= n) break; /* over the edge */
480 self_pid=processes[self]->XXXID;
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);
488 show_tree(i++, n, level+1, more_children);
489 if(!more_children) break;
491 /* chop prefix that children added -- do we need this? */
492 forest_prefix[level] = '\0';
493 // memset(processes[self], '$', sizeof(proc_t)); /* debug */
496 /***** show forest */
497 static void show_forest(const int n){
500 while(i--){ /* cover whole array looking for trees */
502 while(j--){ /* search for parent: if none, i is a tree! */
503 if(processes[j]->XXXID == processes[i]->ppid) goto not_root;
509 /* don't free the array because it takes time and ps will exit anyway */
512 static int want_this_proc_nop(proc_t *dummy){
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 */
523 ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
525 fprintf(stderr, "Error: can not access /proc.\n");
529 if(thread_flags & TF_loose_tasks){
530 pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp);
532 pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp);
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);
547 int main(int argc, char *argv[]){
548 #if (__GNU_LIBRARY__ >= 6)
549 setlocale (LC_CTYPE, "");
553 init_stack_trace(argv[0]);
558 memset(&sa, 0, sizeof(sa));
559 sa.sa_handler = signal_handler;
560 sigfillset(&sa.sa_mask);
561 while(i--) switch(i){
563 sigaction(i,&sa,NULL);
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 */
578 reset_global(); /* must be before parser */
579 arg_parse(argc,argv);
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");
586 init_output(); /* must be between parser and output */
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? */