misc: Move all binaries to src

*.c -> src/
ps/* src/ps/
top/* src/top/

Signed-off-by: Craig Small <csmall@dropbear.xyz>
This commit is contained in:
Craig Small
2022-08-29 18:29:28 +10:00
parent 8e889ae682
commit dd60d6d6e8
35 changed files with 117 additions and 117 deletions

460
src/free.c Normal file
View File

@@ -0,0 +1,460 @@
/*
* free.c - free(1)
* procps-ng utility to display free memory information
*
* Copyright (C) 1992-2012
*
* Mostly new, Sami Kerola <kerolasa@iki.fi> 15 Apr 2011
* All new, Robert Love <rml@tech9.net> 18 Nov 2002
* Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992
*
* Copyright 2003 Robert Love
* Copyright 2004 Albert Cahalan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <locale.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wchar.h>
#include "config.h"
#include "c.h"
#include "nls.h"
#include "strutils.h"
#include "fileutils.h"
#include <proc/meminfo.h>
#ifndef SIZE_MAX
#define SIZE_MAX 32
#endif
#define FREE_HUMANREADABLE (1 << 1)
#define FREE_LOHI (1 << 2)
#define FREE_WIDE (1 << 3)
#define FREE_TOTAL (1 << 4)
#define FREE_SI (1 << 5)
#define FREE_REPEAT (1 << 6)
#define FREE_REPEATCOUNT (1 << 7)
#define FREE_COMMITTED (1 << 8)
struct commandline_arguments {
int exponent; /* demanded in kilos, magas... */
float repeat_interval; /* delay in seconds */
int repeat_counter; /* number of repeats */
};
/* function prototypes */
static void usage(FILE * out);
double power(unsigned int base, unsigned int expo);
static const char *scale_size(unsigned long size, int flags, struct commandline_arguments args);
static void __attribute__ ((__noreturn__))
usage(FILE * out)
{
fputs(USAGE_HEADER, out);
fprintf(out,
_(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" -b, --bytes show output in bytes\n"), out);
fputs(_(" --kilo show output in kilobytes\n"), out);
fputs(_(" --mega show output in megabytes\n"), out);
fputs(_(" --giga show output in gigabytes\n"), out);
fputs(_(" --tera show output in terabytes\n"), out);
fputs(_(" --peta show output in petabytes\n"), out);
fputs(_(" -k, --kibi show output in kibibytes\n"), out);
fputs(_(" -m, --mebi show output in mebibytes\n"), out);
fputs(_(" -g, --gibi show output in gibibytes\n"), out);
fputs(_(" --tebi show output in tebibytes\n"), out);
fputs(_(" --pebi show output in pebibytes\n"), out);
fputs(_(" -h, --human show human-readable output\n"), out);
fputs(_(" --si use powers of 1000 not 1024\n"), out);
fputs(_(" -l, --lohi show detailed low and high memory statistics\n"), out);
fputs(_(" -t, --total show total for RAM + swap\n"), out);
fputs(_(" -v, --committed show committed memory and commit limit\n"), out);
fputs(_(" -s N, --seconds N repeat printing every N seconds\n"), out);
fputs(_(" -c N, --count N repeat printing N times, then exit\n"), out);
fputs(_(" -w, --wide wide output\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_(" --help display this help and exit\n"), out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("free(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
double power(unsigned int base, unsigned int expo)
{
return (expo == 0) ? 1 : base * power(base, expo - 1);
}
/* idea of this function is copied from top size scaling */
static const char *scale_size(unsigned long size, int flags, struct commandline_arguments args)
{
static char up[] = { 'B', 'K', 'M', 'G', 'T', 'P', 0 };
static char buf[BUFSIZ];
int i;
float base;
long long bytes;
base = (flags & FREE_SI) ? 1000.0 : 1024.0;
bytes = size * 1024LL;
if (!(flags & FREE_HUMANREADABLE)) {
switch (args.exponent) {
case 0:
/* default output */
snprintf(buf, sizeof(buf), "%ld", size);
return buf;
case 1:
/* in bytes, which can not be in SI */
snprintf(buf, sizeof(buf), "%lld", bytes);
return buf;
default:
/* In desired scale. */
snprintf(buf, sizeof(buf), "%ld",
(long)(bytes / power(base, args.exponent-1)));
return buf;
}
}
/* human readable output */
if (4 >= snprintf(buf, sizeof(buf), "%lld%c", bytes, up[0]))
return buf;
for (i = 1; up[i] != 0; i++) {
if (flags & FREE_SI) {
if (4 >= snprintf(buf, sizeof(buf), "%.1f%c",
(float)(bytes / power(base, i)), up[i]))
return buf;
if (4 >= snprintf(buf, sizeof(buf), "%ld%c",
(long)(bytes / power(base, i)), up[i]))
return buf;
} else {
if (5 >= snprintf(buf, sizeof(buf), "%.1f%ci",
(float)(bytes / power(base, i)), up[i]))
return buf;
if (5 >= snprintf(buf, sizeof(buf), "%ld%ci",
(long)(bytes / power(base, i)), up[i]))
return buf;
}
}
/*
* On system where there is more than exbibyte of memory or swap the
* output does not fit to column. For incoming few years this should
* not be a big problem (wrote at Apr, 2015).
*/
return buf;
}
static void check_unit_set(int *unit_set)
{
if (*unit_set)
xerrx(EXIT_FAILURE,
_("Multiple unit options don't make sense."));
*unit_set = 1;
}
/*
* Print the header columns.
* We cannot simply use the second printf because the length of the
* translated strings doesn't work with it. Instead we need to find
* the wide length of the string and use that.
* This method also removes the messy wprintf/printf buffering issues
*/
#define HC_WIDTH 9
static void print_head_col(const char *str)
{
int len;
int spaces = 9;
wchar_t wstr[BUFSIZ];
len = mbstowcs(wstr, str, BUFSIZ);
if (len < 0)
spaces = 9;
else if (len < HC_WIDTH) {
int width;
if ( (width = wcswidth(wstr, 99)) > 0)
spaces = HC_WIDTH - width;
else
spaces = HC_WIDTH - len;
} else
spaces = 0;
printf("%s%.*s", str, spaces, " ");
}
int main(int argc, char **argv)
{
int c, flags = 0, unit_set = 0, rc = 0;
struct commandline_arguments args;
struct meminfo_info *mem_info = NULL;
/*
* For long options that have no equivalent short option, use a
* non-character as a pseudo short option, starting with CHAR_MAX + 1.
*/
enum {
SI_OPTION = CHAR_MAX + 1,
KILO_OPTION,
MEGA_OPTION,
GIGA_OPTION,
TERA_OPTION,
PETA_OPTION,
TEBI_OPTION,
PEBI_OPTION,
HELP_OPTION
};
static const struct option longopts[] = {
{ "bytes", no_argument, NULL, 'b' },
{ "kilo", no_argument, NULL, KILO_OPTION },
{ "mega", no_argument, NULL, MEGA_OPTION },
{ "giga", no_argument, NULL, GIGA_OPTION },
{ "tera", no_argument, NULL, TERA_OPTION },
{ "peta", no_argument, NULL, PETA_OPTION },
{ "kibi", no_argument, NULL, 'k' },
{ "mebi", no_argument, NULL, 'm' },
{ "gibi", no_argument, NULL, 'g' },
{ "tebi", no_argument, NULL, TEBI_OPTION },
{ "pebi", no_argument, NULL, PEBI_OPTION },
{ "human", no_argument, NULL, 'h' },
{ "si", no_argument, NULL, SI_OPTION },
{ "lohi", no_argument, NULL, 'l' },
{ "total", no_argument, NULL, 't' },
{ "committed", no_argument, NULL, 'v' },
{ "seconds", required_argument, NULL, 's' },
{ "count", required_argument, NULL, 'c' },
{ "wide", no_argument, NULL, 'w' },
{ "help", no_argument, NULL, HELP_OPTION },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
/* defaults */
args.exponent = 0;
args.repeat_interval = 1000000;
args.repeat_counter = 0;
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
while ((c = getopt_long(argc, argv, "bkmghltvc:ws:V", longopts, NULL)) != -1)
switch (c) {
case 'b':
check_unit_set(&unit_set);
args.exponent = 1;
break;
case 'k':
check_unit_set(&unit_set);
args.exponent = 2;
break;
case 'm':
check_unit_set(&unit_set);
args.exponent = 3;
break;
case 'g':
check_unit_set(&unit_set);
args.exponent = 4;
break;
case TEBI_OPTION:
check_unit_set(&unit_set);
args.exponent = 5;
break;
case PEBI_OPTION:
check_unit_set(&unit_set);
args.exponent = 6;
break;
case KILO_OPTION:
check_unit_set(&unit_set);
args.exponent = 2;
flags |= FREE_SI;
break;
case MEGA_OPTION:
check_unit_set(&unit_set);
args.exponent = 3;
flags |= FREE_SI;
break;
case GIGA_OPTION:
check_unit_set(&unit_set);
args.exponent = 4;
flags |= FREE_SI;
break;
case TERA_OPTION:
check_unit_set(&unit_set);
args.exponent = 5;
flags |= FREE_SI;
break;
case PETA_OPTION:
check_unit_set(&unit_set);
args.exponent = 6;
flags |= FREE_SI;
break;
case 'h':
flags |= FREE_HUMANREADABLE;
break;
case SI_OPTION:
flags |= FREE_SI;
break;
case 'l':
flags |= FREE_LOHI;
break;
case 't':
flags |= FREE_TOTAL;
break;
case 'v':
flags |= FREE_COMMITTED;
break;
case 's':
flags |= FREE_REPEAT;
errno = 0;
args.repeat_interval = (1000000 * strtod_nol_or_err(optarg, "seconds argument failed"));
if (args.repeat_interval < 1)
xerrx(EXIT_FAILURE,
_("seconds argument `%s' is not positive number"), optarg);
break;
case 'c':
flags |= FREE_REPEAT;
flags |= FREE_REPEATCOUNT;
args.repeat_counter = strtol_or_err(optarg,
_("failed to parse count argument"));
if (args.repeat_counter < 1)
error(EXIT_FAILURE, ERANGE,
_("failed to parse count argument: '%s'"), optarg);
break;
case 'w':
flags |= FREE_WIDE;
break;
case HELP_OPTION:
usage(stdout);
case 'V':
printf(PROCPS_NG_VERSION);
exit(EXIT_SUCCESS);
default:
usage(stderr);
}
if (optind != argc)
usage(stderr);
if ( (rc = procps_meminfo_new(&mem_info)) < 0)
{
if (rc == -ENOENT)
xerrx(EXIT_FAILURE,
_("Memory information file /proc/meminfo does not exist"));
else
xerrx(EXIT_FAILURE,
_("Unable to create meminfo structure"));
}
do {
/* Translation Hint: You can use 9 character words in
* the header, and the words need to be right align to
* beginning of a number. */
if (flags & FREE_WIDE) {
printf(_(" total used free shared buffers cache available"));
} else {
printf(_(" total used free shared buff/cache available"));
}
printf("\n");
print_head_col(_("Mem:"));
printf("%11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_TOTAL, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_USED, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_FREE, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_SHARED, ul_int), flags, args));
if (flags & FREE_WIDE) {
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_BUFFERS, ul_int),
flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_CACHED_ALL, ul_int)
, flags, args));
} else {
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_BUFFERS, ul_int) +
MEMINFO_GET(mem_info, MEMINFO_MEM_CACHED_ALL, ul_int), flags, args));
}
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_AVAILABLE, ul_int), flags, args));
printf("\n");
/*
* Print low vs. high information, if the user requested it.
* Note we check if low_total == 0: if so, then this kernel
* does not export the low and high stats. Note we still want
* to print the high info, even if it is zero.
*/
if (flags & FREE_LOHI) {
print_head_col(_("Low:"));
printf("%11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_LOW_TOTAL, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_LOW_USED, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_LOW_FREE, ul_int), flags, args));
printf("\n");
print_head_col( _("High:"));
printf("%11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_HIGH_TOTAL, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_HIGH_USED, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_HIGH_FREE, ul_int), flags, args));
printf("\n");
}
print_head_col(_("Swap:"));
printf("%11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_SWAP_TOTAL, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_SWAP_USED, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_SWAP_FREE, ul_int), flags, args));
printf("\n");
if (flags & FREE_TOTAL) {
print_head_col(_("Total:"));
printf("%11s", scale_size(
MEMINFO_GET(mem_info, MEMINFO_MEM_TOTAL, ul_int) +
MEMINFO_GET(mem_info, MEMINFO_SWAP_TOTAL, ul_int), flags, args));
printf(" %11s", scale_size(
MEMINFO_GET(mem_info, MEMINFO_MEM_USED, ul_int) +
MEMINFO_GET(mem_info, MEMINFO_SWAP_USED, ul_int), flags, args));
printf(" %11s", scale_size(
MEMINFO_GET(mem_info, MEMINFO_MEM_FREE, ul_int) +
MEMINFO_GET(mem_info, MEMINFO_SWAP_FREE, ul_int), flags, args));
printf("\n");
}
if (flags & FREE_COMMITTED) {
print_head_col(_("Comm:"));
printf("%11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_COMMIT_LIMIT, ul_int), flags, args));
printf(" %11s", scale_size(MEMINFO_GET(mem_info, MEMINFO_MEM_COMMITTED_AS, ul_int), flags, args));
printf(" %11s", scale_size(
MEMINFO_GET(mem_info, MEMINFO_MEM_COMMIT_LIMIT, ul_int) -
MEMINFO_GET(mem_info, MEMINFO_MEM_COMMITTED_AS, ul_int), flags, args));
printf("\n");
}
fflush(stdout);
if (flags & FREE_REPEATCOUNT) {
args.repeat_counter--;
if (args.repeat_counter < 1)
exit(EXIT_SUCCESS);
}
if (flags & FREE_REPEAT) {
printf("\n");
usleep(args.repeat_interval);
}
} while ((flags & FREE_REPEAT));
exit(EXIT_SUCCESS);
}

165
src/kill.c Normal file
View File

@@ -0,0 +1,165 @@
/*
* kill.c - send a signal to process
* 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 <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <signal.h>
#include <ctype.h>
#include "c.h"
#include "signals.h"
#include "strutils.h"
#include "nls.h"
/* kill help */
static void __attribute__ ((__noreturn__)) print_usage(FILE * out)
{
fputs(USAGE_HEADER, out);
fprintf(out,
_(" %s [options] <pid> [...]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" <pid> [...] send signal to every <pid> listed\n"), out);
fputs(_(" -<signal>, -s, --signal <signal>\n"
" specify the <signal> to be sent\n"), out);
fputs(_(" -q, --queue <value> integer value to be sent with the signal\n"), out);
fputs(_(" -l, --list=[<signal>] list all signal names, or convert one to a name\n"), out);
fputs(_(" -L, --table list all signal names in a nice table\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("kill(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
inline static int execute_kill(pid_t pid, int sig_num, const bool use_sigqueue, union sigval sigval)
{
if (use_sigqueue)
return sigqueue(pid, sig_num, sigval);
else
return kill(pid, sig_num);
}
int main(int argc, char **argv)
{
int signo, i;
long pid;
int exitvalue = EXIT_SUCCESS;
int optindex;
union sigval sigval;
bool use_sigqueue = false;
char *sig_option;
static const struct option longopts[] = {
{"list", optional_argument, NULL, 'l'},
{"table", no_argument, NULL, 'L'},
{"signal", required_argument, NULL, 's'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"queue", required_argument, NULL, 'q'},
{NULL, 0, NULL, 0}
};
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc < 2)
print_usage(stderr);
signo = skill_sig_option(&argc, argv);
if (signo < 0)
signo = SIGTERM;
opterr=0; /* suppress errors on -123 */
while ((i = getopt_long(argc, argv, "l::Ls:hVq:", longopts, &optindex)) != -1)
switch (i) {
case 'l':
sig_option = NULL;
if (optarg) {
sig_option = optarg;
} else if (argv[optind] != NULL && argv[optind][0] != '-') {
sig_option = argv[optind];
}
if (sig_option) {
char *s;
s = strtosig(sig_option);
if (s)
printf("%s\n", s);
else
xwarnx(_("unknown signal name %s"),
sig_option);
free(s);
} else {
unix_print_signals();
}
exit(EXIT_SUCCESS);
case 'L':
pretty_print_signals();
exit(EXIT_SUCCESS);
case 's':
signo = signal_name_to_number(optarg);
break;
case 'h':
print_usage(stdout);
case 'V':
fprintf(stdout, PROCPS_NG_VERSION);
exit(EXIT_SUCCESS);
case 'q':
sigval.sival_int = strtol_or_err(optarg, _("must be an integer value to be passed with the signal."));
use_sigqueue = true;
break;
case '?':
if (!isdigit(optopt)) {
xwarnx(_("invalid argument %c"), optopt);
print_usage(stderr);
} else {
/* Special case for signal digit negative
* PIDs */
pid = (long)('0' - optopt);
if (!execute_kill((pid_t) pid, signo, use_sigqueue, sigval))
exitvalue = EXIT_FAILURE;
exit(exitvalue);
}
xerrx(EXIT_FAILURE, _("internal error"));
default:
print_usage(stderr);
}
argc -= optind;
argv += optind;
if (argc < 1)
print_usage(stderr);
for (i = 0; i < argc; i++) {
pid = strtol_or_err(argv[i], _("failed to parse argument"));
if (!execute_kill((pid_t) pid, signo, use_sigqueue, sigval))
continue;
error(0, errno, "(%ld)", pid);
exitvalue = EXIT_FAILURE;
continue;
}
return exitvalue;
}

1095
src/pgrep.c Normal file

File diff suppressed because it is too large Load Diff

409
src/pidof.c Normal file
View File

@@ -0,0 +1,409 @@
/*
* pidof.c - Utility for listing pids of running processes
*
* Copyright (C) 2013 Jaromir Capik <jcapik@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <limits.h>
#include <sys/types.h>
#include "c.h"
#include "fileutils.h"
#include "nls.h"
#include "xalloc.h"
#include <proc/pids.h>
#define grow_size(x) do { \
if ((x) < 0 || (size_t)(x) >= INT_MAX / 5 / sizeof(struct el)) \
xerrx(EXIT_FAILURE, _("integer overflow")); \
(x) = (x) * 5 / 4 + 1024; \
} while (0)
#define safe_free(x) if (x) { free(x); x=NULL; }
struct el {
pid_t pid;
};
struct el *procs = NULL;
static int proc_count = 0;
struct el *omitted_procs = NULL;
static int omit_count = 0;
static char *program = NULL;
/* switch flags */
static int opt_single_shot = 0; /* -s */
static int opt_scripts_too = 0; /* -x */
static int opt_rootdir_check = 0; /* -c */
static int opt_with_workers = 0; /* -w */
static int opt_quiet = 0; /* -q */
static char *pidof_root = NULL;
static int __attribute__ ((__noreturn__)) usage(int opt)
{
int err = (opt == '?');
FILE *fp = err ? stderr : stdout;
fputs(USAGE_HEADER, fp);
fprintf(fp, _(" %s [options] [program [...]]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, fp);
fputs(_(" -s, --single-shot return one PID only\n"), fp);
fputs(_(" -c, --check-root omit processes with different root\n"), fp);
fputs(_(" -q, quiet mode, only set the exit code\n"), fp);
fputs(_(" -w, --with-workers show kernel workers too\n"), fp);
fputs(_(" -x also find shells running the named scripts\n"), fp);
fputs(_(" -o, --omit-pid <PID,...> omit processes with PID\n"), fp);
fputs(_(" -S, --separator SEP use SEP as separator put between PIDs"), fp);
fputs(USAGE_SEPARATOR, fp);
fputs(USAGE_HELP, fp);
fputs(USAGE_VERSION, fp);
fprintf(fp, USAGE_MAN_TAIL("pidof(1)"));
exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
static int is_omitted (pid_t pid)
{
int i;
for (i = 0; i < omit_count; i++) {
if (pid == omitted_procs[i].pid) return 1;
}
return 0;
}
static char *get_basename (char *filename)
{
char *pos;
char *result;
pos = result = filename;
while (*pos != '\0') {
if (*(pos++) == '/') result = pos;
}
return result;
}
static char *pid_link (pid_t pid, const char *base_name)
{
char link [1000];
char *result;
ssize_t path_alloc_size;
ssize_t len;
snprintf(link, sizeof(link), "/proc/%d/%s", pid, base_name);
len = path_alloc_size = 0;
result = NULL;
do {
grow_size(path_alloc_size);
result = xrealloc(result, path_alloc_size);
if ((len = readlink(link, result, path_alloc_size)) < 0) {
len = 0;
break;
}
} while (len == path_alloc_size);
result[len] = '\0';
return result;
}
static void select_procs (void)
{
enum pids_item items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_CMDLINE_V };
enum rel_items { rel_pid, rel_cmd, rel_cmdline };
struct pids_info *info = NULL;
struct pids_stack *stack;
int match;
static int size = 0;
char *cmd_arg0, *cmd_arg0base;
char *cmd_arg1, *cmd_arg1base;
char *program_base;
char *root_link;
char *exe_link;
char *exe_link_base;
/* get the input base name */
program_base = get_basename(program);
procps_pids_new(&info, items, 3);
exe_link = root_link = NULL;
while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
char *p_cmd = PIDS_VAL(rel_cmd, str, stack, info),
**p_cmdline = PIDS_VAL(rel_cmdline, strv, stack, info);
int tid = PIDS_VAL(rel_pid, s_int, stack, info);
if (opt_rootdir_check) {
/* get the /proc/<pid>/root symlink value */
root_link = pid_link(tid, "root");
match = !strcmp(pidof_root, root_link);
safe_free(root_link);
if (!match) { /* root check failed */
continue;
}
}
if (!is_omitted(tid) && ((p_cmdline && *p_cmdline) || opt_with_workers)) {
cmd_arg0 = (p_cmdline && *p_cmdline) ? *p_cmdline : "\0";
/* processes starting with '-' are login shells */
if (*cmd_arg0 == '-') {
cmd_arg0++;
}
/* get the argv0 base name */
cmd_arg0base = get_basename(cmd_arg0);
/* get the /proc/<pid>/exe symlink value */
exe_link = pid_link(tid, "exe");
/* get the exe_link base name */
exe_link_base = get_basename(exe_link);
match = 0;
if (!strcmp(program, cmd_arg0base) ||
!strcmp(program_base, cmd_arg0) ||
!strcmp(program, cmd_arg0) ||
(opt_with_workers && !strcmp(program, p_cmd)) ||
!strcmp(program, exe_link_base) ||
!strcmp(program, exe_link))
{
match = 1;
} else if (opt_scripts_too && p_cmdline && *(p_cmdline+1)) {
cmd_arg1 = *(p_cmdline+1);
/* get the arg1 base name */
cmd_arg1base = get_basename(cmd_arg1);
/* if script, then cmd = argv1, otherwise cmd = argv0 */
if (p_cmd &&
!strncmp(p_cmd, cmd_arg1base, strlen(p_cmd)) &&
(!strcmp(program, cmd_arg1base) ||
!strcmp(program_base, cmd_arg1) ||
!strcmp(program, cmd_arg1)))
{
match = 1;
}
}
/* If there is a space in arg0 then process probably has
* setproctitle so use the cmdline
*/
if (!match && strchr(cmd_arg0, ' ')) {
match = (strcmp(program, p_cmd)==0);
}
safe_free(exe_link);
if (match) {
if (proc_count == size) {
grow_size(size);
procs = xrealloc(procs, size * (sizeof *procs));
}
if (procs) {
procs[proc_count++].pid = tid;
} else {
xerrx(EXIT_FAILURE, _("internal error"));
}
}
}
}
procps_pids_unref(&info);
}
static void add_to_omit_list (char *input_arg)
{
static int omit_size = 0;
char *omit_str;
char *endptr;
pid_t omit_pid;
omit_str = NULL;
omit_str = strtok(input_arg, ",;:");
while (omit_str) {
if (!strcmp(omit_str,"%PPID")) { /* keeping this %PPID garbage for backward compatibility only */
omit_pid = getppid(); /* ... as it can be replaced with $$ in common shells */
endptr = omit_str + sizeof("%PPID") - 1;
} else {
omit_pid = strtoul(omit_str, &endptr, 10);
}
if (*endptr == '\0') {
if (omit_count == omit_size) {
grow_size(omit_size);
omitted_procs = xrealloc(omitted_procs, omit_size * sizeof(*omitted_procs));
}
if (omitted_procs) {
omitted_procs[omit_count++].pid = omit_pid;
} else {
xerrx(EXIT_FAILURE, _("internal error"));
}
} else {
xwarnx(_("illegal omit pid value (%s)!\n"), omit_str);
}
omit_str = strtok(NULL, ",;:");
}
}
int main (int argc, char **argv)
{
int opt;
signed int i;
int found = 0;
int first_pid = 1;
const char *separator = " ";
const char *opts = "scnqxwmo:S:?Vh";
static const struct option longopts[] = {
{"check-root", no_argument, NULL, 'c'},
{"single-shot", no_argument, NULL, 's'},
{"omit-pid", required_argument, NULL, 'o'},
{"separator", required_argument, NULL, 'S'},
{"quiet", no_argument, NULL, 'q'},
{"with-workers", no_argument, NULL, 'w'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
/* process command-line options */
while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != -1) {
switch (opt) {
case 'q':
opt_quiet = 1;
/* fallthrough */
case 's':
opt_single_shot = 1;
break;
case 'o':
add_to_omit_list (optarg);
break;
case 'x':
opt_scripts_too = 1;
break;
case 'w':
opt_with_workers = 1;
break;
case 'c':
if (geteuid() == 0) {
opt_rootdir_check = 1;
safe_free(pidof_root);
pidof_root = pid_link(getpid(), "root");
}
break;
case 'd': /* sysv pidof uses this for S */
case 'S':
separator = optarg;
break;
case 'V':
printf (PROCPS_NG_VERSION);
exit (EXIT_SUCCESS);
case 'h':
case '?':
usage (opt);
break;
/* compatibility-only switches */
case 'n': /* avoiding stat(2) on NFS volumes doesn't make any sense anymore ... */
/* ... as this reworked solution does not use stat(2) at all */
case 'm': /* omitting relatives with argv[0] & argv[1] matching the argv[0] & argv[1] ...*/
/* ... of explicitly omitted PIDs is too 'expensive' and as we don't know */
/* ... wheter it is still needed, we won't re-implement it unless ... */
/* ... somebody gives us a good reason to do so :) */
break;
}
}
/* main loop */
while (argc - optind) { /* for each program */
program = argv[optind++];
if (*program == '\0') continue;
select_procs(); /* get the list of matching processes */
if (proc_count) {
found = 1;
for (i = proc_count - 1; i >= 0; i--) { /* and display their PIDs */
if (!opt_quiet) {
if (first_pid) {
first_pid = 0;
printf ("%ld", (long) procs[i].pid);
} else {
printf ("%s%ld", separator, (long) procs[i].pid);
}
}
if (opt_single_shot) break;
}
proc_count = 0;
}
}
/* final line feed */
if (!opt_quiet && found) printf("\n");
/* some cleaning */
safe_free(procs);
safe_free(omitted_procs);
safe_free(pidof_root);
return !found;
}

1207
src/pmap.c Normal file

File diff suppressed because it is too large Load Diff

481
src/ps/COPYING Normal file
View File

@@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library 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
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

46
src/ps/HACKING Normal file
View File

@@ -0,0 +1,46 @@
Warning:
This code must corrctly handle lots of picky little details to meet
the Unix98 standard while simultaneously being as compatible as
possible with the original Linux ps. Don't "fix" something without
considering the impact on all the special-case code. For example,
the "tty" format _must_ use "TT" as the header, even though the SysV
output formats _must_ use "TTY".
File overview:
display.c main(), debug code, iterates over processes
escape.c Does stuff like \202 and &lt; to command and environment.
global.c Data + code to init it.
help.c Help message.
output.c Giant tables and lots of output functions.
parser.c Initial command parsing.
select.c want_this_proc() checks a process against flags & lists
sortformat.c Parses sort & format specifier lists. Picks output format.
stacktrace.c Debug code, not normally used.
../proc/* Library used to gather data.
regression Regression tests that ought to be run.
common.h Lots of interesting stuff.
Makefile Makefile
p Script used to test ps when the library is not installed.
utf Empty file used to test "ps ut?" unmangling behavior.
ps.1 Man page.
Operation:
Unless the personality forces BSD parsing, parser.c tries to parse the
command line as a mixed BSD+SysV+Gnu mess. On failure, BSD parsing is
attempted. If BSD parsing fails _after_ SysV parsing has been attempted,
the error message comes from the original SysV parse.
Control goes to sortformat.c, which must pick apart ambiguous options
like "O". Failure can reset the whole program and set PER_FORCE_BSD,
which means a second trip through parser.c and sortformat.c.
The choice of output format happens in sortformat.c. There is a switch()
with all the valid format_flags combinations. The SysV and default
options are NULL (unless overridden by personality), which causes a
trip through SysV output format generation hackery. Note that the
default format always goes through there, even if it is for BSD.
Formats that came from the switch() (generally BSD, plus overrides)
get mangled a bit to support various SysV output modifiers.

500
src/ps/common.h Normal file
View File

@@ -0,0 +1,500 @@
/*
* Copyright 1998-2002 by Albert Cahalan; all rights resered.
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program 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 Library General Public License for more details.
*/
#ifndef PROCPS_PS_H
#define PROCPS_PS_H
#include "../include/nls.h"
#include <proc/meminfo.h>
#include <proc/misc.h>
#include <proc/pids.h>
#include <proc/stat.h>
// --- <pids> interface begin ||||||||||||||||||||||||||||||||||||||||||||
// -----------------------------------------------------------------------
// hack to minimize code impact
#undef proc_t
#define proc_t struct pids_stack
/* this is for allocation of the Pids_items and represents a compromise.
we can't predict how many fields will actually be requested yet there
are numerous duplicate format_array entries. here are the statistics:
252 entries in the format_array
82 of those entries are unique
60 equals a former proc_t size
in reality, only a small portion of the stack depth will be occupied,
and the excess represents storage cost only, not a run-time cpu cost! */
#define PIDSITEMS 70
/* a 'results stack value' extractor macro
where: E=rel enum, T=data type, S=stack */
#define rSv(E,T,S) PIDS_VAL(rel_ ## E, T, S, Pids_info)
#define namREL(e) rel_ ## e
#define makEXT(e) extern int namREL(e);
#define makREL(e) int namREL(e) = -1;
#define chkREL(e) if (namREL(e) < 0) { \
Pids_items[Pids_index] = PIDS_ ## e; \
namREL(e) = (Pids_index < PIDSITEMS) ? Pids_index++ : rel_noop; }
#define setREL1(e) { \
if (!outbuf) { \
chkREL(e) \
return 0; \
} }
#define setREL2(e1,e2) { \
if (!outbuf) { \
chkREL(e1) chkREL(e2) \
return 0; \
} }
#define setREL3(e1,e2,e3) { \
if (!outbuf) { \
chkREL(e1) chkREL(e2) chkREL(e3) \
return 0; \
} }
#define setREL4(e1,e2,e3,e4) { \
if (!outbuf) { \
chkREL(e1) chkREL(e2) chkREL(e3) chkREL(e4) \
return 0; \
} }
extern struct pids_info *Pids_info;
extern enum pids_item *Pids_items;
extern int Pids_index;
// most of these need not be extern, they're unique to output.c
// (but for future flexibility the easiest path has been taken)
makEXT(ADDR_CODE_END)
makEXT(ADDR_CODE_START)
makEXT(ADDR_CURR_EIP)
makEXT(ADDR_CURR_ESP)
makEXT(ADDR_STACK_START)
makEXT(AUTOGRP_ID)
makEXT(AUTOGRP_NICE)
makEXT(CGNAME)
makEXT(CGROUP)
makEXT(CMD)
makEXT(CMDLINE)
makEXT(ENVIRON)
makEXT(EXE)
makEXT(FLAGS)
makEXT(FLT_MAJ)
makEXT(FLT_MAJ_C)
makEXT(FLT_MIN)
makEXT(FLT_MIN_C)
makEXT(ID_EGID)
makEXT(ID_EGROUP)
makEXT(ID_EUID)
makEXT(ID_EUSER)
makEXT(ID_FGID)
makEXT(ID_FGROUP)
makEXT(ID_FUID)
makEXT(ID_FUSER)
makEXT(ID_LOGIN)
makEXT(ID_PGRP)
makEXT(ID_PID)
makEXT(ID_PPID)
makEXT(ID_RGID)
makEXT(ID_RGROUP)
makEXT(ID_RUID)
makEXT(ID_RUSER)
makEXT(ID_SESSION)
makEXT(ID_SGID)
makEXT(ID_SGROUP)
makEXT(ID_SUID)
makEXT(ID_SUSER)
makEXT(ID_TGID)
makEXT(ID_TPGID)
makEXT(IO_READ_BYTES)
makEXT(IO_READ_CHARS)
makEXT(IO_READ_OPS)
makEXT(IO_WRITE_BYTES)
makEXT(IO_WRITE_CBYTES)
makEXT(IO_WRITE_CHARS)
makEXT(IO_WRITE_OPS)
makEXT(LXCNAME)
makEXT(NICE)
makEXT(NLWP)
makEXT(NS_CGROUP)
makEXT(NS_IPC)
makEXT(NS_MNT)
makEXT(NS_NET)
makEXT(NS_PID)
makEXT(NS_TIME)
makEXT(NS_USER)
makEXT(NS_UTS)
makEXT(OOM_ADJ)
makEXT(OOM_SCORE)
makEXT(PRIORITY)
makEXT(PRIORITY_RT)
makEXT(PROCESSOR)
makEXT(PROCESSOR_NODE)
makEXT(RSS)
makEXT(RSS_RLIM)
makEXT(SCHED_CLASS)
makEXT(SD_MACH)
makEXT(SD_OUID)
makEXT(SD_SEAT)
makEXT(SD_SESS)
makEXT(SD_SLICE)
makEXT(SD_UNIT)
makEXT(SD_UUNIT)
makEXT(SIGBLOCKED)
makEXT(SIGCATCH)
makEXT(SIGIGNORE)
makEXT(SIGNALS)
makEXT(SIGPENDING)
makEXT(SMAP_PRV_TOTAL)
makEXT(SMAP_PSS)
makEXT(STATE)
makEXT(SUPGIDS)
makEXT(SUPGROUPS)
makEXT(TICS_ALL)
makEXT(TICS_ALL_C)
makEXT(TIME_ALL)
makEXT(TIME_ELAPSED)
makEXT(TICS_BEGAN)
makEXT(TTY)
makEXT(TTY_NAME)
makEXT(TTY_NUMBER)
makEXT(UTILIZATION)
makEXT(UTILIZATION_C)
makEXT(VM_DATA)
makEXT(VM_RSS_LOCKED)
makEXT(VM_RSS)
makEXT(VM_SIZE)
makEXT(VM_STACK)
makEXT(VSIZE_BYTES)
makEXT(WCHAN_NAME)
makEXT(extra)
makEXT(noop)
// -----------------------------------------------------------------------
// --- <pids> interface end ||||||||||||||||||||||||||||||||||||||||||||||
#if 0
#define trace(...) printf(## __VA_ARGS__)
#else
#define trace(...)
#endif
/***************** GENERAL DEFINE ********************/
/* selection list */
#define SEL_RUID 1
#define SEL_EUID 2
#define SEL_SUID 3
#define SEL_FUID 4
#define SEL_RGID 5
#define SEL_EGID 6
#define SEL_SGID 7
#define SEL_FGID 8
#define SEL_PGRP 9
#define SEL_PID 10
#define SEL_TTY 11
#define SEL_SESS 12
#define SEL_COMM 13
#define SEL_PPID 14
#define SEL_PID_QUICK 15
/* Since an enum could be smashed by a #define, it would be bad. */
#define U98 0 /* Unix98 standard */ /* This must be 0 */
#define XXX 1 /* Common extension */
#define DEC 2 /* Digital Unix */
#define AIX 3 /* AIX */
#define SCO 4 /* SCO */
#define LNX 5 /* Linux original :-) */
#define BSD 6 /* FreeBSD and OpenBSD */
#define SUN 7 /* SunOS 5 (Solaris) */
#define HPU 8 /* HP-UX */
#define SGI 9 /* Irix */
#define SOE 10 /* IBM's S/390 OpenEdition */
#define TST 11 /* test code */
/*
* Try not to overflow the output buffer:
* 32 pages for env+cmd
* 64 kB pages on IA-64
* plus some slack for other stuff
* That is about 8.5 MB on IA-64, or 0.6 MB on i386
*
* Sadly, current kernels only supply one page of env/command data.
* The buffer is now protected with a guard page, and via other means
* to avoid hitting the guard page.
*/
/* output buffer size */
#define OUTBUF_SIZE (2 * 64*1024)
/******************* PS DEFINE *******************/
// Column flags
// Justification control for flags field comes first.
#define CF_JUST_MASK 0x0f
// CF_AIXHACK 0
#define CF_USER 1 // left if text, right if numeric
#define CF_LEFT 2
#define CF_RIGHT 3
#define CF_UNLIMITED 4
#define CF_WCHAN 5 // left if text, right if numeric
#define CF_SIGNAL 6 // right in 9, or 16 if screen_cols>107
// Then the other flags
#define CF_PIDMAX 0x00000010 // react to pid_max
// Only one allowed; use separate bits to catch errors.
#define CF_PRINT_THREAD_ONLY 0x10000000
#define CF_PRINT_PROCESS_ONLY 0x20000000
#define CF_PRINT_EVERY_TIME 0x40000000
#define CF_PRINT_AS_NEEDED 0x80000000 // means we have no clue, so assume EVERY TIME
#define CF_PRINT_MASK 0xf0000000
/* thread_flags */
#define TF_B_H 0x0001
#define TF_B_m 0x0002
#define TF_U_m 0x0004
#define TF_U_T 0x0008
#define TF_U_L 0x0010
#define TF_show_proc 0x0100 // show the summary line
#define TF_show_task 0x0200 // show the per-thread lines
#define TF_show_both 0x0400 // distinct proc/task format lists
#define TF_loose_tasks 0x0800 // let sorting break up task groups (BSDish)
#define TF_no_sort 0x1000 // don't know if thread-grouping should survive a sort
#define TF_no_forest 0x2000 // don't see how to do threads w/ forest option
#define TF_must_use 0x4000 // options only make sense if LWP/SPID column added
/* personality control flags */
#define PER_BROKEN_o 0x0001
#define PER_BSD_h 0x0002
#define PER_BSD_m 0x0004
#define PER_IRIX_l 0x0008
#define PER_FORCE_BSD 0x0010
#define PER_GOOD_o 0x0020
#define PER_OLD_m 0x0040
#define PER_NO_DEFAULT_g 0x0080
#define PER_ZAP_ADDR 0x0100
#define PER_SANE_USER 0x0200
#define PER_HPUX_x 0x0400
#define PER_SVR4_x 0x0800
#define PER_BSD_COLS 0x1000
#define PER_UNIX_COLS 0x2000
/* Simple selections by bit mask */
#define SS_B_x 0x01
#define SS_B_g 0x02
#define SS_U_d 0x04
#define SS_U_a 0x08
#define SS_B_a 0x10
/* predefined format flags such as: -l -f l u s -j */
#define FF_Uf 0x0001 /* -f */
#define FF_Uj 0x0002 /* -j */
#define FF_Ul 0x0004 /* -l */
#define FF_Bj 0x0008 /* j */
#define FF_Bl 0x0010 /* l */
#define FF_Bs 0x0020 /* s */
#define FF_Bu 0x0040 /* u */
#define FF_Bv 0x0080 /* v */
#define FF_LX 0x0100 /* X */
#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */
#define FF_Fc 0x0400 /* --context */ /* Flask security context format */
/* predefined format modifier flags such as: -l -f l u s -j */
#define FM_c 0x0001 /* -c */
#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */
#define FM_y 0x0004 /* -y */
//#define FM_L 0x0008 /* -L */
#define FM_P 0x0010 /* -P */
#define FM_M 0x0020 /* -M */
//#define FM_T 0x0040 /* -T */
#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */
/* sorting & formatting */
/* U,B,G is Unix,BSD,Gnu and then there is the option itself */
#define SF_U_O 1
#define SF_U_o 2
#define SF_B_O 3
#define SF_B_o 4
#define SF_B_m 5 /* overloaded: threads, sort, format */
#define SF_G_sort 6
#define SF_G_format 7
/* headers */
#define HEAD_SINGLE 0 /* default, must be 0 */
#define HEAD_NONE 1
#define HEAD_MULTI 2
/********************** GENERAL TYPEDEF *******************/
/* Other fields that might be useful:
*
* char *name; user-defined column name (format specification)
* int reverse; sorting in reverse (sort specification)
*
* name in place of u
* reverse in place of n
*/
typedef union sel_union {
pid_t pid;
pid_t ppid;
uid_t uid;
gid_t gid;
dev_t tty;
char cmd[64]; /* this is _not_ \0 terminated */
} sel_union;
typedef struct selection_node {
struct selection_node *next;
sel_union *u; /* used if selection type has a list of values */
int n; /* used if selection type has a list of values */
int typecode;
} selection_node;
typedef struct sort_node {
struct sort_node *next;
enum pids_item sr;
int (*xe)(char *, proc_t *); // special format_node 'pr' guy
enum pids_sort_order reverse;
int typecode;
} sort_node;
typedef struct format_node {
struct format_node *next;
char *name; /* user can override default name */
int (*pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function
int width;
int vendor; /* Vendor that invented this */
int flags;
int typecode;
} format_node;
typedef struct format_struct {
const char *spec; /* format specifier */
const char *head; /* default header in the POSIX locale */
int (* const pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function
enum pids_item sr;
const int width;
const int vendor; /* Where does this come from? */
const int flags;
} format_struct;
/* though ps-specific, needed by general file */
typedef struct macro_struct {
const char *spec; /* format specifier */
const char *head; /* default header in the POSIX locale */
} macro_struct;
/**************** PS TYPEDEF ***********************/
typedef struct aix_struct {
const int desc; /* 1-character format code */
const char *spec; /* format specifier */
const char *head; /* default header in the POSIX locale */
} aix_struct;
typedef struct shortsort_struct {
const int desc; /* 1-character format code */
const char *spec; /* format specifier */
} shortsort_struct;
/* Save these options for later: -o o -O O --format --sort */
typedef struct sf_node {
struct sf_node *next; /* next arg */
format_node *f_cooked; /* convert each arg alone, then merge */
sort_node *s_cooked; /* convert each arg alone, then merge */
char *sf;
int sf_code;
} sf_node;
/********************* UNDECIDED GLOBALS **************/
/* output.c */
extern void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt);
extern void print_format_specifiers(void);
extern const aix_struct *search_aix_array(const int findme);
extern const shortsort_struct *search_shortsort_array(const int findme);
extern const format_struct *search_format_array(const char *findme);
extern const macro_struct *search_macro_array(const char *findme);
extern void init_output(void);
extern int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp);
/* global.c */
extern void reset_global(void);
/* global.c */
extern int all_processes;
extern const char *bsd_j_format;
extern const char *bsd_l_format;
extern const char *bsd_s_format;
extern const char *bsd_u_format;
extern const char *bsd_v_format;
extern int bsd_c_option;
extern int bsd_e_option;
extern uid_t cached_euid;
extern int cached_tty;
extern char forest_prefix[4 * 32*1024 + 100];
extern int forest_type;
extern unsigned format_flags; /* -l -f l u s -j... */
extern format_node *format_list; /* digested formatting options */
extern unsigned format_modifiers; /* -c -j -y -P -L... */
extern int header_gap;
extern int header_type; /* none, single, multi... */
extern int include_dead_children;
extern int lines_to_next_header;
extern int max_line_width;
extern int negate_selection;
extern int page_size; // "int" for math reasons?
extern unsigned personality;
extern int prefer_bsd_defaults;
extern int running_only;
extern int screen_cols;
extern int screen_rows;
extern selection_node *selection_list;
extern unsigned simple_select;
extern sort_node *sort_list;
extern const char *sysv_f_format;
extern const char *sysv_fl_format;
extern const char *sysv_j_format;
extern const char *sysv_l_format;
extern unsigned thread_flags;
extern int unix_f_option;
extern int user_is_number;
extern int wchan_is_number;
extern const char *the_word_help;
/************************* PS GLOBALS *********************/
/* display.c */
extern char *myname;
/* sortformat.c */
extern int defer_sf_option(const char *arg, int source);
extern const char *process_sf_options();
extern void reset_sortformat(void);
/* select.c */
extern int want_this_proc(proc_t *buf);
extern const char *select_bits_setup(void);
/* help.c */
extern void __attribute__ ((__noreturn__)) do_help(const char *opt, int rc);
/* global.c */
extern void self_info(void);
extern void catastrophic_failure(const char *filename, unsigned int linenum,
const char *message);
/* parser.c */
extern int arg_parse(int argc, char *argv[]);
#endif

677
src/ps/display.c Normal file
View File

@@ -0,0 +1,677 @@
/*
* 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;
}

645
src/ps/global.c Normal file
View File

@@ -0,0 +1,645 @@
/*
* 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 "../include/c.h"
#include "../include/xalloc.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
// --- <pids> interface begin ||||||||||||||||||||||||||||||||||||||||||||
// -----------------------------------------------------------------------
struct pids_info *Pids_info = NULL; // our required <pids> context
enum pids_item *Pids_items; // allocated as PIDSITEMS
int Pids_index; // actual number of active enums
// most of these could be defined as static in the output.c module
// (but for future flexibility, the easiest route has been chosen)
makREL(ADDR_CODE_END)
makREL(ADDR_CODE_START)
makREL(ADDR_CURR_EIP)
makREL(ADDR_CURR_ESP)
makREL(ADDR_STACK_START)
makREL(AUTOGRP_ID)
makREL(AUTOGRP_NICE)
makREL(CGNAME)
makREL(CGROUP)
makREL(CMD)
makREL(CMDLINE)
makREL(ENVIRON)
makREL(EXE)
makREL(FLAGS)
makREL(FLT_MAJ)
makREL(FLT_MAJ_C)
makREL(FLT_MIN)
makREL(FLT_MIN_C)
makREL(ID_EGID)
makREL(ID_EGROUP)
makREL(ID_EUID)
makREL(ID_EUSER)
makREL(ID_FGID)
makREL(ID_FGROUP)
makREL(ID_FUID)
makREL(ID_FUSER)
makREL(ID_LOGIN)
makREL(ID_PGRP)
makREL(ID_PID)
makREL(ID_PPID)
makREL(ID_RGID)
makREL(ID_RGROUP)
makREL(ID_RUID)
makREL(ID_RUSER)
makREL(ID_SESSION)
makREL(ID_SGID)
makREL(ID_SGROUP)
makREL(ID_SUID)
makREL(ID_SUSER)
makREL(ID_TGID)
makREL(ID_TPGID)
makREL(IO_READ_BYTES)
makREL(IO_READ_CHARS)
makREL(IO_READ_OPS)
makREL(IO_WRITE_BYTES)
makREL(IO_WRITE_CBYTES)
makREL(IO_WRITE_CHARS)
makREL(IO_WRITE_OPS)
makREL(LXCNAME)
makREL(NICE)
makREL(NLWP)
makREL(NS_CGROUP)
makREL(NS_IPC)
makREL(NS_MNT)
makREL(NS_NET)
makREL(NS_PID)
makREL(NS_TIME)
makREL(NS_USER)
makREL(NS_UTS)
makREL(OOM_ADJ)
makREL(OOM_SCORE)
makREL(PRIORITY)
makREL(PRIORITY_RT)
makREL(PROCESSOR)
makREL(PROCESSOR_NODE)
makREL(RSS)
makREL(RSS_RLIM)
makREL(SCHED_CLASS)
makREL(SD_MACH)
makREL(SD_OUID)
makREL(SD_SEAT)
makREL(SD_SESS)
makREL(SD_SLICE)
makREL(SD_UNIT)
makREL(SD_UUNIT)
makREL(SIGBLOCKED)
makREL(SIGCATCH)
makREL(SIGIGNORE)
makREL(SIGNALS)
makREL(SIGPENDING)
makREL(SMAP_PRV_TOTAL)
makREL(SMAP_PSS)
makREL(STATE)
makREL(SUPGIDS)
makREL(SUPGROUPS)
makREL(TICS_ALL)
makREL(TICS_ALL_C)
makREL(TIME_ALL)
makREL(TIME_ELAPSED)
makREL(TICS_BEGAN)
makREL(TTY)
makREL(TTY_NAME)
makREL(TTY_NUMBER)
makREL(UTILIZATION)
makREL(UTILIZATION_C)
makREL(VM_DATA)
makREL(VM_RSS_LOCKED)
makREL(VM_RSS)
makREL(VM_SIZE)
makREL(VM_STACK)
makREL(VSIZE_BYTES)
makREL(WCHAN_NAME)
makREL(extra)
makREL(noop)
// -----------------------------------------------------------------------
// --- <pids> interface end ||||||||||||||||||||||||||||||||||||||||||||||
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;
unsigned cached_euid = 0xffffffff;
int 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;
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 */
case_unknown: /* defaults, but also check inferior environment variables */
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')
return NULL;
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_HPUX_x;
return NULL;
case_svr4:
case_sysv:
case_sco:
personality = PER_SVR4_x;
return NULL;
case_posix:
case_solaris2:
case_unix95:
case_unix98:
case_unix:
return NULL;
}
/************ Call this to reinitialize everything ***************/
void reset_global(void){
proc_t *p;
int i;
reset_selection_list();
// --- <pids> interface --------------------------------------------------
if (!Pids_items)
Pids_items = xcalloc(PIDSITEMS, sizeof(enum pids_item));
for (i = 0; i < PIDSITEMS; i++)
Pids_items[i] = PIDS_noop;
if (!Pids_info) {
if (procps_pids_new(&Pids_info, Pids_items, i)) {
fprintf(stderr, _("fatal library error, context\n"));
exit(EXIT_FAILURE);
}
}
Pids_items[0] = PIDS_TTY;
procps_pids_reset(Pids_info, Pids_items, 1);
if (!(p = fatal_proc_unmounted(Pids_info, 1))) {
fprintf(stderr, _("fatal library error, lookup self\n"));
exit(EXIT_FAILURE);
}
// --- <pids> interface --------------------------------------------------
set_screen_size();
set_personality();
all_processes = 0;
bsd_c_option = 0;
bsd_e_option = 0;
cached_euid = geteuid();
cached_tty = PIDS_VAL(0, s_int, p, Pids_info);
/* 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;
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);
/* __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 page_size=%d\n",
personality, saved_personality_text,
cached_euid, (int)major(cached_tty), (int)minor(cached_tty),
(int)(page_size)
);
fprintf(stderr,
"sizeof(proc_t)=%d sizeof(long)=%d sizeof(long)=%d\n",
(int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(long)
);
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);
}

217
src/ps/help.c Normal file
View File

@@ -0,0 +1,217 @@
/*
* help.c - ps help output
* Copyright 1998-2004 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
enum {
HELP_SMP, HELP_LST, HELP_OUT,
HELP_THD, HELP_MSC, HELP_ALL,
HELP_default
};
static struct {
const char *word;
const char *abrv;
} help_tab[HELP_default];
static int parse_help_opt (const char *opt) {
/* Translation Notes for ps Help #1 ---------------------------------
. This next group of lines represents 6 pairs of words + abbreviations
. which are the basis of the 'ps' program help text.
.
. The words and abbreviations you provide will alter program behavior.
. They will also appear in the help usage summary associated with the
. "Notes for ps Help #2" below.
.
. In their English form, help text would look like this:
. Try 'ps --help <simple|list|output|threads|misc|all>'
. or 'ps --help <s|l|o|t|m|a>'
. for additional help text.
.
. When translating these 6 pairs you may choose any appropriate
. language equivalents and the only requirement is the abbreviated
. representations must be unique.
.
. By default, those abbreviations are single characters. However,
. they are not limited to only one character after translation.
. */
/* Translation Hint, Pair #1 */
help_tab[HELP_SMP].word = _("simple"); help_tab[HELP_SMP].abrv = _("s");
/* Translation Hint, Pair #2 */
help_tab[HELP_LST].word = _("list"); help_tab[HELP_LST].abrv = _("l");
/* Translation Hint, Pair #3 */
help_tab[HELP_OUT].word = _("output"); help_tab[HELP_OUT].abrv = _("o");
/* Translation Hint, Pair #4 */
help_tab[HELP_THD].word = _("threads"); help_tab[HELP_THD].abrv = _("t");
/* Translation Hint, Pair #5 */
help_tab[HELP_MSC].word = _("misc"); help_tab[HELP_MSC].abrv = _("m");
/* Translation Hint, Pair #6 */
help_tab[HELP_ALL].word = _("all"); help_tab[HELP_ALL].abrv = _("a");
/*
* the above are doubled on each line so they carry the same .pot
* line # reference and thus appear more like true "pairs" even
* though xgettext will produce separate msgid/msgstr groups */
if(opt) {
int i;
for (i = HELP_SMP; i < HELP_default; i++)
if (!strcmp(opt, help_tab[i].word) || !strcmp(opt, help_tab[i].abrv))
return i;
}
return HELP_default;
}
void do_help (const char *opt, int rc);
void do_help (const char *opt, int rc) {
FILE *out = (rc == EXIT_SUCCESS) ? stdout : stderr;
int section = parse_help_opt(opt);
fprintf(out, _("\n"
"Usage:\n"
" %s [options]\n"), myname);
if (section == HELP_SMP || section == HELP_ALL) {
fputs(_("\nBasic options:\n"), out);
fputs(_(" -A, -e all processes\n"), out);
fputs(_(" -a all with tty, except session leaders\n"), out);
fputs(_(" a all with tty, including other users\n"), out);
fputs(_(" -d all except session leaders\n"), out);
fputs(_(" -N, --deselect negate selection\n"), out);
fputs(_(" r only running processes\n"), out);
fputs(_(" T all processes on this terminal\n"), out);
fputs(_(" x processes without controlling ttys\n"), out);
}
if (section == HELP_LST || section == HELP_ALL) {
fputs(_("\nSelection by list:\n"), out);
fputs(_(" -C <command> command name\n"), out);
fputs(_(" -G, --Group <GID> real group id or name\n"), out);
fputs(_(" -g, --group <group> session or effective group name\n"), out);
fputs(_(" -p, p, --pid <PID> process id\n"), out);
fputs(_(" --ppid <PID> parent process id\n"), out);
fputs(_(" -q, q, --quick-pid <PID>\n"
" process id (quick mode)\n"), out);
fputs(_(" -s, --sid <session> session id\n"), out);
fputs(_(" -t, t, --tty <tty> terminal\n"), out);
fputs(_(" -u, U, --user <UID> effective user id or name\n"), out);
fputs(_(" -U, --User <UID> real user id or name\n"), out);
fputs(_("\n"
" The selection options take as their argument either:\n"
" a comma-separated list e.g. '-u root,nobody' or\n"
" a blank-separated list e.g. '-p 123 4567'\n"), out);
}
if (section == HELP_OUT || section == HELP_ALL) {
fputs(_("\nOutput formats:\n"), out);
fputs(_(" -F extra full\n"), out);
fputs(_(" -f full-format, including command lines\n"), out);
fputs(_(" f, --forest ascii art process tree\n"), out);
fputs(_(" -H show process hierarchy\n"), out);
fputs(_(" -j jobs format\n"), out);
fputs(_(" j BSD job control format\n"), out);
fputs(_(" -l long format\n"), out);
fputs(_(" l BSD long format\n"), out);
fputs(_(" -M, Z add security data (for SELinux)\n"), out);
fputs(_(" -O <format> preloaded with default columns\n"), out);
fputs(_(" O <format> as -O, with BSD personality\n"), out);
fputs(_(" -o, o, --format <format>\n"
" user-defined format\n"), out);
fputs(_(" -P add psr column\n"), out);
fputs(_(" s signal format\n"), out);
fputs(_(" u user-oriented format\n"), out);
fputs(_(" v virtual memory format\n"), out);
fputs(_(" X register format\n"), out);
fputs(_(" -y do not show flags, show rss vs. addr (used with -l)\n"), out);
fputs(_(" --context display security context (for SELinux)\n"), out);
fputs(_(" --headers repeat header lines, one per page\n"), out);
fputs(_(" --no-headers do not print header at all\n"), out);
fputs(_(" --cols, --columns, --width <num>\n"
" set screen width\n"), out);
fputs(_(" --rows, --lines <num>\n"
" set screen height\n"), out);
}
if (section == HELP_THD || section == HELP_ALL) {
fputs(_("\nShow threads:\n"), out);
fputs(_(" H as if they were processes\n"), out);
fputs(_(" -L possibly with LWP and NLWP columns\n"), out);
fputs(_(" -m, m after processes\n"), out);
fputs(_(" -T possibly with SPID column\n"), out);
}
if (section == HELP_MSC || section == HELP_ALL) {
fputs(_("\nMiscellaneous options:\n"), out);
fputs(_(" -c show scheduling class with -l option\n"), out);
fputs(_(" c show true command name\n"), out);
fputs(_(" e show the environment after command\n"), out);
fputs(_(" k, --sort specify sort order as: [+|-]key[,[+|-]key[,...]]\n"), out);
fputs(_(" L show format specifiers\n"), out);
fputs(_(" n display numeric uid and wchan\n"), out);
fputs(_(" S, --cumulative include some dead child process data\n"), out);
fputs(_(" -y do not show flags, show rss (only with -l)\n"), out);
fputs(_(" -V, V, --version display version information and exit\n"), out);
fputs(_(" -w, w unlimited output width\n"), out);
fprintf(out, _("\n"
" --%s <%s|%s|%s|%s|%s|%s>\n"
" display help and exit\n")
, the_word_help
, help_tab[HELP_SMP].word, help_tab[HELP_LST].word
, help_tab[HELP_OUT].word, help_tab[HELP_THD].word
, help_tab[HELP_MSC].word, help_tab[HELP_ALL].word);
}
if (section == HELP_default) {
/* Translation Notes for ps Help #2 ---------------------------------
. Most of the following c-format string is derived from the 6
. pairs of words + chars mentioned above in "Notes for ps Help #1".
.
. In its full English form, help text would look like this:
. Try 'ps --help <simple|list|output|threads|misc|all>'
. or 'ps --help <s|l|o|t|m|a>'
. for additional help text.
.
. The word for "help" will be translated elsewhere. Thus, the only
. translations below will be: "Try", "or" and "for additional...".
. */
fprintf(out, _("\n"
" Try '%s --%s <%s|%s|%s|%s|%s|%s>'\n"
" or '%s --%s <%s|%s|%s|%s|%s|%s>'\n"
" for additional help text.\n")
, myname, the_word_help
, help_tab[HELP_SMP].word, help_tab[HELP_LST].word
, help_tab[HELP_OUT].word, help_tab[HELP_THD].word
, help_tab[HELP_MSC].word, help_tab[HELP_ALL].word
, myname, the_word_help
, help_tab[HELP_SMP].abrv, help_tab[HELP_LST].abrv
, help_tab[HELP_OUT].abrv, help_tab[HELP_THD].abrv
, help_tab[HELP_MSC].abrv, help_tab[HELP_ALL].abrv);
}
fprintf(out, _("\nFor more details see ps(1).\n"));
exit(rc);
}
/* Missing:
*
* -P e k
*
*/

2333
src/ps/output.c Normal file

File diff suppressed because it is too large Load Diff

1242
src/ps/parser.c Normal file

File diff suppressed because it is too large Load Diff

26
src/ps/regression Normal file
View File

@@ -0,0 +1,26 @@
-u 500 -o pid,ppid,fname,comm,args # right margin trouble
-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan
-u 500 -o pid,pid,pid,pid,user,user,user,args # had trouble
-u 500 -o user,user,user,pid,pid,pid,pid,args # no trouble!
Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the
edge of the screen and each type of field to the left of the one that
hangs off the edge.
Test "ps ef" as _both_ normal user and root. Especially after su!
On a 108-col screen, try "ps alx" and "ps alx | cat"
These ought to be the same:
CMD_ENV=old ps -m
CMD_ENV=old ps m
These ought to be the same:
CMD_ENV=old ps -X
CMD_ENV=old ps X
ps X
ps -X # needs to be a non-SysV option
This should fail:
ps x -x

159
src/ps/select.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* select.c - ps process selection
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
//#define process_group_leader(p) (rSv(ID_PID, s_int, p) == rSv(ID_TGID, s_int, p))
//#define some_other_user(p) (rSv(ID_EUID, u_int, p) != cached_euid)
#define has_our_euid(p) (rSv(ID_EUID, u_int, p) == cached_euid)
#define on_our_tty(p) (rSv(TTY, s_int, p) == cached_tty)
#define running(p) (rSv(STATE, s_ch, p) == 'R' || rSv(STATE, s_ch, p) == 'D')
#define session_leader(p) (rSv(ID_SESSION, s_int, p) == rSv(ID_TGID, s_int, p))
#define without_a_tty(p) (!rSv(TTY, s_int, p))
static unsigned long select_bits = 0;
/***** prepare select_bits for use */
const char *select_bits_setup(void){
int switch_val = 0;
/* don't want a 'g' screwing up simple_select */
if(!simple_select && !prefer_bsd_defaults){
select_bits = 0xaa00; /* the STANDARD selection */
return NULL;
}
/* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */
if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) )
switch_val = simple_select|SS_B_g;
else
switch_val = simple_select;
switch(switch_val){
/* UNIX options */
case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */
case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */
case SS_U_d: select_bits = 0x3333; break;
/* SunOS 4 only (others have 'g' enabled all the time) */
case 0: select_bits = 0x0202; break;
case SS_B_a: select_bits = 0x0303; break;
case SS_B_x : select_bits = 0x2222; break;
case SS_B_x | SS_B_a: select_bits = 0x3333; break;
/* General BSD options */
case SS_B_g : select_bits = 0x0a0a; break;
case SS_B_g | SS_B_a: select_bits = 0x0f0f; break;
case SS_B_g | SS_B_x : select_bits = 0xaaaa; break;
case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */
all_processes = 1;
simple_select = 0;
break;
default:
return _("process selection options conflict");
break;
}
return NULL;
}
/***** selected by simple option? */
static int table_accept(proc_t *buf){
unsigned proc_index;
proc_index = (has_our_euid(buf) <<0)
| (session_leader(buf) <<1)
| (without_a_tty(buf) <<2)
| (on_our_tty(buf) <<3);
return (select_bits & (1<<proc_index));
}
/***** selected by some kind of list? */
static int proc_was_listed(proc_t *buf){
selection_node *sn = selection_list;
int i;
if(!sn) return 0;
while(sn){
switch(sn->typecode){
default:
catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
#define return_if_match(foo,bar) \
i=sn->n; while(i--) \
if((unsigned)foo == (unsigned)(*(sn->u+i)).bar) \
return 1
break; case SEL_RUID: return_if_match(rSv(ID_RUID, u_int, buf),uid);
break; case SEL_EUID: return_if_match(rSv(ID_EUID, u_int, buf),uid);
break; case SEL_SUID: return_if_match(rSv(ID_SUID, u_int, buf),uid);
break; case SEL_FUID: return_if_match(rSv(ID_FUID, u_int, buf),uid);
break; case SEL_RGID: return_if_match(rSv(ID_RGID, u_int, buf),gid);
break; case SEL_EGID: return_if_match(rSv(ID_EGID, u_int, buf),gid);
break; case SEL_SGID: return_if_match(rSv(ID_SGID, u_int, buf),gid);
break; case SEL_FGID: return_if_match(rSv(ID_FGID, u_int, buf),gid);
break; case SEL_PGRP: return_if_match(rSv(ID_PGRP, s_int, buf),pid);
break; case SEL_PID : return_if_match(rSv(ID_TGID, s_int, buf),pid);
break; case SEL_PID_QUICK : return_if_match(rSv(ID_TGID, s_int, buf),pid);
break; case SEL_PPID: return_if_match(rSv(ID_PPID, s_int, buf),ppid);
break; case SEL_TTY : return_if_match(rSv(TTY, s_int, buf),tty);
break; case SEL_SESS: return_if_match(rSv(ID_SESSION, s_int, buf),pid);
break;
case SEL_COMM:
i=sn->n;
while(i--) {
/* special case, comm is 16 characters but match is longer */
if (strlen(rSv(CMD, str, buf)) == 15 && strlen((*(sn->u+i)).cmd) >= 15)
if(!strncmp( rSv(CMD, str, buf), (*(sn->u+i)).cmd, 15 )) return 1;
if(!strncmp( rSv(CMD, str, buf), (*(sn->u+i)).cmd, 63 )) return 1;
}
#undef return_if_match
}
sn = sn->next;
}
return 0;
}
/***** This must satisfy Unix98 and as much BSD as possible */
int want_this_proc(proc_t *buf){
int accepted_proc = 1; /* assume success */
/* elsewhere, convert T to list, U sets x implicitly */
/* handle -e -A */
if(all_processes) goto finish;
/* use table for -a a d g x */
if((simple_select || !selection_list))
if(table_accept(buf)) goto finish;
/* search lists */
if(proc_was_listed(buf)) goto finish;
/* fail, fall through to loose ends */
accepted_proc = 0;
/* do r N */
finish:
if(running_only && !running(buf)) accepted_proc = 0;
if(negate_selection) return !accepted_proc;
return accepted_proc;
}

945
src/ps/sortformat.c Normal file
View File

@@ -0,0 +1,945 @@
/*
* sortformat - ps output sorting
* Copyright 1998-2004 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 <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include "../proc/misc.h"
#include "../include/xalloc.h"
#include "common.h"
static sf_node *sf_list = NULL; /* deferred sorting and formatting */
static int have_gnu_sort = 0; /* if true, "O" must be format */
static int already_parsed_sort = 0; /* redundantly set in & out of fn */
static int already_parsed_format = 0;
/**************** Parse single format specifier *******************/
static format_node *do_one_spec(const char *spec, const char *override){
const format_struct *fs;
const macro_struct *ms;
fs = search_format_array(spec);
if(fs){
int w1, w2;
format_node *thisnode;
thisnode = xmalloc(sizeof(format_node));
if(fs->flags & CF_PIDMAX){
w1 = (int)procps_pid_length();
w2 = strlen(fs->head);
if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
}else{
w1 = fs->width;
}
if(override){
w2 = strlen(override);
thisnode->width = (w1>w2)?w1:w2;
thisnode->name = strdup(override);
}else{
thisnode->width = w1;
thisnode->name = strdup(fs->head);
}
thisnode->pr = fs->pr;
thisnode->vendor = fs->vendor;
thisnode->flags = fs->flags;
thisnode->next = NULL;
return thisnode;
}
/* That failed, so try it as a macro. */
ms = search_macro_array(spec);
if(ms){
format_node *list = NULL;
format_node *newnode;
const char *walk;
int dist;
char buf[16]; /* trust strings will be short (from above, not user) */
walk = ms->head;
while(*walk){
dist = strcspn(walk, ", ");
strncpy(buf,walk,dist);
buf[dist] = '\0';
newnode = do_one_spec(buf,override); /* call self, assume success */
newnode->next = list;
list = newnode;
walk += dist;
if(*walk) walk++;
}
return list;
}
return NULL; /* bad, spec not found */
}
/************ must wrap user format in default *************/
static void O_wrap(sf_node *sfn, int otype){
format_node *fnode;
format_node *endp;
const char *trailer;
trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
fnode = do_one_spec("pid",NULL);
if(!fnode)catastrophic_failure(__FILE__, __LINE__, _("seriously crashing: goodbye cruel world"));
endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
endp->next = fnode;
fnode = do_one_spec(trailer,NULL);
if(!fnode)catastrophic_failure(__FILE__, __LINE__, _("seriously crashing: goodbye cruel world"));
endp = fnode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
}
/******************************************************************
* Used to parse option AIX field descriptors.
* Put each completed format_node onto the list starting at ->f_cooked
*/
static const char *aix_format_parse(sf_node *sfn){
char *buf; /* temp copy of arg to hack on */
char *walk;
int items;
/*** sanity check and count items ***/
items = 0;
walk = sfn->sf;
/* state machine */ {
int c = *walk++;
initial:
if(c=='%') goto get_desc;
if(!c) goto looks_ok;
/* get_text: */
items++;
get_more:
c = *walk++;
if(c=='%') goto get_desc;
if(c==' ') goto get_more;
if(c) goto aix_oops;
goto looks_ok;
get_desc:
items++;
c = *walk++;
if(c&&c!=' ') goto initial;
return _("missing AIX field descriptor");
aix_oops:
return _("improper AIX field descriptor");
looks_ok:
;
}
/*** sanity check passed ***/
buf = strdup(sfn->sf);
walk = sfn->sf;
while(items--){
format_node *fnode; /* newly allocated */
format_node *endp; /* for list manipulation */
if(*walk == '%'){
const aix_struct *aix;
walk++;
if(*walk == '%')
return _("missing AIX field descriptor");
aix = search_aix_array(*walk);
walk++;
if(!aix){
free(buf);
return _("unknown AIX field descriptor");
}
fnode = do_one_spec(aix->spec, aix->head);
if(!fnode){
free(buf);
return _("AIX field descriptor processing bug");
}
} else {
size_t len;
len = strcspn(walk, "%");
memcpy(buf,walk,len);
buf[len] = '\0';
walk += len;
fnode = xmalloc(sizeof(format_node));
fnode->width = len < INT_MAX ? len : INT_MAX;
fnode->name = strdup(buf);
fnode->pr = NULL; /* checked for */
fnode->vendor = AIX;
fnode->flags = CF_PRINT_EVERY_TIME;
fnode->next = NULL;
}
endp = fnode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
}
free(buf);
already_parsed_format = 1;
return NULL;
}
/***************************************************************
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* Put each completed format_node onto the list starting at ->f_cooked
*/
static const char *format_parse(sf_node *sfn){
char *buf; /* temp copy of arg to hack on */
char *sep_loc; /* separator location: " \t,\n" */
char *walk;
const char *err; /* error code that could or did happen */
format_node *fnode;
int items;
int need_item;
static char errbuf[80]; /* for variable-text error message */
/*** prepare to operate ***/
buf = strdup(sfn->sf);
/*** sanity check and count items ***/
need_item = 1; /* true */
items = 0;
walk = buf;
do{
switch(*walk){
case ' ': case ',': case '\t': case '\n': case '\0':
/* Linux extension: allow \t and \n as delimiters */
if(need_item){
free(buf);
goto improper;
}
need_item=1;
break;
default:
if(need_item) items++;
need_item=0;
}
} while (*++walk);
if(!items){
free(buf);
goto empty;
}
#ifdef STRICT_LIST
if(need_item){ /* can't have trailing deliminator */
free(buf);
goto improper;
}
#else
if(need_item){ /* allow 1 trailing deliminator */
*--walk='\0'; /* remove the trailing deliminator */
}
#endif
/*** actually parse the list ***/
walk = buf;
while(items--){
format_node *endp;
char *equal_loc;
char *colon_loc;
if(!walk) catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
sep_loc = strpbrk(walk," ,\t\n");
/* if items left, then sep_loc is not in header override */
if(items && sep_loc) *sep_loc = '\0';
equal_loc = strpbrk(walk,"=");
if(equal_loc){ /* if header override */
*equal_loc = '\0';
equal_loc++;
}
colon_loc = strpbrk(walk,":");
if(colon_loc){ /* if width override */
*colon_loc = '\0';
colon_loc++;
if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc || atoi(colon_loc) <= 0){
free(buf);
goto badwidth;
}
}
fnode = do_one_spec(walk,equal_loc);
if(!fnode){
if(!*errbuf){ /* if didn't already create an error string */
snprintf(
errbuf,
sizeof(errbuf),
_("unknown user-defined format specifier \"%s\""),
walk
);
}
free(buf);
goto unknown;
}
if(colon_loc){
if(fnode->next){
free(buf);
goto notmacro;
}
// FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals)
fnode->width = atoi(colon_loc); // already verified to be a number
if(fnode->width <= 0) catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
}
endp = fnode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->f_cooked;
sfn->f_cooked = fnode;
walk = sep_loc ? sep_loc + 1 : NULL; /* point to next item, if any */
}
free(buf);
already_parsed_format = 1;
return NULL;
/* errors may cause a retry looking for AIX format codes */
if(0) unknown: err=errbuf;
if(0) empty: err=_("empty format list");
if(0) improper: err=_("improper format list");
if(0) badwidth: err=_("column widths must be unsigned decimal numbers");
if(0) notmacro: err=_("can not set width for a macro (multi-column) format specifier");
if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
return err;
}
/**************** Parse single sort specifier *******************/
static sort_node *do_one_sort_spec(const char *spec){
const format_struct *fs;
enum pids_sort_order reverse = PIDS_SORT_ASCEND;
if(*spec == '-'){
reverse = PIDS_SORT_DESCEND;
spec++;
} else if(*spec == '+'){
spec++;
}
fs = search_format_array(spec);
if(fs){
sort_node *thisnode;
thisnode = xmalloc(sizeof(sort_node));
thisnode->sr = fs->sr;
// next is a special pointer, called to help with rel enums
thisnode->xe = (int(*)(char*,proc_t*))fs->pr;
thisnode->reverse = reverse;
thisnode->next = NULL;
return thisnode;
}
return NULL; /* bad, spec not found */
}
/**************************************************************
* Used to parse long sorting options.
* Put each completed sort_node onto the list starting at ->s_cooked
*/
static const char *long_sort_parse(sf_node *sfn){
char *buf; /* temp copy of arg to hack on */
char *sep_loc; /* separator location: " \t,\n" */
char *walk;
sort_node *snode;
int items;
int need_item;
/*** prepare to operate ***/
buf = strdup(sfn->sf);
/*** sanity check and count items ***/
need_item = 1; /* true */
items = 0;
walk = buf;
do{
switch(*walk){
case ' ': case ',': case '\t': case '\n': case '\0':
if(need_item){
free(buf);
return _("improper sort list");
}
need_item=1;
break;
default:
if(need_item) items++;
need_item=0;
}
} while (*++walk);
if(!items){
free(buf);
return _("empty sort list");
}
#ifdef STRICT_LIST
if(need_item){ /* can't have trailing deliminator */
free(buf);
return _("improper sort list");
}
#else
if(need_item){ /* allow 1 trailing deliminator */
*--walk='\0'; /* remove the trailing deliminator */
}
#endif
/*** actually parse the list ***/
walk = buf;
while(items--){
sort_node *endp;
sep_loc = strpbrk(walk," ,\t\n");
if(sep_loc) *sep_loc = '\0';
snode = do_one_sort_spec(walk);
if(!snode){
free(buf);
return _("unknown sort specifier");
}
endp = snode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->s_cooked;
sfn->s_cooked = snode;
walk = sep_loc + 1; /* point to next item, if any */
}
free(buf);
already_parsed_sort = 1;
return NULL;
}
/************ pre-parse short sorting option *************/
/* Errors _must_ be detected so that the "O" option can try to
* reparse as formatting codes.
*/
static const char *verify_short_sort(const char *arg){
const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
char checkoff[256];
int i;
const char *walk;
int tmp;
if(strspn(arg,all) != strlen(arg)) return _("bad sorting code");
for(i=256; i--;) checkoff[i] = 0;
walk = arg;
for(;;){
tmp = *walk;
if(tmp < 0 || (size_t)tmp >= sizeof(checkoff)) return _("bad sorting code");
switch(tmp){
case '\0':
return NULL; /* looks good */
case '+':
case '-':
tmp = *(walk+1);
if(!tmp || tmp=='+' || tmp=='-') return _("bad sorting code");
break;
case 'P':
if(forest_type) return _("PPID sort and forest output conflict");
/* fall through */
default:
if(checkoff[tmp]) return _("bad sorting code"); /* repeated */
/* ought to check against already accepted sort options */
checkoff[tmp] = 1;
break;
}
walk++;
}
}
/************ parse short sorting option *************/
static const char *short_sort_parse(sf_node *sfn){
enum pids_sort_order direction = PIDS_SORT_ASCEND;
const char *walk;
int tmp;
sort_node *snode;
sort_node *endp;
const struct shortsort_struct *ss;
walk = sfn->sf;
for(;;){
tmp = *walk;
switch(tmp){
case '\0':
already_parsed_sort = 1;
return NULL;
case '+':
direction = PIDS_SORT_ASCEND;
break;
case '-':
direction = PIDS_SORT_DESCEND;
break;
default:
ss = search_shortsort_array(tmp);
if(!ss) return _("unknown sort specifier");
snode = do_one_sort_spec(ss->spec);
if(!snode) return _("unknown sort specifier");
snode->reverse = direction;
endp = snode; while(endp->next) endp = endp->next; /* find end */
endp->next = sfn->s_cooked;
sfn->s_cooked = snode;
direction = 0;
break;
}
walk++;
}
}
/******************* high-level below here *********************/
/*
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* Recursion is to preserve original order.
*/
static const char *parse_O_option(sf_node *sfn){
const char *err; /* error code that could or did happen */
if(sfn->next){
err = parse_O_option(sfn->next);
if(err) return err;
}
switch(sfn->sf_code){
case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
err = format_parse(sfn);
if(!err) already_parsed_format = 1;
break;
case SF_U_O: /*** format ***/
/* Can have -l -f f u... set already_parsed_format like DEC does */
if(already_parsed_format) return _("option -O can not follow other format options");
err = format_parse(sfn);
if(err) return err;
already_parsed_format = 1;
O_wrap(sfn,'u'); /* must wrap user format in default */
break;
case SF_B_O: /*** both ***/
if(have_gnu_sort || already_parsed_sort) err = _("multiple sort options");
else err = verify_short_sort(sfn->sf);
if(!err){ /* success as sorting code */
short_sort_parse(sfn);
already_parsed_sort = 1;
return NULL;
}
if(already_parsed_format){
err = _("option O is neither first format nor sort order");
break;
}
if(!format_parse(sfn)){ /* if success as format code */
already_parsed_format = 1;
O_wrap(sfn,'b'); /* must wrap user format in default */
return NULL;
}
break;
case SF_G_sort: case SF_B_m: /*** sort ***/
if(already_parsed_sort) err = _("multiple sort options");
else err = long_sort_parse(sfn);
already_parsed_sort = 1;
break;
default: /*** junk ***/
catastrophic_failure(__FILE__, __LINE__, _("please report this bug"));
}
return err; /* could be NULL */
}
/************ Main parser calls this to save lists for later **********/
/* store data for later and return 1 if arg looks non-standard */
int defer_sf_option(const char *arg, int source){
sf_node *sfn;
char buf[16];
int dist;
const format_struct *fs;
int need_item = 1;
sfn = xmalloc(sizeof(sf_node));
sfn->sf = strdup(arg);
sfn->sf_code = source;
sfn->s_cooked = NULL;
sfn->f_cooked = NULL;
sfn->next = sf_list;
sf_list = sfn;
if(source == SF_G_sort) have_gnu_sort = 1;
/* Now try to find an excuse to ignore broken Unix98 parsing. */
if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
do{
switch(*arg){
case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */
if(need_item) return 1; /* something wrong */
need_item=1;
break;
case '=':
if(need_item) return 1; /* something wrong */
return 0; /* broken Unix98 parsing is required */
default:
if(!need_item) break;
need_item=0;
dist = strcspn(arg,", =");
if(dist>15) return 1; /* something wrong, sort maybe? */
strncpy(buf,arg,dist); /* no '\0' on end */
buf[dist] = '\0'; /* fix that problem */
fs = search_format_array(buf);
if(!fs) return 1; /* invalid spec, macro or sort maybe? */
if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
}
} while (*++arg);
return 0; /* boring, Unix98 is no change */
}
/***** Since ps is not long-lived, the memory leak can be ignored. ******/
void reset_sortformat(void){
sf_list = NULL; /* deferred sorting and formatting */
format_list = NULL; /* digested formatting options */
sort_list = NULL; /* digested sorting options (redundant?) */
have_gnu_sort = 0;
already_parsed_sort = 0;
already_parsed_format = 0;
}
/***** Search format_list for findme, then insert putme after findme. ****/
static int fmt_add_after(const char *findme, format_node *putme){
format_node *walk;
if(!strcmp(format_list->name, findme)){
putme->next = format_list->next;
format_list->next = putme;
return 1; /* success */
}
walk = format_list;
while(walk->next){
if(!strcmp(walk->next->name, findme)){
putme->next = walk->next->next;
walk->next->next = putme;
return 1; /* success */
}
walk = walk->next;
}
return 0; /* fail */
}
/******* Search format_list for findme, then delete it. ********/
static int fmt_delete(const char *findme){
format_node *walk;
format_node *old;
if(!strcmp(format_list->name, findme)){
old = format_list;
format_list = format_list->next;
free(old);
return 1; /* success */
}
walk = format_list;
while(walk->next){
if(!strcmp(walk->next->name, findme)){
old = walk->next;
walk->next = walk->next->next;
free(old);
return 1; /* success */
}
walk = walk->next;
}
return 0; /* fail */
}
/************ Build a SysV format backwards. ***********/
#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
static const char *generate_sysv_list(void){
format_node *fn;
if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
return _("modifier -y without format -l makes no sense");
if(prefer_bsd_defaults){
if(format_flags) PUSH("cmd");
else PUSH("args");
PUSH("bsdtime");
if(!(format_flags & FF_Ul)) PUSH("stat");
}else{
if(format_flags & FF_Uf) PUSH("cmd");
else PUSH("ucmd");
PUSH("time");
}
PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */
if(format_flags & FF_Uf) PUSH("stime");
/* avoid duplicate columns from -FP and -Fly */
if(format_modifiers & FM_F){
/* if -FP take the Sun-style column instead (sorry about "sgi_p") */
if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */
/* if -Fly take the ADDR-replacement RSS instead */
if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
}
if(format_flags & FF_Ul){
PUSH("wchan");
}
/* since FM_y adds RSS anyway, don't do this hack when that is true */
if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
PUSH("sgi_rss");
fn = xmalloc(sizeof(format_node));
fn->width = 1;
fn->name = strdup(":");
fn->pr = NULL; /* checked for */
fn->vendor = AIX; /* yes, for SGI weirdness */
fn->flags = CF_PRINT_EVERY_TIME;
fn->next = format_list;
format_list=fn;
}
}
if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
PUSH("sz");
}
if(format_flags & FF_Ul){
if(format_modifiers & FM_y) PUSH("rss");
else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
else PUSH("addr_1");
}
if(format_modifiers & FM_c){
PUSH("pri"); PUSH("class");
}else if(format_flags & FF_Ul){
PUSH("ni");
if(personality & PER_IRIX_l) PUSH("priority");
else /* is this good? */ PUSH("opri");
}
// FIXME TODO XXX -- this is a serious problem
// These somehow got flipped around.
// The bug is in procps-3.1.1, procps-990211, prior too?
if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp");
if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
if(format_modifiers & FM_P) PUSH("psr");
if(thread_flags & TF_U_L) PUSH("lwp");
if(format_modifiers & FM_j){
PUSH("sid");
PUSH("pgid");
}
if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
if(thread_flags & TF_U_T) PUSH("spid");
PUSH("pid");
if(format_flags & FF_Uf){
if(personality & PER_SANE_USER) PUSH("user");
else PUSH("uid_hack");
}else if(format_flags & FF_Ul){
PUSH("uid");
}
if(format_flags & FF_Ul){
PUSH("s");
if(!(format_modifiers & FM_y)) PUSH("f");
}
if(format_modifiers & FM_M){
PUSH("label"); /* Mandatory Access Control */
}
return NULL;
}
/**************************************************************************
* Used to parse option O lists. Option O is shared between
* sorting and formatting. Users may expect one or the other.
* The "broken" flag enables a really bad Unix98 misfeature.
*/
const char *process_sf_options(void){
sf_node *sf_walk;
if(sf_list){
const char *err;
err = parse_O_option(sf_list);
if(err) return err;
}
if(format_list) catastrophic_failure(__FILE__, __LINE__, _("bug: must reset the list first"));
/* merge formatting info of sf_list into format_list here */
sf_walk = sf_list;
while(sf_walk){
format_node *fmt_walk;
fmt_walk = sf_walk->f_cooked;
sf_walk->f_cooked = NULL;
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
sf_walk = sf_walk->next;
}
/* merge sorting info of sf_list into sort_list here */
sf_walk = sf_list;
while(sf_walk){
sort_node *srt_walk;
srt_walk = sf_walk->s_cooked;
sf_walk->s_cooked = NULL;
if (srt_walk) {
sort_node *travler = srt_walk;
while (travler->next) travler = travler->next;
travler->next = sort_list;
sort_list = srt_walk;
}
sf_walk = sf_walk->next;
}
// Get somebody to explain how -L/-T is supposed to interact
// with sorting. Do the threads remain grouped, with sorting
// by process, or do the threads get sorted by themselves?
if(sort_list && (thread_flags&TF_no_sort)){
return _("tell <procps@freelists.org> what you expected");
}
// If nothing else, try to use $PS_FORMAT before the default.
if(!format_flags && !format_modifiers && !format_list){
char *tmp;
tmp = getenv("PS_FORMAT"); /* user override kills default */
if(tmp && *tmp){
const char *err;
sf_node sfn;
if(thread_flags&TF_must_use) return _("tell <procps@freelists.org> what you want (-L/-T, -m/m/H, and $PS_FORMAT)");
sfn.sf = tmp;
sfn.f_cooked = NULL;
err = format_parse(&sfn);
if(!err){
format_node *fmt_walk;
fmt_walk = sfn.f_cooked;
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
return NULL;
}
// FIXME: prove that this won't be hit on valid bogus-BSD options
fprintf(stderr, _("warning: $PS_FORMAT ignored. (%s)\n"), err);
}
}
if(format_list){
if(format_flags) return _("conflicting format options");
if(format_modifiers) return _("can not use output modifiers with user-defined output");
if(thread_flags&TF_must_use) return _("-L/-T with H/m/-m and -o/-O/o/O is nonsense");
return NULL;
}
do{
const char *spec;
switch(format_flags){
default: return _("conflicting format options");
/* These can be NULL, which enables SysV list generation code. */
case 0: spec=NULL; break;
case FF_Uf | FF_Ul: spec=sysv_fl_format; break;
case FF_Uf: spec=sysv_f_format; break;
case FF_Ul: spec=sysv_l_format; break;
/* These are NOT REACHED for normal -j processing. */
case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */
case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */
case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */
/* These are true BSD options. */
case FF_Bj: spec=bsd_j_format; break;
case FF_Bl: spec=bsd_l_format; break;
case FF_Bs: spec=bsd_s_format; break;
case FF_Bu: spec=bsd_u_format; break;
case FF_Bv: spec=bsd_v_format; break;
/* These are old Linux options. Option m is overloaded. */
case FF_LX: spec="OL_X"; break;
case FF_Lm: spec="OL_m"; break;
/* This is the sole FLASK security option. */
case FF_Fc: spec="FLASK_context"; break;
} /* end switch(format_flags) */
// not just for case 0, since sysv_l_format and such may be NULL
if(!spec) return generate_sysv_list();
do{
format_node *fmt_walk;
fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
while(fmt_walk){ /* put any nodes onto format_list in opposite way */
format_node *travler;
travler = fmt_walk;
fmt_walk = fmt_walk->next;
travler->next = format_list;
format_list = travler;
}
}while(0);
}while(0);
do{
format_node *fn;
if(format_modifiers & FM_j){
fn = do_one_spec("pgid", NULL);
if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
catastrophic_failure(__FILE__, __LINE__, _("internal error: no PID or PPID for -j option"));
fn = do_one_spec("sid", NULL);
if(!fmt_add_after("PGID", fn)) return _("lost my PGID");
}
if(format_modifiers & FM_y){
/* TODO: check for failure to do something, and complain if so */
fmt_delete("F");
fn = do_one_spec("rss", NULL);
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
}
if(format_modifiers & FM_c){
fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
fmt_delete("NI");
fn = do_one_spec("class", NULL);
if(!fmt_add_after("PRI", fn))
catastrophic_failure(__FILE__, __LINE__, _("internal error: no PRI for -c option"));
fmt_delete("PRI"); /* we want a different one */
fn = do_one_spec("pri", NULL);
if(!fmt_add_after("CLS", fn)) return _("lost my CLS");
}
if(thread_flags & TF_U_T){
fn = do_one_spec("spid", NULL);
if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use))
return _("-T with H/-m/m but no PID for SPID to follow");
}
if(thread_flags & TF_U_L){
fn = do_one_spec("lwp", NULL);
if(fmt_add_after("SID", fn)) goto did_lwp;
if(fmt_add_after("SESS", fn)) goto did_lwp;
if(fmt_add_after("PGID", fn)) goto did_lwp;
if(fmt_add_after("PGRP", fn)) goto did_lwp;
if(fmt_add_after("PPID", fn)) goto did_lwp;
if(fmt_add_after("PID", fn)) goto did_lwp;
if(thread_flags&TF_must_use)
return _("-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow");
did_lwp:
fn = do_one_spec("nlwp", NULL);
fmt_add_after("%CPU", fn);
}
if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style
fn = do_one_spec("label", NULL);
fn->next=format_list;
format_list=fn;
}
/* Do personality-specific translations not covered by format_flags.
* Generally, these only get hit when personality overrides unix output.
* That (mostly?) means the Digital and Debian personalities.
*/
if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
fn = do_one_spec("sgi_p", NULL);
if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
}
if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
fn = do_one_spec("user", NULL);
if(fmt_add_after("UID", fn)) fmt_delete("UID");
}
}while(0);
return NULL;
}

