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:
460
src/free.c
Normal file
460
src/free.c
Normal 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
165
src/kill.c
Normal 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
1095
src/pgrep.c
Normal file
File diff suppressed because it is too large
Load Diff
409
src/pidof.c
Normal file
409
src/pidof.c
Normal 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
1207
src/pmap.c
Normal file
File diff suppressed because it is too large
Load Diff
481
src/ps/COPYING
Normal file
481
src/ps/COPYING
Normal 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
46
src/ps/HACKING
Normal 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 < 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
500
src/ps/common.h
Normal 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
677
src/ps/display.c
Normal 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
645
src/ps/global.c
Normal 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
217
src/ps/help.c
Normal 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
2333
src/ps/output.c
Normal file
File diff suppressed because it is too large
Load Diff
1242
src/ps/parser.c
Normal file
1242
src/ps/parser.c
Normal file
File diff suppressed because it is too large
Load Diff
26
src/ps/regression
Normal file
26
src/ps/regression
Normal 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
159
src/ps/select.c
Normal 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
945
src/ps/sortformat.c
Normal 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
187
src/ps/stacktrace.c
Normal 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
149
src/pwdx.c
Normal 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
594
src/skill.c
Normal 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
386
src/slabtop.c
Normal 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
1068
src/sysctl.c
Normal file
File diff suppressed because it is too large
Load Diff
232
src/tload.c
Normal file
232
src/tload.c
Normal 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
377
src/top/README.top
Normal 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
7202
src/top/top.c
Normal file
File diff suppressed because it is too large
Load Diff
784
src/top/top.h
Normal file
784
src/top/top.h
Normal 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
851
src/top/top_nls.c
Normal 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
108
src/top/top_nls.h
Normal 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
123
src/uptime.c
Normal 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
1089
src/vmstat.c
Normal file
File diff suppressed because it is too large
Load Diff
714
src/w.c
Normal file
714
src/w.c
Normal 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
1014
src/watch.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user