caeebdc71f
This patch was made necessary by those library changes in support of recently revised/simplified wchan logic. In addition, this commit eliminates a broken alternate 'namelist' provision which was intended to allow users to specify a System.map file to be used in translating addresses into function names. But, the real effect of the now defunct 'N' and '-n' options was to indirectly force addresses (not names) to be displayed since such user named map files could not be successfully parsed. Besides when the required FRAME_POINTER kconfig option is absent there is no address to translate and when it is present /proc/PID/wchan is already translated. Thus an alternate mapping is unnecessary and inappropriate. [ we'll forgive POSIX for documenting '-n namelist' ] Reference(s): http://www.freelists.org/post/procps/WCHAN,11 Signed-off-by: Jim Warner <james.warner@comcast.net>
523 lines
15 KiB
C
523 lines
15 KiB
C
/*
|
|
* global.c - generic ps symbols and functions
|
|
* Copyright 1998-2002 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 <fcntl.h>
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "../proc/wchan.h"
|
|
#include "../proc/version.h"
|
|
#include "../proc/sysinfo.h"
|
|
|
|
#include "../include/c.h"
|
|
#include "common.h"
|
|
|
|
#ifndef __GNU_LIBRARY__
|
|
#define __GNU_LIBRARY__ -1
|
|
#endif
|
|
#ifndef __GLIBC__
|
|
#define __GLIBC__ -1
|
|
#endif
|
|
#ifndef __GLIBC_MINOR__
|
|
#define __GLIBC_MINOR__ -1
|
|
#endif
|
|
|
|
|
|
static const char * saved_personality_text = "You found a bug!";
|
|
|
|
int all_processes = -1;
|
|
const char *bsd_j_format = (const char *)0xdeadbeef;
|
|
const char *bsd_l_format = (const char *)0xdeadbeef;
|
|
const char *bsd_s_format = (const char *)0xdeadbeef;
|
|
const char *bsd_u_format = (const char *)0xdeadbeef;
|
|
const char *bsd_v_format = (const char *)0xdeadbeef;
|
|
int bsd_c_option = -1;
|
|
int bsd_e_option = -1;
|
|
uid_t cached_euid = -1;
|
|
dev_t cached_tty = -1;
|
|
char forest_prefix[4 * 32*1024 + 100]; // FIXME
|
|
int forest_type = -1;
|
|
unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */
|
|
format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */
|
|
unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */
|
|
int header_gap = -1;
|
|
int header_type = -1;
|
|
int include_dead_children = -1;
|
|
int lines_to_next_header = -1;
|
|
int negate_selection = -1;
|
|
int running_only = -1;
|
|
int page_size = -1; // "int" for math reasons?
|
|
unsigned personality = 0xffffffff;
|
|
int prefer_bsd_defaults = -1;
|
|
int screen_cols = -1;
|
|
int screen_rows = -1;
|
|
time_t seconds_since_boot = -1;
|
|
selection_node *selection_list = (selection_node *)0xdeadbeef;
|
|
unsigned simple_select = 0xffffffff;
|
|
sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
|
|
const char *sysv_f_format = (const char *)0xdeadbeef;
|
|
const char *sysv_fl_format = (const char *)0xdeadbeef;
|
|
const char *sysv_j_format = (const char *)0xdeadbeef;
|
|
const char *sysv_l_format = (const char *)0xdeadbeef;
|
|
unsigned thread_flags = 0xffffffff;
|
|
int unix_f_option = -1;
|
|
int user_is_number = -1;
|
|
int wchan_is_number = -1;
|
|
const char *the_word_help;
|
|
|
|
static void reset_selection_list(void){
|
|
selection_node *old;
|
|
selection_node *walk = selection_list;
|
|
if(selection_list == (selection_node *)0xdeadbeef){
|
|
selection_list = NULL;
|
|
return;
|
|
}
|
|
while(walk){
|
|
old = walk;
|
|
walk = old->next;
|
|
free(old->u);
|
|
free(old);
|
|
}
|
|
selection_list = NULL;
|
|
}
|
|
|
|
// The rules:
|
|
// 1. Defaults are implementation-specific. (ioctl,termcap,guess)
|
|
// 2. COLUMNS and LINES override the defaults. (standards compliance)
|
|
// 3. Command line options override everything else.
|
|
// 4. Actual output may be more if the above is too narrow.
|
|
//
|
|
// SysV tends to spew semi-wide output in all cases. The args
|
|
// will be limited to 64 or 80 characters, without regard to
|
|
// screen size. So lines of 120 to 160 chars are normal.
|
|
// Tough luck if you want more or less than that! HP-UX has a
|
|
// new "-x" option for 1024-char args in place of comm that
|
|
// we'll implement at some point.
|
|
//
|
|
// BSD tends to make a good effort, then fall back to 80 cols.
|
|
// Use "ww" to get infinity. This is nicer for "ps | less"
|
|
// and "watch ps". It can run faster too.
|
|
static void set_screen_size(void){
|
|
struct winsize ws;
|
|
char *columns; /* Unix98 environment variable */
|
|
char *lines; /* Unix98 environment variable */
|
|
|
|
do{
|
|
int fd;
|
|
if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
|
|
if(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
|
|
if(ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
|
|
fd = open("/dev/tty", O_NOCTTY|O_NONBLOCK|O_RDONLY);
|
|
if(fd != -1){
|
|
int ret = ioctl(fd, TIOCGWINSZ, &ws);
|
|
close(fd);
|
|
if(ret != -1 && ws.ws_col>0 && ws.ws_row>0) break;
|
|
}
|
|
// TODO: ought to do tgetnum("co") and tgetnum("li") here
|
|
ws.ws_col = 80;
|
|
ws.ws_row = 24;
|
|
}while(0);
|
|
screen_cols = ws.ws_col; // hmmm, NetBSD subtracts 1
|
|
screen_rows = ws.ws_row;
|
|
|
|
// TODO: delete this line
|
|
if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE;
|
|
|
|
columns = getenv("COLUMNS");
|
|
if(columns && *columns){
|
|
long t;
|
|
char *endptr;
|
|
t = strtol(columns, &endptr, 0);
|
|
if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t;
|
|
}
|
|
|
|
lines = getenv("LINES");
|
|
if(lines && *lines){
|
|
long t;
|
|
char *endptr;
|
|
t = strtol(lines, &endptr, 0);
|
|
if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t;
|
|
}
|
|
|
|
if((screen_cols<9) || (screen_rows<2))
|
|
fprintf(stderr,_("your %dx%d screen size is bogus. expect trouble\n"),
|
|
screen_cols, screen_rows
|
|
);
|
|
}
|
|
|
|
/**************** personality control **************/
|
|
|
|
typedef struct personality_table_struct {
|
|
const char *name; /* personality name */
|
|
const void *jump; /* See gcc extension info. :-) */
|
|
} personality_table_struct;
|
|
|
|
static int compare_personality_table_structs(const void *a, const void *b){
|
|
return strcasecmp(((const personality_table_struct*)a)->name,((const personality_table_struct*)b)->name);
|
|
}
|
|
|
|
static const char *set_personality(void){
|
|
const char *s;
|
|
size_t sl;
|
|
char buf[16];
|
|
personality_table_struct findme = { buf, NULL};
|
|
personality_table_struct *found;
|
|
static const personality_table_struct personality_table[] = {
|
|
{"390", &&case_390},
|
|
{"aix", &&case_aix},
|
|
{"bsd", &&case_bsd},
|
|
{"compaq", &&case_compaq},
|
|
{"debian", &&case_debian},
|
|
{"default", &&case_default},
|
|
{"digital", &&case_digital},
|
|
{"gnu", &&case_gnu},
|
|
{"hp", &&case_hp},
|
|
{"hpux", &&case_hpux},
|
|
{"irix", &&case_irix},
|
|
{"linux", &&case_linux},
|
|
{"old", &&case_old},
|
|
{"os390", &&case_os390},
|
|
{"posix", &&case_posix},
|
|
{"s390", &&case_s390},
|
|
{"sco", &&case_sco},
|
|
{"sgi", &&case_sgi},
|
|
{"solaris2", &&case_solaris2},
|
|
{"sunos4", &&case_sunos4},
|
|
{"svr4", &&case_svr4},
|
|
{"sysv", &&case_sysv},
|
|
{"tru64", &&case_tru64},
|
|
{"unix", &&case_unix},
|
|
{"unix95", &&case_unix95},
|
|
{"unix98", &&case_unix98},
|
|
{"unknown", &&case_unknown}
|
|
};
|
|
const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct);
|
|
|
|
personality = 0;
|
|
prefer_bsd_defaults = 0;
|
|
|
|
bsd_j_format = "OL_j";
|
|
bsd_l_format = "OL_l";
|
|
bsd_s_format = "OL_s";
|
|
bsd_u_format = "OL_u";
|
|
bsd_v_format = "OL_v";
|
|
|
|
/* When these are NULL, the code does SysV output modifier logic */
|
|
sysv_f_format = NULL;
|
|
sysv_fl_format = NULL;
|
|
sysv_j_format = NULL;
|
|
sysv_l_format = NULL;
|
|
|
|
s = getenv("PS_PERSONALITY");
|
|
if(!s || !*s) s = getenv("CMD_ENV");
|
|
if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */
|
|
if(getenv("I_WANT_A_BROKEN_PS")) s="old";
|
|
sl = strlen(s);
|
|
if(sl > 15) return _("environment specified an unknown personality");
|
|
strncpy(buf, s, sl);
|
|
buf[sl] = '\0';
|
|
if ((saved_personality_text = strdup(buf))==NULL) {
|
|
fprintf(stderr, _("cannot strdup() personality text\n"));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
found = bsearch(&findme, personality_table, personality_table_count,
|
|
sizeof(personality_table_struct), compare_personality_table_structs
|
|
);
|
|
|
|
if(!found) return _("environment specified an unknown personality");
|
|
|
|
goto *(found->jump); /* See gcc extension info. :-) */
|
|
|
|
case_bsd:
|
|
personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m;
|
|
prefer_bsd_defaults = 1;
|
|
bsd_j_format = "FB_j";
|
|
bsd_l_format = "FB_l";
|
|
/* bsd_s_format not used */
|
|
bsd_u_format = "FB_u";
|
|
bsd_v_format = "FB_v";
|
|
return NULL;
|
|
|
|
case_old:
|
|
personality = PER_FORCE_BSD | PER_OLD_m;
|
|
prefer_bsd_defaults = 1;
|
|
return NULL;
|
|
|
|
case_debian: /* Toss this? They don't seem to care much. */
|
|
case_gnu:
|
|
personality = PER_GOOD_o | PER_OLD_m;
|
|
prefer_bsd_defaults = 1;
|
|
sysv_f_format = "RD_f";
|
|
/* sysv_fl_format = "RD_fl"; */ /* old Debian ps can't do this! */
|
|
sysv_j_format = "RD_j";
|
|
sysv_l_format = "RD_l";
|
|
return NULL;
|
|
|
|
case_linux:
|
|
personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER;
|
|
return NULL;
|
|
|
|
case_default: /* use defaults for ps, ignoring other environment variables */
|
|
return NULL;
|
|
|
|
case_unknown: /* defaults, but also check inferior environment variables */
|
|
if(
|
|
getenv("UNIX95") /* Irix */
|
|
|| getenv("POSIXLY_CORRECT") /* most gnu stuff */
|
|
|| (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */
|
|
) personality = PER_BROKEN_o;
|
|
return NULL;
|
|
|
|
case_aix:
|
|
bsd_j_format = "FB_j";
|
|
bsd_l_format = "FB_l";
|
|
/* bsd_s_format not used */
|
|
bsd_u_format = "FB_u";
|
|
bsd_v_format = "FB_v";
|
|
return NULL;
|
|
|
|
case_tru64:
|
|
case_compaq:
|
|
case_digital:
|
|
// no PER_NO_DEFAULT_g even though man page claims it
|
|
// Reality: the g is a NOP
|
|
personality = PER_GOOD_o | PER_BSD_h;
|
|
prefer_bsd_defaults = 1;
|
|
sysv_f_format = "F5FMT";
|
|
sysv_fl_format = "FL5FMT";
|
|
sysv_j_format = "JFMT";
|
|
sysv_l_format = "L5FMT";
|
|
bsd_j_format = "JFMT";
|
|
bsd_l_format = "LFMT";
|
|
bsd_s_format = "SFMT";
|
|
bsd_u_format = "UFMT";
|
|
bsd_v_format = "VFMT";
|
|
return NULL;
|
|
|
|
case_sunos4:
|
|
personality = PER_NO_DEFAULT_g;
|
|
prefer_bsd_defaults = 1;
|
|
bsd_j_format = "FB_j";
|
|
bsd_l_format = "FB_l";
|
|
/* bsd_s_format not used */
|
|
bsd_u_format = "FB_u";
|
|
bsd_v_format = "FB_v";
|
|
return NULL;
|
|
|
|
case_irix:
|
|
case_sgi:
|
|
s = getenv("_XPG");
|
|
if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o;
|
|
else personality = PER_IRIX_l;
|
|
return NULL;
|
|
|
|
case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */
|
|
case_s390:
|
|
case_390:
|
|
sysv_j_format = "J390"; /* don't know what -jl and -jf do */
|
|
return NULL;
|
|
|
|
case_hp:
|
|
case_hpux:
|
|
personality = PER_BROKEN_o | PER_HPUX_x;
|
|
return NULL;
|
|
|
|
case_svr4:
|
|
case_sysv:
|
|
case_sco:
|
|
personality = PER_BROKEN_o | PER_SVR4_x;
|
|
return NULL;
|
|
|
|
case_posix:
|
|
case_solaris2:
|
|
case_unix95:
|
|
case_unix98:
|
|
case_unix:
|
|
personality = PER_BROKEN_o;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/************ Call this to reinitialize everything ***************/
|
|
void reset_global(void){
|
|
static proc_t p;
|
|
reset_selection_list();
|
|
look_up_our_self(&p);
|
|
set_screen_size();
|
|
set_personality();
|
|
|
|
all_processes = 0;
|
|
bsd_c_option = 0;
|
|
bsd_e_option = 0;
|
|
cached_euid = geteuid();
|
|
cached_tty = p.tty;
|
|
/* forest_prefix must be all zero because of POSIX */
|
|
forest_type = 0;
|
|
format_flags = 0; /* -l -f l u s -j... */
|
|
format_list = NULL; /* digested formatting options */
|
|
format_modifiers = 0; /* -c -j -y -P -L... */
|
|
header_gap = -1; /* send lines_to_next_header to -infinity */
|
|
header_type = HEAD_SINGLE;
|
|
include_dead_children = 0;
|
|
lines_to_next_header = 1;
|
|
negate_selection = 0;
|
|
page_size = getpagesize();
|
|
running_only = 0;
|
|
seconds_since_boot = uptime(0,0);
|
|
selection_list = NULL;
|
|
simple_select = 0;
|
|
sort_list = NULL;
|
|
thread_flags = 0;
|
|
unix_f_option = 0;
|
|
user_is_number = 0;
|
|
wchan_is_number = 0;
|
|
/* Translation Note:
|
|
. The following translatable word will be used to recognize the
|
|
. user's request for help text. In other words, the translation
|
|
. you provide will alter program behavior.
|
|
.
|
|
. It must be limited to 15 characters or less.
|
|
*/
|
|
the_word_help = _("help");
|
|
}
|
|
|
|
static const char archdefs[] =
|
|
#ifdef __alpha__
|
|
" alpha"
|
|
#endif
|
|
#ifdef __arm__
|
|
" arm"
|
|
#endif
|
|
#ifdef __hppa__
|
|
" hppa"
|
|
#endif
|
|
#ifdef __i386__
|
|
" i386"
|
|
#endif
|
|
#ifdef __ia64__
|
|
" ia64"
|
|
#endif
|
|
#ifdef __mc68000__
|
|
" mc68000"
|
|
#endif
|
|
#ifdef __mips64__
|
|
" mips64"
|
|
#endif
|
|
#ifdef __mips__
|
|
" mips"
|
|
#endif
|
|
#ifdef __powerpc__
|
|
" powerpc"
|
|
#endif
|
|
#ifdef __sh3__
|
|
" sh3"
|
|
#endif
|
|
#ifdef __sh__
|
|
" sh"
|
|
#endif
|
|
#ifdef __sparc__
|
|
" sparc"
|
|
#endif
|
|
#ifdef __sparc_v9__
|
|
" sparc_v9"
|
|
#endif
|
|
#ifdef __x86_64__
|
|
" x86_64"
|
|
#endif
|
|
"";
|
|
|
|
/*********** spew variables ***********/
|
|
void self_info(void){
|
|
fprintf(stderr,
|
|
"BSD j %s\n"
|
|
"BSD l %s\n"
|
|
"BSD s %s\n"
|
|
"BSD u %s\n"
|
|
"BSD v %s\n"
|
|
"SysV -f %s\n"
|
|
"SysV -fl %s\n"
|
|
"SysV -j %s\n"
|
|
"SysV -l %s\n"
|
|
"\n",
|
|
bsd_j_format ? bsd_j_format : "(none)",
|
|
bsd_l_format ? bsd_l_format : "(none)",
|
|
bsd_s_format ? bsd_s_format : "(none)",
|
|
bsd_u_format ? bsd_u_format : "(none)",
|
|
bsd_v_format ? bsd_v_format : "(none)",
|
|
sysv_f_format ? sysv_f_format : "(none)",
|
|
sysv_fl_format ? sysv_fl_format : "(none)",
|
|
sysv_j_format ? sysv_j_format : "(none)",
|
|
sysv_l_format ? sysv_l_format : "(none)"
|
|
);
|
|
|
|
fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
|
|
fprintf(stderr, "Linux version %d.%d.%d\n",
|
|
LINUX_VERSION_MAJOR(linux_version_code),
|
|
LINUX_VERSION_MINOR(linux_version_code),
|
|
LINUX_VERSION_PATCH(linux_version_code)
|
|
);
|
|
/* __libc_print_version(); */ /* how can we get the run-time version? */
|
|
fprintf(stderr, "Compiled with: glibc %d.%d, gcc %d.%d\n\n",
|
|
__GLIBC__, __GLIBC_MINOR__, __GNUC__, __GNUC_MINOR__
|
|
);
|
|
|
|
fprintf(stderr,
|
|
"header_gap=%d lines_to_next_header=%d\n"
|
|
"screen_cols=%d screen_rows=%d\n"
|
|
"\n",
|
|
header_gap, lines_to_next_header,
|
|
screen_cols, screen_rows
|
|
);
|
|
|
|
fprintf(stderr,
|
|
"personality=0x%08x (from \"%s\")\n"
|
|
"EUID=%d TTY=%d,%d Hertz=%lld page_size=%d\n",
|
|
personality, saved_personality_text,
|
|
cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz,
|
|
(int)(page_size)
|
|
);
|
|
|
|
fprintf(stderr,
|
|
"sizeof(proc_t)=%d sizeof(long)=%d sizeof(KLONG)=%d\n",
|
|
(int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(KLONG)
|
|
);
|
|
|
|
fprintf(stderr, "archdefs:%s\n", archdefs);
|
|
}
|
|
|
|
void __attribute__ ((__noreturn__))
|
|
catastrophic_failure(const char *filename,
|
|
unsigned int linenum,
|
|
const char *message)
|
|
{
|
|
error_at_line(0, 0, filename, linenum, "%s", message);
|
|
exit(EXIT_FAILURE);
|
|
}
|