187
src/ps/stacktrace.c Normal file
View File

@@ -0,0 +1,187 @@
/*
* stacktrace.c - ps debugging additions
* Gnu debugger stack trace code provided by Peter Mattis
* <petm@CSUA.Berkeley.EDU> on Thu, 2 Nov 1995
*
* Modified for easy use 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 <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "common.h"
#define INTERACTIVE 0
#define STACK_TRACE 1
char *stored_prog_name = "you forgot to set \"program\"";
static int stack_trace_done;
/***********/
static void debug_stop(char **args){
execvp (args[0], args);
perror ("exec failed");
_exit (0);
}
/***********/
static void stack_trace_sigchld(int signum){
(void)signum;
stack_trace_done = 1;
}
/************/
static void stack_trace(char **args){
pid_t pid;
int in_fd[2];
int out_fd[2];
fd_set fdset;
fd_set readset;
struct timeval tv;
int sel, index, state;
char buffer[256];
char c;
stack_trace_done = 0;
signal(SIGCHLD, stack_trace_sigchld);
if((pipe (in_fd) == -1) || (pipe (out_fd) == -1)){
perror ("could open pipe");
_exit (0);
}
pid = fork ();
if (pid == 0){
close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
execvp (args[0], args); /* exec gdb */
perror ("exec failed");
_exit (0);
} else {
if(pid == (pid_t) -1){
perror ("could not fork");
_exit (0);
}
}
FD_ZERO (&fdset);
FD_SET (out_fd[0], &fdset);
write (in_fd[1], "backtrace\n", 10);
write (in_fd[1], "p x = 0\n", 8);
write (in_fd[1], "quit\n", 5);
index = 0;
state = 0;
for(;;){
readset = fdset;
tv.tv_sec = 1;
tv.tv_usec = 0;
sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
if (sel == -1) break;
if((sel > 0) && (FD_ISSET (out_fd[0], &readset))){
if(read (out_fd[0], &c, 1)){
switch(state){
case 0:
if(c == '#'){
state = 1;
index = 0;
buffer[index++] = c;
}
break;
case 1:
buffer[index++] = c;
if((c == '\n') || (c == '\r')){
buffer[index] = 0;
fprintf (stderr, "%s", buffer);
state = 0;
index = 0;
}
break;
default:
break;
}
}
}
else if(stack_trace_done) break;
}
close (in_fd[0]);
close (in_fd[1]);
close (out_fd[0]);
close (out_fd[1]);
_exit (0);
}
/************/
void debug(int method, char *prog_name){
pid_t pid;
char buf[16];
char *args[4] = { "gdb", NULL, NULL, NULL };
int x;
snprintf (buf, sizeof(buf), "%d", getpid ());
args[1] = prog_name;
args[2] = buf;
pid = fork ();
if(pid == 0){
switch (method){
case INTERACTIVE:
fprintf (stderr, "debug_stop\n");
debug_stop(args);
break;
case STACK_TRACE:
fprintf (stderr, "stack_trace\n");
stack_trace(args);
break;
}
_exit(0);
} else if(pid == (pid_t) -1){
perror ("could not fork");
return;
}
x = 1;
while(x); /* wait for debugger? */
}
#ifdef DEBUG
/************/
static void stack_trace_sigsegv(int signum){
(void)signum;
debug(STACK_TRACE, stored_prog_name);
}
/************/
void init_stack_trace(char *prog_name){
stored_prog_name = prog_name;
signal(SIGSEGV, stack_trace_sigsegv);
}
#endif

