procps/ps/display.c
liutie e9445a07cf signal: fix suspending ps when receving SIGTERM or SIGHUP
Call trace:
  #0 __lll_lock_wait_private () at
     ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:95
  #1 0x00007f95c059f9d7 in _L_lock_638 () from /lib64/libc.so.6
  #2 0x00007f95c059f8b6 in _nl_expand_alias
  #3 0x00007f95c059dad8 in _nl_find_domain
  #4 0x00007f95c059d22e in __dcigettext
  #5 0x00007f95c059c05f in __GI___dcgettext
  #6  0x00000000004032b3 in signal_handler (signo=15) at display.c:54
  #7  <signal handler called>
  #8  __memcpy_sse2 () at ../sysdeps/x86_64/memcpy.S:104
  #9  0x00007f95c05d9934 in __GI__IO_getline_info
  #10 0x00007f95c05d99b8 in __GI__IO_getline
  #11 0x00007f95c05e2a5d in __GI_fgets_unlocked
  #12 0x00007f95c059f478 in read_alias_file
  #13 0x00007f95c059f97a in _nl_expand_alias
  #14 0x00007f95c059dad8 in _nl_find_domain
  #15 0x00007f95c059d22e in __dcigettext
  #16 0x00007f95c059c05f in __GI___dcgettext
  #17 0x0000000000403a8d in reset_global () at global.c:410
  #18 0x0000000000402605 in main at display.c:650

The above call trace happens when the ps process is suspending, and the
signal SIGTERM is sent to the ps process at the same time.
Just cancel the SIGTERM and SIGHUP handler when suspending to prevent
the problem.

Signed-off-by: liutie <liutie4@huawei.com>
Signed-off-by: fu.lin <fulin10@huawei.com>
2022-04-26 20:24:42 +10:00

678 lines
19 KiB
C

/*
* display.c - display ps output
* Copyright 1998-2003 by 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 "../include/c.h"
#include "../include/fileutils.h"
#include "../include/signals.h"
#include "../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 */
lists_and_needs();
finalize_stacks();
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;
}