diff options
Diffstat (limited to 'src/ps/display.c')
-rw-r--r-- | src/ps/display.c | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/src/ps/display.c b/src/ps/display.c new file mode 100644 index 0000000..e1a3b0d --- /dev/null +++ b/src/ps/display.c @@ -0,0 +1,681 @@ +/* + * display.c - display ps output + * + * Copyright © 2011-2023 Jim Warner <james.warner@comcast.net + * Copyright © 2004-2023 Craig Small <csmall@dropbear.xyz> + * Copyright © 2012-2014 Jaromir Capik <jcapik@redhat.com + * Copyright © 1998-2003 Albert Cahalan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <grp.h> +#include <locale.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sys/sysmacros.h> +#include <sys/types.h> + +#include "c.h" +#include "fileutils.h" +#include "signals.h" +#include "xalloc.h" + +#include "common.h" + +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif + +#define SIG_IS_TERM_OR_HUP(signo) (((signo) == SIGTERM) || (signo) == SIGHUP) + +char *myname; +long Hertz; + +/* just reports a crash */ +static void signal_handler(int signo){ + sigset_t ss; + + sigfillset(&ss); + sigprocmask(SIG_BLOCK, &ss, NULL); + if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */ + /* fprintf() is not reentrant, but we _exit() anyway */ + if (!SIG_IS_TERM_OR_HUP(signo)) { + fprintf(stderr, + _("Signal %d (%s) caught by %s (%s).\n"), + signo, + signal_number_to_name(signo), + myname, + PACKAGE_VERSION + ); + } + switch (signo) { + case SIGHUP: + case SIGUSR1: + case SIGUSR2: + exit(EXIT_FAILURE); + default: + if (!SIG_IS_TERM_OR_HUP(signo)) + error_at_line(0, 0, __FILE__, __LINE__, "%s", _("please report this bug")); + signal(signo, SIG_DFL); /* allow core file creation */ + sigemptyset(&ss); + sigaddset(&ss, signo); + sigprocmask(SIG_UNBLOCK, &ss, NULL); + kill(getpid(), signo); + _exit(EXIT_FAILURE); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +#undef DEBUG +#ifdef DEBUG +void init_stack_trace(char *prog_name); + +#include <ctype.h> + +void hex_dump(void *vp){ + char *charlist; + int i = 0; + int line = 45; + char *cp = (char *)vp; + + while(line--){ + printf("%8lx ", (unsigned long)cp); + charlist = cp; + cp += 16; + for(i=0; i<16; i++){ + if((charlist[i]>31) && (charlist[i]<127)){ + printf("%c", charlist[i]); + }else{ + printf("."); + } + } + printf(" "); + for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i]))); + printf("\n"); + i=0; + } +} + +static void show_tgid(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d,", data[n].tgid); + } + printf("%d\n", data[0].tgid); +} + +static void show_uid(char *s, int n, sel_union *data){ + struct passwd *pw_data; + printf("%s ", s); + while(--n){ + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s,", pw_data->pw_name); + else printf("%d,", data[n].uid); + } + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s\n", pw_data->pw_name); + else printf("%d\n", data[n].uid); +} + +static void show_gid(char *s, int n, sel_union *data){ + struct group *gr_data; + printf("%s ", s); + while(--n){ + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s,", gr_data->gr_name); + else printf("%d,", data[n].gid); + } + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s\n", gr_data->gr_name); + else printf("%d\n", data[n].gid); +} + +static void show_tty(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty)); + } + printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty)); +} + +static void show_cmd(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%.8s,", data[n].cmd); + } + printf("%.8s\n", data[0].cmd); +} + +static void arg_show(void){ + selection_node *walk = selection_list; + while(walk){ + switch(walk->typecode){ + case SEL_RUID: show_uid("RUID", walk->n, walk->u); break; + case SEL_EUID: show_uid("EUID", walk->n, walk->u); break; + case SEL_SUID: show_uid("SUID", walk->n, walk->u); break; + case SEL_FUID: show_uid("FUID", walk->n, walk->u); break; + case SEL_RGID: show_gid("RGID", walk->n, walk->u); break; + case SEL_EGID: show_gid("EGID", walk->n, walk->u); break; + case SEL_SGID: show_gid("SGID", walk->n, walk->u); break; + case SEL_FGID: show_gid("FGID", walk->n, walk->u); break; + case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break; + case SEL_PID : show_pid("PID ", walk->n, walk->u); break; + case SEL_PID_QUICK : show_pid("PID_QUICK ", walk->n, walk->u); break; + case SEL_PPID: show_pid("PPID", walk->n, walk->u); break; + case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break; + case SEL_SESS: show_pid("SESS", walk->n, walk->u); break; + case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break; + default: printf("Garbage typecode value!\n"); + } + walk = walk->next; + } +} + +#endif +////////////////////////////////////////////////////////////////////////// + + +/***** check the header */ +/* Unix98: must not print empty header */ +static void check_headers(void){ + format_node *walk = format_list; + int head_normal = 0; + if(header_type==HEAD_MULTI){ + header_gap = screen_rows-1; /* true BSD */ + return; + } + if(header_type==HEAD_NONE){ + lines_to_next_header = -1; /* old Linux */ + return; + } + while(walk){ + if(!*(walk->name)){ + walk = walk->next; + continue; + } + if(walk->pr){ + head_normal++; + walk = walk->next; + continue; + } + walk = walk->next; + } + if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */ +} + +static format_node *proc_format_list; +static format_node *task_format_list; + + +/***** munge lists and determine final needs */ +static void lists_and_needs(void){ + check_headers(); + + // only care about the difference when showing both + if(thread_flags & TF_show_both){ + format_node pfn, tfn; // junk, to handle special case at begin of list + format_node *walk = format_list; + format_node *p_end = &pfn; + format_node *t_end = &tfn; + while(walk){ + format_node *new = xmalloc(sizeof(format_node)); + memcpy(new,walk,sizeof(format_node)); + p_end->next = walk; + t_end->next = new; + p_end = walk; + t_end = new; + switch(walk->flags & CF_PRINT_MASK){ + case CF_PRINT_THREAD_ONLY: + p_end->pr = pr_nop; + break; + case CF_PRINT_PROCESS_ONLY: + t_end->pr = pr_nop; + break; + default: + catastrophic_failure(__FILE__, __LINE__, _("please report this bug")); + // FALL THROUGH + case CF_PRINT_AS_NEEDED: + case CF_PRINT_EVERY_TIME: + break; + } + walk = walk->next; + } + t_end->next = NULL; + p_end->next = NULL; + proc_format_list = pfn.next; + task_format_list = tfn.next; + }else{ + proc_format_list = format_list; + task_format_list = format_list; + } +} + +////////////////////////////////////////////////////////////////////////// + +/***** fill in %CPU; not in libproc because of include_dead_children */ +/* Note: for sorting, not display, so 0..0x7fffffff would be OK */ +static void value_this_proc_pcpu(proc_t *buf){ + unsigned long long used_jiffies; + unsigned long pcpu = 0; + unsigned long long seconds; + + if(want_this_proc(buf)) { + + if(include_dead_children) used_jiffies = rSv(TICS_ALL_C, ull_int, buf); + else used_jiffies = rSv(TICS_ALL, ull_int, buf); + + seconds = rSv(TIME_ELAPSED, real, buf); + if(seconds) pcpu = (used_jiffies * 1000ULL / Hertz) / seconds; + + // if xtra-procps-debug.h active, can't use PIDS_VAL as base due to assignment + buf->head[rel_extra].result.ul_int = pcpu; + } +} + +/***** just display */ +static void simple_spew(void){ + struct pids_fetch *pidread; + proc_t *buf; + int i; + + // -q option (only single SEL_PID_QUICK typecode entry expected in the list, if present) + if (selection_list && selection_list->typecode == SEL_PID_QUICK) { + unsigned *pidlist = xcalloc(selection_list->n, sizeof(unsigned)); + enum pids_select_type which; + for (i = 0; i < selection_list->n; i++) + pidlist[i] = selection_list->u[selection_list->n-i-1].pid; + which = (thread_flags & (TF_loose_tasks|TF_show_task)) + ? PIDS_SELECT_PID_THREADS : PIDS_SELECT_PID; + pidread = procps_pids_select(Pids_info, pidlist, selection_list->n, which); + free(pidlist); + } else { + enum pids_fetch_type which; + which = (thread_flags & (TF_loose_tasks|TF_show_task)) + ? PIDS_FETCH_THREADS_TOO : PIDS_FETCH_TASKS_ONLY; + pidread = procps_pids_reap(Pids_info, which); + } + if (!pidread) { + fprintf(stderr, _("fatal library error, reap\n")); + exit(EXIT_FAILURE); + } + + switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){ + case TF_show_proc: // normal non-thread output + for (i = 0; i < pidread->counts->total; i++) { + buf = pidread->stacks[i]; + if (want_this_proc(buf)) + show_one_proc(buf, proc_format_list); + } + break; + case TF_show_task: // -L and -T options + case TF_show_proc|TF_loose_tasks: // H option + for (i = 0; i < pidread->counts->total; i++) { + buf = pidread->stacks[i]; + if (want_this_proc(buf)) + show_one_proc(buf, task_format_list); + } + break; + case TF_show_proc|TF_show_task: // m and -m options + procps_pids_sort(Pids_info, pidread->stacks + , pidread->counts->total, PIDS_TICS_BEGAN, PIDS_SORT_ASCEND); + procps_pids_sort(Pids_info, pidread->stacks + , pidread->counts->total, PIDS_ID_TGID, PIDS_SORT_ASCEND); + for (i = 0; i < pidread->counts->total; i++) { + buf = pidread->stacks[i]; +next_proc: + if (want_this_proc(buf)) { + int self = rSv(ID_PID, s_int, buf); + show_one_proc(buf, proc_format_list); + for (; i < pidread->counts->total; i++) { + buf = pidread->stacks[i]; + if (rSv(ID_TGID, s_int, buf) != self) goto next_proc; + show_one_proc(buf, task_format_list); + } + } + } + break; + } +} + + +/***** forest output requires sorting by ppid; add start_time by default */ +static void prep_forest_sort(void){ + sort_node *tmp_list = sort_list; + const format_struct *incoming; + + if(!sort_list) { /* assume start time order */ + incoming = search_format_array("ppid"); + if(!incoming) { fprintf(stderr, _("could not find ppid\n")); exit(1); } + tmp_list = xmalloc(sizeof(sort_node)); + tmp_list->reverse = PIDS_SORT_ASCEND; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->next = sort_list; + sort_list = tmp_list; + } + /* this is required for the forest option */ + incoming = search_format_array("start_time"); + if(!incoming) { fprintf(stderr, _("could not find start_time\n")); exit(1); } + tmp_list = xmalloc(sizeof(sort_node)); + tmp_list->reverse = PIDS_SORT_ASCEND; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->next = sort_list; + sort_list = tmp_list; +} + +/* we rely on the POSIX requirement for zeroed memory */ +static proc_t **processes; + +/***** show pre-sorted array of process pointers */ +static void show_proc_array(int n){ + proc_t **p = processes; + while(n--){ + show_one_proc(*p, proc_format_list); + p++; + } +} + +/***** show tree */ +/* this needs some optimization work */ +#define ADOPTED(x) 1 + +#define IS_LEVEL_SAFE(level) \ + ((level) >= 0 && (size_t)(level) < sizeof(forest_prefix)) + +static void show_tree(const int self, const int n, const int level, const int have_sibling){ + int i = 0; + + if(!IS_LEVEL_SAFE(level)) + catastrophic_failure(__FILE__, __LINE__, _("please report this bug")); + + if(level){ + /* add prefix of "+" or "L" */ + if(have_sibling) forest_prefix[level-1] = '+'; + else forest_prefix[level-1] = 'L'; + } + forest_prefix[level] = '\0'; + show_one_proc(processes[self],format_list); /* first show self */ + for(;;){ /* look for children */ + if(i >= n) return; /* no children */ + if(rSv(ID_PPID, s_int, processes[i]) == rSv(ID_PID, s_int, processes[self])) break; + i++; + } + if(level){ + /* change our prefix to "|" or " " for the children */ + if(have_sibling) forest_prefix[level-1] = '|'; + else forest_prefix[level-1] = ' '; + } + forest_prefix[level] = '\0'; + for(;;){ + int self_pid; + int more_children = 1; + if(i >= n) break; /* over the edge */ + self_pid=rSv(ID_PID, s_int, processes[self]); + if(i+1 >= n) + more_children = 0; + else + if(rSv(ID_PPID, s_int, processes[i+1]) != self_pid) more_children = 0; + if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u') + show_tree(i++, n, level, more_children); + else + show_tree(i++, n, IS_LEVEL_SAFE(level+1) ? level+1 : level, more_children); + if(!more_children) break; + } + /* chop prefix that children added -- do we need this? */ + /* chop prefix that children added */ + forest_prefix[level] = '\0'; +} + +#undef IS_LEVEL_SAFE + +/***** show forest */ +static void show_forest(const int n){ + int i = n; + int j; + while(i--){ /* cover whole array looking for trees */ + j = n; + while(j--){ /* search for parent: if none, i is a tree! */ + if(rSv(ID_PID, s_int, processes[j]) == rSv(ID_PPID, s_int, processes[i])) goto not_root; + } + show_tree(i,n,0,0); +not_root: + ; + } + /* don't free the array because it takes time and ps will exit anyway */ +} + +#if 0 +static int want_this_proc_nop(proc_t *dummy){ + (void)dummy; + return 1; +} +#endif + +/***** sorted or forest */ +static void fancy_spew(void){ + struct pids_fetch *pidread; + enum pids_fetch_type which; + proc_t *buf; + int i, n = 0; + + which = (thread_flags & TF_loose_tasks) + ? PIDS_FETCH_THREADS_TOO : PIDS_FETCH_TASKS_ONLY; + + pidread = procps_pids_reap(Pids_info, which); + if (!pidread || !pidread->counts->total) { + fprintf(stderr, _("fatal library error, reap\n")); + exit(EXIT_FAILURE); + } + processes = xcalloc(pidread->counts->total, sizeof(void*)); + for (i = 0; i < pidread->counts->total; i++) { + buf = pidread->stacks[i]; + value_this_proc_pcpu(buf); + if (want_this_proc(buf)) + processes[n++] = buf; + } + if (n) { + if(forest_type) prep_forest_sort(); + while(sort_list) { + procps_pids_sort(Pids_info, processes, n, sort_list->sr, sort_list->reverse); + sort_list = sort_list->next; + } + if(forest_type) show_forest(n); + else show_proc_array(n); + } + free(processes); +} + +static void arg_check_conflicts(void) +{ + int selection_list_len; + int has_quick_pid; + selection_node *walk = selection_list; + has_quick_pid = 0; + selection_list_len = 0; + + while (walk) { + if (walk->typecode == SEL_PID_QUICK) has_quick_pid++; + walk = walk->next; + selection_list_len++; + } + + /* -q doesn't allow multiple occurrences */ + if (has_quick_pid > 1) { + fprintf(stderr, "q/-q/--quick-pid can only be used once.\n"); + exit(1); + } + + /* -q doesn't allow combinations with other selection switches */ + if (has_quick_pid && selection_list_len > has_quick_pid) { + fprintf(stderr, "q/-q/--quick-pid cannot be combined with other selection options.\n"); + exit(1); + } + + /* -q cannot be used with forest type listings */ + if (has_quick_pid && forest_type) { + fprintf(stderr, "q/-q/--quick-pid cannot be used together with forest type listings.\n"); + exit(1); + } + + /* -q cannot be used with sort */ + if (has_quick_pid && sort_list) { + fprintf(stderr, "q/-q,--quick-pid cannot be used together with sort options.\n"); + exit(1); + } + + /* -q cannot be used with -N */ + if (has_quick_pid && negate_selection) { + fprintf(stderr, "q/-q/--quick-pid cannot be used together with negation switches.\n"); + exit(1); + } + +} + +static void finalize_stacks (void) +{ + format_node *f_node; + sort_node *s_node; + +#if (PIDSITEMS < 60) + # error PIDSITEMS (common.h) should be at least 60! +#endif + + /* first, ensure minimum result structures for items + which may or may not actually be displayable ... */ + Pids_index = 0; + + // needed by for selections + chkREL(CMD) + chkREL(ID_EGID) + chkREL(ID_EUID) + chkREL(ID_FGID) + chkREL(ID_FUID) + chkREL(ID_PID) + chkREL(ID_PPID) + chkREL(ID_RGID) + chkREL(ID_RUID) + chkREL(ID_SESSION) + chkREL(ID_SGID) + chkREL(ID_SUID) + chkREL(ID_TGID) + chkREL(STATE) + chkREL(TTY) + // needed to creata an enhanced 'stat/state' + chkREL(ID_PGRP) + chkREL(ID_TPGID) + chkREL(NICE) + chkREL(NLWP) + chkREL(RSS) + chkREL(VM_RSS_LOCKED) + // needed with 's' switch, previously assured + chkREL(SIGBLOCKED) + chkREL(SIGCATCH) + chkREL(SIGIGNORE) + chkREL(SIGNALS) + chkREL(SIGPENDING) + // needed with loss of defunct 'cook_time' macros + chkREL(TICS_ALL) + chkREL(TICS_ALL_C) + chkREL(TIME_ALL) + chkREL(TIME_ELAPSED) + chkREL(TICS_BEGAN) + // special items with 'extra' used as former pcpu + chkREL(extra) + chkREL(noop) + + // now accommodate any results not yet satisfied + f_node = format_list; + while (f_node) { + if (*f_node->pr) (*f_node->pr)(NULL, NULL); + f_node = f_node->next; + } + s_node = sort_list; + while (s_node) { + if (s_node->xe) (*s_node->xe)(NULL, NULL); + s_node = s_node->next; + } + + procps_pids_reset(Pids_info, Pids_items, Pids_index); +} + +/***** no comment */ +int main(int argc, char *argv[]){ + atexit(close_stdout); + myname = strrchr(*argv, '/'); + if (myname) ++myname; else myname = *argv; + Hertz = procps_hertz_get(); + + setlocale (LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + setenv("TZ", ":/etc/localtime", 0); + +#ifdef DEBUG + init_stack_trace(argv[0]); +#else + do { + struct sigaction sa; + int i = 32; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigfillset(&sa.sa_mask); + while(i--) switch(i){ + default: + sigaction(i,&sa,NULL); + case 0: + case SIGCONT: + case SIGINT: /* ^C */ + case SIGTSTP: /* ^Z */ + case SIGTTOU: /* see stty(1) man page */ + case SIGQUIT: /* ^\ */ + case SIGPROF: /* profiling */ + case SIGKILL: /* can not catch */ + case SIGSTOP: /* can not catch */ + case SIGWINCH: /* don't care if window size changes */ + case SIGURG: /* Urgent condition on socket (4.2BSD) */ + ; + } + } while (0); +#endif + + reset_global(); /* must be before parser */ + arg_parse(argc,argv); + + /* check for invalid combination of arguments */ + arg_check_conflicts(); + +/* arg_show(); */ + trace("screen is %ux%u\n",screen_cols,screen_rows); +/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */ + trace("======= ps output follows =======\n"); + + init_output(); /* must be between parser and output */ + + finalize_stacks(); + lists_and_needs(); + + if(forest_type || sort_list) fancy_spew(); /* sort or forest */ + else simple_spew(); /* no sort, no forest */ + show_one_proc((proc_t *)-1,format_list); /* no output yet? */ + + procps_pids_unref(&Pids_info); + return 0; +} |