procps/ps/global.c
2014-08-28 13:54:24 +02:00

528 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;
const char *namelist_file = (const char *)0xdeadbeef;
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;
namelist_file = NULL;
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)"
);
display_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);
open_psdb(namelist_file);
fprintf(stderr,"namelist_file=\"%s\"\n",namelist_file?namelist_file:"<no System.map file>");
}
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);
}