149
src/pwdx.c Normal file
View File

@@ -0,0 +1,149 @@
/*
* pwdx.c - print process working directory
* Copyright 2004 Nicholas Miell
*
* 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 <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "c.h"
#include "nls.h"
#include "xalloc.h"
#include "fileutils.h"
static void __attribute__ ((__noreturn__)) usage(FILE * out)
{
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options] pid...\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("pwdx(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
int check_pid_argument(char *input)
{
int skip = 0;
long pid;
char *end = NULL;
if (!strncmp("/proc/", input, 6))
skip = 6;
errno = 0;
pid = strtol(input + skip, &end, 10);
if (errno || input + skip == end || (end && *end))
return 1;
if (pid < 1)
return 1;
return 0;
}
int main(int argc, char *argv[])
{
int ch;
int retval = 0, i;
ssize_t alloclen = 128;
char *pathbuf;
static const struct option longopts[] = {
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
{NULL, 0, 0, 0}
};
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
while ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
switch (ch) {
case 'V':
printf(PROCPS_NG_VERSION);
return EXIT_SUCCESS;
case 'h':
usage(stdout);
default:
usage(stderr);
}
argc -= optind;
argv += optind;
if (argc == 0)
usage(stderr);
pathbuf = xmalloc(alloclen);
for (i = 0; i < argc; i++) {
char *s;
ssize_t len, buflen;
/* Constant 10 is the length of strings "/proc/" + "/cwd" */
char *buf;
buflen = 10 + strlen(argv[i]) + 1;
buf = xmalloc(buflen);
if (check_pid_argument(argv[i]))
xerrx(EXIT_FAILURE, _("invalid process id: %s"),
argv[i]);
/*
* At this point, all arguments are in the form
* /proc/NNNN or NNNN, so a simple check based on
* the first char is possible
*/
if (argv[i][0] != '/')
snprintf(buf, buflen, "/proc/%s/cwd", argv[i]);
else
snprintf(buf, buflen, "%s/cwd", argv[i]);
/*
* buf contains /proc/NNNN/cwd symlink name
* on entry, the target of that symlink on return
*/
while ((len = readlink(buf, pathbuf, alloclen)) == alloclen) {
alloclen *= 2;
pathbuf = xrealloc(pathbuf, alloclen);
}
free(buf);
if (len < 0) {
s = strerror(errno == ENOENT ? ESRCH : errno);
retval = EXIT_FAILURE;
fprintf(stderr, "%s: %s\n", argv[i], s);
continue;
} else {
pathbuf[len] = 0;
s = pathbuf;
}
printf("%s: %s\n", argv[i], s);
}
free(pathbuf);
return retval;
}

594
src/skill.c Normal file
View File

@@ -0,0 +1,594 @@
/*
* skill.c - send a signal to process
* 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 <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "c.h"
#include "fileutils.h"
#include "signals.h"
#include "strutils.h"
#include "nls.h"
#include "xalloc.h"
#include "rpmatch.h"
#include <proc/misc.h>
#include <proc/pids.h>
#define DEFAULT_NICE 4
struct run_time_conf_t {
int fast;
int interactive;
int verbose;
int warnings;
int noaction;
int debugging;
};
static int tty_count, uid_count, cmd_count, pid_count, namespace_count;
static int *ttys;
static uid_t *uids;
static const char **cmds;
static int *pids;
static char **namespaces;
static int ns_pid;
static struct procps_ns match_namespaces;
static int ns_flags = 0x3f;
#define ENLIST(thing,addme) do{ \
if(thing##_count < 0 || (size_t)thing##_count >= INT_MAX / sizeof(*thing##s)) \
xerrx(EXIT_FAILURE, _("integer overflow")); \
thing##s = xrealloc(thing##s, sizeof(*thing##s)*(thing##_count+1)); \
thing##s[thing##_count++] = addme; \
}while(0)
struct pids_info *Pids_info;
enum pids_item items[] = {
PIDS_ID_PID,
PIDS_ID_EUID,
PIDS_ID_EUSER,
PIDS_TTY,
PIDS_TTY_NAME,
PIDS_CMD};
enum rel_items {
EU_PID, EU_EUID, EU_EUSER, EU_TTY, EU_TTYNAME, EU_CMD};
static int my_pid;
static int sig_or_pri;
enum {
PROG_UNKNOWN,
PROG_SKILL,
PROG_SNICE
};
static int program = PROG_UNKNOWN;
static int parse_namespaces(char *optarg)
{
char *ptr = optarg, *tmp;
int len, id;
ns_flags = 0;
while (1) {
if (strchr(ptr, ',') == NULL) {
len = -1;
tmp = strdup(ptr);
} else {
len = strchr(ptr, ',') - ptr;
tmp = strndup(ptr, len);
}
id = procps_ns_get_id(tmp);
if (id == -1) {
fprintf(stderr, "%s is not a valid namespace\n", tmp);
free(tmp);
return 1;
}
ns_flags |= (1 << id);
ENLIST(namespace, tmp);
if (len == -1)
break;
ptr+= len + 1;
}
return 0;
}
static int match_intlist(const int value, const int len, int *list)
{
int i;
for(i=0; i<len; i++)
if (list[i] == value)
return 1;
return 0;
}
static int match_strlist(const char *value, const int len, const char **list)
{
int i;
for(i=0; i<len; i++)
if (strcmp(list[i], value) == 0)
return 1;
return 0;
}
static int match_ns(const int pid)
{
struct procps_ns proc_ns;
int found = 1;
int i;
if (procps_ns_read_pid(pid, &proc_ns) < 0)
xerrx(EXIT_FAILURE,
_("Unable to read process namespace information"));
for (i = 0; i < PROCPS_NS_COUNT; i++) {
if (ns_flags & (1 << i)) {
if (proc_ns.ns[i] != match_namespaces.ns[i])
found = 0;
}
}
return found;
}
#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, stack, Pids_info)
#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, stack, Pids_info)
static int ask_user(struct pids_stack *stack)
{
char *buf=NULL;
size_t len=0;
fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
PIDS_GETSTR(TTYNAME),
PIDS_GETSTR(EUSER),
PIDS_GETINT(PID),
PIDS_GETSTR(CMD));
fflush(stdout);
if (getline(&buf, &len, stdin) == -1) {
free(buf);
return 0;
}
if (rpmatch(buf) < 1) {
free(buf);
return 0;
}
free(buf);
return 1;
}
static void nice_or_kill(struct pids_stack *stack,
struct run_time_conf_t *run_time)
{
int failed;
if (run_time->interactive && !ask_user(stack))
return;
/* do the actual work */
errno = 0;
if (program == PROG_SKILL)
failed = kill(PIDS_GETINT(PID), sig_or_pri);
else
failed = setpriority(PRIO_PROCESS, PIDS_GETINT(PID), sig_or_pri);
if ((run_time->warnings && failed) || run_time->debugging || run_time->verbose) {
fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
PIDS_GETSTR(TTYNAME),
PIDS_GETSTR(EUSER),
PIDS_GETINT(PID),
PIDS_GETSTR(CMD));
perror("");
return;
}
if (run_time->interactive)
return;
if (run_time->noaction) {
printf("%d\n", PIDS_GETINT(PID));
return;
}
}
#undef PIDS_GETINT
#undef PIDS_GETSTR
/* debug function */
static void show_lists(void)
{
int i;
fprintf(stderr, "signal: %d\n", sig_or_pri);
fprintf(stderr, "%d TTY: ", tty_count);
if (ttys) {
i = tty_count;
while (i--) {
fprintf(stderr, "%d,%d%c", (ttys[i] >> 8) & 0xff,
ttys[i] & 0xff, i ? ' ' : '\n');
}
} else
fprintf(stderr, "\n");
fprintf(stderr, "%d UID: ", uid_count);
if (uids) {
i = uid_count;
while (i--)
fprintf(stderr, "%d%c", uids[i], i ? ' ' : '\n');
} else
fprintf(stderr, "\n");
fprintf(stderr, "%d PID: ", pid_count);
if (pids) {
i = pid_count;
while (i--)
fprintf(stderr, "%d%c", pids[i], i ? ' ' : '\n');
} else
fprintf(stderr, "\n");
fprintf(stderr, "%d CMD: ", cmd_count);
if (cmds) {
i = cmd_count;
while (i--)
fprintf(stderr, "%s%c", cmds[i], i ? ' ' : '\n');
} else
fprintf(stderr, "\n");
}
static void scan_procs(struct run_time_conf_t *run_time)
{
#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, reap->stacks[i], Pids_info)
#define PIDS_GETUNT(e) PIDS_VAL(EU_ ## e, u_int, reap->stacks[i], Pids_info)
#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i], Pids_info)
struct pids_fetch *reap;
int i, total_procs;
if (procps_pids_new(&Pids_info, items, 6) < 0)
xerrx(EXIT_FAILURE,
_("Unable to create pid Pids_info structure"));
if ((reap = procps_pids_reap(Pids_info, PIDS_FETCH_TASKS_ONLY)) == NULL)
xerrx(EXIT_FAILURE,
_("Unable to load process information"));
total_procs = reap->counts->total;
for (i=0; i < total_procs; i++) {
if (PIDS_GETINT(PID) == my_pid || PIDS_GETINT(PID) == 0)
continue;
if (uids && !match_intlist(PIDS_GETUNT(EUID), uid_count, (int *)uids))
continue;
if (ttys && !match_intlist(PIDS_GETINT(TTY), tty_count, ttys))
continue;
if (cmds && !match_strlist(PIDS_GETSTR(CMD), cmd_count, cmds))
continue;
if (namespaces && !match_ns(PIDS_GETINT(PID)))
continue;
nice_or_kill(reap->stacks[i], run_time);
}
#undef PIDS_GETINT
#undef PIDS_GETUNT
#undef PIDS_GETSTR
}
/* skill and snice help */
static void __attribute__ ((__noreturn__)) skillsnice_usage(FILE * out)
{
fputs(USAGE_HEADER, out);
if (program == PROG_SKILL) {
fprintf(out,
_(" %s [signal] [options] <expression>\n"),
program_invocation_short_name);
} else {
fprintf(out,
_(" %s [new priority] [options] <expression>\n"),
program_invocation_short_name);
}
fputs(USAGE_OPTIONS, out);
fputs(_(" -f, --fast fast mode (not implemented)\n"), out);
fputs(_(" -i, --interactive interactive\n"), out);
fputs(_(" -l, --list list all signal names\n"), out);
fputs(_(" -L, --table list all signal names in a nice table\n"), out);
fputs(_(" -n, --no-action do not actually kill processes; just print what would happen\n"), out);
fputs(_(" -v, --verbose explain what is being done\n"), out);
fputs(_(" -w, --warnings enable warnings (not implemented)\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_("Expression can be: terminal, user, pid, command.\n"
"The options below may be used to ensure correct interpretation.\n"), out);
fputs(_(" -c, --command <command> expression is a command name\n"), out);
fputs(_(" -p, --pid <pid> expression is a process id number\n"), out);
fputs(_(" -t, --tty <tty> expression is a terminal\n"), out);
fputs(_(" -u, --user <username> expression is a username\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_("Alternatively, expression can be:\n"), out);
fputs(_(" --ns <pid> match the processes that belong to the same\n"
" namespace as <pid>\n"), out);
fputs(_(" --nslist <ns,...> list which namespaces will be considered for\n"
" the --ns option; available namespaces are\n:"
" ipc, mnt, net, pid, user, uts\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
if (program == PROG_SKILL) {
fprintf(out,
_("\n"
"The default signal is TERM. Use -l or -L to list available signals.\n"
"Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
"Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"));
fprintf(out, USAGE_MAN_TAIL("skill(1)"));
} else {
fprintf(out,
_("\n"
"The default priority is +4. (snice +4 ...)\n"
"Priority numbers range from +20 (slowest) to -20 (fastest).\n"
"Negative priority numbers are restricted to administrative users.\n"));
fprintf(out, USAGE_MAN_TAIL("snice(1)"));
}
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
static int snice_prio_option(int *argc, char **argv)
{
int i = 1, nargs = *argc;
long prio = DEFAULT_NICE;
while (i < nargs) {
if ((argv[i][0] == '-' || argv[i][0] == '+')
&& isdigit(argv[i][1])) {
prio = strtol_or_err(argv[i],
_("failed to parse argument"));
if (prio < INT_MIN || INT_MAX < prio)
xerrx(EXIT_FAILURE,
_("priority %lu out of range"), prio);
memmove(argv + i, argv + i + 1,
sizeof(char *) * (nargs - i));
nargs--;
} else
i++;
}
*argc = nargs;
return (int)prio;
}
static void parse_options(int argc,
char **argv, struct run_time_conf_t *run_time)
{
int signo = -1;
int prino = DEFAULT_NICE;
int ch, i;
enum {
NS_OPTION = CHAR_MAX + 1,
NSLIST_OPTION,
};
static const struct option longopts[] = {
{"command", required_argument, NULL, 'c'},
{"debug", no_argument, NULL, 'd'},
{"fast", no_argument, NULL, 'f'},
{"interactive", no_argument, NULL, 'i'},
{"list", no_argument, NULL, 'l'},
{"no-action", no_argument, NULL, 'n'},
{"pid", required_argument, NULL, 'p'},
{"table", no_argument, NULL, 'L'},
{"tty", required_argument, NULL, 't'},
{"user", required_argument, NULL, 'u'},
{"ns", required_argument, NULL, NS_OPTION},
{"nslist", required_argument, NULL, NSLIST_OPTION},
{"verbose", no_argument, NULL, 'v'},
{"warnings", no_argument, NULL, 'w'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
if (argc < 2)
skillsnice_usage(stderr);
sig_or_pri = -1;
if (program == PROG_SNICE)
prino = snice_prio_option(&argc, argv);
else if (program == PROG_SKILL) {
signo = skill_sig_option(&argc, argv);
if (-1 < signo)
sig_or_pri = signo;
}
while ((ch =
getopt_long(argc, argv, "c:dfilnp:Lt:u:vwhV", longopts,
NULL)) != -1)
switch (ch) {
case 'c':
ENLIST(cmd, optarg);
break;
case 'd':
run_time->debugging = 1;
break;
case 'f':
run_time->fast = 1;
break;
case 'i':
run_time->interactive = 1;
break;
case 'l':
unix_print_signals();
exit(EXIT_SUCCESS);
case 'n':
run_time->noaction = 1;
break;
case 'p':
ENLIST(pid,
strtol_or_err(optarg,
_("failed to parse argument")));
break;
case 'L':
pretty_print_signals();
exit(EXIT_SUCCESS);
case 't':
{
struct stat sbuf;
char path[32];
snprintf(path, 32, "/dev/%s", optarg);
if (stat(path, &sbuf) >= 0
&& S_ISCHR(sbuf.st_mode)) {
ENLIST(tty, sbuf.st_rdev);
}
}
break;
case 'u':
{
struct passwd *passwd_data;
passwd_data = getpwnam(optarg);
if (passwd_data) {
ENLIST(uid, passwd_data->pw_uid);
}
}
break;
case NS_OPTION:
ns_pid = atoi(optarg);
if (ns_pid == 0) {
xwarnx(_("invalid pid number %s"), optarg);
skillsnice_usage(stderr);
}
if (procps_ns_read_pid(ns_pid, &match_namespaces) < 0) {
xwarnx(_("error reading reference namespace "
"information"));
skillsnice_usage(stderr);
}
break;
case NSLIST_OPTION:
if (parse_namespaces(optarg)) {
xwarnx(_("invalid namespace list"));
skillsnice_usage(stderr);
}
break;
case 'v':
run_time->verbose = 1;
break;
case 'w':
run_time->warnings = 1;
break;
case 'h':
skillsnice_usage(stdout);
case 'V':
fprintf(stdout, PROCPS_NG_VERSION);
exit(EXIT_SUCCESS);
default:
skillsnice_usage(stderr);
}
argc -= optind;
argv += optind;
for (i = 0; i < argc; i++) {
long num;
char *end = NULL;
errno = 0;
num = strtol(argv[0], &end, 10);
if (errno == 0 && argv[0] != end && end != NULL && *end == '\0') {
ENLIST(pid, num);
} else {
ENLIST(cmd, argv[0]);
}
argv++;
}
/* No more arguments to process. Must sanity check. */
if (!tty_count && !uid_count && !cmd_count && !pid_count && !ns_pid)
xerrx(EXIT_FAILURE, _("no process selection criteria"));
if ((run_time->fast | run_time->interactive | run_time->
verbose | run_time->warnings | run_time->noaction) & ~1)
xerrx(EXIT_FAILURE, _("general flags may not be repeated"));
if (run_time->interactive
&& (run_time->verbose | run_time->fast | run_time->noaction))
xerrx(EXIT_FAILURE, _("-i makes no sense with -v, -f, and -n"));
if (run_time->verbose && (run_time->interactive | run_time->fast))
xerrx(EXIT_FAILURE, _("-v makes no sense with -i and -f"));
if (run_time->noaction) {
program = PROG_SKILL;
/* harmless */
sig_or_pri = 0;
}
if (program == PROG_SNICE)
sig_or_pri = prino;
else if (sig_or_pri < 0)
sig_or_pri = SIGTERM;
}
/* main body */
int main(int argc, char ** argv)
{
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
struct run_time_conf_t run_time;
memset(&run_time, 0, sizeof(struct run_time_conf_t));
my_pid = getpid();
if (strcmp(program_invocation_short_name, "skill") == 0 ||
strcmp(program_invocation_short_name, "lt-skill") == 0)
program = PROG_SKILL;
else if (strcmp(program_invocation_short_name, "snice") == 0 ||
strcmp(program_invocation_short_name, "lt-snice") == 0)
program = PROG_SNICE;
#ifdef __CYGWIN__
else if (strcmp(program_invocation_short_name, "prockill") == 0 ||
strcmp(program_invocation_short_name, "lt-prockill") == 0)
program = PROG_KILL;
#endif
switch (program) {
case PROG_SNICE:
case PROG_SKILL:
setpriority(PRIO_PROCESS, my_pid, -20);
parse_options(argc, argv, &run_time);
if (run_time.debugging)
show_lists();
scan_procs(&run_time);
break;
default:
fprintf(stderr, _("skill: \"%s\" is not supported\n"),
program_invocation_short_name);
fprintf(stderr, USAGE_MAN_TAIL("skill(1)"));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

386
src/slabtop.c Normal file
View File

@@ -0,0 +1,386 @@
/*
* slabtop.c - utility to display kernel slab information.
*
* Chris Rivera <cmrivera@ufl.edu>
* Robert Love <rml@tech9.net>
*
* Copyright (C) 2003 Chris Rivera
*
* 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 <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <locale.h>
#include <ncurses.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "c.h"
#include "fileutils.h"
#include "nls.h"
#include "strutils.h"
#include <proc/slabinfo.h>
#define DEFAULT_SORT SLAB_NUM_OBJS
#define CHAINS_ALLOC 150
#define MAXTBL(t) (int)( sizeof(t) / sizeof(t[0]) )
#define DEFAULT_DELAY 3
static unsigned short Cols, Rows;
static struct termios Saved_tty;
static long Delay = 0;
static int Run_once = 0;
static struct slabinfo_info *Slab_info;
enum slabinfo_item Sort_item = DEFAULT_SORT;
enum slabinfo_sort_order Sort_Order = SLABINFO_SORT_DESCEND;
enum slabinfo_item Node_items[] = {
SLAB_NUM_OBJS, SLAB_ACTIVE_OBJS, SLAB_PERCENT_USED,
SLAB_OBJ_SIZE, SLAB_NUMS_SLABS, SLAB_OBJ_PER_SLAB,
SLAB_SIZE_TOTAL, SLAB_NAME,
/* next 2 are sortable but are not displayable,
thus they need not be represented in the Relative_enums */
SLAB_PAGES_PER_SLAB, SLAB_ACTIVE_SLABS };
enum Relative_node {
nod_OBJS, nod_AOBJS, nod_USE, nod_OSIZE,
nod_SLABS, nod_OPS, nod_SIZE, nod_NAME };
#define MAX_ITEMS (int)(sizeof(Node_items) / sizeof(Node_items[0]))
#define PRINT_line(fmt, ...) if (Run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__)
/*
* term_resize - set the globals 'Cols' and 'Rows' to the current terminal size
*/
static void term_resize (int unusused __attribute__ ((__unused__)))
{
struct winsize ws;
if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) {
Cols = ws.ws_col;
Rows = ws.ws_row;
} else {
Cols = 80;
Rows = 24;
}
}
static void sigint_handler (int unused __attribute__ ((__unused__)))
{
Delay = 0;
}
static void __attribute__((__noreturn__)) usage (FILE *out)
{
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" -d, --delay <secs> delay updates\n"), out);
fputs(_(" -o, --once only display once, then exit\n"), out);
fputs(_(" -s, --sort <char> specify sort criteria by character (see below)\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fputs(_("\nThe following are valid sort criteria:\n"), out);
fputs(_(" a: sort by number of active objects\n"), out);
fputs(_(" b: sort by objects per slab\n"), out);
fputs(_(" c: sort by cache size\n"), out);
fputs(_(" l: sort by number of slabs\n"), out);
fputs(_(" v: sort by (non display) number of active slabs\n"), out);
fputs(_(" n: sort by name\n"), out);
fputs(_(" o: sort by number of objects (the default)\n"), out);
fputs(_(" p: sort by (non display) pages per slab\n"), out);
fputs(_(" s: sort by object size\n"), out);
fputs(_(" u: sort by cache utilization\n"), out);
fprintf(out, USAGE_MAN_TAIL("slabtop(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
static void set_sort_stuff (const char key)
{
Sort_item = DEFAULT_SORT;
Sort_Order = SLABINFO_SORT_DESCEND;
switch (tolower(key)) {
case 'n':
Sort_item = SLAB_NAME;
Sort_Order = SLABINFO_SORT_ASCEND;
break;
case 'o':
Sort_item = SLAB_NUM_OBJS;
break;
case 'a':
Sort_item = SLAB_ACTIVE_OBJS;
break;
case 's':
Sort_item = SLAB_OBJ_SIZE;
break;
case 'b':
Sort_item = SLAB_OBJ_PER_SLAB;
break;
case 'p':
Sort_item = SLAB_PAGES_PER_SLAB;
break;
case 'l':
Sort_item = SLAB_NUMS_SLABS;
break;
case 'v':
Sort_item = SLAB_ACTIVE_SLABS;
break;
case 'c':
Sort_item = SLAB_SIZE_TOTAL;
break;
case 'u':
Sort_item = SLAB_PERCENT_USED;
break;
default:
break;
}
}
static void parse_opts (int argc, char **argv)
{
static const struct option longopts[] = {
{ "delay", required_argument, NULL, 'd' },
{ "sort", required_argument, NULL, 's' },
{ "once", no_argument, NULL, 'o' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }};
int o;
while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) {
switch (o) {
case 'd':
if (Run_once)
xerrx(EXIT_FAILURE, _("Cannot combine -d and -o options"));
errno = 0;
Delay = strtol_or_err(optarg, _("illegal delay"));
if (Delay < 1)
xerrx(EXIT_FAILURE, _("delay must be positive integer"));
break;
case 's':
set_sort_stuff(optarg[0]);
break;
case 'o':
if (Delay != 0)
xerrx(EXIT_FAILURE, _("Cannot combine -d and -o options"));
Run_once=1;
break;
case 'V':
printf(PROCPS_NG_VERSION);
exit(EXIT_SUCCESS);
case 'h':
usage(stdout);
default:
usage(stderr);
}
}
if (optind != argc)
usage(stderr);
if (!Run_once && Delay == 0)
Delay = DEFAULT_DELAY;
}
static void print_summary (void)
{
#define totalVAL(e,t) SLABINFO_VAL(e, t, p, Slab_info)
enum slabinfo_item items[] = {
SLABS_ACTIVE_OBJS, SLABS_NUM_OBJS,
SLABS_ACTIVE_SLABS, SLABS_NUMS_SLABS,
SLABS_CACHES_ACTIVE, SLABS_CACHES_TOTAL,
SLABS_SIZE_ACTIVE, SLABS_SIZE_TOTAL,
SLABS_OBJ_SIZE_MIN, SLABS_OBJ_SIZE_AVG,
SLABS_OBJ_SIZE_MAX
};
enum rel_items {
tot_AOBJS, tot_OBJS, tot_ASLABS, tot_SLABS,
tot_ACACHES, tot_CACHES, tot_ACTIVE, tot_TOTAL,
tot_MIN, tot_AVG, tot_MAX
};
struct slabinfo_stack *p;
if (!(p = procps_slabinfo_select(Slab_info, items, MAXTBL(items))))
xerrx(EXIT_FAILURE, _("Error getting slab summary results"));
PRINT_line(" %-35s: %u / %u (%.1f%%)\n"
, /* Translation Hint: Next five strings must not
* exceed a length of 35 characters. */
/* xgettext:no-c-format */
_("Active / Total Objects (% used)")
, totalVAL(tot_AOBJS, u_int)
, totalVAL(tot_OBJS, u_int)
, 100.0 * totalVAL(tot_AOBJS, u_int) / totalVAL(tot_OBJS, u_int));
PRINT_line(" %-35s: %u / %u (%.1f%%)\n"
, /* xgettext:no-c-format */
_("Active / Total Slabs (% used)")
, totalVAL(tot_ASLABS, u_int)
, totalVAL(tot_SLABS, u_int)
, 100.0 * totalVAL(tot_ASLABS, u_int) / totalVAL(tot_SLABS, u_int));
PRINT_line(" %-35s: %u / %u (%.1f%%)\n"
, /* xgettext:no-c-format */
_("Active / Total Caches (% used)")
, totalVAL(tot_ACACHES, u_int)
, totalVAL(tot_CACHES, u_int)
, 100.0 * totalVAL(tot_ACACHES, u_int) / totalVAL(tot_CACHES, u_int));
PRINT_line(" %-35s: %.2fK / %.2fK (%.1f%%)\n"
, /* xgettext:no-c-format */
_("Active / Total Size (% used)")
, totalVAL(tot_ACTIVE, ul_int) / 1024.0
, totalVAL(tot_TOTAL, ul_int) / 1024.0
, 100.0 * totalVAL(tot_ACTIVE, ul_int) / totalVAL(tot_TOTAL, ul_int));
PRINT_line(" %-35s: %.2fK / %.2fK / %.2fK\n\n"
, _("Minimum / Average / Maximum Object")
, totalVAL(tot_MIN, u_int) / 1024.0
, totalVAL(tot_AVG, u_int) / 1024.0
, totalVAL(tot_MAX, u_int) / 1024.0);
#undef totalVAL
}
static void print_headings (void)
{
/* Translation Hint: Please keep alignment of the
* following intact. */
PRINT_line("%-78s\n", _(" OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME"));
}
static void print_details (struct slabinfo_stack *stack)
{
#define nodeVAL(e,t) SLABINFO_VAL(e, t, stack, Slab_info)
PRINT_line("%6u %6u %3u%% %7.2fK %6u %8u %9luK %-23s\n"
, nodeVAL(nod_OBJS, u_int)
, nodeVAL(nod_AOBJS, u_int)
, nodeVAL(nod_USE, u_int)
, nodeVAL(nod_OSIZE, u_int) / 1024.0
, nodeVAL(nod_SLABS, u_int)
, nodeVAL(nod_OPS, u_int)
, nodeVAL(nod_SIZE, ul_int) / 1024
, nodeVAL(nod_NAME, str));
return;
#undef nodeVAL
}
int main(int argc, char *argv[])
{
int is_tty, rc = EXIT_SUCCESS;
unsigned short old_rows;
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
parse_opts(argc, argv);
if (procps_slabinfo_new(&Slab_info) < 0)
xerr(EXIT_FAILURE, _("Unable to create slabinfo structure"));
if (!Run_once) {
is_tty = isatty(STDIN_FILENO);
if (is_tty && tcgetattr(STDIN_FILENO, &Saved_tty) == -1)
xwarn(_("terminal setting retrieval"));
old_rows = Rows;
term_resize(0);
initscr();
resizeterm(Rows, Cols);
signal(SIGWINCH, term_resize);
signal(SIGINT, sigint_handler);
}
do {
struct slabinfo_reaped *reaped;
struct timeval tv;
fd_set readfds;
int i;
if (!(reaped = procps_slabinfo_reap(Slab_info, Node_items, MAXTBL(Node_items)))) {
xwarn(_("Unable to get slabinfo node data"));
rc = EXIT_FAILURE;
break;
}
if (!(procps_slabinfo_sort(Slab_info, reaped->stacks, reaped->total, Sort_item, Sort_Order))) {
xwarn(_("Unable to sort slab nodes"));
rc = EXIT_FAILURE;
break;
}
if (Run_once) {
print_summary();
print_headings();
for (i = 0; i < reaped->total; i++)
print_details(reaped->stacks[i]);
break;
}
if (old_rows != Rows) {
resizeterm(Rows, Cols);
old_rows = Rows;
}
move(0, 0);
print_summary();
attron(A_REVERSE);
print_headings();
attroff(A_REVERSE);
for (i = 0; i < Rows - 8 && i < reaped->total; i++)
print_details(reaped->stacks[i]);
refresh();
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
tv.tv_sec = Delay;
tv.tv_usec = 0;
if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) {
char c;
if (read(STDIN_FILENO, &c, 1) != 1
|| (c == 'Q' || c == 'q'))
break;
set_sort_stuff(c);
}
// made zero by sigint_handler()
} while (Delay);
if (!Run_once) {
if (is_tty)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Saved_tty);
endwin();
}
procps_slabinfo_unref(&Slab_info);
return rc;
}

1068
src/sysctl.c Normal file

File diff suppressed because it is too large Load Diff

232
src/tload.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* tload.c - terminal version of xload
*
* Options:
* -s initial scaling exponent (default = 6)
* -d delay
*
* Copyright (c) 1992 Branko Lankester
* /proc changes by David Engel (david@ods.com)
* Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu)
*
* 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 <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <limits.h>
#include "c.h"
#include "fileutils.h"
#include "nls.h"
#include "strutils.h"
#include "xalloc.h"
#include <proc/misc.h>
static char *screen;
static int nrows = 25;
static int ncols = 80;
static int scr_size;
static int fd = STDOUT_FILENO;
static unsigned int dly = 5;
static jmp_buf jb;
static void alrm(int signo __attribute__ ((__unused__)))
{
signal(SIGALRM, alrm);
alarm(dly);
}
static void setsize(int i)
{
struct winsize win;
signal(SIGWINCH, setsize);
if (ioctl(fd, TIOCGWINSZ, &win) != -1) {
if (win.ws_col > 0)
ncols = win.ws_col;
if (win.ws_row > 0)
nrows = win.ws_row;
}
if (ncols < 2 || ncols >= INT_MAX)
xerrx(EXIT_FAILURE, _("screen too small or too large"));
if (nrows < 2 || nrows >= INT_MAX / ncols)
xerrx(EXIT_FAILURE, _("screen too small or too large"));
scr_size = nrows * ncols;
if (scr_size < 2)
xerrx(EXIT_FAILURE, _("screen too small"));
if (screen == NULL)
screen = (char *)xmalloc(scr_size);
else
screen = (char *)xrealloc(screen, scr_size);
memset(screen, ' ', scr_size - 1);
*(screen + scr_size - 2) = '\0';
if (i)
longjmp(jb, 1);
}
static void __attribute__ ((__noreturn__)) usage(FILE * out)
{
fputs(USAGE_HEADER, out);
fprintf(out,
_(" %s [options] [tty]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" -d, --delay <secs> update delay in seconds\n"), out);
fputs(_(" -s, --scale <num> vertical scale\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("tload(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
int lines, row, col = 0;
int i, opt;
double av[3];
static double max_scale = 0, scale_fact;
long tmpdly;
static const struct option longopts[] = {
{"scale", required_argument, NULL, 's'},
{"delay", required_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
while ((opt =
getopt_long(argc, argv, "s:d:Vh", longopts, NULL)) != -1)
switch (opt) {
case 's':
max_scale = strtod_or_err(optarg, _("failed to parse argument"));
if (max_scale < 0)
xerrx(EXIT_FAILURE, _("scale cannot be negative"));
break;
case 'd':
tmpdly = strtol_or_err(optarg, _("failed to parse argument"));
if (tmpdly < 1)
xerrx(EXIT_FAILURE, _("delay must be positive integer"));
else if (UINT_MAX < tmpdly)
xerrx(EXIT_FAILURE, _("too large delay value"));
dly = tmpdly;
break;
case 'V':
printf(PROCPS_NG_VERSION);
return EXIT_SUCCESS;
break;
case 'h':
usage(stdout);
default:
usage(stderr);
}
if (argc > optind)
if ((fd = open(argv[optind], O_WRONLY)) == -1)
xerr(EXIT_FAILURE, _("can not open tty"));
setsize(0);
if (max_scale == 0)
max_scale = nrows;
scale_fact = max_scale;
setjmp(jb);
col = 0;
alrm(0);
while (1) {
int rc;
if (scale_fact < max_scale)
scale_fact *= 2.0; /* help it drift back up. */
if ((rc = procps_loadavg(&av[0], &av[1], &av[2])) < 0)
{
if (rc == -ENOENT)
xerrx(EXIT_FAILURE,
_("Load average file /proc/loadavg does not exist"));
else
xerrx(EXIT_FAILURE,
_("Unable to get load average"));
}
do {
lines = av[0] * scale_fact;
row = nrows - 1;
while (0 <= --lines) {
*(screen + row * ncols + col) = '*';
if (--row < 0) {
scale_fact /= 2.0;
break;
}
}
} while (0 <= lines);
while (row >= 0)
*(screen + row-- * ncols + col) = ' ';
for (i = 1;; ++i) {
char *p;
row = nrows - (i * scale_fact);
if (row < 0 || row >= nrows)
break;
if (*(p = screen + row * ncols + col) == ' ')
*p = '-';
else
*p = '=';
}
if (++col == ncols) {
--col;
memmove(screen, screen + 1, scr_size - 1);
for (row = nrows - 2; row >= 0; --row)
*(screen + row * ncols + col) = ' ';
}
i = snprintf(screen, scr_size, " %.2f, %.2f, %.2f", av[0], av[1], av[2]);
if (i > 0 && i < scr_size)
screen[i] = ' ';
if (write(fd, "\033[H", 3) < 0)
xerr(EXIT_FAILURE, _("writing to tty failed"));
if (write(fd, screen, scr_size - 1) < 0)
xerr(EXIT_FAILURE, _("writing to tty failed"));
pause();
}
}

377
src/top/README.top Normal file
View File

@@ -0,0 +1,377 @@
This file summarizes changes to the top program and supporting documentation
introduced on March 31, 2011.
Contents:
DOCUMENT Changes
INTERNAL Improvements
EXTERNAL Improvements
BUGS Previously Fixed and Preserved
BUGS Newly/Nearly Fixed
BUGS/WISH-LISTS That Should Go Bye-bye
BUGS FIXED You Didn't Know You Had
OTHER Changes, Hopefully They Won't Bite You
BENCHMARKS
DOCUMENT Changes =========================================================
. The entire file was cleaned up, standardized and expanded to include:
- a new section "2. SUMMARY Display" added for symmetry with Fields
- nine new fields were added to section "3a. DESCRIPTIONS of Fields"
- a new section "3b. MANAGING Fields" replaced the obsolete section
"2b. SELECTING and ORDERING Columns"
- section "5c. SCROLLING a Window" was added for that new feature
. I don't know when the explanations for CODE and DATA were changed to
show 'virtual' memory, but I think there's a reason their alternate
names contain the word 'resident'. Thus they were changed back to
say 'physical memory'.
. And as I indicated in a previous email, the former string identifier
'ME' was restored as were the 'h' key/command conventions (vs. <h>).
Oops, the 'h' key/command conventions remain restored, but subsequent
testing revealed problems with the .ME string identifier. Thus, it was
changed to .WE (along with the companion .Me/.We id).
. Also previously mentioned, the 'man2html' program translates top.1 to
HTML with near perfect fidelity. I take that to mean there should be
no problems with the top.1 source on most other platforms.
To further improve translation to HTML, several .Bd and .Ed macros
were added to preserve literal (fixed width) spacing.
INTERNAL Improvements ====================================================
. The old restriction of 26 fields has been lifted. With this new-top
100+ fields are now possible. It currently supports up to 55, of
which 35 are in use. Adding a new field is almost too easy.
. Task row construction has been considerably improved -- both from
a programming perspective and a performance perspective.
. The column highlighting costs for sort field visibility were
virtually eliminated.
An optional define (USE_X_COLHDR) can be enabled to completely
eliminate any costs associated with the 'x' command toggle.
. The management of the HST_t structures, used for %cpu calculations,
was optimized with a hashing scheme. Thus the need for a qsort then
a binary search in each frame was completely eliminated.
An optional define can restore the former qsort/bsearch approach but
with an internal inlined binary search function offering substantially
better performance than the old top.
. This far more capable new-top executable is no larger than old top.
. The above combine to produce substantially improved performance
whose details are documented below under BENCHMARKS.
EXTERNAL Improvements ====================================================
. Field management has been completely redesigned. It's now embodied
on a single screen where display-ability, position and sort selection
can be handled in one place -- for all windows at one time!
This function is dependent on cursor motion keys and should a device
not have the customary arrow keys, alternatives are provided and
documented under "Operation" near the beginning of the man page.
. The following new fields have been added:
Group Id
Minor Page Faults
Number of Threads
Process Group Id
Real User Id
Saved User Id
Saved User Name
Session Id
Tty Process Group Id
. Scrolling keys now allow one to move the view of any window vertically
or horizontally to reveal any desired task or column. Previously, only
some tasks were viewable even with reversible, selectable sort columns.
Each of the four windows is capable of maintaining its own scrolled
coordinates and an optional toggle ('C') displays a message aiding
navigation within the available tasks and displayable fields.
. User interactive line oriented input now provides for true line
editing supported by these new keys:
Left/Right arrow keys, Delete key, Backspace and
Home/End keys (likely limited to xterm, not terminal)
. User filtering via the -u | -U interactive commands is now window
based which means that different windows could be used to filter
different users.
. Signal handling has been normalized and is now consistent regardless
of the particular top screen a user may have been using.
. The 'i' toggle now shows any task that has used *some* cpu since the
last screen update. It's no longer limited to just running tasks.
. The summary area 'task states' line now reflects either 'Threads'
or 'Tasks' depending on the -H toggle.
BUGS Previously Fixed and Preserved ======================================
( but not necessarily literally)
. 228822, suspending top leaves xterm in slightly messed-up state
. 256376, segfaults, if the xterm is to small
. 320289, segv on sigwinch
. 351065, wrong highlight 1st column (escape characters displayed)
. 358724, accepts extra numeric args
. 378695, seg fault if "/proc" is not mounted
. 426782, UID field is too narrow
. 458986, should check xterm for EOF/EIO
. 459890, Irix mode should use %#4.1f when threads shown
BUGS Newly/Nearly Fixed ==================================================
. 225542, 'Unknown command' message blocks further commands
The message is now displayed using usleep for 1.25 seconds, instead
of the former full 2 seconds. And while it still blocks further
commands, the delay is much more tolerable.
Can we consider this bug 'nearly' fixed?
. 410292, interface error when using backspace
Full line editing was added but could be disabled via a #define.
And via that define, even under basic termios support, the backspace
problem was cured.
. 567509, top idle command ('i') not working for threaded programs
Since the 'i' command now reflects tasks that have used *some* cpu,
and is no longer dependent on an 'R' state, I *believe/hope* this
bug has been swatted.
BUGS/WISH-LISTS That Should Go Bye-bye ===================================
. 340751, wish for hostname to benefit multiple top sessions
Craig's suggestion regarding symlinks is the perfect solution.
How dare Craig say that the solution was "not ideal" !
. 586497, wish for graceful degradation on small screen sizes
This objective could be accomplished by setting up 2 symlinks for
top, personalizing them for the 2 tiny phone displays, then writing
the respective configuration files.
I shudder at the programming effort suggested by Paul. And when it
was done you'd find everybody else would have different criteria.
BUGS FIXED You Didn't Know You Had =======================================
. Without amplifying the dirty details, the long standing occasionally
reported display corruption, and an unreported source of performance
degradation, has been eliminated. The cure is in the elimination of
the Pseudo_cols variable and the improved PUFF macro.
. Line oriented input was not sensitive to screen width. Thus a user
could hold down any key and ultimately line wrap, overwriting the
columns header and the entire screen. New top prevents this.
. User filtering (-u|-U) via a user ID (not name) now validates that
number. The old-top just made sure it was numeric, then blindly
displayed no matching users (i.e. an empty window).
. The threads toggle ('H') is no longer window based but more properly
applies to all windows. The previous implementation produced the
following aberration if multiple windows were being shown:
. -H would be acknowledged and applied to all visible windows
. keying 'a' or 'w' would silently turn it off
. then keying -H would turn it back on, but the user expected off
. If you hit ^Z on any help or fields screen to suspend old-top, after
issuing 'fg' you would then be left with a seemingly hung application
inviting ^C. In truth, one could recover with the space bar, but that
was far from intuitive.
. The old-top consistently writes 1 extra byte for each task row or 1
byte too few for columns headers, depending on your perspective.
The new top writes the same number of bytes for each.
. By failing to clear to eol, old top left the display in a terrible
state after exiting a 'fields' screen when only a few columns were
being displayed.
. The old-top used a zero value for the L_NONE library flag which could
cause repeated rebuilding of columns headers with each frame. In truth,
this was not likely to happen in real life since only two fields actually
used that flag. However, if it did happen, performance could be degraded
by 800%.
OTHER Changes, Hopefully They Won't Bite You =============================
. The undocumented TOPRC environment variable is no longer supported.
Any similar need can be met through a symlink alias.
. The use of environment variables to override terminal size is now
off by default but could be enabled through '#define TTYGETENVYES'.
. The global 'bold enable' toggle is active by default and thus agrees
with the documentation. It's been wrong ever since Al's wholesale
'cosmetic' changes in procps-3.2.2.
. Task defaults now show bold (not reverse) and row highlighting.
This agrees with what was always stated in the documentation.
. The 'H' toggle (thread mode) is not persistent. Persistence can be
achieved with a simple shell script employing the -H switch.
. Then 'g' and 'G' commands were reversed to reflect their likely use.
BENCHMARKS ===============================================================
Tested as root with nice -10 and using only common fields
( on a pretty old, slow laptop under Debian Lenny )
but rcfiles specified identical sort fields and identical
settings for the 'B', 'b', 'x' and 'y' toggles (even though
the defaults are not necessarily identical).
In every case new-top outperforms old-top, but I've shown %
improvements for only the most significant. Those cases mostly
involve colors with both row & column highlighting. I suggested
above that the highlighting cost was virtually eliminated in
new-top, and these tests bare that out.
Note the much smaller differences for new-top between the 24x80
window results and full screen (but don't mix apples_terminal
with oranges_xterm). This is a reflection of the simplification
of task row construction, also mentioned above.
It's always been the case that any top in an xterm outperforms
that top under the terminal application, even when the xterm
provides additional rows and columns. It's true below with
Gnome and it was true nine years ago under KDE.
----------------------------------------------------------
The following comparisons were run with:
100 tasks & 160 threads
-d0 -n5000
new-top old-top
xterm 24x80
a 1 win, lflgs_none 11.2 secs 51.8 secs + 462.6%
1 win, default 61.0 secs 66.8 secs
1 win, colors w/ x+y 61.3 secs 83.0 secs + 135.4%
1 win, thread mode 88.3 secs 94.2 secs
b 1 win, every field on 99.7 secs 106.0 secs
1 win, cmdline 71.2 secs 76.6 secs
4 wins, defaults 101.3 secs 107.2 secs
4 wins, colors w/ x+y 101.5 secs 122.8 secs + 121.0%
xterm, full screen (53x170)
a 1 win, lflgs_none 15.9 secs 54.2 secs + 340.9%
1 win, default 70.0 secs 73.2 secs
1 win, colors w/ x+y 69.4 secs 131.3 secs + 189.2%
1 win, thread mode 97.6 secs 102.6 secs
c 1 win, every field on 122.1 secs 128.1 secs
1 win, cmdline 80.8 secs 83.7 secs
4 wins, defaults 111.4 secs 115.8 secs
4 wins, colors w/ x+y 112.0 secs 172.9 secs + 154.4%
terminal 24x80
a 1 win, lflgs_none 8.9 secs 58.6 secs + 658.4%
1 win, default 70.1 secs 80.3 secs
1 win, colors w/ x+y 70.6 secs 157.3 secs + 222.8%
1 win, thread mode 104.7 secs 120.5 secs
b 1 win, every field on 111.2 secs 134.5 secs
1 win, cmdline 83.8 secs 94.5 secs
4 wins, defaults 125.6 secs 146.7 secs
4 wins, colors w/ x+y 125.6 secs 206.9 secs + 176.7%
terminal, full screen (39x125)
a 1 win, lflgs_none 9.1 secs 60.6 secs + 665.9%
1 win, default 74.3 secs 88.0 secs
1 win, colors w/ x+y 73.9 secs 314.5 secs + 425.6%
1 win, thread mode 113.0 secs 140.9 secs
b 1 win, every field on 117.7 secs 154.9 secs
1 win, cmdline 87.4 secs 107.2 secs
4 wins, defaults 139.1 secs 166.7 secs
4 wins, colors w/ x+y 157.3 secs 423.2 secs + 269.0%
----------------------------------------------------------
The following comarisons were run with:
300 tasks & 360 threads
-d0 -n3000
new-top old-top
xterm, full screen (53x170)
a 1 win, lflgs_none 14.3 secs 79.0 secs + 552.4%
1 win, default 101.1 secs 104.5 secs
1 win, colors w/ x+y 101.3 secs 140.0 secs + 138.2%
1 win, thread mode 120.1 secs 123.1 secs
c 1 win, every field on 179.8 secs 185.6 secs
1 win, cmdline 124.9 secs 132.8 secs
4 wins, defaults 174.8 secs 179.2 secs
4 wins, colors w/ x+y 175.0 secs 215.2 secs + 123.0%
terminal, full screen (39x125)
a 1 win, lflgs_none 12.3 secs 98.5 secs + 800.8%
1 win, default 117.4 secs 134.0 secs
1 win, colors w/ x+y 111.6 secs 296.1 secs + 265.3%
1 win, thread mode 141.3 secs 155.3 secs
b 1 win, every field on 197.7 secs 204.8 secs
1 win, cmdline 143.9 secs 157.3 secs
4 wins, defaults 204.0 secs 226.2 secs
4 wins, colors w/ x+y 216.9 secs 434.5 secs + 200.3%
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
notes:
a these results represent the library flags L_NONE zero value and
thus the hidden cost of rebuilding column headers w/ every frame
b while every common field was turned on, not all fields could be
displayed due to limited screen width
c only in a full screen xterm window could all common fields
actually be displayed
BENCHMARKS, Redux (for NLS) ==============================================
December, 2011 benchmarks produced on a much more modern
platform containing:
Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz
SMP with 4 cpus
reflected in the substantially reduced elapsed times.
Tested as root with nice -10 and using only common fields
but rcfiles specified identical sort fields and identical
settings for the 'B', 'b', 'x' and 'y' toggles (even though
the defaults are not necessarily identical).
Each test was run outside of X-windows at a linux console
offering 48 rows and 170 columns. This was done to reduce
contention which sometimes made comparisons problematic.
old-top = procps-3.2.8 (debian patched and memory leaking)
new-top = procps-ng-3.3.2 with NLS support
----------------------------------------------------------
The following comparisons were run with
-d0 -n5000
140 tasks & 275 threads
linux console (48x170) new-top old-top
d 1 win, lflgs_none 2.6 secs 15.0 secs + 577.0%
1 win, default 16.1 secs 19.3 secs
1 win, colors w/ x+y 16.6 secs 35.0 secs + 210.8%
e 1 win, show cpus 16.2 secs 20.1 secs + 124.1%
1 win, thread mode 31.8 secs 34.1 secs
f 1 win, every field on 30.5 secs 34.0 secs
1 win, cmdline 19.9 secs 23.1 secs
4 wins, default 31.9 secs 35.2 secs
4 wins, colors w/ x+y 29.2 secs 47.4 secs + 162.3%
g 1 win, b&w w/ bold x 30.0 secs 33.2 secs
h 1 win, scroll msg on 31.1 secs 33.9 secs
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
notes:
d these represent the same anamoly as the original 'a' footnote
e these represent the '1' toggle, where each of 4 cpus was shown
(not possible on the original uniprocessor)
f every common field was turned on and all fields were visible
g on a black and white display, sort column was shown in bold
(further proof of column highlighting improvements)
h similar to 'g', but new top was showing scroll msg
(old top has no such provision)

7202
src/top/top.c Normal file

File diff suppressed because it is too large Load Diff

784
src/top/top.h Normal file
View File

@@ -0,0 +1,784 @@
/* top.h - Header file: show Linux processes */
/*
* Copyright (c) 2002-2022, by: Jim Warner <james.warner@comcast.net
*
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program 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 Library General Public License for more details.
*/
/* For contributions to this program, the author wishes to thank:
* Craig Small, <csmall@dropbear.xyz>
* Albert D. Cahalan, <albert@users.sf.net>
* Sami Kerola, <kerolasa@iki.fi>
*/
#ifndef _Itop
#define _Itop
/* Defines represented in configure.ac ----------------------------- */
//#define BOOST_MEMORY /* enable extra precision in memory fields */
//#define BOOST_PERCNT /* enable extra precision for two % fields */
//#define ORIG_TOPDEFS /* with no rcfile retain original defaults */
//#define SIGNALS_LESS /* favor reduced signal load over response */
/* Development/Debugging defines ----------------------------------- */
//#define ATEOJ_RPTSTD /* report on some miscellany at end-of-job */
//#define BOT_DEAD_ZAP /* zap Ctrl bottom window when target dies */
//#define BOT_STRV_OFF /* don't emphasize strv w/ focus if spaces */
//#define CASEUP_HEXES /* show all those hex values in upper case */
//#define CASEUP_SUFIX /* show time/mem/cnts suffix in upper case */
//#define EQUCOLHDRYES /* yes, equalize the column header lengths */
//#define FOCUS_HARD_Y /* 'F' will avoid topmost task distortions */
//#define FOCUS_TREE_X /* 'F' resets forest view indentation to 0 */
//#define FOCUS_VIZOFF /* 'F' doesn't provide the visual clue '|' */
//#define GETOPTFIX_NO /* do not address getopt_long deficiencies */
//#define INSP_JUSTNOT /* do not smooth unprintable right margins */
//#define INSP_OFFDEMO /* disable demo screens, issue msg instead */
//#define INSP_SAVEBUF /* preserve 'Insp_buf' contents via a file */
//#define INSP_SLIDE_1 /* when scrolling left/right, don't move 8 */
//#define MEMGRAPH_OLD /* don't use 'available' when graphing Mem */
//#define OFF_SCROLLBK /* disable tty emulators scrollback buffer */
//#define OFF_STDERROR /* disable our stderr buffering (redirect) */
//#define OFF_STDIOLBF /* disable our own stdout 'IOFBF' override */
//#define OFF_XTRAWIDE /* disable our extra wide multi-byte logic */
//#define OVERTYPE_SEE /* display a visual hint for overtype mode */
//#define PRETEND0NUMA /* pretend that there ain't any numa nodes */
//#define PRETEND48CPU /* pretend we're smp with 48 ticsers (sic) */
//#define PRETENDNOCAP /* pretend terminal missing essential caps */
//#define QUICK_GRAPHS /* use fast algorithm & accept +2% distort */
//#define RCFILE_NOERR /* rcfile errs silently default, vs. fatal */
//#define RECALL_FIXED /* don't reorder saved strings if recalled */
//#define RMAN_IGNORED /* don't consider auto right margin glitch */
//#define SCALE_FORMER /* scale_tics() guy shouldn't mimic uptime */
//#define SCALE_POSTFX /* scale_tics() try without a 'h,d' suffix */
//#define SCROLLVAR_NO /* disable intra-column horizontal scrolls */
//#define SCROLLV_BY_1 /* when scrolling left/right do not move 8 */
//#define STRINGCASENO /* case insenstive compare/locate versions */
//#define TERMIOS_ONLY /* use native input only (just limp along) */
//#define THREADED_CPU /* separate background thread for cpu updt */
//#define THREADED_MEM /* separate background thread for mem updt */
//#define THREADED_TSK /* separate background thread for tsk updt */
//#define TOG4_NOFORCE /* no force 2 abreast mode with '4' toggle */
//#define TOG4_NOTRUNC /* ensure no truncation for 2 abreast mode */
//#define TOG4_OFF_MEM /* don't show two abreast memory statistic */
//#define TOG4_OFF_SEP /* don't show two abreast visual separator */
//#define TREE_NORESET /* sort keys should not force 'V' view off */
//#define TREE_SCANALL /* rescan array w/ forest view, avoid sort */
//#define TREE_VALTMRK /* use an indented '+' with collapsed pids */
//#define TREE_VCPUOFF /* a collapsed parent excludes child's cpu */
//#define TREE_VPROMPT /* pid collapse/expand prompt, vs. top row */
//#define TREE_VWINALL /* pid collapse/expand impacts all windows */
//#define USE_X_COLHDR /* emphasize header vs. whole col, for 'x' */
//#define VALIDATE_NLS /* ensure the integrity of four nls tables */
//#define WIDEN_COLUMN /* base column widths on translated header */
/*###### Notes, etc. ###################################################*/
/* For introducing inaugural cgroup support, thanks to:
Jan Gorig <jgorig@redhat.com> - April, 2011 */
/* For the motivation and path to nls support, thanks to:
Sami Kerola, <kerolasa@iki.fi> - December, 2011 */
/* There are still some short strings that may yet be candidates
for nls support inclusion. They're identified with:
// nls_maybe */
/* For the impetus and NUMA/Node prototype design, thanks to:
Lance Shelton <LShelton@fusionio.com> - April, 2013 */
/* For prompting & helping with top's utf-8 support, thanks to:
Göran Uddeborg <goeran@uddeborg.se> - September, 2017 */
// pretend as if #define _GNU_SOURCE
char *strcasestr(const char *haystack, const char *needle);
#ifdef STRINGCASENO
#define STRSTR strcasestr
#define STRCMP strcasecmp
#else
#define STRSTR strstr
#define STRCMP strcmp
#endif
/*###### Some Miscellaneous constants ##################################*/
/* The default delay twix updates */
#ifdef ORIG_TOPDEFS
#define DEF_DELAY 3.0
#else
#define DEF_DELAY 1.5
#endif
/* Length of time a message is displayed and the duration
of a 'priming' wait during library startup (in microseconds) */
#define MSG_USLEEP 1250000
#define LIB_USLEEP 150000
/* Specific process id monitoring support (command line only) */
#define MONPIDMAX 20
/* Output override minimums (the -w switch and/or env vars) */
#define W_MIN_COL 3
#define W_MIN_ROW 3
/* Miscellaneous buffers with liberal values and some other defines
-- mostly just to pinpoint source code usage/dependancies */
#define SCREENMAX 512
/* the above might seem pretty stingy, until you consider that with every
field displayed the column header would be approximately 250 bytes
-- so SCREENMAX provides for all fields plus a 250+ byte command line */
#define TNYBUFSIZ 16
#define CAPBUFSIZ 32
#define CLRBUFSIZ 64
#define PFLAGSSIZ 128
#define SMLBUFSIZ 128
#define MEDBUFSIZ 256
#define LRGBUFSIZ 512
#define OURPATHSZ 1024
#define BIGBUFSIZ 2048
#define BOTBUFSIZ 16384
// next is same as library's max buffer size
#define MAXBUFSIZ (1024*64*2)
/* in addition to the actual display data, our row might have to accommodate
many termcap/color transitions - these definitions ensure we have room */
#define ROWMINSIZ ( SCREENMAX + 8 * (CAPBUFSIZ + CLRBUFSIZ) )
#define ROWMAXSIZ ( SCREENMAX + 16 * (CAPBUFSIZ + CLRBUFSIZ) )
// minimum size guarantee for dynamically acquired 'readfile' buffer
#define READMINSZ 2048
// size of preallocated search string buffers, same as ioline()
#define FNDBUFSIZ MEDBUFSIZ
// space between task fields/columns
#define COLPADSTR " "
#define COLPADSIZ ( sizeof(COLPADSTR) - 1 )
// continuation ch when field/column truncated
#define COLPLUSCH '+'
// support for keyboard stuff (cursor motion keystrokes, mostly)
#define kbd_ESC '\033'
#define kbd_SPACE ' '
#define kbd_ENTER '\n'
#define kbd_UP 129
#define kbd_DOWN 130
#define kbd_LEFT 131
#define kbd_RIGHT 132
#define kbd_PGUP 133
#define kbd_PGDN 134
#define kbd_HOME 135
#define kbd_END 136
#define kbd_BKSP 137
#define kbd_INS 138
#define kbd_DEL 139
#define kbd_BTAB 140
#define kbd_CtrlE '\005'
#define kbd_CtrlG '\007'
#define kbd_CtrlI '\011'
#define kbd_CtrlK '\013'
#define kbd_CtrlL '\014'
#define kbd_CtrlN '\016'
#define kbd_CtrlO '\017'
#define kbd_CtrlP '\020'
#define kbd_CtrlR '\022'
#define kbd_CtrlU '\025'
/* Special value in Pseudo_row to force an additional procs refresh
-- used at startup and for task/thread mode transitions */
#define PROC_XTRA -1
/* ##### Enum's and Typedef's ############################################ */
/* Flags for each possible field (and then some) --
these MUST be kept in sync with the Fieldstab[] array !! */
enum pflag {
EU_PID = 0, EU_PPD,
EU_UED, EU_UEN, EU_URD, EU_URN, EU_USD, EU_USN,
EU_GID, EU_GRP, EU_PGD, EU_TTY, EU_TPG, EU_SID,
EU_PRI, EU_NCE, EU_THD,
EU_CPN, EU_CPU, EU_TME, EU_TM2,
EU_MEM, EU_VRT, EU_SWP, EU_RES, EU_COD, EU_DAT, EU_SHR,
EU_FL1, EU_FL2, EU_DRT,
EU_STA, EU_CMD, EU_WCH, EU_FLG, EU_CGR,
EU_SGD, EU_SGN, EU_TGD,
EU_OOA, EU_OOM,
EU_ENV,
EU_FV1, EU_FV2,
EU_USE,
EU_NS1, EU_NS2, EU_NS3, EU_NS4, EU_NS5, EU_NS6,
EU_LXC,
EU_RZA, EU_RZF, EU_RZL, EU_RZS,
EU_CGN,
EU_NMA,
EU_LID,
EU_EXE,
EU_RSS, EU_PSS, EU_PZA, EU_PZF, EU_PZS, EU_USS,
EU_IRB, EU_IRO, EU_IWB, EU_IWO,
EU_AGI, EU_AGN,
EU_TM3, EU_TM4, EU_CUU, EU_CUC,
EU_NS7, EU_NS8,
#ifdef USE_X_COLHDR
// not really pflags, used with tbl indexing
EU_MAXPFLGS
#else
// not really pflags, used with tbl indexing & col highlighting
EU_MAXPFLGS, EU_XON, EU_XOF
#endif
};
/* The scaling 'target' used with memory fields */
enum scale_enum {
SK_Kb, SK_Mb, SK_Gb, SK_Tb, SK_Pb, SK_Eb
};
/* Used to manipulate (and document) the Frames_signal states */
enum resize_states {
BREAK_off = 0, BREAK_kbd, BREAK_sig, BREAK_autox, BREAK_screen
};
/* This typedef just ensures consistent 'process flags' handling */
typedef int FLG_t;
/* These typedefs attempt to ensure consistent 'ticks' handling */
typedef unsigned long long TIC_t;
typedef long long SIC_t;
/* /////////////////////////////////////////////////////////////// */
/* Special Section: multiple windows/field groups --------------- */
/* ( kind of a header within a header: constants, types & macros ) */
#define CAPTABMAX 9 /* max entries in each win's caps table */
#define GROUPSMAX 4 /* the max number of simultaneous windows */
#define WINNAMSIZ 4 /* size of RCW_t winname buf (incl '\0') */
#define GRPNAMSIZ WINNAMSIZ+2 /* window's name + number as in: '#:...' */
/* The Persistent 'Mode' flags!
These are preserved in the rc file, as a single integer and the
letter shown is the corresponding 'command' toggle */
// 'View_' flags affect the summary (minimum), taken from 'Curwin'
#define View_CPUSUM 0x008000 // '1' - show combined cpu stats (vs. each)
#define View_CPUNOD 0x400000 // '2' - show numa node cpu stats ('3' also)
#define View_LOADAV 0x004000 // 'l' - display load avg and uptime summary
#define View_STATES 0x002000 // 't' - display task/cpu(s) states summary
#define View_MEMORY 0x001000 // 'm' - display memory summary
#define View_NOBOLD 0x000008 // 'B' - disable 'bold' attribute globally
#define View_SCROLL 0x080000 // 'C' - enable coordinates msg w/ scrolling
// 'Show_' & 'Qsrt_' flags are for task display in a visible window
#define Show_COLORS 0x000800 // 'z' - show in color (vs. mono)
#define Show_HIBOLD 0x000400 // 'b' - rows and/or cols bold (vs. reverse)
#define Show_HICOLS 0x000200 // 'x' - show sort column emphasized
#define Show_HIROWS 0x000100 // 'y' - show running tasks highlighted
#define Show_CMDLIN 0x000080 // 'c' - show cmdline vs. name
#define Show_CTIMES 0x000040 // 'S' - show times as cumulative
#define Show_IDLEPS 0x000020 // 'i' - show idle processes (all tasks)
#define Show_TASKON 0x000010 // '-' - tasks showable when Mode_altscr
#define Show_FOREST 0x000002 // 'V' - show cmd/cmdlines with ascii art
#define Qsrt_NORMAL 0x000004 // 'R' - reversed column sort (high to low)
#define Show_JRSTRS 0x040000 // 'j' - right justify "string" data cols
#define Show_JRNUMS 0x020000 // 'J' - right justify "numeric" data cols
// these flag(s) have no command as such - they're for internal use
#define NOPRINT_xxx 0x010000 // build task rows only (not for display)
#define EQUWINS_xxx 0x000001 // rebalance all wins & tasks (off i,n,u/U)
// Default flags if there's no rcfile to provide user customizations
#ifdef ORIG_TOPDEFS
#define DEF_WINFLGS ( View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY \
| Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Show_TASKON | Show_JRNUMS \
| Qsrt_NORMAL )
#define DEF_GRAPHS2 0, 0
#define DEF_SCALES2 SK_Mb, SK_Kb
#define ALT_WINFLGS DEF_WINFLGS
#define ALT_GRAPHS2 0, 0
#else
#define DEF_WINFLGS ( View_LOADAV | View_STATES | View_MEMORY | Show_CMDLIN \
| Show_COLORS | Show_FOREST | Show_HIROWS | Show_IDLEPS | Show_JRNUMS | Show_TASKON \
| Qsrt_NORMAL )
#define DEF_GRAPHS2 1, 2
#define DEF_SCALES2 SK_Gb, SK_Mb
#define ALT_WINFLGS (DEF_WINFLGS | Show_HIBOLD) & ~Show_FOREST
#define ALT_GRAPHS2 2, 0
#endif
/* These are used to direct wins_reflag */
enum reflag_enum {
Flags_TOG, Flags_SET, Flags_OFF
};
/* These are used to direct win_warn */
enum warn_enum {
Warn_ALT, Warn_VIZ
};
/* This type helps support both a window AND the rcfile */
typedef struct RCW_t { // the 'window' portion of an rcfile
int sortindx, // sort field (represented as procflag)
winflags, // 'view', 'show' and 'sort' mode flags
maxtasks, // user requested maximum, 0 equals all
graph_cpus, // 't' - View_STATES supplementary vals
graph_mems, // 'm' - View_MEMORY supplememtary vals
double_up, // '4' - show individual cpus 2 abreast
combine_cpus, // '!' - keep combining additional cpus
summclr, // a colors 'number' used for summ info
msgsclr, // " in msgs/pmts
headclr, // " in cols head
taskclr; // " in task rows
char winname [WINNAMSIZ]; // name for the window, user changeable
FLG_t fieldscur [PFLAGSSIZ]; // the fields for display & their order
} RCW_t;
/* This represents the complete rcfile */
typedef struct RCF_t {
char id; // rcfile version id
int mode_altscr; // 'A' - Alt display mode (multi task windows)
int mode_irixps; // 'I' - Irix vs. Solaris mode (SMP-only)
float delay_time; // 'd'/'s' - How long to sleep twixt updates
int win_index; // Curwin, as index
RCW_t win [GROUPSMAX]; // a 'WIN_t.rc' for each window
int fixed_widest; // 'X' - wider non-scalable col addition
int summ_mscale; // 'E' - scaling of summary memory values
int task_mscale; // 'e' - scaling of process memory values
int zero_suppress; // '0' - suppress scaled zeros toggle
int tics_scaled; // ^E - scale TIME and/or TIME+ columns
} RCF_t;
/* This structure stores configurable information for each window.
By expending a little effort in its creation and user requested
maintenance, the only real additional per frame cost of having
windows is an extra sort -- but that's just on pointers! */
typedef struct WIN_t {
FLG_t pflgsall [PFLAGSSIZ], // all 'active/on' fieldscur, as enum
procflgs [PFLAGSSIZ]; // fieldscur subset, as enum
RCW_t rc; // stuff that gets saved in the rcfile
int winnum, // a window's number (array pos + 1)
winlines, // current task window's rows (volatile)
maxpflgs, // number of displayed procflgs ("on" in fieldscur)
totpflgs, // total of displayable procflgs in pflgsall array
begpflg, // scrolled beginning pos into pflgsall array
endpflg, // scrolled ending pos into pflgsall array
begtask, // scrolled beginning pos into total tasks
begnext, // new scrolled delta for next frame's begtask
#ifndef SCROLLVAR_NO
varcolbeg, // scrolled position within variable width col
#endif
varcolsz, // max length of variable width column(s)
usrseluid, // validated uid for 'u/U' user selection
usrseltyp, // the basis for matching above uid
usrselflg, // flag denoting include/exclude matches
hdrcaplen; // column header xtra caps len, if any
char capclr_sum [CLRBUFSIZ], // terminfo strings built from
capclr_msg [CLRBUFSIZ], // RCW_t colors (& rebuilt too),
capclr_pmt [CLRBUFSIZ], // but NO recurring costs !
capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs
capclr_rowhigh [SMLBUFSIZ], // are only used when this
capclr_rownorm [CLRBUFSIZ], // window is the 'Curwin'!
cap_bold [CAPBUFSIZ], // support for View_NOBOLD toggle
grpname [GRPNAMSIZ], // window number:name, printable
#ifdef USE_X_COLHDR
columnhdr [ROWMINSIZ], // column headings for procflgs
#else
columnhdr [SCREENMAX], // column headings for procflgs
#endif
*captab [CAPTABMAX]; // captab needed by show_special()
struct osel_s *osel_1st; // other selection criteria anchor
int osel_tot; // total of other selection criteria
char *findstr; // window's current/active search string
int findlen; // above's strlen, without call overhead
int focus_pid; // target pid when 'F' toggle is active
int focus_beg; // ppt index where 'F' toggle has begun
int focus_end; // ppt index where 'F' toggle has ended
#ifdef FOCUS_TREE_X
int focus_lvl; // the indentation level of parent task
#endif
struct pids_stack **ppt; // this window's stacks ptr array
struct WIN_t *next, // next window in window stack
*prev; // prior window in window stack
} WIN_t;
// Used to test/manipulate the window flags
#define CHKw(q,f) (int)((q)->rc.winflags & (f))
#define TOGw(q,f) (q)->rc.winflags ^= (f)
#define SETw(q,f) (q)->rc.winflags |= (f)
#define OFFw(q,f) (q)->rc.winflags &= ~(f)
#define ALTCHKw (Rc.mode_altscr ? 1 : win_warn(Warn_ALT))
#define VIZISw(q) (!Rc.mode_altscr || CHKw(q,Show_TASKON))
#define VIZCHKw(q) (VIZISw(q)) ? 1 : win_warn(Warn_VIZ)
#define VIZTOGw(q,f) (VIZISw(q)) ? TOGw(q,(f)) : win_warn(Warn_VIZ)
// Used to test/manipulte fieldscur values
#define FLDon 0x01
#define FLDoff 0x00
#define FLDget(q,i) ( (((q)->rc.fieldscur[i]) >> 1) - FLD_OFFSET )
#define FLDtog(q,i) ( (q)->rc.fieldscur[i] ^= FLDon )
#define FLDviz(q,i) ( (q)->rc.fieldscur[i] & FLDon )
#define ENUviz(w,E) ( NULL != msch((w)->procflgs, E, w->maxpflgs) )
#define ENUpos(w,E) ( (int)(msch((w)->pflgsall, E, (w)->totpflgs) - (w)->pflgsall) )
#define ENUcvt(E,x) ( (int)((E + FLD_OFFSET) << 1) | x )
// Support for variable width columns (and potentially scrolling too)
#define VARcol(E) (-1 == Fieldstab[E].width)
#ifndef SCROLLVAR_NO
#ifdef USE_X_COLHDR
#define VARright(w) (1 == w->maxpflgs && VARcol(w->procflgs[0]))
#else
#define VARright(w) ((1 == w->maxpflgs && VARcol(w->procflgs[0])) || \
(3 == w->maxpflgs && EU_XON == w->procflgs[0] && VARcol(w->procflgs[1])))
#endif
#define VARleft(w) (w->varcolbeg && VARright(w))
#ifdef SCROLLV_BY_1
#define SCROLLAMT 1
#else
#define SCROLLAMT 8
#endif
#endif
// Support for a proper (visible) row #1 whenever Curwin changes
// ( or a key which might affect vertical scrolling was struck )
#define mkVIZyes ( Curwin->begnext != 0 )
#define mkVIZrow1 { Curwin->begnext = +1; Curwin->begtask -= 1; }
#define mkVIZrowX(n) { Curwin->begnext = (n); }
#define mkVIZoff(w) { w->begnext = 0; }
/* Special Section: end ------------------------------------------ */
/* /////////////////////////////////////////////////////////////// */
/*###### Some Miscellaneous Macro definitions ##########################*/
/* Yield table size as 'int' */
#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0]))
/* A null-terminating strncpy, assuming strlcpy is not available.
( and assuming callers don't need the string length returned ) */
#define STRLCPY(dst,src) { memccpy(dst, src, '\0', sizeof(dst)); dst[sizeof(dst) - 1] = '\0'; }
/* Used to clear all or part of our Pseudo_screen */
#define PSU_CLREOS(y) memset(&Pseudo_screen[ROWMAXSIZ*y], '\0', Pseudo_size-(ROWMAXSIZ*y))
/*
* The following three macros are used to 'inline' those portions of the
* display process involved in formatting, while protecting against any
* potential embedded 'millesecond delay' escape sequences.
*/
/** PUTT - Put to Tty (used in many places)
. for temporary, possibly interactive, 'replacement' output
. may contain ANY valid terminfo escape sequences
. need NOT represent an entire screen row */
#define PUTT(fmt,arg...) do { \
char _str[ROWMAXSIZ]; \
snprintf(_str, sizeof(_str), fmt, ## arg); \
putp(_str); \
} while (0)
/** PUFF - Put for Frame (used in only 3 places)
. for more permanent frame-oriented 'update' output
. may NOT contain cursor motion terminfo escapes
. assumed to represent a complete screen ROW
. subject to optimization, thus MAY be discarded */
#define PUFF(fmt,arg...) do { \
char _str[ROWMAXSIZ]; \
const int _len = snprintf(_str, sizeof(_str), fmt, ## arg); \
if (Batch) { \
char *_eol = _str + (_len < 0 ? 0 : (size_t)_len >= sizeof(_str) ? sizeof(_str)-1 : (size_t)_len); \
while (_eol > _str && _eol[-1] == ' ') _eol--; \
*_eol = '\0'; putp(_str); } \
else if (Pseudo_row >= 0 && Pseudo_row < Screen_rows) { \
char *_ptr = &Pseudo_screen[Pseudo_row++ * ROWMAXSIZ]; \
if (!strcmp(_ptr, _str)) putp("\n"); \
else { \
strcpy(_ptr, _str); \
putp(_ptr); } } \
} while (0)
/** POOF - Pulled Out of Frame (used in only 1 place)
. for output that is/was sent directly to the terminal
but would otherwise have been counted as a Pseudo_row */
#define POOF(str,cap) do { \
putp(str); putp(cap); \
Pseudo_screen[Pseudo_row * ROWMAXSIZ] = '\0'; \
if (Pseudo_row + 1 < Screen_rows) ++Pseudo_row; \
} while (0)
/* Orderly end, with any sort of message - see fmtmk */
#define debug_END(s) { \
void error_exit (const char *); \
fputs(Cap_clr_scr, stdout); \
error_exit(s); \
}
/* A poor man's breakpoint, if he's too lazy to learn gdb */
#define its_YOUR_fault { *((char *)0) = '!'; }
/*###### Some Display Support *Data* ###################################*/
/* ( see module top_nls.c for the nls translatable data ) */
/* Configuration files support */
#define SYS_RCRESTRICT "/etc/toprc"
#define SYS_RCDEFAULTS "/etc/topdefaultrc"
#define RCF_EYECATCHER "Config File (Linux processes with windows)\n"
#define RCF_PLUS_H "\\]^_`abcdefghij"
#define RCF_PLUS_J "klmnopqrstuvwxyz"
// this next guy must never, ever change
// ( transitioned from 'char' to 'int' )
#define RCF_XFORMED_ID 'k'
// this next guy is incremented when columns change
// ( to prevent older top versions from accessing )
#define RCF_VERSION_ID 'k'
#define FLD_OFFSET ( (int)'%' )
#define FLD_ROWMAX 20
/* The default fields displayed and their order,
if nothing is specified by the loser, oops user. */
#ifdef ORIG_TOPDEFS
#define DEF_FORMER "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ķ<EFBFBD><C4B7><EFBFBD>&')*+,-./012568<>?ABCFGHIJKLMNOPQRSTUVWXYZ[" RCF_PLUS_H RCF_PLUS_J
#else
#define DEF_FORMER "<22>&K<><4B><EFBFBD><EFBFBD><EFBFBD>@<40><><EFBFBD>56<35>F<EFBFBD>')*+,-./0128<>?ABCGHIJLMNOPQRSTUVWXYZ[" RCF_PLUS_H RCF_PLUS_J
#endif
/* Pre-configured windows/field groups */
#define JOB_FORMER "<22><><EFBFBD><EFBFBD><EFBFBD>(<28><>Ļ<EFBFBD>@<<3C><>)*+,-./012568>?ABCFGHIJKLMNOPQRSTUVWXYZ[" RCF_PLUS_H RCF_PLUS_J
#define MEM_FORMER "<22><><EFBFBD><<3C><><EFBFBD><EFBFBD><EFBFBD>MBN<42>D34<33><34>&'()*+,-./0125689FGHIJKLOPQRSTUVWXYZ[" RCF_PLUS_H RCF_PLUS_J
#define USR_FORMER "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)+,-./1234568;<=>?@ABCFGHIJKLMNOPQRSTUVWXYZ[" RCF_PLUS_H RCF_PLUS_J
// old top fields ( 'a'-'z' ) in positions 0-25
// other suse old top fields ( '{|' ) in positions 26-27
#define CVT_FORMER "%&*'(-0346789:;<=>?@ACDEFGML)+,./125BHIJKNOPQRSTUVWXYZ[" RCF_PLUS_H RCF_PLUS_J
#define CVT_FLDMAX 28
#ifdef ORIG_TOPDEFS
#define DEF_FIELDS { \
75, 81, 103, 105, 119, 123, 129, 137, 111, 117, 115, 139, 76, 78, 82, 84, 86, 88, 90, 92, \
94, 96, 98, 100, 106, 108, 112, 120, 124, 126, 130, 132, 134, 140, 142, 144, 146, 148, 150, 152, \
154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, \
194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, \
234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272 }
#else
#define DEF_FIELDS { \
75, 76, 150, 81, 103, 105, 119, 123, 128, 111, 117, 115, 106, 108, 137, 140, 139, 78, 82, 84, \
86, 88, 90, 92, 94, 96, 98, 100, 112, 120, 124, 126, 130, 132, 134, 142, 144, 146, 148, 152, \
154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, \
194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, \
234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272 }
#endif
#define JOB_FIELDS { \
75, 77, 115, 111, 117, 80, 103, 105, 137, 119, 123, 128, 120, 79, 139, 82, 84, 86, 88, 90, \
92, 94, 96, 98, 100, 106, 108, 112, 124, 126, 130, 132, 134, 140, 142, 144, 146, 148, 150, 152, \
154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, \
194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, \
234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272 }
#define MEM_FIELDS { \
75, 117, 119, 120, 123, 125, 127, 129, 131, 154, 132, 156, 135, 136, 102, 104, 111, 139, 76, 78, \
80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 106, 108, 112, 114, 140, 142, 144, 146, 148, \
150, 152, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, \
194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, \
234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272 }
#define USR_FIELDS { \
75, 77, 79, 81, 85, 97, 115, 111, 117, 137, 139, 82, 86, 88, 90, 92, 94, 98, 100, 102, \
104, 106, 108, 112, 118, 120, 122, 124, 126, 128, 130, 132, 134, 140, 142, 144, 146, 148, 150, 152, \
154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, \
194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, \
234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272 }
/* The default values for the local config file */
#define DEF_RCFILE { \
RCF_VERSION_ID, 0, 1, DEF_DELAY, 0, { \
{ EU_CPU, DEF_WINFLGS, 0, DEF_GRAPHS2, 1, 0, \
COLOR_RED, COLOR_RED, COLOR_YELLOW, COLOR_RED, \
"Def", DEF_FIELDS }, \
{ EU_PID, ALT_WINFLGS, 0, ALT_GRAPHS2, 0, 0, \
COLOR_CYAN, COLOR_CYAN, COLOR_WHITE, COLOR_CYAN, \
"Job", JOB_FIELDS }, \
{ EU_MEM, ALT_WINFLGS, 0, ALT_GRAPHS2, 0, 0, \
COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA, \
"Mem", MEM_FIELDS }, \
{ EU_UEN, ALT_WINFLGS, 0, ALT_GRAPHS2, 0, 0, \
COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \
"Usr", USR_FIELDS } \
}, 0, DEF_SCALES2, 0, 0 }
/* Summary Lines specially formatted string(s) --
see 'show_special' for syntax details + other cautions. */
#define LOADAV_line "%s -%s\n"
#define LOADAV_line_alt "%s~6 -%s\n"
/*###### For Piece of mind #############################################*/
/* just sanity check(s)... */
#if defined(RECALL_FIXED) && defined(TERMIOS_ONLY)
# error 'RECALL_FIXED' conflicts with 'TERMIOS_ONLY'
#endif
#if (LRGBUFSIZ < SCREENMAX)
# error 'LRGBUFSIZ' must NOT be less than 'SCREENMAX'
#endif
#if defined(TERMIOS_ONLY)
# warning 'TERMIOS_ONLY' disables input recall and makes man doc incorrect
#endif
#if defined(MEMGRAPH_OLD)
# warning 'MEMGRAPH_OLD' will make the man document Section 2c. misleading
#endif
#if defined(SCALE_FORMER) && defined(SCALE_POSTFX)
# warning 'SCALE_POSTFX' is ignored when 'SCALE_FORMER' is active
#endif
#if defined(USE_X_COLHDR)
# warning 'USE_X_COLHDR' makes parts of man page misleading (4e, 5d & 5e)
#endif
/*###### Some Prototypes (ha!) #########################################*/
/* These 'prototypes' are here exclusively for documentation purposes. */
/* ( see the find_string function for the one true required protoype ) */
/*------ Tiny useful routine(s) ----------------------------------------*/
//atic const char *fmtmk (const char *fmts, ...);
//atic inline int mlen (const int *mem);
//atic inline int *msch (const int *mem, int obj, int max);
//atic inline char *scat (char *dst, const char *src);
//atic const char *tg2 (int x, int y);
/*------ Exit/Interrput routines ---------------------------------------*/
//atic void at_eoj (void);
//atic void bye_bye (const char *str);
//atic void error_exit (const char *str);
//atic void sig_abexit (int sig);
//atic void sig_endpgm (int dont_care_sig);
//atic void sig_paused (int dont_care_sig);
//atic void sig_resize (int dont_care_sig);
/*------ Special UTF-8 Multi-Byte support ------------------------------*/
/*atic char UTF8_tab[] = { ... } */
//atic inline int utf8_cols (const unsigned char *p, int n);
//atic int utf8_delta (const char *str);
//atic int utf8_embody (const char *str, int width);
//atic const char *utf8_justify (const char *str, int width, int justr);
//atic int utf8_proper_col (const char *str, int col, int tophysical);
/*------ Misc Color/Display support ------------------------------------*/
//atic void capsmk (WIN_t *q);
//atic void show_msg (const char *str);
//atic int show_pmt (const char *str);
//atic void show_scroll (void);
//atic void show_special (int interact, const char *glob);
/*------ Low Level Memory/Keyboard/File I/O support --------------------*/
//atic void *alloc_c (size_t num);
//atic void *alloc_r (void *ptr, size_t num);
//atic char *alloc_s (const char *str);
//atic inline int ioa (struct timespec *ts);
//atic int ioch (int ech, char *buf, unsigned cnt);
//atic int iokey (int action);
//atic char *ioline (const char *prompt);
//atic int mkfloat (const char *str, float *num, int whole);
//atic int readfile (FILE *fp, char **baddr, size_t *bsize, size_t *bread);
/*------ Small Utility routines ----------------------------------------*/
//atic float get_float (const char *prompt);
//atic int get_int (const char *prompt);
//atic inline const char *hex_make (long num, int noz);
//atic const char *user_certify (WIN_t *q, const char *str, char typ);
/*------ Basic Formatting support --------------------------------------*/
//atic inline const char *justify_pad (const char *str, int width, int justr);
//atic inline const char *make_chr (const char ch, int width, int justr);
//atic inline const char *make_num (long num, int width, int justr, int col, int noz);
//atic inline const char *make_str (const char *str, int width, int justr, int col);
//atic inline const char *make_str_utf8 (const char *str, int width, int justr, int col);
//atic const char *scale_mem (int target, float num, int width, int justr);
//atic const char *scale_num (float num, int width, int justr);
//atic const char *scale_pcnt (float num, int width, int justr, int xtra);
//atic const char *scale_tics (TIC_t tics, int width, int justr, int target);
/*------ Fields Management support -------------------------------------*/
/*atic struct Fieldstab[] = { ... } */
//atic void adj_geometry (void);
//atic void build_headers (void);
//atic void calibrate_fields (void);
//atic void display_fields (int focus, int extend);
//atic void fields_utility (void);
//atic inline void widths_resize (void);
//atic void zap_fieldstab (void);
/*------ Library Interface (as separate threads) -----------------------*/
//atic void *cpus_refresh (void *unused);
//atic void *memory_refresh (void *unused);
//atic void *tasks_refresh (void *unused);
/*------ Inspect Other Output ------------------------------------------*/
//atic void insp_cnt_nl (void);
#ifndef INSP_OFFDEMO
//atic void insp_do_demo (char *fmts, int pid);
#endif
//atic void insp_do_file (char *fmts, int pid);
//atic void insp_do_pipe (char *fmts, int pid);
//atic inline int insp_find_ofs (int col, int row);
//atic void insp_find_str (int ch, int *col, int *row);
//atic void insp_mkrow_raw (int col, int row);
//atic void insp_mkrow_utf8 (int col, int row);
//atic void insp_show_pgs (int col, int row, int max);
//atic int insp_view_choice (struct pids_stack *p);
//atic void inspection_utility (int pid);
/*------ Other Filtering ------------------------------------------------*/
//atic const char *osel_add (WIN_t *q, int ch, char *glob, int push);
//atic void osel_clear (WIN_t *q);
//atic inline int osel_matched (const WIN_t *q, FLG_t enu, const char *str);
/*------ Startup routines ----------------------------------------------*/
//atic void before (char *me);
//atic int cfg_xform (WIN_t *q, char *flds, const char *defs);
//atic int config_insp (FILE *fp, char *buf, size_t size);
//atic int config_osel (FILE *fp, char *buf, size_t size);
//atic const char *configs_file (FILE *fp, const char *name, float *delay);
//atic int configs_path (const char *const fmts, ...);
//atic void configs_reads (void);
//atic void parse_args (int argc, char **argv);
//atic void signals_set (void);
//atic void whack_terminal (void);
/*------ Windows/Field Groups support ----------------------------------*/
//atic void win_names (WIN_t *q, const char *name);
//atic void win_reset (WIN_t *q);
//atic WIN_t *win_select (int ch);
//atic int win_warn (int what);
//atic void wins_clrhlp (WIN_t *q, int save);
//atic void wins_colors (void);
//atic void wins_reflag (int what, int flg);
//atic void wins_stage_1 (void);
//atic void wins_stage_2 (void);
//atic inline int wins_usrselect (const WIN_t *q, int idx);
/*------ Forest View support -------------------------------------------*/
//atic void forest_adds (const int self, int level);
//atic void forest_begin (WIN_t *q);
//atic void forest_config (WIN_t *q);
//atic inline const char *forest_display (const WIN_t *q, int idx);
/*------ Special Separate Bottom Window support ------------------------*/
//atic void bot_do (const char *str, int focus);
//atic int bot_focus_str (const char *hdr, const char *str);
//atic int bot_focus_strv (const char *hdr, const char **strv);
//atic void *bot_item_hlp (struct pids_stack *p);
//atic void bot_item_show (void);
//atic void bot_item_toggle (int what, const char *head, char sep);
/*------ Interactive Input Tertiary support ----------------------------*/
//atic inline int find_ofs (const WIN_t *q, const char *buf);
//atic void find_string (int ch);
//atic void help_view (void);
//atic void other_filters (int ch);
//atic void write_rcfile (void);
/*------ Interactive Input Secondary support (do_key helpers) ----------*/
//atic void keys_global (int ch);
//atic void keys_summary (int ch);
//atic void keys_task (int ch);
//atic void keys_window (int ch);
//atic void keys_xtra (int ch);
/*------ Tertiary summary display support (summary_show helpers) -------*/
//atic inline int sum_see (const char *str, int nobuf);
//atic int sum_tics (struct stat_stack *this, const char *pfx, int nobuf);
//atic int sum_unify (struct stat_stack *this, int nobuf);
/*------ Secondary summary display support (summary_show helpers) ------*/
//atic void do_cpus (void);
//atic void do_memory (void);
/*------ Main Screen routines ------------------------------------------*/
//atic void do_key (int ch);
//atic void summary_show (void);
//atic const char *task_show (const WIN_t *q, int idx);
//atic void window_hlp (void);
//atic int window_show (WIN_t *q, int wmax);
/*------ Entry point plus two ------------------------------------------*/
//atic void frame_hlp (int wix, int max);
//atic void frame_make (void);
// int main (int argc, char *argv[]);
#endif /* _Itop */

851
src/top/top_nls.c Normal file
View File

@@ -0,0 +1,851 @@
/* top_nls.c - provide the basis for future nls translations */
/*
* Copyright (c) 2011-2022, by: Jim Warner <james.warner@comcast.net
*
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program 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 Library General Public License for more details.
*/
/* For contributions to this program, the author wishes to thank:
* Craig Small, <csmall@dropbear.xyz>
* Sami Kerola, <kerolasa@iki.fi>
*/
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include "../include/nls.h"
#include "top.h"
#include "top_nls.h"
#ifdef VALIDATE_NLS
#include <stdlib.h>
#endif
// Programmer Note(s):
// Preparation ---------------------------------------------
// Unless you have *something* following the gettext macro,
// xgettext will refuse to see any TRANSLATORS comments.
// Thus empty strings have been added for potential future
// comment additions.
//
// Also, by omitting the argument for the --add-comments
// XGETTEXT_OPTION in po/Makevars, *any* preceding c style
// comment will be propagated to the .pot file, providing
// that the gettext macro isn't empty as discussed above.
// However, this is far too aggressive so we have chosen
// the word 'Translation' to denote xgettext comments.
//
// /* Need Not Say 'TRANSLATORS': ...
// snprintf(buf, sizeof(buf), "%s", _( // unseen comment
//
// /* Translation Hint: ...
// snprintf(buf, sizeof(buf), "%s", _("" // now it's seen!
//
// Translation, from po/ directory after make --------------
// ( this is the procedure used before any translations were )
// ( available in the po/ directory, which contained only the )
// ( procps-ng.pot, this domain's template file. )
//
// ( below: ll_CC = language/country as in 'zh_CN' or 'en_AU' )
//
// msginit --locale=ll_CC --no-wrap
// . creates a ll_CC.po file from the template procps-ng.pot
// . may also duplicate msgid as msgstr if languages similar
// msgen --no-wrap ll_CC.po --output-file=ll_CC.po
// . duplicates every msgid literal as msgstr value
// . this is the file that's edited
// . replace "Content-Type: ... charset=ASCII\n"
// with "... charset=UTF-8\n"
// . translate msgstr values, leaving msgid unchanged
// msgfmt ll_CC.po --strict --output-file=procps-ng.mo
// . after which ensure, chmod 644
// . then copy
// to /usr/share/locale-langpack/ll_CC/LC_MESSAGES/
// or /usr/share/locale/ll_CC/LC_MESSAGES/
// Testing -------------------------------------------------
// export LC_ALL= && export LANGUAGE=ll_CC
// run some capable program like top
//
/*
* These are our string tables with the following contents:
* Head : column headings with varying size limits
* Desc : fields descriptions not to exceed 20 screen positions
* Norm : regular text possibly also containing c-format specifiers
* Uniq : show_special specially formatted strings
*
* The latter table presents the greatest translation challenge !
*
* We go to the trouble of creating the nls string tables to achieve
* these objectives:
* + the overhead of repeated runtime calls to gettext()
* will be avoided
* + the order of the strings in the template (.pot) file
* can be completely controlled
* + none of the important translator only comments will
* clutter and obscure the main program
*/
const char *Head_nlstab[EU_MAXPFLGS];
const char *Desc_nlstab[EU_MAXPFLGS];
const char *Norm_nlstab[norm_MAX];
const char *Uniq_nlstab[uniq_MAX];
/*
* This routine builds the nls table containing plain text only
* used as the field descriptions. Each translated line MUST be
* kept to a maximum of 20 characters or less! */
static void build_two_nlstabs (void) {
/* Translation Notes ------------------------------------------------
. It is strongly recommend that the --no-wrap command line option
. be used with all supporting translation tools, when available.
.
. The following line pairs contain only plain text and consist of:
. 1) a field name/column header - mostly upper case
. 2) the related description - both upper and lower case
.
. To avoid truncation on the main top display, each column header
. is noted with its maximum size, while a few are 'variable' width.
. Names for the latter should probably be 10 or fewer characters.
.
. Those fields shown with a '+' are also eligible for user resizing
. using the 'X' command. That means the default width might produce
. truncation but need not if widened (see the man page 'X' command).
.
. All headers are subject to a maximum of 8 on the Fields Management
. screen where truncation is entirely acceptable.
.
. The associated descriptions are always limited to 20 characters,
. and are used only on the Fields Management screen.
.
. In all cases, fewer characters are just fine.
. */
/* Translation Hint: maximum 'PID' = 5 */
Head_nlstab[EU_PID] = _("PID");
Desc_nlstab[EU_PID] = _("Process Id");
/* Translation Hint: maximum 'PPID' = 5 */
Head_nlstab[EU_PPD] = _("PPID");
Desc_nlstab[EU_PPD] = _("Parent Process pid");
/* Translation Hint: maximum 'UID' = 5 + */
Head_nlstab[EU_UED] = _("UID");
Desc_nlstab[EU_UED] = _("Effective User Id");
/* Translation Hint: maximum 'USER' = 8 + */
Head_nlstab[EU_UEN] = _("USER");
Desc_nlstab[EU_UEN] = _("Effective User Name");
/* Translation Hint: maximum 'RUID' = 5 + */
Head_nlstab[EU_URD] = _("RUID");
Desc_nlstab[EU_URD] = _("Real User Id");
/* Translation Hint: maximum 'RUSER' = 8 + */
Head_nlstab[EU_URN] = _("RUSER");
Desc_nlstab[EU_URN] = _("Real User Name");
/* Translation Hint: maximum 'SUID' = 5 + */
Head_nlstab[EU_USD] = _("SUID");
Desc_nlstab[EU_USD] = _("Saved User Id");
/* Translation Hint: maximum 'SUSER' = 8 + */
Head_nlstab[EU_USN] = _("SUSER");
Desc_nlstab[EU_USN] = _("Saved User Name");
/* Translation Hint: maximum 'GID' = 5 + */
Head_nlstab[EU_GID] = _("GID");
Desc_nlstab[EU_GID] = _("Group Id");
/* Translation Hint: maximum 'GROUP' = 8 + */
Head_nlstab[EU_GRP] = _("GROUP");
Desc_nlstab[EU_GRP] = _("Group Name");
/* Translation Hint: maximum 'PGRP' = 5 */
Head_nlstab[EU_PGD] = _("PGRP");
Desc_nlstab[EU_PGD] = _("Process Group Id");
/* Translation Hint: maximum 'TTY' = 8 + */
Head_nlstab[EU_TTY] = _("TTY");
Desc_nlstab[EU_TTY] = _("Controlling Tty");
/* Translation Hint: maximum 'TPGID' = 5 */
Head_nlstab[EU_TPG] = _("TPGID");
Desc_nlstab[EU_TPG] = _("Tty Process Grp Id");
/* Translation Hint: maximum 'SID' = 5 */
Head_nlstab[EU_SID] = _("SID");
Desc_nlstab[EU_SID] = _("Session Id");
/* Translation Hint: maximum 'PR' = 3 */
Head_nlstab[EU_PRI] = _("PR");
Desc_nlstab[EU_PRI] = _("Priority");
/* Translation Hint: maximum 'NI' = 3 */
Head_nlstab[EU_NCE] = _("NI");
Desc_nlstab[EU_NCE] = _("Nice Value");
/* Translation Hint: maximum 'nTH' = 3 */
Head_nlstab[EU_THD] = _("nTH");
Desc_nlstab[EU_THD] = _("Number of Threads");
/* Translation Hint: maximum 'P' = 1 */
Head_nlstab[EU_CPN] = _("P");
Desc_nlstab[EU_CPN] = _("Last Used Cpu (SMP)");
/* Translation Hint: maximum '%CPU' = 4 */
Head_nlstab[EU_CPU] = _("%CPU");
Desc_nlstab[EU_CPU] = _("CPU Usage");
/* Translation Hint: maximum '' = 6 */
Head_nlstab[EU_TME] = _("TIME");
Desc_nlstab[EU_TME] = _("CPU Time");
/* Translation Hint: maximum 'TIME+' = 9 */
Head_nlstab[EU_TM2] = _("TIME+");
Desc_nlstab[EU_TM2] = _("CPU Time, hundredths");
/* Translation Hint: maximum '%MEM' = 4 */
Head_nlstab[EU_MEM] = _("%MEM");
Desc_nlstab[EU_MEM] = _("Memory Usage (RES)");
/* Translation Hint: maximum 'VIRT' = 7 */
Head_nlstab[EU_VRT] = _("VIRT");
Desc_nlstab[EU_VRT] = _("Virtual Image (KiB)");
/* Translation Hint: maximum 'SWAP' = 6 */
Head_nlstab[EU_SWP] = _("SWAP");
Desc_nlstab[EU_SWP] = _("Swapped Size (KiB)");
/* Translation Hint: maximum 'RES' = 6 */
Head_nlstab[EU_RES] = _("RES");
Desc_nlstab[EU_RES] = _("Resident Size (KiB)");
/* Translation Hint: maximum 'CODE' = 4 */
Head_nlstab[EU_COD] = _("CODE");
Desc_nlstab[EU_COD] = _("Code Size (KiB)");
/* Translation Hint: maximum 'DATA' = 7 */
Head_nlstab[EU_DAT] = _("DATA");
Desc_nlstab[EU_DAT] = _("Data+Stack (KiB)");
/* Translation Hint: maximum 'SHR' = 6 */
Head_nlstab[EU_SHR] = _("SHR");
Desc_nlstab[EU_SHR] = _("Shared Memory (KiB)");
/* Translation Hint: maximum 'nMaj' = 4 */
Head_nlstab[EU_FL1] = _("nMaj");
Desc_nlstab[EU_FL1] = _("Major Page Faults");
/* Translation Hint: maximum 'nMin' = 4 */
Head_nlstab[EU_FL2] = _("nMin");
Desc_nlstab[EU_FL2] = _("Minor Page Faults");
/* Translation Hint: maximum 'nDRT' = 4 */
Head_nlstab[EU_DRT] = _("nDRT");
Desc_nlstab[EU_DRT] = _("Dirty Pages Count");
/* Translation Hint: maximum 'S' = 1 */
Head_nlstab[EU_STA] = _("S");
Desc_nlstab[EU_STA] = _("Process Status");
/* Translation Hint: maximum 'COMMAND' = variable */
Head_nlstab[EU_CMD] = _("COMMAND");
Desc_nlstab[EU_CMD] = _("Command Name/Line");
/* Translation Hint: maximum 'WCHAN' = 10 + */
Head_nlstab[EU_WCH] = _("WCHAN");
Desc_nlstab[EU_WCH] = _("Sleeping in Function");
/* Translation Hint: maximum 'Flags' = 8 */
Head_nlstab[EU_FLG] = _("Flags");
Desc_nlstab[EU_FLG] = _("Task Flags <sched.h>");
/* Translation Hint: maximum 'CGROUPS' = variable */
Head_nlstab[EU_CGR] = _("CGROUPS");
Desc_nlstab[EU_CGR] = _("Control Groups");
/* Translation Hint: maximum 'SUPGIDS' = variable */
Head_nlstab[EU_SGD] = _("SUPGIDS");
Desc_nlstab[EU_SGD] = _("Supp Groups IDs");
/* Translation Hint: maximum 'SUPGRPS' = variable */
Head_nlstab[EU_SGN] = _("SUPGRPS");
Desc_nlstab[EU_SGN] = _("Supp Groups Names");
/* Translation Hint: maximum 'TGID' = 5 */
Head_nlstab[EU_TGD] = _("TGID");
Desc_nlstab[EU_TGD] = _("Thread Group Id");
/* Translation Hint: maximum 'OOMa' = 5 */
Head_nlstab[EU_OOA] = _("OOMa");
Desc_nlstab[EU_OOA] = _("OOMEM Adjustment");
/* Translation Hint: maximum 'OOMs' = 4 */
Head_nlstab[EU_OOM] = _("OOMs");
Desc_nlstab[EU_OOM] = _("OOMEM Score current");
/* Translation Hint: maximum 'ENVIRON' = variable */
Head_nlstab[EU_ENV] = _("ENVIRON");
/* Translation Hint: the abbreviation 'vars' below is shorthand for
'variables' */
Desc_nlstab[EU_ENV] = _("Environment vars");
/* Translation Hint: maximum 'vMj' = 3 */
Head_nlstab[EU_FV1] = _("vMj");
Desc_nlstab[EU_FV1] = _("Major Faults delta");
/* Translation Hint: maximum 'vMn' = 3 */
Head_nlstab[EU_FV2] = _("vMn");
Desc_nlstab[EU_FV2] = _("Minor Faults delta");
/* Translation Hint: maximum 'USED' = 6 */
Head_nlstab[EU_USE] = _("USED");
Desc_nlstab[EU_USE] = _("Res+Swap Size (KiB)");
/* Translation Hint: maximum 'nsIPC' = 10 + */
Head_nlstab[EU_NS1] = _("nsIPC");
Desc_nlstab[EU_NS1] = _("IPC namespace Inode");
/* Translation Hint: maximum 'nsMNT' = 10 + */
Head_nlstab[EU_NS2] = _("nsMNT");
Desc_nlstab[EU_NS2] = _("MNT namespace Inode");
/* Translation Hint: maximum 'nsNET' = 10 + */
Head_nlstab[EU_NS3] = _("nsNET");
Desc_nlstab[EU_NS3] = _("NET namespace Inode");
/* Translation Hint: maximum 'nsPID' = 10 + */
Head_nlstab[EU_NS4] = _("nsPID");
Desc_nlstab[EU_NS4] = _("PID namespace Inode");
/* Translation Hint: maximum 'nsUSER' = 10 + */
Head_nlstab[EU_NS5] = _("nsUSER");
Desc_nlstab[EU_NS5] = _("USER namespace Inode");
/* Translation Hint: maximum 'nsUTS' = 10 + */
Head_nlstab[EU_NS6] = _("nsUTS");
Desc_nlstab[EU_NS6] = _("UTS namespace Inode");
/* Translation Hint: maximum 'LXC' = 8 + */
Head_nlstab[EU_LXC] = _("LXC");
Desc_nlstab[EU_LXC] = _("LXC container name");
/* Translation Hint: maximum 'RSan' = 6 */
Head_nlstab[EU_RZA] = _("RSan");
Desc_nlstab[EU_RZA] = _("RES Anonymous (KiB)");
/* Translation Hint: maximum 'RSfd' = 6 */
Head_nlstab[EU_RZF] = _("RSfd");
Desc_nlstab[EU_RZF] = _("RES File-based (KiB)");
/* Translation Hint: maximum 'RSlk' = 6 */
Head_nlstab[EU_RZL] = _("RSlk");
Desc_nlstab[EU_RZL] = _("RES Locked (KiB)");
/* Translation Hint: maximum 'RSsh' = 6 */
Head_nlstab[EU_RZS] = _("RSsh");
Desc_nlstab[EU_RZS] = _("RES Shared (KiB)");
/* Translation Hint: maximum 'CGNAME' = variable */
Head_nlstab[EU_CGN] = _("CGNAME");
Desc_nlstab[EU_CGN] = _("Control Group name");
/* Translation Hint: maximum 'NU' = 2 */
Head_nlstab[EU_NMA] = _("NU");
Desc_nlstab[EU_NMA] = _("Last Used NUMA node");
/* Translation Hint: maximum 'LOGID' = 5 + */
Head_nlstab[EU_LID] = _("LOGID");
Desc_nlstab[EU_LID] = _("Login User Id");
/* Translation Hint: maximum 'EXE' = variable */
Head_nlstab[EU_EXE] = _("EXE");
Desc_nlstab[EU_EXE] = _("Executable Path");
/* Translation Hint: With the next 5 fields, notice how an extra space
has been added ahead of one 'KiB' so that they all
align. You need not preserve such alignment. */
/* Translation Hint: maximum 'RSS' = 6 */
Head_nlstab[EU_RSS] = _("RSS");
Desc_nlstab[EU_RSS] = _("Res Mem (smaps), KiB");
/* Translation Hint: maximum 'PSS' = 6 */
Head_nlstab[EU_PSS] = _("PSS");
Desc_nlstab[EU_PSS] = _("Proportion RSS, KiB");
/* Translation Hint: maximum 'PSan' = 6 */
Head_nlstab[EU_PZA] = _("PSan");
Desc_nlstab[EU_PZA] = _("Proportion Anon, KiB");
/* Translation Hint: maximum 'PSfd' = 6 */
Head_nlstab[EU_PZF] = _("PSfd");
Desc_nlstab[EU_PZF] = _("Proportion File, KiB");
/* Translation Hint: maximum 'PSsh' = 6 */
Head_nlstab[EU_PZS] = _("PSsh");
Desc_nlstab[EU_PZS] = _("Proportion Shrd, KiB");
/* Translation Hint: maximum 'USS' = 6 */
Head_nlstab[EU_USS] = _("USS");
Desc_nlstab[EU_USS] = _("Unique RSS, KiB");
/* Translation Hint: maximum 'ioR' = 6 */
Head_nlstab[EU_IRB] = _("ioR");
Desc_nlstab[EU_IRB] = _("I/O Bytes Read");
/* Translation Hint: maximum 'ioRop' = 5 */
Head_nlstab[EU_IRO] = _("ioRop");
Desc_nlstab[EU_IRO] = _("I/O Read Operations");
/* Translation Hint: maximum 'ioW' = 6 */
Head_nlstab[EU_IWB] = _("ioW");
Desc_nlstab[EU_IWB] = _("I/O Bytes Written");
/* Translation Hint: maximum 'ioWop' = 5 */
Head_nlstab[EU_IWO] = _("ioWop");
Desc_nlstab[EU_IWO] = _("I/O Write Operations");
/* Translation Hint: maximum 'AGID' = 5 */
Head_nlstab[EU_AGI] = _("AGID");
Desc_nlstab[EU_AGI] = _("Autogroup Identifier");
/* Translation Hint: maximum 'AGNI' = 4 */
Head_nlstab[EU_AGN] = _("AGNI");
Desc_nlstab[EU_AGN] = _("Autogroup Nice Value");
/* Translation Hint: maximum 'STARTED' = 7 */
Head_nlstab[EU_TM3] = _("STARTED");
Desc_nlstab[EU_TM3] = _("Start Time from boot");
/* Translation Hint: maximum 'ELAPSED' = 7 */
Head_nlstab[EU_TM4] = _("ELAPSED");
Desc_nlstab[EU_TM4] = _("Elapsed Running Time");
/* Translation Hint: maximum '%CUU' = 6 */
Head_nlstab[EU_CUU] = _("%CUU");
Desc_nlstab[EU_CUU] = _("CPU Utilization");
/* Translation Hint: maximum '%CUC' = 7 */
Head_nlstab[EU_CUC] = _("%CUC");
Desc_nlstab[EU_CUC] = _("Utilization + child");
/* Translation Hint: maximum 'nsCGROUP' = 10 + */
Head_nlstab[EU_NS7] = _("nsCGROUP");
Desc_nlstab[EU_NS7] = _("CGRP namespace Inode");
/* Translation Hint: maximum 'nsTIME' = 10 + */
Head_nlstab[EU_NS8] = _("nsTIME");
Desc_nlstab[EU_NS8] = _("TIME namespace Inode");
}
/*
* This routine builds the nls table containing both plain text
* and regular c-format strings. */
static void build_norm_nlstab (void) {
/* Translation Notes ------------------------------------------------
. It is strongly recommend that the --no-wrap command line option
. be used with all supporting translation tools, when available.
.
. This group of lines contains both plain text and c-format strings.
.
. Some strings reflect switches used to affect the running program
. and should not be translated without also making corresponding
. c-code logic changes.
. */
Norm_nlstab[EXIT_signals_fmt] = _(""
"\tsignal %d (%s) was caught by %s, please\n"
"\tsend bug reports to <procps@freelists.org>\n");
Norm_nlstab[WRONG_switch_fmt] = _(""
"inappropriate '%s'\n"
"Usage:\n %s%s");
Norm_nlstab[HELP_cmdline_fmt] = _("\n"
"Usage:\n"
" %s [options]\n"
"\n"
"Options:\n"
" -b, --batch-mode run in non-interactive batch mode\n"
" -c, --cmdline-toggle reverse last remembered 'c' state\n"
" -d, --delay =SECS [.TENTHS] iterative delay as SECS [.TENTHS]\n"
" -E, --scale-summary-mem =SCALE set mem as: k,m,g,t,p,e for SCALE\n"
" -e, --scale-task-mem =SCALE set mem with: k,m,g,t,p for SCALE\n"
" -H, --threads-show show tasks plus all their threads\n"
" -i, --idle-toggle reverse last remembered 'i' state\n"
" -n, --iterations =NUMBER exit on maximum iterations NUMBER\n"
" -O, --list-fields output all field names, then exit\n"
" -o, --sort-override =FIELD force sorting on this named FIELD\n"
" -p, --pid =PIDLIST monitor only the tasks in PIDLIST\n"
" -S, --accum-time-toggle reverse last remembered 'S' state\n"
" -s, --secure-mode run with secure mode restrictions\n"
" -U, --filter-any-user =USER show only processes owned by USER\n"
" -u, --filter-only-euser =USER show only processes owned by USER\n"
" -w, --width [=COLUMNS] change print width [,use COLUMNS]\n"
" -1, --single-cpu-toggle reverse last remembered '1' state\n"
"\n"
" -h, --help display this help text, then exit\n"
" -V, --version output version information & exit\n"
"\n"
"For more details see top(1).");
Norm_nlstab[BAD_delayint_fmt] = _("bad delay interval '%s'");
Norm_nlstab[BAD_niterate_fmt] = _("bad iterations argument '%s'");
Norm_nlstab[LIMIT_exceed_fmt] = _("pid limit (%d) exceeded");
Norm_nlstab[BAD_mon_pids_fmt] = _("bad pid '%s'");
Norm_nlstab[MISSING_args_fmt] = _("-%c argument missing");
Norm_nlstab[BAD_widtharg_fmt] = _("bad width arg '%s'");
Norm_nlstab[UNKNOWN_opts_fmt] = _("unknown option '%s'");
Norm_nlstab[DELAY_secure_txt] = _("-d disallowed in \"secure\" mode");
Norm_nlstab[DELAY_badarg_txt] = _("-d requires positive argument");
Norm_nlstab[ON_word_only_txt] = _("On");
Norm_nlstab[OFF_one_word_txt] = _("Off");
Norm_nlstab[VERSION_opts_fmt] = _("%s from %s");
Norm_nlstab[FOREST_modes_fmt] = _("Forest mode %s");
Norm_nlstab[FAIL_tty_get_txt] = _("failed tty get");
Norm_nlstab[FAIL_tty_set_fmt] = _("failed tty set: %s");
Norm_nlstab[CHOOSE_group_txt] = _("Choose field group (1 - 4)");
Norm_nlstab[DISABLED_cmd_txt] = _("Command disabled, 'A' mode required");
Norm_nlstab[DISABLED_win_fmt] = _("Command disabled, activate %s with '-' or '_'");
Norm_nlstab[COLORS_nomap_txt] = _("No colors to map!");
Norm_nlstab[FAIL_rc_open_fmt] = _("Failed '%s' open: %s");
Norm_nlstab[WRITE_rcfile_fmt] = _("Wrote configuration to '%s'");
Norm_nlstab[DELAY_change_fmt] = _("Change delay from %.1f to");
Norm_nlstab[THREADS_show_fmt] = _("Show threads %s");
Norm_nlstab[IRIX_curmode_fmt] = _("Irix mode %s");
Norm_nlstab[GET_pid2kill_fmt] = _("PID to signal/kill [default pid = %d]");
Norm_nlstab[GET_sigs_num_fmt] = _("Send pid %d signal [%d/sigterm]");
Norm_nlstab[FAIL_signals_fmt] = _("Failed signal pid '%d' with '%d': %s");
Norm_nlstab[BAD_signalid_txt] = _("Invalid signal");
Norm_nlstab[GET_pid2nice_fmt] = _("PID to renice [default pid = %d]");
Norm_nlstab[GET_nice_num_fmt] = _("Renice PID %d to value");
Norm_nlstab[FAIL_re_nice_fmt] = _("Failed renice of PID %d to %d: %s");
Norm_nlstab[NAME_windows_fmt] = _("Rename window '%s' to (1-3 chars)");
Norm_nlstab[TIME_accumed_fmt] = _("Cumulative time %s");
Norm_nlstab[GET_max_task_fmt] = _("Maximum tasks = %d, change to (0 is unlimited)");
Norm_nlstab[BAD_max_task_txt] = _("Invalid maximum");
Norm_nlstab[GET_user_ids_txt] = _("Which user (blank for all)");
Norm_nlstab[UNKNOWN_cmds_txt] = _("Unknown command - try 'h' for help");
Norm_nlstab[SCROLL_coord_fmt] = _("scroll coordinates: y = %d/%d (tasks), x = %d/%d (fields)");
Norm_nlstab[FAIL_alloc_c_txt] = _("failed memory allocate");
Norm_nlstab[FAIL_alloc_r_txt] = _("failed memory re-allocate");
Norm_nlstab[BAD_numfloat_txt] = _("Unacceptable floating point");
Norm_nlstab[BAD_username_txt] = _("Invalid user");
Norm_nlstab[FOREST_views_txt] = _("forest view");
Norm_nlstab[FAIL_widepid_txt] = _("failed pid maximum size test");
Norm_nlstab[FAIL_widecpu_txt] = _("failed number of cpus test");
Norm_nlstab[RC_bad_files_fmt] = _("incompatible rcfile, you should delete '%s'");
Norm_nlstab[RC_bad_entry_fmt] = _("window entry #%d corrupt, please delete '%s'");
Norm_nlstab[NOT_onsecure_txt] = _("Unavailable in secure mode");
Norm_nlstab[NOT_smp_cpus_txt] = _("Only 1 cpu detected");
Norm_nlstab[BAD_integers_txt] = _("Unacceptable integer");
Norm_nlstab[SELECT_clash_txt] = _("conflicting process selections (U/p/u)");
/* Translation Hint: This is an abbreviation (limit 3 characters) for:
. kibibytes (1024 bytes) */
Norm_nlstab[AMT_kilobyte_txt] = _("KiB");
/* Translation Hint: This is an abbreviation (limit 3 characters) for:
. mebibytes (1,048,576 bytes) */
Norm_nlstab[AMT_megabyte_txt] = _("MiB");
/* Translation Hint: This is an abbreviation (limit 3 characters) for:
. gibibytes (1,073,741,824 bytes) */
Norm_nlstab[AMT_gigabyte_txt] = _("GiB");
/* Translation Hint: This is an abbreviation (limit 3 characters) for:
. tebibytes (1,099,511,627,776 bytes) */
Norm_nlstab[AMT_terabyte_txt] = _("TiB");
/* Translation Hint: This is an abbreviation (limit 3 characters) for:
. pebibytes (1,024 tebibytes) */
Norm_nlstab[AMT_petabyte_txt] = _("PiB");
/* Translation Hint: This is an abbreviation (limit 3 characters) for:
. exbibytes (1,024 pebibytes) */
Norm_nlstab[AMT_exxabyte_txt] = _("EiB");
Norm_nlstab[WORD_threads_txt] = _("Threads");
Norm_nlstab[WORD_process_txt] = _("Tasks");
/* Translation Hint: The following "word" is meant to represent either a single
. cpu or all of the processors in a multi-processor computer
. (should be exactly 6 characters, excluding leading % & colon) */
Norm_nlstab[WORD_allcpus_txt] = _("%Cpu(s):");
/* Translation Hint: The following "word" is meant to represent a single processor
. (should be exactly 3 characters, excluding leading %%, fmt chars & colon) */
Norm_nlstab[WORD_eachcpu_fmt] = _("%%Cpu%-3d:");
/* Translation Hint: The following word "another" must have 1 trailing space */
Norm_nlstab[WORD_another_txt] = _("another ");
Norm_nlstab[FIND_no_next_txt] = _("Locate next inactive, use \"L\"");
Norm_nlstab[GET_find_str_txt] = _("Locate string");
Norm_nlstab[FIND_no_find_fmt] = _("%s\"%s\" not found");
Norm_nlstab[XTRA_fixwide_fmt] = _("width incr is %d, change to (0 default, -1 auto)");
Norm_nlstab[XTRA_warncfg_txt] = _("rcfile has inspect/other-filter error(s), save anyway?");
Norm_nlstab[XTRA_badflds_fmt] = _("unrecognized field name '%s'");
Norm_nlstab[XTRA_winsize_txt] = _("even using field names only, window is now too small");
Norm_nlstab[YINSP_demo01_txt] = _("Open Files");
Norm_nlstab[YINSP_demo02_txt] = _("NUMA Info");
Norm_nlstab[YINSP_demo03_txt] = _("Log");
Norm_nlstab[YINSP_deqfmt_txt] = _("the '=' key will eventually show the actual file read or command(s) executed ...");
Norm_nlstab[YINSP_deqtyp_txt] = _("demo");
Norm_nlstab[YINSP_dstory_txt] = _(""
"This is simulated output representing the contents of some file or the output\n"
"from some command. Exactly which commands and/or files are solely up to you.\n"
"\n"
"Although this text is for information purposes only, it can still be scrolled\n"
"and searched like real output will be. You are encouraged to experiment with\n"
"those features as explained in the prologue above.\n"
"\n"
"To enable real Inspect functionality, entries must be added to the end of the\n"
"top personal personal configuration file. You could use your favorite editor\n"
"to accomplish this, taking care not to disturb existing entries.\n"
"\n"
"Another way to add entries is illustrated below, but it risks overwriting the\n"
"rcfile. Redirected echoes must not replace (>) but append (>>) to that file.\n"
"\n"
" /bin/echo -e \"pipe\\tOpen Files\\tlsof -P -p %d 2>&1\" >> ~/.toprc\n"
" /bin/echo -e \"file\\tNUMA Info\\t/proc/%d/numa_maps\" >> ~/.toprc\n"
" /bin/echo -e \"pipe\\tLog\\ttail -n200 /var/log/syslog | sort -Mr\" >> ~/.toprc\n"
"\n"
"If you don't know the location or name of the top rcfile, use the 'W' command\n"
"and note those details. After backing up the current rcfile, try issuing the\n"
"above echoes exactly as shown, replacing '.toprc' as appropriate. The safest\n"
"approach would be to use copy then paste to avoid any typing mistakes.\n"
"\n"
"Finally, restart top to reveal what actual Inspect entries combined with this\n"
"new command can offer. The possibilities are endless, especially considering\n"
"that 'pipe' type entries can include shell scripts too!\n"
"\n"
"For additional important information, please consult the top(1) man document.\n"
"Then, enhance top with your very own customized 'file' and/or 'pipe' entries.\n"
"\n"
"Enjoy!\n");
Norm_nlstab[YINSP_noent1_txt] = _("to enable 'Y' press <Enter> then type 'W' and restart top");
Norm_nlstab[YINSP_noent2_txt] = _("to enable 'Y' please consult the top man page (press Enter)");
Norm_nlstab[YINSP_failed_fmt] = _("Selection failed with: %s\n");
Norm_nlstab[YINSP_pidbad_fmt] = _("unable to inspect, pid %d not found");
Norm_nlstab[YINSP_pidsee_fmt] = _("inspect at PID [default pid = %d]");
Norm_nlstab[YINSP_status_fmt] = _("%s: %*d-%-*d lines, %*d-%*d columns, %lu bytes read");
Norm_nlstab[YINSP_waitin_txt] = _("patience please, working ...");
Norm_nlstab[YINSP_workin_txt] = _("working, use Ctrl-C to end ...");
/* Translation Hint: Below are 2 abbreviations which can be as long as needed:
. FLD = FIELD, VAL = VALUE */
Norm_nlstab[OSEL_prompts_fmt] = _("add filter #%d (%s) as: [!]FLD?VAL");
Norm_nlstab[OSEL_casenot_txt] = _("ignoring case");
Norm_nlstab[OSEL_caseyes_txt] = _("case sensitive");
Norm_nlstab[OSEL_errdups_txt] = _("duplicate filter was ignored");
Norm_nlstab[OSEL_errdelm_fmt] = _("'%s' filter delimiter is missing");
Norm_nlstab[OSEL_errvalu_fmt] = _("'%s' filter value is missing");
Norm_nlstab[WORD_include_txt] = _("include");
Norm_nlstab[WORD_exclude_txt] = _("exclude");
Norm_nlstab[OSEL_statlin_fmt] = _("<Enter> to resume, filters: %s");
Norm_nlstab[WORD_noneone_txt] = _("none");
/* Translation Hint: The following word 'Node' should be exactly
4 characters, excluding leading %%, fmt chars & colon) */
Norm_nlstab[NUMA_nodenam_fmt] = _("%%Node%-2d:");
Norm_nlstab[NUMA_nodeget_fmt] = _("expand which numa node (0-%d)");
Norm_nlstab[NUMA_nodebad_txt] = _("invalid numa node");
Norm_nlstab[NUMA_nodenot_txt] = _("sorry, NUMA extensions unavailable");
/* Translation Hint: 'Mem ' is an abbreviation for physical memory/ram
. 'Swap' represents the linux swap file --
. please make both translations exactly 4 characters,
. padding with extra spaces as necessary */
Norm_nlstab[WORD_abv_mem_txt] = _("Mem ");
Norm_nlstab[WORD_abv_swp_txt] = _("Swap");
Norm_nlstab[LIB_errormem_fmt] = _("library failed memory statistics, at %d: %s");
Norm_nlstab[LIB_errorcpu_fmt] = _("library failed cpu statistics, at %d: %s");
Norm_nlstab[LIB_errorpid_fmt] = _("library failed pids statistics, at %d: %s");
Norm_nlstab[BAD_memscale_fmt] = _("bad memory scaling arg '%s'");
Norm_nlstab[XTRA_vforest_fmt] = _("PID to collapse/expand [default pid = %d]");
Norm_nlstab[XTRA_size2up_txt] = _("terminal is not wide enough");
Norm_nlstab[XTRA_modebad_txt] = _("wrong mode, command inactive");
Norm_nlstab[XTRA_warnold_txt] = _("saving prevents older top from reading, save anyway?");
Norm_nlstab[X_SEMAPHORES_fmt] = _("failed sem_init() at %d: %s");
Norm_nlstab[X_THREADINGS_fmt] = _("failed pthread_create() at %d: %s");
Norm_nlstab[X_RESTRICTED_txt] = _("sorry, restricted namespace with reduced functionality");
Norm_nlstab[AGNI_valueof_fmt] = _("set pid %d AGNI value to");
Norm_nlstab[AGNI_invalid_txt] = _("valid AGNI range is -20 to +19");
Norm_nlstab[AGNI_notopen_fmt] = _("autogroup open failed, %s");
Norm_nlstab[AGNI_nowrite_fmt] = _("autogroup write failed, %s");
Norm_nlstab[X_BOT_cmdlin_fmt] = _("command line for pid %d, %s");
Norm_nlstab[X_BOT_ctlgrp_fmt] = _("control groups for pid %d, %s");
Norm_nlstab[X_BOT_envirn_fmt] = _("environment for pid %d, %s");
Norm_nlstab[X_BOT_namesp_fmt] = _("namespaces for pid %d, %s");
Norm_nlstab[X_BOT_nodata_txt] = _("n/a");
Norm_nlstab[X_BOT_supgrp_fmt] = _("supplementary groups for pid %d, %s");
Norm_nlstab[X_BOT_msglog_txt] = _("message log, last 10 messages:");
}
/*
* This routine builds the nls table containing specially
* formatted strings designed to fit within an 80x24 terminal. */
static void build_uniq_nlstab (void) {
/* Translation Notes ------------------------------------------------
. It is strongly recommend that the --no-wrap command line option
. be used with all supporting translation tools, when available.
.
. The next several text groups contain special escape sequences
. representing values used to index a table at run-time.
.
. Each such sequence consists of a tilde (~) followed by an ascii
. number in the range of '1' - '8'. Examples are '~2', '~8', etc.
. These escape sequences must never themselves be translated but
. could be deleted.
.
. If you remove these escape sequences (both tilde and number) it
. would make translation easier. However, the ability to display
. colors and bold text at run-time will have been lost.
.
. Additionally, each of these text groups was designed to display
. in a 80x24 terminal window. Hopefully, any translations will
. adhere to that goal lest the translated text be truncated.
.
. If you would like additional information regarding these strings,
. please see the prologue to the show_special function in the top.c
. source file.
.
. Caution:
. The next three items represent pages for interacting with a user.
. In all cases, the last lines of text must be treated with care.
.
. They must not end with a newline character so that at runtime the
. cursor will remain on that final text line.
.
. Also, the special sequences (tilde+number) must not appear on the
. last line (the one without the newline). So please avoid any line
. wraps that could place them there.
. */
Uniq_nlstab[KEYS_helpbas_fmt] = _(""
"Help for Interactive Commands~2 - %s\n"
"Window ~1%s~6: ~1Cumulative mode ~3%s~2. ~1System~6: ~1Delay ~3%.1f secs~2; ~1Secure mode ~3%s~2.\n"
"\n"
" Z~5,~1B~5,E,e Global: '~1Z~2' colors; '~1B~2' bold; '~1E~2'/'~1e~2' summary/task memory scale\n"
" l,t,m,I Toggle: '~1l~2' load avg; '~1t~2' task/cpu; '~1m~2' memory; '~1I~2' Irix mode\n"
" 0,1,2,3,4 Toggle: '~10~2' zeros; '~11~2/~12~2/~13~2' cpu/numa views; '~14~2' cpus two abreast\n"
" f,X Fields: '~1f~2' add/remove/order/sort; '~1X~2' increase fixed-width fields\n"
"\n"
" L,&,<,> . Locate: '~1L~2'/'~1&~2' find/again; Move sort column: '~1<~2'/'~1>~2' left/right\n"
" R,H,J,C . Toggle: '~1R~2' Sort; '~1H~2' Threads; '~1J~2' Num justify; '~1C~2' Coordinates\n"
" c,i,S,j . Toggle: '~1c~2' Cmd name/line; '~1i~2' Idle; '~1S~2' Time; '~1j~2' Str justify\n"
" x~5,~1y~5 . Toggle highlights: '~1x~2' sort field; '~1y~2' running tasks\n"
" z~5,~1b~5 . Toggle: '~1z~2' color/mono; '~1b~2' bold/reverse (only if 'x' or 'y')\n"
" u,U,o,O . Filter by: '~1u~2'/'~1U~2' effective/any user; '~1o~2'/'~1O~2' other criteria\n"
" n,#,^O . Set: '~1n~2'/'~1#~2' max tasks displayed; Show: ~1Ctrl~2+'~1O~2' other filter(s)\n"
" V,v,F . Toggle: '~1V~2' forest view; '~1v~2' hide/show children; '~1F~2' keep focused\n"
"\n"
"%s"
" ^G,K,N,U View: ctl groups ~1^G~2; cmdline ~1^K~2; environment ~1^N~2; supp groups ~1^U~2\n"
" W,Y,!,^E Write cfg '~1W~2'; Inspect '~1Y~2'; Combine Cpus '~1!~2'; Scale time ~1^E~2'\n"
" q Quit\n"
" ( commands shown with '.' require a ~1visible~2 task display ~1window~2 ) \n"
"Press '~1h~2' or '~1?~2' for help with ~1Windows~2,\n"
"Type 'q' or <Esc> to continue ");
Uniq_nlstab[WINDOWS_help_fmt] = _(""
"Help for Windows / Field Groups~2 - \"Current Window\" = ~1 %s ~6\n"
"\n"
". Use multiple ~1windows~2, each with separate config opts (color,fields,sort,etc)\n"
". The 'current' window controls the ~1Summary Area~2 and responds to your ~1Commands~2\n"
" . that window's ~1task display~2 can be turned ~1Off~2 & ~1On~2, growing/shrinking others\n"
" . with ~1NO~2 task display, some commands will be ~1disabled~2 ('i','R','n','c', etc)\n"
" until a ~1different window~2 has been activated, making it the 'current' window\n"
". You ~1change~2 the 'current' window by: ~1 1~2) cycling forward/backward;~1 2~2) choosing\n"
" a specific field group; or~1 3~2) exiting the color mapping or fields screens\n"
". Commands ~1available anytime -------------~2\n"
" A . Alternate display mode toggle, show ~1Single~2 / ~1Multiple~2 windows\n"
" g . Choose another field group and make it 'current', or change now\n"
" by selecting a number from: ~1 1~2 =%s;~1 2~2 =%s;~1 3~2 =%s; or~1 4~2 =%s\n"
". Commands ~1requiring~2 '~1A~2' mode~1 -------------~2\n"
" G . Change the ~1Name~5 of the 'current' window/field group\n"
" ~1*~4 a , w . Cycle through all four windows: '~1a~5' Forward; '~1w~5' Backward\n"
" ~1*~4 - , _ . Show/Hide: '~1-~5' ~1Current~2 window; '~1_~5' all ~1Visible~2/~1Invisible~2\n"
" The screen will be divided evenly between task displays. But you can make\n"
" some ~1larger~2 or ~1smaller~2, using '~1n~2' and '~1i~2' commands. Then later you could:\n"
" ~1*~4 = , + . Rebalance tasks: '~1=~5' ~1Current~2 window; '~1+~5' ~1Every~2 window\n"
" (this also forces the ~1current~2 or ~1every~2 window to become visible)\n"
"\n"
"In '~1A~2' mode, '~1*~4' keys are your ~1essential~2 commands. Please try the '~1a~2' and '~1w~2'\n"
"commands plus the 'g' sub-commands NOW. Press <Enter> to make 'Current' ");
/* Translation Notes ------------------------------------------------
. The following 'Help for color mapping' simulated screen should
. probably NOT be translated. It is terribly hard to follow in
. this form and any translation could produce unpleasing results
. that are unlikely to parallel the running top program.
.
. If you decide to proceed with translation, please take care
. to not disturb the spaces and the tilde + number delimiters.
. */
Uniq_nlstab[COLOR_custom_fmt] = _(""
"Help for color mapping~2 - \"Current Window\" = ~1 %s ~6\n"
"\n"
" color - 04:25:44 up 8 days, 50 min, 7 users, load average:\n"
" Tasks:~3 64 ~2total,~3 2 ~3running,~3 62 ~2sleeping,~3 0 ~2stopped,~3\n"
" %%Cpu(s):~3 76.5 ~2user,~3 11.2 ~2system,~3 0.0 ~2nice,~3 12.3 ~2idle~3\n"
" ~1 Nasty Message! ~4 -or- ~1Input Prompt~5\n"
" ~1 PID TTY PR NI %%CPU TIME+ VIRT SWAP S COMMAND ~6\n"
" 17284 ~8pts/2 ~7 8 0 0.0 0:00.75 1380 0 S /bin/bash ~8\n"
" ~1 8601 pts/1 7 -10 0.4 0:00.03 916 0 R color -b -z~7\n"
" 11005 ~8? ~7 9 0 0.0 0:02.50 2852 1008 S amor -sessi~8\n"
" available toggles: ~1B~2 =disable bold globally (~1%s~2),\n"
" ~1z~2 =color/mono (~1%s~2), ~1b~2 =tasks \"bold\"/reverse (~1%s~2)\n"
"\n"
"1) Select a ~1target~2 as an upper case letter, ~1current target~2 is ~1 %c ~4:\n"
" S~2 = Summary Data,~1 M~2 = Messages/Prompts,\n"
" H~2 = Column Heads,~1 T~2 = Task Information\n"
"2) Select a ~1color~2 as a number or use the up/down arrow keys\n"
" to raise/lower the %d colors value, ~1current color~2 is ~1 %d ~4:\n"
" 0~2 = black,~1 1~2 = red, ~1 2~2 = green,~1 3~2 = yellow,\n"
" 4~2 = blue, ~1 5~2 = magenta,~1 6~2 = cyan, ~1 7~2 = white\n"
"\n"
"3) Then use these keys when finished:\n"
" 'q' or <Esc> to abort changes to window '~1%s~2'\n"
" 'a' or 'w' to commit & change another, <Enter> to commit and end ");
/* Translation Hint: As is true for the text above, the "keys" shown to the left and
. also imbedded in the translatable text (along with escape seqs)
. should never themselves be translated. */
Uniq_nlstab[KEYS_helpext_fmt] = _(""
" d,k,r,^R '~1d~2' set delay; '~1k~2' kill; '~1r~2' renice; ~1Ctrl~2+'~1R~2' renice autogroup\n");
/* Translation Hint:
. This Fields Management header should be 3 lines long so as
. to allow 1 blank line before the fields & descriptions.
. If absoultely necessary, 4 lines could be used (but never more).
. */
Uniq_nlstab[FIELD_header_fmt] = _(""
"Fields Management~2 for window ~1%s~6, whose current sort field is ~1%s~2\n"
" Navigate with Up/Dn, Right selects for move then <Enter> or Left commits,\n"
" 'd' or <Space> toggles display, 's' sets sort. Use 'q' or <Esc> to end!\n");
/* Translation Hint:
. The next 5 items must each be translated as a single line.
. */
Uniq_nlstab[STATE_line_1_fmt] = _("%s:~3"
" %3u ~2total,~3 %3u ~2running,~3 %3u ~2sleeping,~3 %3u ~2stopped,~3 %3u ~2zombie~3\n");
/* Translation Hint: Only the following abbreviations need be translated
. us = user, sy = system, ni = nice, id = idle, wa = wait,
. hi hardware interrupt, si = software interrupt */
Uniq_nlstab[STATE_lin2x6_fmt] = _("%s~3"
" %#5.1f ~2us,~3 %#5.1f ~2sy,~3 %#5.1f ~2ni,~3 %#5.1f ~2id,~3 %#5.1f ~2wa,~3 %#5.1f ~2hi,~3 %#5.1f ~2si~3");
/* Translation Hint: Only the following abbreviations need be translated
. us = user, sy = system, ni = nice, id = idle, wa = wait,
. hi hardware interrupt, si = software interrupt, st = steal time */
Uniq_nlstab[STATE_lin2x7_fmt] = _("%s~3"
"%#5.1f ~2us,~3%#5.1f ~2sy,~3%#5.1f ~2ni,~3%#5.1f ~2id,~3%#5.1f ~2wa,~3%#5.1f ~2hi,~3%#5.1f ~2si,~3%#5.1f ~2st~3");
/* Translation Hint: next 2 must be treated together, with WORDS above & below aligned */
Uniq_nlstab[MEMORY_line1_fmt] = _(""
"%s %s:~3 %9.9s~2total,~3 %9.9s~2free,~3 %9.9s~2used,~3 %9.9s~2buff/cache~3 ");
Uniq_nlstab[MEMORY_line2_fmt] = _(""
"%s %s:~3 %9.9s~2total,~3 %9.9s~2free,~3 %9.9s~2used.~3 %9.9s~2avail %s~3");
/* Translation Hint:
. The next 2 headers for 'Inspection' must each be 3 lines or less
. */
Uniq_nlstab[YINSP_hdsels_fmt] = _(""
"Inspection~2 Pause at: pid ~1%d~6, running ~1%s~6\n"
"Use~2: left/right then <Enter> to ~1select~5 an option; 'q' or <Esc> to ~1end~5 !\n"
"Options~2: ~1%s\n");
Uniq_nlstab[YINSP_hdview_fmt] = _(""
"Inspection~2 View at pid: ~1%s~3, running ~1%s~3. Locating: ~1%s~6\n"
"Use~2: left/right/up/down/etc to ~1navigate~5 the output; 'L'/'&' to ~1locate~5/~1next~5.\n"
"Or~2: <Enter> to ~1select another~5; 'q' or <Esc> to ~1end~5 !\n");
}
/*
* This function must be called very early at startup, before
* any other function call, and especially before any changes
* have been made to the terminal if VALIDATE_NLS is defined!
*
* The gettext documentation suggests that alone among locale
* variables LANGUAGE=ll_CC may be abbreviated as LANGUAGE=ll
* to denote the language's main dialect. Unfortunately this
* does not appear to be true. One must specify the complete
* ll_CC. Optionally, a '.UTF-8' or '.uft8' suffix, as shown
* in the following examples, may also be included:
* export LANGUAGE=ll_CC # minimal requirement
* export LANGUAGE=ll_CC.UTF-8 # optional convention
* export LANGUAGE=ll_CC.utf8 # ok, too
*
* Additionally, as suggested in the gettext documentation, a
* user will also have to export an empty LC_ALL= to actually
* enable any translations.
*/
void initialize_nls (void) {
#ifdef VALIDATE_NLS
static const char *nls_err ="\t%s_nlstab[%d] == NULL\n";
int i;
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
memset(Head_nlstab, 0, sizeof(Head_nlstab));
memset(Desc_nlstab, 0, sizeof(Desc_nlstab));
build_two_nlstabs();
for (i = 0; i < EU_MAXPFLGS; i++) {
if (!Head_nlstab[i]) {
fprintf(stderr, nls_err, "Head", i);
exit(1);
}
if (!Desc_nlstab[i]) {
fprintf(stderr, nls_err, "Desc", i);
exit(1);
}
}
memset(Norm_nlstab, 0, sizeof(Norm_nlstab));
build_norm_nlstab();
for (i = 0; i < norm_MAX; i++)
if (!Norm_nlstab[i]) {
fprintf(stderr, nls_err, "Norm", i);
exit(1);
}
memset(Uniq_nlstab, 0, sizeof(Uniq_nlstab));
build_uniq_nlstab();
for (i = 0; i < uniq_MAX; i++)
if (!Uniq_nlstab[i]) {
fprintf(stderr, nls_err, "Uniq", i);
exit(1);
}
#else
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
build_two_nlstabs();
build_norm_nlstab();
build_uniq_nlstab();
#endif
}

108
src/top/top_nls.h Normal file
View File

@@ -0,0 +1,108 @@
/* top_nls.h - provide the basis for future nls translations */
/*
* Copyright (c) 2011-2022, by: Jim Warner <james.warner@comcast.net
*
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program 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 Library General Public License for more details.
*/
/* For contributions to this program, the author wishes to thank:
* Craig Small, <csmall@dropbear.xyz>
* Sami Kerola, <kerolasa@iki.fi>
*/
#ifndef _Itop_nls
#define _Itop_nls
/*
* These are our string tables with the following contents:
* Head : column headings with varying size limits
* Desc : field descriptions not to exceed 20 screen positions
* Norm : regular text possibly also containing c-format specifiers
* Uniq : show_special specially formatted strings
*
* The latter table presents the greatest translation challenge !
*
* We go to the trouble of creating the nls string tables to achieve
* these objectives:
* + the overhead of repeated runtime calls to gettext()
* will be avoided
* + the order of the strings in the template (.pot) file
* can be completely controlled
* + none of the important translator only comments will
* clutter and obscure the main program
*/
extern const char *Head_nlstab[];
extern const char *Desc_nlstab[];
extern const char *Norm_nlstab[];
extern const char *Uniq_nlstab[];
/*
* Simple optional macros to ease table access.
* The N_txt and N_fmt macros are interchangeable but
* highlight the two types of strings found in Norm_nlstable.
*/
#define N_col(e) Head_nlstab[e]
#define N_fld(e) Desc_nlstab[e]
#define N_txt(e) Norm_nlstab[e]
#define N_fmt(e) Norm_nlstab[e]
#define N_unq(e) Uniq_nlstab[e]
/*
* These enums are the means to access two of our four tables.
* The Head_nlstab and Desc_nlstab are accessed with standard
* top pflag enums.
*
* The norm_nls enums carry a suffix distinguishing plain text
* from any text also containiing c-format specifiers.
*/
enum norm_nls {
AGNI_invalid_txt, AGNI_notopen_fmt, AGNI_nowrite_fmt, AGNI_valueof_fmt,
AMT_exxabyte_txt, AMT_gigabyte_txt, AMT_kilobyte_txt, AMT_megabyte_txt,
AMT_petabyte_txt, AMT_terabyte_txt, BAD_delayint_fmt, BAD_integers_txt,
BAD_max_task_txt, BAD_memscale_fmt, BAD_mon_pids_fmt, BAD_niterate_fmt,
BAD_numfloat_txt, BAD_signalid_txt, BAD_username_txt, BAD_widtharg_fmt,
CHOOSE_group_txt, COLORS_nomap_txt, DELAY_badarg_txt, DELAY_change_fmt,
DELAY_secure_txt, DISABLED_cmd_txt, DISABLED_win_fmt, EXIT_signals_fmt,
FAIL_alloc_c_txt, FAIL_alloc_r_txt, FAIL_rc_open_fmt, FAIL_re_nice_fmt,
FAIL_signals_fmt, FAIL_tty_get_txt, FAIL_tty_set_fmt, FAIL_widecpu_txt,
FAIL_widepid_txt, FIND_no_find_fmt, FIND_no_next_txt, FOREST_modes_fmt,
FOREST_views_txt, GET_find_str_txt, GET_max_task_fmt, GET_nice_num_fmt,
GET_pid2kill_fmt, GET_pid2nice_fmt, GET_sigs_num_fmt, GET_user_ids_txt,
HELP_cmdline_fmt, IRIX_curmode_fmt, LIB_errorcpu_fmt, LIB_errormem_fmt,
LIB_errorpid_fmt, LIMIT_exceed_fmt, MISSING_args_fmt, NAME_windows_fmt,
NOT_onsecure_txt, NOT_smp_cpus_txt, NUMA_nodebad_txt, NUMA_nodeget_fmt,
NUMA_nodenam_fmt, NUMA_nodenot_txt, OFF_one_word_txt, ON_word_only_txt,
OSEL_casenot_txt, OSEL_caseyes_txt, OSEL_errdelm_fmt, OSEL_errdups_txt,
OSEL_errvalu_fmt, OSEL_prompts_fmt, OSEL_statlin_fmt, RC_bad_entry_fmt,
RC_bad_files_fmt, SCROLL_coord_fmt, SELECT_clash_txt, THREADS_show_fmt,
TIME_accumed_fmt, UNKNOWN_cmds_txt, UNKNOWN_opts_fmt, VERSION_opts_fmt,
WORD_abv_mem_txt, WORD_abv_swp_txt, WORD_allcpus_txt, WORD_another_txt,
WORD_eachcpu_fmt, WORD_exclude_txt, WORD_include_txt, WORD_noneone_txt,
WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt,
XTRA_badflds_fmt, XTRA_fixwide_fmt, XTRA_modebad_txt, XTRA_size2up_txt,
XTRA_vforest_fmt, XTRA_warncfg_txt, XTRA_warnold_txt, XTRA_winsize_txt,
X_BOT_cmdlin_fmt, X_BOT_ctlgrp_fmt, X_BOT_envirn_fmt, X_BOT_msglog_txt,
X_BOT_namesp_fmt, X_BOT_nodata_txt, X_BOT_supgrp_fmt, X_RESTRICTED_txt,
X_SEMAPHORES_fmt, X_THREADINGS_fmt,
YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt,
YINSP_deqtyp_txt, YINSP_dstory_txt,
YINSP_failed_fmt, YINSP_noent1_txt, YINSP_noent2_txt, YINSP_pidbad_fmt,
YINSP_pidsee_fmt, YINSP_status_fmt, YINSP_waitin_txt, YINSP_workin_txt,
norm_MAX
};
enum uniq_nls {
COLOR_custom_fmt, FIELD_header_fmt, KEYS_helpbas_fmt, KEYS_helpext_fmt,
MEMORY_line1_fmt, MEMORY_line2_fmt, STATE_lin2x6_fmt, STATE_lin2x7_fmt,
STATE_line_1_fmt, WINDOWS_help_fmt, YINSP_hdsels_fmt, YINSP_hdview_fmt,
uniq_MAX
};
void initialize_nls (void);
#endif /* _Itop_nls */

123
src/uptime.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* uptime.c - display system uptime
* Copyright (C) 2012-2015 Craig Small <csmall@dropbear.xyz>
*
* 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 <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include "c.h"
#include "fileutils.h"
#include "nls.h"
#include <proc/misc.h>
static void print_uptime_since()
{
double now, uptime_secs, idle_secs;
time_t up_since_secs;
struct tm *up_since;
struct timeval tim;
/* Get the current time and convert it to a double */
if (gettimeofday(&tim, NULL) != 0)
xerr(EXIT_FAILURE, "gettimeofday");
now = tim.tv_sec + (tim.tv_usec / 1000000.0);
/* Get the uptime and calculate when that was */
if (procps_uptime(&uptime_secs, &idle_secs) < 0)
xerr(EXIT_FAILURE, _("Cannot get system uptime"));
up_since_secs = (time_t) ((now - uptime_secs) + 0.5);
/* Show this */
if ((up_since = localtime(&up_since_secs)) == NULL)
xerrx(EXIT_FAILURE, "localtime");
printf("%04d-%02d-%02d %02d:%02d:%02d\n",
up_since->tm_year + 1900, up_since->tm_mon + 1, up_since->tm_mday,
up_since->tm_hour, up_since->tm_min, up_since->tm_sec);
}
static void __attribute__ ((__noreturn__)) usage(FILE * out)
{
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" -p, --pretty show uptime in pretty format\n"), out);
fputs(USAGE_HELP, out);
fputs(_(" -s, --since system up since\n"), out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("uptime(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
int c, p = 0;
char *uptime_str;
static const struct option longopts[] = {
{"pretty", no_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{"since", no_argument, NULL, 's'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
while ((c = getopt_long(argc, argv, "phsV", longopts, NULL)) != -1)
switch (c) {
case 'p':
p = 1;
break;
case 'h':
usage(stdout);
case 's':
print_uptime_since();
return EXIT_SUCCESS;
case 'V':
printf(PROCPS_NG_VERSION);
return EXIT_SUCCESS;
default:
usage(stderr);
}
if (optind != argc)
usage(stderr);
if (p)
uptime_str = procps_uptime_sprint_short();
else
uptime_str = procps_uptime_sprint();
if (!uptime_str || uptime_str[0] == '\0')
xerr(EXIT_FAILURE, _("Cannot get system uptime"));
printf("%s\n", uptime_str);
return EXIT_SUCCESS;
}

1089
src/vmstat.c Normal file

File diff suppressed because it is too large Load Diff

714
src/w.c Normal file
View File

@@ -0,0 +1,714 @@
/*
* w - show what logged in users are doing.
*
* Almost entirely rewritten from scratch by Charles Blake circa
* June 1996. Some vestigal traces of the original may exist.
* That was done in 1993 by Larry Greenfield with some fixes by
* Michael K. Johnson.
*
* Changes by Albert Cahalan, 2002.
*
* 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <locale.h>
#include <pwd.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_UTMPX_H
# include <utmpx.h>
#else
# include <utmp.h>
#endif
#include <arpa/inet.h>
#include "c.h"
#include "fileutils.h"
#include "nls.h"
#include <proc/misc.h>
#include <proc/pids.h>
static int ignoreuser = 0; /* for '-u' */
static int oldstyle = 0; /* for '-o' */
#ifdef HAVE_UTMPX_H
typedef struct utmpx utmp_t;
#else
typedef struct utmp utmp_t;
#endif
#if !defined(UT_HOSTSIZE) || defined(__UT_HOSTSIZE)
# define UT_HOSTSIZE __UT_HOSTSIZE
# define UT_LINESIZE __UT_LINESIZE
# define UT_NAMESIZE __UT_NAMESIZE
#endif
#ifdef W_SHOWFROM
# define FROM_STRING "on"
#else
# define FROM_STRING "off"
#endif
#define MAX_CMD_WIDTH 512
#define MIN_CMD_WIDTH 7
/*
* This routine is careful since some programs leave utmp strings
* unprintable. Always outputs at least 16 chars padded with
* spaces on the right if necessary.
*/
static void print_host(const char *restrict host, int len, const int fromlen)
{
const char *last;
int width = 0;
if (len > fromlen)
len = fromlen;
last = host + len;
for (; host < last; host++) {
if (*host == '\0') break;
if (isprint(*host) && *host != ' ') {
fputc(*host, stdout);
++width;
} else {
fputc('-', stdout);
++width;
break;
}
}
/*
* space-fill, and a '-' too if needed to ensure the
* column exists
*/
if (!width) {
fputc('-', stdout);
++width;
}
while (width++ < fromlen)
fputc(' ', stdout);
}
/* This routine prints the display part of the host or IPv6 link address interface */
static void print_display_or_interface(const char *restrict host, int len, int restlen)
{
const char *const end = host + (len > 0 ? len : 0);
const char *disp, *tmp;
if (restlen <= 0) return; /* not enough space for printing anything */
/* search for a collon (might be a display) */
disp = host;
while ( (disp < end) && (*disp != ':') && isprint(*disp) ) disp++;
/* colon found */
if (disp < end && *disp == ':') {
/* detect multiple colons -> IPv6 in the host (not a display) */
tmp = disp+1;
while ( (tmp < end) && (*tmp != ':') && isprint(*tmp) ) tmp++;
if (tmp >= end || *tmp != ':') { /* multiple colons not found - it's a display */
/* number of chars till the end of the input field */
len -= (disp - host);
/* if it is still longer than the rest of the output field, then cut it */
if (len > restlen) len = restlen;
/* print the display */
while ((len > 0) && isprint(*disp) && (*disp != ' ')) {
len--; restlen--;
fputc(*disp, stdout);
disp++;
}
if ((len > 0) && (*disp != '\0')) { /* space or nonprintable found - replace with dash and stop printing */
restlen--;
fputc('-', stdout);
}
} else { /* multiple colons found - it's an IPv6 address */
/* search for % (interface separator in case of IPv6 link address) */
while ( (tmp < end) && (*tmp != '%') && isprint(*tmp) ) tmp++;
if (tmp < end && *tmp == '%') { /* interface separator found */
/* number of chars till the end of the input field */
len -= (tmp - host);
/* if it is still longer than the rest of the output field, then cut it */
if (len > restlen) len = restlen;
/* print the interface */
while ((len > 0) && isprint(*tmp) && (*tmp != ' ')) {
len--; restlen--;
fputc(*tmp, stdout);
tmp++;
}
if ((len > 0) && (*tmp != '\0')) { /* space or nonprintable found - replace with dash and stop printing */
restlen--;
fputc('-', stdout);
}
}
}
}
/* padding with spaces */
while (restlen > 0) {
fputc(' ', stdout);
restlen--;
}
}
/* This routine prints either the hostname or the IP address of the remote */
static void print_from(const utmp_t *restrict const u, const int ip_addresses, const int fromlen) {
char buf[fromlen + 1];
char buf_ipv6[INET6_ADDRSTRLEN];
int len;
#ifndef __CYGWIN__
int32_t ut_addr_v6[4]; /* IP address of the remote host */
if (ip_addresses) { /* -i switch used */
memcpy(&ut_addr_v6, &u->ut_addr_v6, sizeof(ut_addr_v6));
if (IN6_IS_ADDR_V4MAPPED(&ut_addr_v6)) {
/* map back */
ut_addr_v6[0] = ut_addr_v6[3];
ut_addr_v6[1] = 0;
ut_addr_v6[2] = 0;
ut_addr_v6[3] = 0;
}
if (ut_addr_v6[1] || ut_addr_v6[2] || ut_addr_v6[3]) {
/* IPv6 */
if (!inet_ntop(AF_INET6, &ut_addr_v6, buf_ipv6, sizeof(buf_ipv6))) {
strcpy(buf, ""); /* invalid address, clean the buffer */
} else {
strncpy(buf, buf_ipv6, fromlen); /* address valid, copy to buffer */
}
} else {
/* IPv4 */
if (!(ut_addr_v6[0] && inet_ntop(AF_INET, &ut_addr_v6[0], buf, sizeof(buf)))) {
strcpy(buf, ""); /* invalid address, clean the buffer */
}
}
buf[fromlen] = '\0';
len = strlen(buf);
if (len) { /* IP address is non-empty, print it (and concatenate with display, if present) */
fputs(buf, stdout);
/* show the display part of the host or IPv6 link addr. interface, if present */
print_display_or_interface(u->ut_host, UT_HOSTSIZE, fromlen - len);
} else { /* IP address is empty, print the host instead */
print_host(u->ut_host, UT_HOSTSIZE, fromlen);
}
} else { /* -i switch NOT used */
print_host(u->ut_host, UT_HOSTSIZE, fromlen);
}
#else
print_host(u->ut_host, UT_HOSTSIZE, fromlen);
#endif
}
/* compact 7 char format for time intervals (belongs in libproc?) */
static void print_time_ival7(time_t t, int centi_sec, FILE * fout)
{
if ((long)t < (long)0) {
/* system clock changed? */
printf(" ? ");
return;
}
if (oldstyle) {
if (t >= 48 * 60 * 60)
/* > 2 days */
fprintf(fout, _(" %2ludays"), t / (24 * 60 * 60));
else if (t >= 60 * 60)
/* > 1 hour */
/* Translation Hint: Hours:Minutes */
fprintf(fout, " %2lu:%02u ", t / (60 * 60),
(unsigned)((t / 60) % 60));
else if (t > 60)
/* > 1 minute */
/* Translation Hint: Minutes:Seconds */
fprintf(fout, _(" %2lu:%02um"), t / 60, (unsigned)t % 60);
else
fprintf(fout, " ");
} else {
if (t >= 48 * 60 * 60)
/* 2 days or more */
fprintf(fout, _(" %2ludays"), t / (24 * 60 * 60));
else if (t >= 60 * 60)
/* 1 hour or more */
/* Translation Hint: Hours:Minutes */
fprintf(fout, _(" %2lu:%02um"), t / (60 * 60),
(unsigned)((t / 60) % 60));
else if (t > 60)
/* 1 minute or more */
/* Translation Hint: Minutes:Seconds */
fprintf(fout, " %2lu:%02u ", t / 60, (unsigned)t % 60);
else
/* Translation Hint: Seconds:Centiseconds */
fprintf(fout, _(" %2lu.%02us"), t, centi_sec);
}
}
/* stat the device file to get an idle time */
static time_t idletime(const char *restrict const tty)
{
struct stat sbuf;
if (stat(tty, &sbuf) != 0)
return 0;
return time(NULL) - sbuf.st_atime;
}
/* 7 character formatted login time */
static void print_logintime(time_t logt, FILE * fout)
{
/* Abbreviated of weekday can be longer than 3 characters,
* see for instance hu_HU. Using 16 is few bytes more than
* enough. */
char time_str[16];
time_t curt;
struct tm *logtm, *curtm;
int today;
curt = time(NULL);
curtm = localtime(&curt);
/* localtime returns a pointer to static memory */
today = curtm->tm_yday;
logtm = localtime(&logt);
if (curt - logt > 12 * 60 * 60 && logtm->tm_yday != today) {
if (curt - logt > 6 * 24 * 60 * 60) {
strftime(time_str, sizeof(time_str), "%b", logtm);
fprintf(fout, " %02d%3s%02d", logtm->tm_mday,
time_str, logtm->tm_year % 100);
} else {
strftime(time_str, sizeof(time_str), "%a", logtm);
fprintf(fout, " %3s%02d ", time_str,
logtm->tm_hour);
}
} else {
fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min);
}
}
/*
* Get the Device ID of the given TTY
*/
static int get_tty_device(const char *restrict const name)
{
struct stat st;
static char buf[32];
char *dev_paths[] = { "/dev/%s", "/dev/tty%s", "/dev/pts/%s", NULL};
int i;
if (name[0] == '/' && stat(name, &st) == 0)
return st.st_rdev;
for (i=0; dev_paths[i] != NULL; i++) {
snprintf(buf, 32, dev_paths[i], name);
if (stat(buf, &st) == 0)
return st.st_rdev;
}
return -1;
}
/*
* This function scans the process table accumulating total cpu
* times for any processes "associated" with this login session.
* It also searches for the "best" process to report as "(w)hat"
* the user for that login session is doing currently. This the
* essential core of 'w'.
*/
static int find_best_proc(
const utmp_t * restrict const u,
const char *restrict const tty,
unsigned long long *restrict const jcpu,
unsigned long long *restrict const pcpu,
char *cmdline)
{
#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, reap->stacks[i], info)
#define PIDS_GETUNT(e) PIDS_VAL(EU_ ## e, u_int, reap->stacks[i], info)
#define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, reap->stacks[i], info)
#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i], info)
unsigned uid = ~0U;
int found_utpid = 0;
int i, total_procs, line;
unsigned long long best_time = 0;
unsigned long long secondbest_time = 0;
struct pids_info *info=NULL;
struct pids_fetch *reap;
enum pids_item items[] = {
PIDS_ID_TGID,
PIDS_TICS_BEGAN,
PIDS_ID_EUID,
PIDS_ID_RUID,
PIDS_ID_TPGID,
PIDS_ID_PGRP,
PIDS_TTY,
PIDS_TICS_ALL,
PIDS_CMDLINE};
enum rel_items {
EU_TGID, EU_START, EU_EUID, EU_RUID, EU_TPGID, EU_PGRP, EU_TTY,
EU_TICS_ALL, EU_CMDLINE};
*jcpu = 0;
*pcpu = 0;
if (!ignoreuser) {
char buf[UT_NAMESIZE + 1];
struct passwd *passwd_data;
strncpy(buf, u->ut_user, UT_NAMESIZE);
buf[UT_NAMESIZE] = '\0';
if ((passwd_data = getpwnam(buf)) == NULL)
return 0;
uid = passwd_data->pw_uid;
/* OK to have passwd_data go out of scope here */
}
line = get_tty_device(tty);
if (procps_pids_new(&info, items, 9) < 0)
xerrx(EXIT_FAILURE,
_("Unable to create pid info structure"));
if ((reap = procps_pids_reap(info, PIDS_FETCH_TASKS_ONLY)) == NULL)
xerrx(EXIT_FAILURE,
_("Unable to load process information"));
total_procs = reap->counts->total;
for (i=0; i < total_procs; i++) {
/* is this the login process? */
if (PIDS_GETINT(TGID) == u->ut_pid) {
found_utpid = 1;
if (!best_time) {
best_time = PIDS_GETULL(START);
strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
*pcpu = PIDS_GETULL(TICS_ALL);
}
}
if (PIDS_GETINT(TTY) != line)
continue;
(*jcpu) += PIDS_VAL(EU_TICS_ALL, ull_int, reap->stacks[i], info);
if (!(secondbest_time && PIDS_GETULL(START) <= secondbest_time)) {
secondbest_time = PIDS_GETULL(START);
if (cmdline[0] == '-' && cmdline[1] == '\0') {
strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
*pcpu = PIDS_GETULL(TICS_ALL);
}
}
if (
(!ignoreuser && uid != PIDS_GETUNT(EUID)
&& uid != PIDS_GETUNT(RUID))
|| (PIDS_GETINT(PGRP) != PIDS_GETINT(TPGID))
|| (PIDS_GETULL(START) <= best_time)
)
continue;
best_time = PIDS_GETULL(START);
strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
*pcpu = PIDS_GETULL(TICS_ALL);
}
procps_pids_unref(&info);
return found_utpid;
#undef PIDS_GETINT
#undef PIDS_GETUNT
#undef PIDS_GETULL
#undef PIDS_GETSTR
}
static void showinfo(
utmp_t * u, int formtype, int maxcmd, int from,
const int userlen, const int fromlen, const int ip_addresses)
{
unsigned long long jcpu, pcpu;
unsigned i;
char uname[UT_NAMESIZE + 1] = "", tty[5 + UT_LINESIZE + 1] = "/dev/";
long hertz;
char cmdline[MAX_CMD_WIDTH + 1];
strcpy(cmdline, "-");
hertz = procps_hertz_get();
for (i = 0; i < UT_LINESIZE; i++)
/* clean up tty if garbled */
if (isalnum(u->ut_line[i]) || (u->ut_line[i] == '/'))
tty[i + 5] = u->ut_line[i];
else
tty[i + 5] = '\0';
if (find_best_proc(u, tty + 5, &jcpu, &pcpu, cmdline) == 0)
/*
* just skip if stale utmp entry (i.e. login proc doesn't
* exist). If there is a desire a cmdline flag could be
* added to optionally show it with a prefix of (stale)
* in front of cmd or something like that.
*/
return;
/* force NUL term for printf */
strncpy(uname, u->ut_user, UT_NAMESIZE);
if (formtype) {
printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, u->ut_line);
if (from)
print_from(u, ip_addresses, fromlen);
#ifdef HAVE_UTMPX_H
print_logintime(u->ut_tv.tv_sec, stdout);
#else
print_logintime(u->ut_time, stdout);
#endif
if (*u->ut_line == ':')
/* idle unknown for xdm logins */
printf(" ?xdm? ");
else
print_time_ival7(idletime(tty), 0, stdout);
print_time_ival7(jcpu / hertz, (jcpu % hertz) * (100. / hertz),
stdout);
if (pcpu > 0)
print_time_ival7(pcpu / hertz,
(pcpu % hertz) * (100. / hertz),
stdout);
else
printf(" ? ");
} else {
printf("%-*.*s%-9.8s", userlen + 1, userlen, u->ut_user,
u->ut_line);
if (from)
print_from(u, ip_addresses, fromlen);
if (*u->ut_line == ':')
/* idle unknown for xdm logins */
printf(" ?xdm? ");
else
print_time_ival7(idletime(tty), 0, stdout);
}
printf(" %.*s\n", maxcmd, cmdline);
}
static void __attribute__ ((__noreturn__))
usage(FILE * out)
{
fputs(USAGE_HEADER, out);
fprintf(out,
_(" %s [options] [user]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, out);
fputs(_(" -h, --no-header do not print header\n"),out);
fputs(_(" -u, --no-current ignore current process username\n"),out);
fputs(_(" -s, --short short format\n"),out);
fputs(_(" -f, --from show remote hostname field\n"),out);
fputs(_(" -o, --old-style old style output\n"),out);
fputs(_(" -i, --ip-addr display IP address instead of hostname (if possible)\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_(" --help display this help and exit\n"), out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("w(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
char *user = NULL, *p;
utmp_t *u;
struct winsize win;
int ch;
int maxcmd = 80;
int userlen = 8;
int fromlen = 16;
char *env_var;
/* switches (defaults) */
int header = 1;
int longform = 1;
int from = 1;
int ip_addresses = 0;
enum {
HELP_OPTION = CHAR_MAX + 1
};
static const struct option longopts[] = {
{"no-header", no_argument, NULL, 'h'},
{"no-current", no_argument, NULL, 'u'},
{"short", no_argument, NULL, 's'},
{"from", no_argument, NULL, 'f'},
{"old-style", no_argument, NULL, 'o'},
{"ip-addr", no_argument, NULL, 'i'},
{"help", no_argument, NULL, HELP_OPTION},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
#endif
setlocale (LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
#ifndef W_SHOWFROM
from = 0;
#endif
while ((ch =
getopt_long(argc, argv, "husfoVi", longopts, NULL)) != -1)
switch (ch) {
case 'h':
header = 0;
break;
case 's':
longform = 0;
break;
case 'f':
from = !from;
break;
case 'V':
printf(PROCPS_NG_VERSION);
exit(0);
case 'u':
ignoreuser = 1;
break;
case 'o':
oldstyle = 1;
break;
case 'i':
ip_addresses = 1;
from = 1;
break;
case HELP_OPTION:
usage(stdout);
default:
usage(stderr);
}
if ((argv[optind]))
user = (argv[optind]);
/* Get user field length from environment */
if ((env_var = getenv("PROCPS_USERLEN")) != NULL) {
int ut_namesize = UT_NAMESIZE;
userlen = atoi(env_var);
if (userlen < 8 || ut_namesize < userlen) {
xwarnx
(_("User length environment PROCPS_USERLEN must be between 8 and %i, ignoring.\n"),
ut_namesize);
userlen = 8;
}
}
/* Get from field length from environment */
if ((env_var = getenv("PROCPS_FROMLEN")) != NULL) {
fromlen = atoi(env_var);
if (fromlen < 8 || UT_HOSTSIZE < fromlen) {
xwarnx
(_("from length environment PROCPS_FROMLEN must be between 8 and %d, ignoring\n"),
UT_HOSTSIZE);
fromlen = 16;
}
}
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
maxcmd = win.ws_col;
else if ((p = getenv("COLUMNS")))
maxcmd = atoi(p);
else
maxcmd = MAX_CMD_WIDTH;
#define CLAMP_CMD_WIDTH(cw) do { \
if ((cw) < MIN_CMD_WIDTH) (cw) = MIN_CMD_WIDTH; \
if ((cw) > MAX_CMD_WIDTH) (cw) = MAX_CMD_WIDTH; \
} while (0)
CLAMP_CMD_WIDTH(maxcmd);
maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0);
CLAMP_CMD_WIDTH(maxcmd);
#undef CLAMP_CMD_WIDTH
if (header) {
/* print uptime and headers */
printf("%s\n", procps_uptime_sprint());
/* Translation Hint: Following five uppercase messages are
* headers. Try to keep alignment intact. */
printf(_("%-*s TTY "), userlen, _("USER"));
if (from)
printf("%-*s", fromlen - 1, _("FROM"));
if (longform)
printf(_(" LOGIN@ IDLE JCPU PCPU WHAT\n"));
else
printf(_(" IDLE WHAT\n"));
}
#ifdef HAVE_UTMPX_H
setutxent();
#else
utmpname(UTMP_FILE);
setutent();
#endif
if (user) {
for (;;) {
#ifdef HAVE_UTMPX_H
u = getutxent();
#else
u = getutent();
#endif
if (!u)
break;
if (u->ut_type != USER_PROCESS)
continue;
if (!strncmp(u->ut_user, user, UT_NAMESIZE))
showinfo(u, longform, maxcmd, from, userlen,
fromlen, ip_addresses);
}
} else {
for (;;) {
#ifdef HAVE_UTMPX_H
u = getutxent();
#else
u = getutent();
#endif
if (!u)
break;
if (u->ut_type != USER_PROCESS)
continue;
if (*u->ut_user)
showinfo(u, longform, maxcmd, from, userlen,
fromlen, ip_addresses);
}
}
#ifdef HAVE_UTMPX_H
endutxent();
#else
endutent();
#endif
return EXIT_SUCCESS;
}

1014
src/watch.c Normal file

File diff suppressed because it is too large Load Diff