diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 13:58:15 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 13:58:15 +0100 |
commit | 4aca87515a5083ae0e31ce3177189fd43b6d05ac (patch) | |
tree | 7b1d9a31393ca090757dc6f0d3859b4fcd93f271 /release/src/router/busybox/procps | |
parent | 008d0be72b2f160382c6e880765e96b64a050c65 (diff) | |
download | tomato-4aca87515a5083ae0e31ce3177189fd43b6d05ac.tar.gz tomato-4aca87515a5083ae0e31ce3177189fd43b6d05ac.tar.bz2 |
patch to Vanilla Tomato 1.28
Diffstat (limited to 'release/src/router/busybox/procps')
-rw-r--r--[-rwxr-xr-x] | release/src/router/busybox/procps/Config.in | 158 | ||||
-rw-r--r-- | release/src/router/busybox/procps/Kbuild | 21 | ||||
-rw-r--r-- | release/src/router/busybox/procps/Makefile | 30 | ||||
-rwxr-xr-x | release/src/router/busybox/procps/Makefile.in | 38 | ||||
-rw-r--r-- | release/src/router/busybox/procps/free.c | 38 | ||||
-rw-r--r-- | release/src/router/busybox/procps/fuser.c | 345 | ||||
-rw-r--r-- | release/src/router/busybox/procps/kill.c | 278 | ||||
-rw-r--r-- | release/src/router/busybox/procps/nmeter.c | 910 | ||||
-rw-r--r-- | release/src/router/busybox/procps/pgrep.c | 144 | ||||
-rw-r--r-- | release/src/router/busybox/procps/pidof.c | 115 | ||||
-rw-r--r-- | release/src/router/busybox/procps/ps.c | 623 | ||||
-rw-r--r-- | release/src/router/busybox/procps/ps.posix | 175 | ||||
-rw-r--r-- | release/src/router/busybox/procps/renice.c | 152 | ||||
-rw-r--r-- | release/src/router/busybox/procps/sysctl.c | 258 | ||||
-rw-r--r-- | release/src/router/busybox/procps/top.c | 1419 | ||||
-rw-r--r-- | release/src/router/busybox/procps/uptime.c | 44 | ||||
-rw-r--r-- | release/src/router/busybox/procps/watch.c | 75 |
17 files changed, 3962 insertions, 861 deletions
diff --git a/release/src/router/busybox/procps/Config.in b/release/src/router/busybox/procps/Config.in index 47f61bec..702442a5 100755..100644 --- a/release/src/router/busybox/procps/Config.in +++ b/release/src/router/busybox/procps/Config.in @@ -5,7 +5,7 @@ menu "Process Utilities" -config CONFIG_FREE +config FREE bool "free" default n help @@ -13,58 +13,175 @@ config CONFIG_FREE memory in the system, as well as the buffers used by the kernel. The shared memory column should be ignored; it is obsolete. -config CONFIG_KILL +config FUSER + bool "fuser" + default n + help + fuser lists all PIDs (Process IDs) that currently have a given + file open. fuser can also list all PIDs that have a given network + (TCP or UDP) port open. + +config KILL bool "kill" default n help The command kill sends the specified signal to the specified - process or process group. If no signal is specified, the TERM + process or process group. If no signal is specified, the TERM signal is sent. -config CONFIG_KILLALL +config KILLALL bool "killall" default n - depends on CONFIG_KILL + depends on KILL help killall sends a signal to all processes running any of the - specified commands. If no signal name is specified, SIGTERM is + specified commands. If no signal name is specified, SIGTERM is sent. -config CONFIG_PIDOF +config KILLALL5 + bool "killall5" + default n + depends on KILL + +config NMETER + bool "nmeter" + default n + help + Prints selected system stats continuously, one line per update. + +config PGREP + bool "pgrep" + default n + help + Look for processes by name. + +config PIDOF bool "pidof" default n help Pidof finds the process id's (pids) of the named programs. It prints those id's on the standard output. -config CONFIG_PS +config FEATURE_PIDOF_SINGLE + bool "Enable argument for single shot (-s)" + default n + depends on PIDOF + help + Support argument '-s' for returning only the first pid found. + +config FEATURE_PIDOF_OMIT + bool "Enable argument for omitting pids (-o)" + default n + depends on PIDOF + help + Support argument '-o' for omitting the given pids in output. + The special pid %PPID can be used to name the parent process + of the pidof, in other words the calling shell or shell script. + +config PKILL + bool "pkill" + default n + help + Send signals to processes by name. + +config PS bool "ps" default n help ps gives a snapshot of the current processes. -config CONFIG_RENICE +config FEATURE_PS_WIDE + bool "Enable argument for wide output (-w)" + default n + depends on PS + help + Support argument 'w' for wide output. + If given once, 132 chars are printed and given more than + one, the length is unlimited. + +config FEATURE_PS_TIME + bool "Enable time and elapsed time output" + default n + depends on PS && DESKTOP + help + Support -o time and -o etime output specifiers. + +config FEATURE_PS_UNUSUAL_SYSTEMS + bool "Support Linux prior to 2.4.0 and non-ELF systems" + default n + depends on FEATURE_PS_TIME + help + Include support for measuring HZ on old kernels and non-ELF systems + (if you are on Linux 2.4.0+ and use ELF, you don't need this) + +config RENICE bool "renice" default n help Renice alters the scheduling priority of one or more running processes. -config CONFIG_TOP +config BB_SYSCTL + bool "sysctl" + default n + help + Configure kernel parameters at runtime. + +config TOP bool "top" - default y + default n help The top program provides a dynamic real-time view of a running system. -config FEATURE_CPU_USAGE_PERCENTAGE - bool " Support show CPU usage percentage (add 2k bytes)" +config FEATURE_TOP_CPU_USAGE_PERCENTAGE + bool "Show CPU per-process usage percentage" default y - depends on CONFIG_TOP + depends on TOP help - Make top display CPU usage. + Make top display CPU usage for each process. + This adds about 2k. -config CONFIG_UPTIME +config FEATURE_TOP_CPU_GLOBAL_PERCENTS + bool "Show CPU global usage percentage" + default y + depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE + help + Makes top display "CPU: NN% usr NN% sys..." line. + This adds about 0.5k. + +config FEATURE_TOP_SMP_CPU + bool "SMP CPU usage display ('c' key)" + default n + depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS + help + Allow 'c' key to switch between individual/cumulative CPU stats + This adds about 0.5k. + +config FEATURE_TOP_DECIMALS + bool "Show 1/10th of a percent in CPU/mem statistics" + default n + depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE + help + Show 1/10th of a percent in CPU/mem statistics. + This adds about 0.3k. + +config FEATURE_TOP_SMP_PROCESS + bool "Show CPU process runs on ('j' field)" + default n + depends on TOP + help + Show CPU where process was last found running on. + This is the 'j' field. + +config FEATURE_TOPMEM + bool "Topmem command ('s' key)" + default n + depends on TOP + help + Enable 's' in top (gives lots of memory info). + +config UPTIME bool "uptime" default n help @@ -72,5 +189,12 @@ config CONFIG_UPTIME the system has been running, how many users are currently logged on, and the system load averages for the past 1, 5, and 15 minutes. -endmenu +config WATCH + bool "watch" + default n + help + watch is used to execute a program periodically, showing + output to the screen. + +endmenu diff --git a/release/src/router/busybox/procps/Kbuild b/release/src/router/busybox/procps/Kbuild new file mode 100644 index 00000000..8e62fdfa --- /dev/null +++ b/release/src/router/busybox/procps/Kbuild @@ -0,0 +1,21 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> +# +# Licensed under the GPL v2, see the file LICENSE in this tarball. + +lib-y:= +lib-$(CONFIG_FREE) += free.o +lib-$(CONFIG_FUSER) += fuser.o +lib-$(CONFIG_KILL) += kill.o +lib-$(CONFIG_ASH) += kill.o # used for built-in kill by ash +lib-$(CONFIG_NMETER) += nmeter.o +lib-$(CONFIG_PGREP) += pgrep.o +lib-$(CONFIG_PKILL) += pgrep.o +lib-$(CONFIG_PIDOF) += pidof.o +lib-$(CONFIG_PS) += ps.o +lib-$(CONFIG_RENICE) += renice.o +lib-$(CONFIG_BB_SYSCTL) += sysctl.o +lib-$(CONFIG_TOP) += top.o +lib-$(CONFIG_UPTIME) += uptime.o +lib-$(CONFIG_WATCH) += watch.o diff --git a/release/src/router/busybox/procps/Makefile b/release/src/router/busybox/procps/Makefile deleted file mode 100644 index 24b70d10..00000000 --- a/release/src/router/busybox/procps/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# Makefile for busybox -# -# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> -# -# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -TOPDIR:= ../ -PROCPS_DIR:=./ -include $(TOPDIR).config -include $(TOPDIR)Rules.mak -include Makefile.in -all: $(libraries-y) --include $(TOPDIR).depend - -clean: - rm -f *.o *.a .depend $(AR_TARGET) - diff --git a/release/src/router/busybox/procps/Makefile.in b/release/src/router/busybox/procps/Makefile.in deleted file mode 100755 index 9d2d2739..00000000 --- a/release/src/router/busybox/procps/Makefile.in +++ /dev/null @@ -1,38 +0,0 @@ -# Makefile for busybox -# -# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> -# -# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -PROCPS_AR:=procps.a -ifndef $(PROCPS_DIR) -PROCPS_DIR:=$(TOPDIR)procps/ -endif - -PROCPS-y:= -PROCPS-$(CONFIG_FREE) += free.o -PROCPS-$(CONFIG_KILL) += kill.o -PROCPS-$(CONFIG_PIDOF) += pidof.o -PROCPS-$(CONFIG_PS) += ps.o -PROCPS-$(CONFIG_RENICE) += renice.o -PROCPS-$(CONFIG_TOP) += top.o -PROCPS-$(CONFIG_UPTIME) += uptime.o - -libraries-y+=$(PROCPS_DIR)$(PROCPS_AR) - -$(PROCPS_DIR)$(PROCPS_AR): $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y)) - $(AR) -ro $@ $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y)) - diff --git a/release/src/router/busybox/procps/free.c b/release/src/router/busybox/procps/free.c index dbc606c9..e76dd21a 100644 --- a/release/src/router/busybox/procps/free.c +++ b/release/src/router/busybox/procps/free.c @@ -2,41 +2,26 @@ /* * Mini free implementation for busybox * - * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * + * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ /* getopt not needed */ -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include "busybox.h" +#include "libbb.h" -extern int free_main(int argc, char **argv) +int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int free_main(int argc, char **argv) { struct sysinfo info; sysinfo(&info); /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */ - if (info.mem_unit==0) { + if (info.mem_unit == 0) { info.mem_unit=1; } - if ( info.mem_unit == 1 ) { + if (info.mem_unit == 1) { info.mem_unit=1024; /* TODO: Make all this stuff not overflow when mem >= 4 Gib */ @@ -61,14 +46,14 @@ extern int free_main(int argc, char **argv) info.bufferram*=info.mem_unit; } - if (argc > 1 && **(argv + 1) == '-') + if (argc > 1 && *argv[1] == '-') bb_show_usage(); - printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free", + printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free", "shared", "buffers"); - printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram, - info.totalram-info.freeram, info.freeram, + printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram, + info.totalram-info.freeram, info.freeram, info.sharedram, info.bufferram); #ifndef __uClinux__ @@ -81,4 +66,3 @@ extern int free_main(int argc, char **argv) #endif return EXIT_SUCCESS; } - diff --git a/release/src/router/busybox/procps/fuser.c b/release/src/router/busybox/procps/fuser.c new file mode 100644 index 00000000..dc3d01bd --- /dev/null +++ b/release/src/router/busybox/procps/fuser.c @@ -0,0 +1,345 @@ +/* vi: set sw=4 ts=4: */ +/* + * tiny fuser implementation + * + * Copyright 2004 Tony J. White + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#define MAX_LINE 255 + +#define OPTION_STRING "mks64" +enum { + OPT_MOUNT = (1 << 0), + OPT_KILL = (1 << 1), + OPT_SILENT = (1 << 2), + OPT_IP6 = (1 << 3), + OPT_IP4 = (1 << 4), +}; + +typedef struct inode_list { + struct inode_list *next; + ino_t inode; + dev_t dev; +} inode_list; + +typedef struct pid_list { + struct pid_list *next; + pid_t pid; +} pid_list; + +static dev_t find_socket_dev(void) +{ + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd >= 0) { + struct stat buf; + int r = fstat(fd, &buf); + close(fd); + if (r == 0) + return buf.st_dev; + } + return 0; +} + +static int file_to_dev_inode(const char *filename, dev_t *dev, ino_t *inode) +{ + struct stat f_stat; + if (stat(filename, &f_stat)) + return 0; + *inode = f_stat.st_ino; + *dev = f_stat.st_dev; + return 1; +} + +static char *parse_net_arg(const char *arg, unsigned *port) +{ + char path[20], tproto[5]; + + if (sscanf(arg, "%u/%4s", port, tproto) != 2) + return NULL; + sprintf(path, "/proc/net/%s", tproto); + if (access(path, R_OK) != 0) + return NULL; + return xstrdup(tproto); +} + +static pid_list *add_pid(pid_list *plist, pid_t pid) +{ + pid_list *curr = plist; + while (curr != NULL) { + if (curr->pid == pid) + return plist; + curr = curr->next; + } + curr = xmalloc(sizeof(pid_list)); + curr->pid = pid; + curr->next = plist; + return curr; +} + +static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode) +{ + inode_list *curr = ilist; + while (curr != NULL) { + if (curr->inode == inode && curr->dev == dev) + return ilist; + curr = curr->next; + } + curr = xmalloc(sizeof(inode_list)); + curr->dev = dev; + curr->inode = inode; + curr->next = ilist; + return curr; +} + +static inode_list *scan_proc_net(const char *proto, + unsigned port, inode_list *ilist) +{ + char path[20], line[MAX_LINE + 1]; + ino_t tmp_inode; + dev_t tmp_dev; + long long uint64_inode; + unsigned tmp_port; + FILE *f; + + tmp_dev = find_socket_dev(); + + sprintf(path, "/proc/net/%s", proto); + f = fopen_for_read(path); + if (!f) + return ilist; + + while (fgets(line, MAX_LINE, f)) { + char addr[68]; + if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x " + "%*x:%*x %*x %*d %*d %llu", + addr, &tmp_port, &uint64_inode) == 3 + ) { + int len = strlen(addr); + if (len == 8 && (option_mask32 & OPT_IP6)) + continue; + if (len > 8 && (option_mask32 & OPT_IP4)) + continue; + if (tmp_port == port) { + tmp_inode = uint64_inode; + ilist = add_inode(ilist, tmp_dev, tmp_inode); + } + } + } + fclose(f); + return ilist; +} + +static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) +{ + while (ilist) { + if (ilist->dev == dev) { + if (option_mask32 & OPT_MOUNT) + return 1; + if (ilist->inode == inode) + return 1; + } + ilist = ilist->next; + } + return 0; +} + +static pid_list *scan_pid_maps(const char *fname, pid_t pid, + inode_list *ilist, pid_list *plist) +{ + FILE *file; + char line[MAX_LINE + 1]; + int major, minor; + ino_t inode; + long long uint64_inode; + dev_t dev; + + file = fopen_for_read(fname); + if (!file) + return plist; + while (fgets(line, MAX_LINE, file)) { + if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) + continue; + inode = uint64_inode; + if (major == 0 && minor == 0 && inode == 0) + continue; + dev = makedev(major, minor); + if (search_dev_inode(ilist, dev, inode)) + plist = add_pid(plist, pid); + } + fclose(file); + return plist; +} + +static pid_list *scan_link(const char *lname, pid_t pid, + inode_list *ilist, pid_list *plist) +{ + ino_t inode; + dev_t dev; + + if (!file_to_dev_inode(lname, &dev, &inode)) + return plist; + if (search_dev_inode(ilist, dev, inode)) + plist = add_pid(plist, pid); + return plist; +} + +static pid_list *scan_dir_links(const char *dname, pid_t pid, + inode_list *ilist, pid_list *plist) +{ + DIR *d; + struct dirent *de; + char *lname; + + d = opendir(dname); + if (!d) + return plist; + while ((de = readdir(d)) != NULL) { + lname = concat_subpath_file(dname, de->d_name); + if (lname == NULL) + continue; + plist = scan_link(lname, pid, ilist, plist); + free(lname); + } + closedir(d); + return plist; +} + +/* NB: does chdir internally */ +static pid_list *scan_proc_pids(inode_list *ilist) +{ + DIR *d; + struct dirent *de; + pid_t pid; + pid_list *plist; + + xchdir("/proc"); + d = opendir("/proc"); + if (!d) + return NULL; + + plist = NULL; + while ((de = readdir(d)) != NULL) { + pid = (pid_t)bb_strtou(de->d_name, NULL, 10); + if (errno) + continue; + if (chdir(de->d_name) < 0) + continue; + plist = scan_link("cwd", pid, ilist, plist); + plist = scan_link("exe", pid, ilist, plist); + plist = scan_link("root", pid, ilist, plist); + plist = scan_dir_links("fd", pid, ilist, plist); + plist = scan_dir_links("lib", pid, ilist, plist); + plist = scan_dir_links("mmap", pid, ilist, plist); + plist = scan_pid_maps("maps", pid, ilist, plist); + xchdir("/proc"); + } + closedir(d); + return plist; +} + +static int print_pid_list(pid_list *plist) +{ + while (plist != NULL) { + printf("%u ", (unsigned)plist->pid); + plist = plist->next; + } + bb_putchar('\n'); + return 1; +} + +static int kill_pid_list(pid_list *plist, int sig) +{ + pid_t mypid = getpid(); + int success = 1; + + while (plist != NULL) { + if (plist->pid != mypid) { + if (kill(plist->pid, sig) != 0) { + bb_perror_msg("kill pid %u", (unsigned)plist->pid); + success = 0; + } + } + plist = plist->next; + } + return success; +} + +int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fuser_main(int argc UNUSED_PARAM, char **argv) +{ + pid_list *plist; + inode_list *ilist; + char **pp; + dev_t dev; + ino_t inode; + unsigned port; + int opt; + int success; + int killsig; +/* +fuser [options] FILEs or PORT/PROTOs +Find processes which use FILEs or PORTs + -m Find processes which use same fs as FILEs + -4 Search only IPv4 space + -6 Search only IPv6 space + -s Silent: just exit with 0 if any processes are found + -k Kill found processes (otherwise display PIDs) + -SIGNAL Signal to send (default: TERM) +*/ + /* Handle -SIGNAL. Oh my... */ + killsig = SIGTERM; + pp = argv; + while (*++pp) { + char *arg = *pp; + if (arg[0] != '-') + continue; + if (arg[1] == '-' && arg[2] == '\0') /* "--" */ + break; + if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') + continue; /* it's "-4" or "-6" */ + opt = get_signum(&arg[1]); + if (opt < 0) + continue; + /* "-SIGNAL" option found. Remove it and bail out */ + killsig = opt; + do { + pp[0] = arg = pp[1]; + pp++; + } while (arg); + break; + } + + opt = getopt32(argv, OPTION_STRING); + argv += optind; + + ilist = NULL; + pp = argv; + while (*pp) { + char *proto = parse_net_arg(*pp, &port); + if (proto) { /* PORT/PROTO */ + ilist = scan_proc_net(proto, port, ilist); + free(proto); + } else { /* FILE */ + if (!file_to_dev_inode(*pp, &dev, &inode)) + bb_perror_msg_and_die("can't open '%s'", *pp); + ilist = add_inode(ilist, dev, inode); + } + pp++; + } + + plist = scan_proc_pids(ilist); /* changes dir to "/proc" */ + + if (!plist) + return EXIT_FAILURE; + success = 1; + if (opt & OPT_KILL) { + success = kill_pid_list(plist, killsig); + } else if (!(opt & OPT_SILENT)) { + success = print_pid_list(plist); + } + return (success != 1); /* 0 == success */ +} diff --git a/release/src/router/busybox/procps/kill.c b/release/src/router/busybox/procps/kill.c index f11623e0..1f820698 100644 --- a/release/src/router/busybox/procps/kill.c +++ b/release/src/router/busybox/procps/kill.c @@ -1,153 +1,223 @@ /* vi: set sw=4 ts=4: */ /* - * Mini kill/killall implementation for busybox + * Mini kill/killall[5] implementation for busybox * * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. - * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * - * 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. + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +/* Note: kill_main is directly called from shell in order to implement + * kill built-in. Shell substitutes job ids with process groups first. * - * 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. + * This brings some complications: * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + we can't use xfunc here + * + we can't use applet_name + * + we can't use bb_show_usage + * (Above doesn't apply for killall[5] cases) * + * kill %n gets translated into kill ' -<process group>' by shell (note space!) + * This is needed to avoid collision with kill -9 ... syntax */ - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> -#include <signal.h> -#include <ctype.h> -#include <string.h> -#include <unistd.h> -#include "busybox.h" - -static const int KILL = 0; -static const int KILLALL = 1; - -extern int kill_main(int argc, char **argv) +int kill_main(int argc, char **argv) { - int whichApp, signo = SIGTERM, quiet = 0; - const char *name; - int errors = 0; - -#ifdef CONFIG_KILLALL - /* Figure out what we are trying to do here */ - whichApp = (strcmp(bb_applet_name, "killall") == 0)? KILLALL : KILL; + char *arg; + pid_t pid; + int signo = SIGTERM, errors = 0, quiet = 0; +#if !ENABLE_KILLALL && !ENABLE_KILLALL5 +#define killall 0 +#define killall5 0 #else - whichApp = KILL; +/* How to determine who we are? find 3rd char from the end: + * kill, killall, killall5 + * ^i ^a ^l - it's unique + * (checking from the start is complicated by /bin/kill... case) */ + const char char3 = argv[0][strlen(argv[0]) - 3]; +#define killall (ENABLE_KILLALL && char3 == 'a') +#define killall5 (ENABLE_KILLALL5 && char3 == 'l') #endif /* Parse any options */ - if (argc < 2) - bb_show_usage(); + argc--; + arg = *++argv; - if(argv[1][0] != '-'){ - argv++; - argc--; + if (argc < 1 || arg[0] != '-') { goto do_it_now; } - /* The -l option, which prints out signal names. */ - if(argv[1][1]=='l' && argv[1][2]=='\0'){ - if(argc==2) { + /* The -l option, which prints out signal names. + * Intended usage in shell: + * echo "Died of SIG`kill -l $?`" + * We try to mimic what kill from coreutils-6.8 does */ + if (arg[1] == 'l' && arg[2] == '\0') { + if (argc == 1) { /* Print the whole signal list */ - int col = 0; - for(signo=1; signo < NSIG; signo++) { - name = u_signal_names(0, &signo, 1); - if(name==NULL) /* unnamed */ - continue; - col += printf("%2d) %-16s", signo, name); - if (col > 60) { - printf("\n"); - col = 0; + print_signames(); + return 0; + } + /* -l <sig list> */ + while ((arg = *++argv)) { + if (isdigit(arg[0])) { + signo = bb_strtou(arg, NULL, 10); + if (errno) { + bb_error_msg("unknown signal '%s'", arg); + return EXIT_FAILURE; } - } - printf("\n"); - - } else { - for(argv++; *argv; argv++) { - name = u_signal_names(*argv, &signo, -1); - if(name!=NULL) - printf("%s\n", name); + /* Exitcodes >= 0x80 are to be treated + * as "killed by signal (exitcode & 0x7f)" */ + puts(get_signame(signo & 0x7f)); + /* TODO: 'bad' signal# - coreutils says: + * kill: 127: invalid signal + * we just print "127" instead */ + } else { + signo = get_signum(arg); + if (signo < 0) { + bb_error_msg("unknown signal '%s'", arg); + return EXIT_FAILURE; + } + printf("%d\n", signo); } } - /* If they specified -l, were all done */ + /* If they specified -l, we are all done */ return EXIT_SUCCESS; } /* The -q quiet option */ - if(argv[1][1]=='q' && argv[1][2]=='\0'){ - quiet++; - argv++; + if (killall && arg[1] == 'q' && arg[2] == '\0') { + quiet = 1; + arg = *++argv; argc--; - if(argv[1][0] != '-'){ + if (argc < 1) + bb_show_usage(); + if (arg[0] != '-') goto do_it_now; - } } - if(!u_signal_names(argv[1]+1, &signo, 0)) - bb_error_msg_and_die( "bad signal name '%s'", argv[1]+1); - argv+=2; - argc-=2; - -do_it_now: + arg++; /* skip '-' */ - if (whichApp == KILL) { - /* Looks like they want to do a kill. Do that */ - while (--argc >= 0) { - int pid; + /* -o PID? (if present, it always is at the end of command line) */ + if (killall5 && arg[0] == 'o') + goto do_it_now; - if (!isdigit(**argv)) - bb_error_msg_and_die( "Bad PID '%s'", *argv); - pid = strtol(*argv, NULL, 0); - if (kill(pid, signo) != 0) { - bb_perror_msg( "Could not kill pid '%d'", pid); - errors++; + if (argc > 1 && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */ + argc--; + arg = *++argv; + } /* else it must be -SIG */ + signo = get_signum(arg); + if (signo < 0) { /* || signo > MAX_SIGNUM ? */ + bb_error_msg("bad signal name '%s'", arg); + return EXIT_FAILURE; + } + arg = *++argv; + argc--; + + do_it_now: + pid = getpid(); + + if (killall5) { + pid_t sid; + procps_status_t* p = NULL; + int ret = 0; + + /* Find out our session id */ + sid = getsid(pid); + /* Stop all processes */ + kill(-1, SIGSTOP); + /* Signal all processes except those in our session */ + while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) { + int i; + + if (p->sid == (unsigned)sid + || p->pid == (unsigned)pid + || p->pid == 1) + continue; + + /* All remaining args must be -o PID options. + * Check p->pid against them. */ + for (i = 0; i < argc; i++) { + pid_t omit; + + arg = argv[i]; + if (arg[0] != '-' || arg[1] != 'o') { + bb_error_msg("bad option '%s'", arg); + ret = 1; + goto resume; + } + arg += 2; + if (!arg[0] && argv[++i]) + arg = argv[i]; + omit = bb_strtoi(arg, NULL, 10); + if (errno) { + bb_error_msg("bad pid '%s'", arg); + ret = 1; + goto resume; + } + if (p->pid == omit) + goto dont_kill; } - argv++; + kill(p->pid, signo); + dont_kill: ; } + resume: + /* And let them continue */ + kill(-1, SIGCONT); + return ret; + } - } -#ifdef CONFIG_KILLALL - else { - pid_t myPid=getpid(); + /* Pid or name is required for kill/killall */ + if (argc < 1) { + bb_error_msg("you need to specify whom to kill"); + return EXIT_FAILURE; + } + + if (killall) { /* Looks like they want to do a killall. Do that */ - while (--argc >= 0) { - long* pidList; + while (arg) { + pid_t* pidList; - pidList = find_pid_by_name(*argv); - if (!pidList || *pidList<=0) { + pidList = find_pid_by_name(arg); + if (*pidList == 0) { errors++; - if (quiet==0) - bb_error_msg( "%s: no process killed", *argv); + if (!quiet) + bb_error_msg("%s: no process killed", arg); } else { - long *pl; + pid_t *pl; - for(pl = pidList; *pl !=0 ; pl++) { - if (*pl==myPid) + for (pl = pidList; *pl; pl++) { + if (*pl == pid) continue; - if (kill(*pl, signo) != 0) { - errors++; - if (quiet==0) - bb_perror_msg( "Could not kill pid '%ld'", *pl); - } + if (kill(*pl, signo) == 0) + continue; + errors++; + if (!quiet) + bb_perror_msg("cannot kill pid %u", (unsigned)*pl); } } free(pidList); - argv++; + arg = *++argv; } + return errors; + } + + /* Looks like they want to do a kill. Do that */ + while (arg) { + /* Support shell 'space' trick */ + if (arg[0] == ' ') + arg++; + pid = bb_strtoi(arg, NULL, 10); + if (errno) { + bb_error_msg("bad pid '%s'", arg); + errors++; + } else if (kill(pid, signo) != 0) { + bb_perror_msg("cannot kill pid %d", (int)pid); + errors++; + } + arg = *++argv; } -#endif return errors; } diff --git a/release/src/router/busybox/procps/nmeter.c b/release/src/router/busybox/procps/nmeter.c new file mode 100644 index 00000000..0358ccd3 --- /dev/null +++ b/release/src/router/busybox/procps/nmeter.c @@ -0,0 +1,910 @@ +/* +** Licensed under the GPL v2, see the file LICENSE in this tarball +** +** Based on nanotop.c from floppyfw project +** +** Contact me: vda.linux@googlemail.com */ + +//TODO: +// simplify code +// /proc/locks +// /proc/stat: +// disk_io: (3,0):(22272,17897,410702,4375,54750) +// btime 1059401962 +//TODO: use sysinfo libc call/syscall, if appropriate +// (faster than open/read/close): +// sysinfo({uptime=15017, loads=[5728, 15040, 16480] +// totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480} +// totalswap=134209536, freeswap=134209536, procs=157}) + +#include "libbb.h" + +typedef unsigned long long ullong; + +enum { /* Preferably use powers of 2 */ + PROC_MIN_FILE_SIZE = 256, + PROC_MAX_FILE_SIZE = 16 * 1024, +}; + +typedef struct proc_file { + char *file; + int file_sz; + smallint last_gen; +} proc_file; + +static const char *const proc_name[] = { + "stat", // Must match the order of proc_file's! + "loadavg", + "net/dev", + "meminfo", + "diskstats", + "sys/fs/file-nr" +}; + +struct globals { + // Sample generation flip-flop + smallint gen; + // Linux 2.6? (otherwise assumes 2.4) + smallint is26; + // 1 if sample delay is not an integer fraction of a second + smallint need_seconds; + char *cur_outbuf; + const char *final_str; + int delta; + int deltanz; + struct timeval tv; +#define first_proc_file proc_stat + proc_file proc_stat; // Must match the order of proc_name's! + proc_file proc_loadavg; + proc_file proc_net_dev; + proc_file proc_meminfo; + proc_file proc_diskstats; + proc_file proc_sys_fs_filenr; +}; +#define G (*ptr_to_globals) +#define gen (G.gen ) +#define is26 (G.is26 ) +#define need_seconds (G.need_seconds ) +#define cur_outbuf (G.cur_outbuf ) +#define final_str (G.final_str ) +#define delta (G.delta ) +#define deltanz (G.deltanz ) +#define tv (G.tv ) +#define proc_stat (G.proc_stat ) +#define proc_loadavg (G.proc_loadavg ) +#define proc_net_dev (G.proc_net_dev ) +#define proc_meminfo (G.proc_meminfo ) +#define proc_diskstats (G.proc_diskstats ) +#define proc_sys_fs_filenr (G.proc_sys_fs_filenr) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + cur_outbuf = outbuf; \ + final_str = "\n"; \ + deltanz = delta = 1000000; \ +} while (0) + +// We depend on this being a char[], not char* - we take sizeof() of it +#define outbuf bb_common_bufsiz1 + +static inline void reset_outbuf(void) +{ + cur_outbuf = outbuf; +} + +static inline int outbuf_count(void) +{ + return cur_outbuf - outbuf; +} + +static void print_outbuf(void) +{ + int sz = cur_outbuf - outbuf; + if (sz > 0) { + xwrite(STDOUT_FILENO, outbuf, sz); + cur_outbuf = outbuf; + } +} + +static void put(const char *s) +{ + int sz = strlen(s); + if (sz > outbuf + sizeof(outbuf) - cur_outbuf) + sz = outbuf + sizeof(outbuf) - cur_outbuf; + memcpy(cur_outbuf, s, sz); + cur_outbuf += sz; +} + +static void put_c(char c) +{ + if (cur_outbuf < outbuf + sizeof(outbuf)) + *cur_outbuf++ = c; +} + +static void put_question_marks(int count) +{ + while (count--) + put_c('?'); +} + +static void readfile_z(proc_file *pf, const char* fname) +{ +// open_read_close() will do two reads in order to be sure we are at EOF, +// and we don't need/want that. + int fd; + int sz, rdsz; + char *buf; + + sz = pf->file_sz; + buf = pf->file; + if (!buf) { + buf = xmalloc(PROC_MIN_FILE_SIZE); + sz = PROC_MIN_FILE_SIZE; + } + again: + fd = xopen(fname, O_RDONLY); + buf[0] = '\0'; + rdsz = read(fd, buf, sz-1); + close(fd); + if (rdsz > 0) { + if (rdsz == sz-1 && sz < PROC_MAX_FILE_SIZE) { + sz *= 2; + buf = xrealloc(buf, sz); + goto again; + } + buf[rdsz] = '\0'; + } + pf->file_sz = sz; + pf->file = buf; +} + +static const char* get_file(proc_file *pf) +{ + if (pf->last_gen != gen) { + pf->last_gen = gen; + readfile_z(pf, proc_name[pf - &first_proc_file]); + } + return pf->file; +} + +static ullong read_after_slash(const char *p) +{ + p = strchr(p, '/'); + if (!p) return 0; + return strtoull(p+1, NULL, 10); +} + +enum conv_type { conv_decimal, conv_slash }; + +// Reads decimal values from line. Values start after key, for example: +// "cpu 649369 0 341297 4336769..." - key is "cpu" here. +// Values are stored in vec[]. arg_ptr has list of positions +// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value. +static int vrdval(const char* p, const char* key, + enum conv_type conv, ullong *vec, va_list arg_ptr) +{ + int indexline; + int indexnext; + + p = strstr(p, key); + if (!p) return 1; + + p += strlen(key); + indexline = 1; + indexnext = va_arg(arg_ptr, int); + while (1) { + while (*p == ' ' || *p == '\t') p++; + if (*p == '\n' || *p == '\0') break; + + if (indexline == indexnext) { // read this value + *vec++ = conv==conv_decimal ? + strtoull(p, NULL, 10) : + read_after_slash(p); + indexnext = va_arg(arg_ptr, int); + } + while (*p > ' ') p++; // skip over value + indexline++; + } + return 0; +} + +// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0": +// rdval(file_contents, "string_to_find", result_vector, value#, value#...) +// value# start with 1 +static int rdval(const char* p, const char* key, ullong *vec, ...) +{ + va_list arg_ptr; + int result; + + va_start(arg_ptr, vec); + result = vrdval(p, key, conv_decimal, vec, arg_ptr); + va_end(arg_ptr); + + return result; +} + +// Parses files with lines like "... ... ... 3/148 ...." +static int rdval_loadavg(const char* p, ullong *vec, ...) +{ + va_list arg_ptr; + int result; + + va_start(arg_ptr, vec); + result = vrdval(p, "", conv_slash, vec, arg_ptr); + va_end(arg_ptr); + + return result; +} + +// Parses /proc/diskstats +// 1 2 3 4 5 6(rd) 7 8 9 10(wr) 11 12 13 14 +// 3 0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933 +// 3 1 hda1 0 0 0 0 <- ignore if only 4 fields +static int rdval_diskstats(const char* p, ullong *vec) +{ + ullong rd = rd; // for compiler + int indexline = 0; + vec[0] = 0; + vec[1] = 0; + while (1) { + indexline++; + while (*p == ' ' || *p == '\t') p++; + if (*p == '\0') break; + if (*p == '\n') { + indexline = 0; + p++; + continue; + } + if (indexline == 6) { + rd = strtoull(p, NULL, 10); + } else if (indexline == 10) { + vec[0] += rd; // TODO: *sectorsize (don't know how to find out sectorsize) + vec[1] += strtoull(p, NULL, 10); + while (*p != '\n' && *p != '\0') p++; + continue; + } + while (*p > ' ') p++; // skip over value + } + return 0; +} + +static void scale(ullong ul) +{ + char buf[5]; + + /* see http://en.wikipedia.org/wiki/Tera */ + smart_ulltoa4(ul, buf, " kmgtpezy"); + buf[4] = '\0'; + put(buf); +} + + +#define S_STAT(a) \ +typedef struct a { \ + struct s_stat *next; \ + void (*collect)(struct a *s); \ + const char *label; +#define S_STAT_END(a) } a; + +S_STAT(s_stat) +S_STAT_END(s_stat) + +static void collect_literal(s_stat *s UNUSED_PARAM) +{ +} + +static s_stat* init_literal(void) +{ + s_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_literal; + return (s_stat*)s; +} + +static s_stat* init_delay(const char *param) +{ + delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */ + deltanz = delta > 0 ? delta : 1; + need_seconds = (1000000%deltanz) != 0; + return NULL; +} + +static s_stat* init_cr(const char *param UNUSED_PARAM) +{ + final_str = "\r"; + return (s_stat*)0; +} + + +// user nice system idle iowait irq softirq (last 3 only in 2.6) +//cpu 649369 0 341297 4336769 11640 7122 1183 +//cpuN 649369 0 341297 4336769 11640 7122 1183 +enum { CPU_FIELDCNT = 7 }; +S_STAT(cpu_stat) + ullong old[CPU_FIELDCNT]; + int bar_sz; + char *bar; +S_STAT_END(cpu_stat) + + +static void collect_cpu(cpu_stat *s) +{ + ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 }; + unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 }; + ullong all = 0; + int norm_all = 0; + int bar_sz = s->bar_sz; + char *bar = s->bar; + int i; + + if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) { + put_question_marks(bar_sz); + return; + } + + for (i = 0; i < CPU_FIELDCNT; i++) { + ullong old = s->old[i]; + if (data[i] < old) old = data[i]; //sanitize + s->old[i] = data[i]; + all += (data[i] -= old); + } + + if (all) { + for (i = 0; i < CPU_FIELDCNT; i++) { + ullong t = bar_sz * data[i]; + norm_all += data[i] = t / all; + frac[i] = t % all; + } + + while (norm_all < bar_sz) { + unsigned max = frac[0]; + int pos = 0; + for (i = 1; i < CPU_FIELDCNT; i++) { + if (frac[i] > max) max = frac[i], pos = i; + } + frac[pos] = 0; //avoid bumping up same value twice + data[pos]++; + norm_all++; + } + + memset(bar, '.', bar_sz); + memset(bar, 'S', data[2]); bar += data[2]; //sys + memset(bar, 'U', data[0]); bar += data[0]; //usr + memset(bar, 'N', data[1]); bar += data[1]; //nice + memset(bar, 'D', data[4]); bar += data[4]; //iowait + memset(bar, 'I', data[5]); bar += data[5]; //irq + memset(bar, 'i', data[6]); bar += data[6]; //softirq + } else { + memset(bar, '?', bar_sz); + } + put(s->bar); +} + + +static s_stat* init_cpu(const char *param) +{ + int sz; + cpu_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_cpu; + sz = strtoul(param, NULL, 0); /* param can be "" */ + if (sz < 10) sz = 10; + if (sz > 1000) sz = 1000; + s->bar = xzalloc(sz+1); + /*s->bar[sz] = '\0'; - xzalloc did it */ + s->bar_sz = sz; + return (s_stat*)s; +} + + +S_STAT(int_stat) + ullong old; + int no; +S_STAT_END(int_stat) + +static void collect_int(int_stat *s) +{ + ullong data[1]; + ullong old; + + if (rdval(get_file(&proc_stat), "intr", data, s->no)) { + put_question_marks(4); + return; + } + + old = s->old; + if (data[0] < old) old = data[0]; //sanitize + s->old = data[0]; + scale(data[0] - old); +} + +static s_stat* init_int(const char *param) +{ + int_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_int; + if (param[0] == '\0') { + s->no = 1; + } else { + int n = xatoi_u(param); + s->no = n + 2; + } + return (s_stat*)s; +} + + +S_STAT(ctx_stat) + ullong old; +S_STAT_END(ctx_stat) + +static void collect_ctx(ctx_stat *s) +{ + ullong data[1]; + ullong old; + + if (rdval(get_file(&proc_stat), "ctxt", data, 1)) { + put_question_marks(4); + return; + } + + old = s->old; + if (data[0] < old) old = data[0]; //sanitize + s->old = data[0]; + scale(data[0] - old); +} + +static s_stat* init_ctx(const char *param UNUSED_PARAM) +{ + ctx_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_ctx; + return (s_stat*)s; +} + + +S_STAT(blk_stat) + const char* lookfor; + ullong old[2]; +S_STAT_END(blk_stat) + +static void collect_blk(blk_stat *s) +{ + ullong data[2]; + int i; + + if (is26) { + i = rdval_diskstats(get_file(&proc_diskstats), data); + } else { + i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2); + // Linux 2.4 reports bio in Kbytes, convert to sectors: + data[0] *= 2; + data[1] *= 2; + } + if (i) { + put_question_marks(9); + return; + } + + for (i=0; i<2; i++) { + ullong old = s->old[i]; + if (data[i] < old) old = data[i]; //sanitize + s->old[i] = data[i]; + data[i] -= old; + } + scale(data[0]*512); // TODO: *sectorsize + put_c(' '); + scale(data[1]*512); +} + +static s_stat* init_blk(const char *param UNUSED_PARAM) +{ + blk_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_blk; + s->lookfor = "page"; + return (s_stat*)s; +} + + +S_STAT(fork_stat) + ullong old; +S_STAT_END(fork_stat) + +static void collect_thread_nr(fork_stat *s UNUSED_PARAM) +{ + ullong data[1]; + + if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) { + put_question_marks(4); + return; + } + scale(data[0]); +} + +static void collect_fork(fork_stat *s) +{ + ullong data[1]; + ullong old; + + if (rdval(get_file(&proc_stat), "processes", data, 1)) { + put_question_marks(4); + return; + } + + old = s->old; + if (data[0] < old) old = data[0]; //sanitize + s->old = data[0]; + scale(data[0] - old); +} + +static s_stat* init_fork(const char *param) +{ + fork_stat *s = xzalloc(sizeof(*s)); + if (*param == 'n') { + s->collect = collect_thread_nr; + } else { + s->collect = collect_fork; + } + return (s_stat*)s; +} + + +S_STAT(if_stat) + ullong old[4]; + const char *device; + char *device_colon; +S_STAT_END(if_stat) + +static void collect_if(if_stat *s) +{ + ullong data[4]; + int i; + + if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) { + put_question_marks(10); + return; + } + + for (i=0; i<4; i++) { + ullong old = s->old[i]; + if (data[i] < old) old = data[i]; //sanitize + s->old[i] = data[i]; + data[i] -= old; + } + put_c(data[1] ? '*' : ' '); + scale(data[0]); + put_c(data[3] ? '*' : ' '); + scale(data[2]); +} + +static s_stat* init_if(const char *device) +{ + if_stat *s = xzalloc(sizeof(*s)); + + if (!device || !device[0]) + bb_show_usage(); + s->collect = collect_if; + + s->device = device; + s->device_colon = xasprintf("%s:", device); + return (s_stat*)s; +} + + +S_STAT(mem_stat) + char opt; +S_STAT_END(mem_stat) + +// "Memory" value should not include any caches. +// IOW: neither "ls -laR /" nor heavy read/write activity +// should affect it. We'd like to also include any +// long-term allocated kernel-side mem, but it is hard +// to figure out. For now, bufs, cached & slab are +// counted as "free" memory +//2.6.16: +//MemTotal: 773280 kB +//MemFree: 25912 kB - genuinely free +//Buffers: 320672 kB - cache +//Cached: 146396 kB - cache +//SwapCached: 0 kB +//Active: 183064 kB +//Inactive: 356892 kB +//HighTotal: 0 kB +//HighFree: 0 kB +//LowTotal: 773280 kB +//LowFree: 25912 kB +//SwapTotal: 131064 kB +//SwapFree: 131064 kB +//Dirty: 48 kB +//Writeback: 0 kB +//Mapped: 96620 kB +//Slab: 200668 kB - takes 7 Mb on my box fresh after boot, +// but includes dentries and inodes +// (== can take arbitrary amount of mem) +//CommitLimit: 517704 kB +//Committed_AS: 236776 kB +//PageTables: 1248 kB +//VmallocTotal: 516052 kB +//VmallocUsed: 3852 kB +//VmallocChunk: 512096 kB +//HugePages_Total: 0 +//HugePages_Free: 0 +//Hugepagesize: 4096 kB +static void collect_mem(mem_stat *s) +{ + ullong m_total = 0; + ullong m_free = 0; + ullong m_bufs = 0; + ullong m_cached = 0; + ullong m_slab = 0; + + if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) { + put_question_marks(4); + return; + } + if (s->opt == 't') { + scale(m_total << 10); + return; + } + + if (rdval(proc_meminfo.file, "MemFree:", &m_free , 1) + || rdval(proc_meminfo.file, "Buffers:", &m_bufs , 1) + || rdval(proc_meminfo.file, "Cached:", &m_cached, 1) + || rdval(proc_meminfo.file, "Slab:", &m_slab , 1) + ) { + put_question_marks(4); + return; + } + + m_free += m_bufs + m_cached + m_slab; + switch (s->opt) { + case 'f': + scale(m_free << 10); break; + default: + scale((m_total - m_free) << 10); break; + } +} + +static s_stat* init_mem(const char *param) +{ + mem_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_mem; + s->opt = param[0]; + return (s_stat*)s; +} + + +S_STAT(swp_stat) +S_STAT_END(swp_stat) + +static void collect_swp(swp_stat *s UNUSED_PARAM) +{ + ullong s_total[1]; + ullong s_free[1]; + if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1) + || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1) + ) { + put_question_marks(4); + return; + } + scale((s_total[0]-s_free[0]) << 10); +} + +static s_stat* init_swp(const char *param UNUSED_PARAM) +{ + swp_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_swp; + return (s_stat*)s; +} + + +S_STAT(fd_stat) +S_STAT_END(fd_stat) + +static void collect_fd(fd_stat *s UNUSED_PARAM) +{ + ullong data[2]; + + if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) { + put_question_marks(4); + return; + } + + scale(data[0] - data[1]); +} + +static s_stat* init_fd(const char *param UNUSED_PARAM) +{ + fd_stat *s = xzalloc(sizeof(*s)); + s->collect = collect_fd; + return (s_stat*)s; +} + + +S_STAT(time_stat) + int prec; + int scale; +S_STAT_END(time_stat) + +static void collect_time(time_stat *s) +{ + char buf[sizeof("12:34:56.123456")]; + struct tm* tm; + int us = tv.tv_usec + s->scale/2; + time_t t = tv.tv_sec; + + if (us >= 1000000) { + t++; + us -= 1000000; + } + tm = localtime(&t); + + sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + if (s->prec) + sprintf(buf+8, ".%0*d", s->prec, us / s->scale); + put(buf); +} + +static s_stat* init_time(const char *param) +{ + int prec; + time_stat *s = xzalloc(sizeof(*s)); + + s->collect = collect_time; + prec = param[0] - '0'; + if (prec < 0) prec = 0; + else if (prec > 6) prec = 6; + s->prec = prec; + s->scale = 1; + while (prec++ < 6) + s->scale *= 10; + return (s_stat*)s; +} + +static void collect_info(s_stat *s) +{ + gen ^= 1; + while (s) { + put(s->label); + s->collect(s); + s = s->next; + } +} + + +typedef s_stat* init_func(const char *param); + +static const char options[] ALIGN1 = "ncmsfixptbdr"; +static init_func *const init_functions[] = { + init_if, + init_cpu, + init_mem, + init_swp, + init_fd, + init_int, + init_ctx, + init_fork, + init_time, + init_blk, + init_delay, + init_cr +}; + +int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nmeter_main(int argc, char **argv) +{ + char buf[32]; + s_stat *first = NULL; + s_stat *last = NULL; + s_stat *s; + char *cur, *prev; + + INIT_G(); + + xchdir("/proc"); + + if (argc != 2) + bb_show_usage(); + + if (open_read_close("version", buf, sizeof(buf)-1) > 0) { + buf[sizeof(buf)-1] = '\0'; + is26 = (strstr(buf, " 2.4.") == NULL); + } + + // Can use argv[1] directly, but this will mess up + // parameters as seen by e.g. ps. Making a copy... + cur = xstrdup(argv[1]); + while (1) { + char *param, *p; + prev = cur; + again: + cur = strchr(cur, '%'); + if (!cur) + break; + if (cur[1] == '%') { // %% + overlapping_strcpy(cur, cur + 1); + cur++; + goto again; + } + *cur++ = '\0'; // overwrite % + if (cur[0] == '[') { + // format: %[foptstring] + cur++; + p = strchr(options, cur[0]); + param = cur+1; + while (cur[0] != ']') { + if (!cur[0]) + bb_show_usage(); + cur++; + } + *cur++ = '\0'; // overwrite [ + } else { + // format: %NNNNNNf + param = cur; + while (cur[0] >= '0' && cur[0] <= '9') + cur++; + if (!cur[0]) + bb_show_usage(); + p = strchr(options, cur[0]); + *cur++ = '\0'; // overwrite format char + } + if (!p) + bb_show_usage(); + s = init_functions[p-options](param); + if (s) { + s->label = prev; + /*s->next = NULL; - all initXXX funcs use xzalloc */ + if (!first) + first = s; + else + last->next = s; + last = s; + } else { + // %NNNNd or %r option. remove it from string + strcpy(prev + strlen(prev), cur); + cur = prev; + } + } + if (prev[0]) { + s = init_literal(); + s->label = prev; + /*s->next = NULL; - all initXXX funcs use xzalloc */ + if (!first) + first = s; + else + last->next = s; + last = s; + } + + // Generate first samples but do not print them, they're bogus + collect_info(first); + reset_outbuf(); + if (delta >= 0) { + gettimeofday(&tv, NULL); + usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz); + } + + while (1) { + gettimeofday(&tv, NULL); + collect_info(first); + put(final_str); + print_outbuf(); + + // Negative delta -> no usleep at all + // This will hog the CPU but you can have REALLY GOOD + // time resolution ;) + // TODO: detect and avoid useless updates + // (like: nothing happens except time) + if (delta >= 0) { + int rem; + // can be commented out, will sacrifice sleep time precision a bit + gettimeofday(&tv, NULL); + if (need_seconds) + rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz; + else + rem = delta - tv.tv_usec%deltanz; + // Sometimes kernel wakes us up just a tiny bit earlier than asked + // Do not go to very short sleep in this case + if (rem < delta/128) { + rem += delta; + } + usleep(rem); + } + } + + /*return 0;*/ +} diff --git a/release/src/router/busybox/procps/pgrep.c b/release/src/router/busybox/procps/pgrep.c new file mode 100644 index 00000000..0e8e5294 --- /dev/null +++ b/release/src/router/busybox/procps/pgrep.c @@ -0,0 +1,144 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pgrep/pkill implementation for busybox + * + * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com> + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" +#include "xregex.h" + +/* Idea taken from kill.c */ +#define pgrep (ENABLE_PGREP && applet_name[1] == 'g') +#define pkill (ENABLE_PKILL && applet_name[1] == 'k') + +enum { + /* "vlfxon" */ + PGREPOPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */ + PGREPOPTBIT_L, + PGREPOPTBIT_F, + PGREPOPTBIT_X, + PGREPOPTBIT_O, + PGREPOPTBIT_N, +}; + +#define OPT_INVERT (opt & (1 << PGREPOPTBIT_V)) +#define OPT_LIST (opt & (1 << PGREPOPTBIT_L)) +#define OPT_FULL (opt & (1 << PGREPOPTBIT_F)) +#define OPT_ANCHOR (opt & (1 << PGREPOPTBIT_X)) +#define OPT_FIRST (opt & (1 << PGREPOPTBIT_O)) +#define OPT_LAST (opt & (1 << PGREPOPTBIT_N)) + +static void act(unsigned pid, char *cmd, int signo, unsigned opt) +{ + if (pgrep) { + if (OPT_LIST) + printf("%d %s\n", pid, cmd); + else + printf("%d\n", pid); + } else + kill(pid, signo); +} + +int pgrep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pgrep_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned pid = getpid(); + int signo = SIGTERM; + unsigned opt; + int scan_mask = PSSCAN_COMM; + char *first_arg; + int first_arg_idx; + int matched_pid; + char *cmd_last; + procps_status_t *proc; + /* These are initialized to 0 */ + struct { + regex_t re_buffer; + regmatch_t re_match[1]; + } Z; +#define re_buffer (Z.re_buffer) +#define re_match (Z.re_match ) + + memset(&Z, 0, sizeof(Z)); + + /* We must avoid interpreting -NUM (signal num) as an option */ + first_arg_idx = 1; + while (1) { + first_arg = argv[first_arg_idx]; + if (!first_arg) + break; + /* not "-<small_letter>..."? */ + if (first_arg[0] != '-' || first_arg[1] < 'a' || first_arg[1] > 'z') { + argv[first_arg_idx] = NULL; /* terminate argv here */ + break; + } + first_arg_idx++; + } + opt = getopt32(argv, "vlfxon"); + argv[first_arg_idx] = first_arg; + + argv += optind; + //argc -= optind; - unused anyway + if (OPT_FULL) + scan_mask |= PSSCAN_ARGVN; + + if (pkill) { + if (OPT_LIST) { /* -l: print the whole signal list */ + print_signames(); + return 0; + } + if (first_arg && first_arg[0] == '-') { + signo = get_signum(&first_arg[1]); + if (signo < 0) /* || signo > MAX_SIGNUM ? */ + bb_error_msg_and_die("bad signal name '%s'", &first_arg[1]); + argv++; + } + } + + /* One pattern is required */ + if (!argv[0] || argv[1]) + bb_show_usage(); + + xregcomp(&re_buffer, argv[0], 0); + matched_pid = 0; + cmd_last = NULL; + proc = NULL; + while ((proc = procps_scan(proc, scan_mask)) != NULL) { + char *cmd; + if (proc->pid == pid) + continue; + cmd = proc->argv0; + if (!cmd) { + cmd = proc->comm; + } else { + int i = proc->argv_len; + while (i) { + if (!cmd[i]) cmd[i] = ' '; + i--; + } + } + /* NB: OPT_INVERT is always 0 or 1 */ + if ((regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */ + && (!OPT_ANCHOR || (re_match[0].rm_so == 0 && re_match[0].rm_eo == (regoff_t)strlen(cmd)))) ^ OPT_INVERT + ) { + matched_pid = proc->pid; + if (OPT_LAST) { + free(cmd_last); + cmd_last = xstrdup(cmd); + continue; + } + act(proc->pid, cmd, signo, opt); + if (OPT_FIRST) + break; + } + } + if (cmd_last) { + act(matched_pid, cmd_last, signo, opt); + if (ENABLE_FEATURE_CLEAN_UP) + free(cmd_last); + } + return matched_pid == 0; /* return 1 if no processes listed/signaled */ +} diff --git a/release/src/router/busybox/procps/pidof.c b/release/src/router/busybox/procps/pidof.c index 2fe8ecd2..19423996 100644 --- a/release/src/router/busybox/procps/pidof.c +++ b/release/src/router/busybox/procps/pidof.c @@ -2,70 +2,85 @@ /* * pidof implementation for busybox * - * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * + * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ +#include "libbb.h" -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> -#include <signal.h> -#include <ctype.h> -#include <string.h> -#include <unistd.h> -#include "busybox.h" - +enum { + USE_FEATURE_PIDOF_SINGLE(OPTBIT_SINGLE,) + USE_FEATURE_PIDOF_OMIT( OPTBIT_OMIT ,) + OPT_SINGLE = USE_FEATURE_PIDOF_SINGLE((1<<OPTBIT_SINGLE)) + 0, + OPT_OMIT = USE_FEATURE_PIDOF_OMIT( (1<<OPTBIT_OMIT )) + 0, +}; -extern int pidof_main(int argc, char **argv) +int pidof_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pidof_main(int argc UNUSED_PARAM, char **argv) { - int opt, n = 0; - int single_flag = 0; - int fail = 1; + unsigned first = 1; + unsigned opt; +#if ENABLE_FEATURE_PIDOF_OMIT + llist_t *omits = NULL; /* list of pids to omit */ + opt_complementary = "o::"; +#endif - /* do normal option parsing */ - while ((opt = getopt(argc, argv, "s")) > 0) { - switch (opt) { - case 's': - single_flag = 1; + /* do unconditional option parsing */ + opt = getopt32(argv, "" + USE_FEATURE_PIDOF_SINGLE ("s") + USE_FEATURE_PIDOF_OMIT("o:", &omits)); + +#if ENABLE_FEATURE_PIDOF_OMIT + /* fill omit list. */ + { + llist_t *omits_p = omits; + while (1) { + omits_p = llist_find_str(omits_p, "%PPID"); + if (!omits_p) break; - default: - bb_show_usage(); + /* are we asked to exclude the parent's process ID? */ + omits_p->data = utoa((unsigned)getppid()); } } +#endif + /* Looks like everything is set to go. */ + argv += optind; + while (*argv) { + pid_t *pidList; + pid_t *pl; - /* Looks like everything is set to go. */ - while(optind < argc) { - long *pidList; - long *pl; - - pidList = find_pid_by_name(argv[optind]); - for(pl = pidList; *pl > 0; pl++) { - printf("%s%ld", (n++ ? " " : ""), *pl); - fail = 0; - if (single_flag) + /* reverse the pidlist like GNU pidof does. */ + pidList = pidlist_reverse(find_pid_by_name(*argv)); + for (pl = pidList; *pl; pl++) { +#if ENABLE_FEATURE_PIDOF_OMIT + if (opt & OPT_OMIT) { + llist_t *omits_p = omits; + while (omits_p) { + if (xatoul(omits_p->data) == (unsigned long)(*pl)) { + goto omitting; + } + omits_p = omits_p->link; + } + } +#endif + printf(" %u" + first, (unsigned)*pl); + first = 0; + if (ENABLE_FEATURE_PIDOF_SINGLE && (opt & OPT_SINGLE)) break; +#if ENABLE_FEATURE_PIDOF_OMIT + omitting: ; +#endif } free(pidList); - optind++; - + argv++; } - printf("\n"); + if (!first) + bb_putchar('\n'); - return fail ? EXIT_FAILURE : EXIT_SUCCESS; +#if ENABLE_FEATURE_PIDOF_OMIT + if (ENABLE_FEATURE_CLEAN_UP) + llist_free(omits, NULL); +#endif + return first; /* 1 (failure) - no processes found */ } diff --git a/release/src/router/busybox/procps/ps.c b/release/src/router/busybox/procps/ps.c index 9dc45d35..395cfcf5 100644 --- a/release/src/router/busybox/procps/ps.c +++ b/release/src/router/busybox/procps/ps.c @@ -2,116 +2,569 @@ /* * Mini ps implementation(s) for busybox * - * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * Fix for SELinux Support:(c)2007 Hiroshi Shinji <shiroshi@my.email.ne.jp> + * (c)2007 Yuichi Nakamura <ynakam@hitachisoft.jp> * - * 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., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA + * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <ctype.h> -#include <string.h> -#include <termios.h> -#include <sys/ioctl.h> -#include "busybox.h" -#ifdef CONFIG_SELINUX -#include <fs_secure.h> -#include <ss.h> -#include <flask_util.h> /* for is_flask_enabled() */ +#include "libbb.h" + +/* Absolute maximum on output line length */ +enum { MAX_WIDTH = 2*1024 }; + +#if ENABLE_DESKTOP + +#include <sys/times.h> /* for times() */ +//#include <sys/sysinfo.h> /* for sysinfo() */ +#ifndef AT_CLKTCK +#define AT_CLKTCK 17 #endif -static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ +#if ENABLE_SELINUX +#define SELINUX_O_PREFIX "label," +#define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" USE_FEATURE_PS_TIME(",time") ",args") +#else +#define DEFAULT_O_STR ("pid,user" USE_FEATURE_PS_TIME(",time") ",args") +#endif +typedef struct { + uint16_t width; + char name[6]; + const char *header; + void (*f)(char *buf, int size, const procps_status_t *ps); + int ps_flags; +} ps_out_t; -extern int ps_main(int argc, char **argv) +struct globals { + ps_out_t* out; + int out_cnt; + int print_header; + int need_flags; + char *buffer; + unsigned terminal_width; +#if ENABLE_FEATURE_PS_TIME + unsigned kernel_HZ; + unsigned long long seconds_since_boot; +#endif + char default_o[sizeof(DEFAULT_O_STR)]; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define out (G.out ) +#define out_cnt (G.out_cnt ) +#define print_header (G.print_header ) +#define need_flags (G.need_flags ) +#define buffer (G.buffer ) +#define terminal_width (G.terminal_width ) +#define kernel_HZ (G.kernel_HZ ) +#define seconds_since_boot (G.seconds_since_boot) +#define default_o (G.default_o ) + +#if ENABLE_FEATURE_PS_TIME +/* for ELF executables, notes are pushed before environment and args */ +static ptrdiff_t find_elf_note(ptrdiff_t findme) { - procps_status_t * p; - int i, len; -#ifdef CONFIG_FEATURE_AUTOWIDTH - struct winsize win = { 0, 0, 0, 0 }; - int terminal_width = TERMINAL_WIDTH; + ptrdiff_t *ep = (ptrdiff_t *) environ; + + while (*ep++); + while (*ep) { + if (ep[0] == findme) { + return ep[1]; + } + ep += 2; + } + return -1; +} + +#if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS +static unsigned get_HZ_by_waiting(void) +{ + struct timeval tv1, tv2; + unsigned t1, t2, r, hz; + unsigned cnt = cnt; /* for compiler */ + int diff; + + r = 0; + + /* Wait for times() to reach new tick */ + t1 = times(NULL); + do { + t2 = times(NULL); + } while (t2 == t1); + gettimeofday(&tv2, NULL); + + do { + t1 = t2; + tv1.tv_usec = tv2.tv_usec; + + /* Wait exactly one times() tick */ + do { + t2 = times(NULL); + } while (t2 == t1); + gettimeofday(&tv2, NULL); + + /* Calculate ticks per sec, rounding up to even */ + diff = tv2.tv_usec - tv1.tv_usec; + if (diff <= 0) diff += 1000000; + hz = 1000000u / (unsigned)diff; + hz = (hz+1) & ~1; + + /* Count how many same hz values we saw */ + if (r != hz) { + r = hz; + cnt = 0; + } + cnt++; + } while (cnt < 3); /* exit if saw 3 same values */ + + return r; +} #else -#define terminal_width TERMINAL_WIDTH +static inline unsigned get_HZ_by_waiting(void) +{ + /* Better method? */ + return 100; +} #endif -#ifdef CONFIG_SELINUX - int use_selinux = 0; - security_id_t sid; - if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") ) - use_selinux = 1; -#endif +static unsigned get_kernel_HZ(void) +{ + //char buf[64]; + struct sysinfo info; + + if (kernel_HZ) + return kernel_HZ; + + /* Works for ELF only, Linux 2.4.0+ */ + kernel_HZ = find_elf_note(AT_CLKTCK); + if (kernel_HZ == (unsigned)-1) + kernel_HZ = get_HZ_by_waiting(); + //if (open_read_close("/proc/uptime", buf, sizeof(buf) <= 0) + // bb_perror_msg_and_die("cannot read %s", "/proc/uptime"); + //buf[sizeof(buf)-1] = '\0'; + ///sscanf(buf, "%llu", &seconds_since_boot); + sysinfo(&info); + seconds_since_boot = info.uptime; -#ifdef CONFIG_FEATURE_AUTOWIDTH - ioctl(fileno(stdout), TIOCGWINSZ, &win); - if (win.ws_col > 0) - terminal_width = win.ws_col - 1; + return kernel_HZ; +} #endif -#ifdef CONFIG_SELINUX - if(use_selinux) - printf(" PID Context Stat Command\n"); +/* Print value to buf, max size+1 chars (including trailing '\0') */ + +static void func_user(char *buf, int size, const procps_status_t *ps) +{ +#if 1 + safe_strncpy(buf, get_cached_username(ps->uid), size+1); +#else + /* "compatible" version, but it's larger */ + /* procps 2.18 shows numeric UID if name overflows the field */ + /* TODO: get_cached_username() returns numeric string if + * user has no passwd record, we will display it + * left-justified here; too long usernames are shown + * as _right-justified_ IDs. Is it worth fixing? */ + const char *user = get_cached_username(ps->uid); + if (strlen(user) <= size) + safe_strncpy(buf, user, size+1); else + sprintf(buf, "%*u", size, (unsigned)ps->uid); #endif - printf(" PID Uid VmSize Stat Command\n"); -#ifdef CONFIG_SELINUX - while ((p = procps_scan(1, use_selinux, &sid)) != 0) { -#else - while ((p = procps_scan(1)) != 0) { +} + +static void func_comm(char *buf, int size, const procps_status_t *ps) +{ + safe_strncpy(buf, ps->comm, size+1); +} + +static void func_args(char *buf, int size, const procps_status_t *ps) +{ + read_cmdline(buf, size, ps->pid, ps->comm); +} + +static void func_pid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->pid); +} + +static void func_ppid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->ppid); +} + +static void func_pgid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->pgid); +} + +static void put_lu(char *buf, int size, unsigned long u) +{ + char buf4[5]; + + /* see http://en.wikipedia.org/wiki/Tera */ + smart_ulltoa4(u, buf4, " mgtpezy"); + buf4[4] = '\0'; + sprintf(buf, "%.*s", size, buf4); +} + +static void func_vsz(char *buf, int size, const procps_status_t *ps) +{ + put_lu(buf, size, ps->vsz); +} + +static void func_rss(char *buf, int size, const procps_status_t *ps) +{ + put_lu(buf, size, ps->rss); +} + +static void func_tty(char *buf, int size, const procps_status_t *ps) +{ + buf[0] = '?'; + buf[1] = '\0'; + if (ps->tty_major) /* tty field of "0" means "no tty" */ + snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); +} + +#if ENABLE_FEATURE_PS_TIME +static void func_etime(char *buf, int size, const procps_status_t *ps) +{ + /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */ + unsigned long mm; + unsigned ss; + + mm = ps->start_time / get_kernel_HZ(); + /* must be after get_kernel_HZ()! */ + mm = seconds_since_boot - mm; + ss = mm % 60; + mm /= 60; + snprintf(buf, size+1, "%3lu:%02u", mm, ss); +} + +static void func_time(char *buf, int size, const procps_status_t *ps) +{ + /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */ + unsigned long mm; + unsigned ss; + + mm = (ps->utime + ps->stime) / get_kernel_HZ(); + ss = mm % 60; + mm /= 60; + snprintf(buf, size+1, "%3lu:%02u", mm, ss); +} +#endif + +#if ENABLE_SELINUX +static void func_label(char *buf, int size, const procps_status_t *ps) +{ + safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1); +} #endif - char *namecmd = p->cmd; -#ifdef CONFIG_SELINUX - if(use_selinux) +/* +static void func_nice(char *buf, int size, const procps_status_t *ps) +{ + ps->??? +} + +static void func_pcpu(char *buf, int size, const procps_status_t *ps) +{ +} +*/ + +static const ps_out_t out_spec[] = { +// Mandated by POSIX: + { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, + { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM }, + { 256 , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, + { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, + { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, + { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, +#if ENABLE_FEATURE_PS_TIME + { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, +#endif +// { sizeof("GROUP" )-1, "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, +// { sizeof("NI" )-1, "nice" ,"NI" ,func_nice ,PSSCAN_ }, +// { sizeof("%CPU" )-1, "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ }, +// { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID }, +// { sizeof("RUSER" )-1, "ruser" ,"RUSER" ,func_ruser ,PSSCAN_UIDGID }, +#if ENABLE_FEATURE_PS_TIME + { 6 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, +#endif + { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, + { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, +// Not mandated by POSIX, but useful: + { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, +#if ENABLE_SELINUX + { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, +#endif +}; + +static ps_out_t* new_out_t(void) +{ + out = xrealloc_vector(out, 2, out_cnt); + return &out[out_cnt++]; +} + +static const ps_out_t* find_out_spec(const char *name) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE(out_spec); i++) { + if (!strcmp(name, out_spec[i].name)) + return &out_spec[i]; + } + bb_error_msg_and_die("bad -o argument '%s'", name); +} + +static void parse_o(char* opt) +{ + ps_out_t* new; + // POSIX: "-o is blank- or comma-separated list" (FIXME) + char *comma, *equal; + while (1) { + comma = strchr(opt, ','); + equal = strchr(opt, '='); + if (comma && (!equal || equal > comma)) { + *comma = '\0'; + *new_out_t() = *find_out_spec(opt); + *comma = ','; + opt = comma + 1; + continue; + } + break; + } + // opt points to last spec in comma separated list. + // This one can have =HEADER part. + new = new_out_t(); + if (equal) + *equal = '\0'; + *new = *find_out_spec(opt); + if (equal) { + *equal = '='; + new->header = equal + 1; + // POSIX: the field widths shall be ... at least as wide as + // the header text (default or overridden value). + // If the header text is null, such as -o user=, + // the field width shall be at least as wide as the + // default header text + if (new->header[0]) { + new->width = strlen(new->header); + print_header = 1; + } + } else + print_header = 1; +} + +static void post_process(void) +{ + int i; + int width = 0; + for (i = 0; i < out_cnt; i++) { + need_flags |= out[i].ps_flags; + if (out[i].header[0]) { + print_header = 1; + } + width += out[i].width + 1; /* "FIELD " */ + } +#if ENABLE_SELINUX + if (!is_selinux_enabled()) + need_flags &= ~PSSCAN_CONTEXT; +#endif + buffer = xmalloc(width + 1); /* for trailing \0 */ +} + +static void format_header(void) +{ + int i; + ps_out_t* op; + char *p; + + if (!print_header) + return; + p = buffer; + i = 0; + if (out_cnt) { + while (1) { + op = &out[i]; + if (++i == out_cnt) /* do not pad last field */ + break; + p += sprintf(p, "%-*s ", op->width, op->header); + } + strcpy(p, op->header); + } + printf("%.*s\n", terminal_width, buffer); +} + +static void format_process(const procps_status_t *ps) +{ + int i, len; + char *p = buffer; + i = 0; + if (out_cnt) while (1) { + out[i].f(p, out[i].width, ps); + // POSIX: Any field need not be meaningful in all + // implementations. In such a case a hyphen ( '-' ) + // should be output in place of the field value. + if (!p[0]) { + p[0] = '-'; + p[1] = '\0'; + } + len = strlen(p); + p += len; + len = out[i].width - len + 1; + if (++i == out_cnt) /* do not pad last field */ + break; + p += sprintf(p, "%*s", len, ""); + } + printf("%.*s\n", terminal_width, buffer); +} + +int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ps_main(int argc UNUSED_PARAM, char **argv) +{ + procps_status_t *p; + llist_t* opt_o = NULL; + USE_SELINUX(int opt;) + + // POSIX: + // -a Write information for all processes associated with terminals + // Implementations may omit session leaders from this list + // -A Write information for all processes + // -d Write information for all processes, except session leaders + // -e Write information for all processes (equivalent to -A.) + // -f Generate a full listing + // -l Generate a long listing + // -o col1,col2,col3=header + // Select which columns to display + /* We allow (and ignore) most of the above. FIXME */ + opt_complementary = "o::"; + USE_SELINUX(opt =) getopt32(argv, "Zo:aAdefl", &opt_o); + if (opt_o) { + do { + parse_o(llist_pop(&opt_o)); + } while (opt_o); + } else { + /* Below: parse_o() needs char*, NOT const char*... */ +#if ENABLE_SELINUX + if (!(opt & 1) || !is_selinux_enabled()) { + /* no -Z or no SELinux: do not show LABEL */ + strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1); + } else +#endif { - char sbuf[128]; - len = sizeof(sbuf); - if(security_sid_to_context(sid, (security_context_t)&sbuf, &len)) - strcpy(sbuf, "unknown"); + strcpy(default_o, DEFAULT_O_STR); + } + parse_o(default_o); + } + post_process(); + + /* Was INT_MAX, but some libc's go belly up with printf("%.*s") + * and such large widths */ + terminal_width = MAX_WIDTH; + if (isatty(1)) { + get_terminal_width_height(0, &terminal_width, NULL); + if (--terminal_width > MAX_WIDTH) + terminal_width = MAX_WIDTH; + } + format_header(); - len = printf("%5d %-32s %s ", p->pid, sbuf, p->state); + p = NULL; + while ((p = procps_scan(p, need_flags))) { + format_process(p); + } + + return EXIT_SUCCESS; +} + + +#else /* !ENABLE_DESKTOP */ + + +int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + procps_status_t *p = NULL; + int len; + SKIP_SELINUX(const) int use_selinux = 0; + USE_SELINUX(int i;) +#if !ENABLE_FEATURE_PS_WIDE + enum { terminal_width = 79 }; +#else + unsigned terminal_width; + int w_count = 0; +#endif + +#if ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX +#if ENABLE_FEATURE_PS_WIDE + opt_complementary = "-:ww"; + USE_SELINUX(i =) getopt32(argv, USE_SELINUX("Z") "w", &w_count); + /* if w is given once, GNU ps sets the width to 132, + * if w is given more than once, it is "unlimited" + */ + if (w_count) { + terminal_width = (w_count==1) ? 132 : MAX_WIDTH; + } else { + get_terminal_width_height(0, &terminal_width, NULL); + /* Go one less... */ + if (--terminal_width > MAX_WIDTH) + terminal_width = MAX_WIDTH; + } +#else /* only ENABLE_SELINUX */ + i = getopt32(argv, "Z"); +#endif +#if ENABLE_SELINUX + if ((i & 1) && is_selinux_enabled()) + use_selinux = PSSCAN_CONTEXT; +#endif +#endif /* ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX */ + + if (use_selinux) + puts(" PID CONTEXT STAT COMMAND"); + else + puts(" PID USER VSZ STAT COMMAND"); + + while ((p = procps_scan(p, 0 + | PSSCAN_PID + | PSSCAN_UIDGID + | PSSCAN_STATE + | PSSCAN_VSZ + | PSSCAN_COMM + | use_selinux + ))) { +#if ENABLE_SELINUX + if (use_selinux) { + len = printf("%5u %-32.32s %s ", + p->pid, + p->context ? p->context : "unknown", + p->state); + } else +#endif + { + const char *user = get_cached_username(p->uid); + //if (p->vsz == 0) + // len = printf("%5u %-8.8s %s ", + // p->pid, user, p->state); + //else + { + char buf6[6]; + smart_ulltoa5(p->vsz, buf6, " mgtpezy"); + buf6[5] = '\0'; + len = printf("%5u %-8.8s %s %s ", + p->pid, user, buf6, p->state); + } } - else -#endif - if(p->rss == 0) - len = printf("%5d %-8s %s ", p->pid, p->user, p->state); - else - len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state); - i = terminal_width-len; - - if(namecmd != 0 && namecmd[0] != 0) { - if(i < 0) - i = 0; - if(strlen(namecmd) > i) - namecmd[i] = 0; - printf("%s\n", namecmd); - } else { - namecmd = p->short_cmd; - if(i < 2) - i = 2; - if(strlen(namecmd) > (i-2)) - namecmd[i-2] = 0; - printf("[%s]\n", namecmd); + + { + int sz = terminal_width - len; + char buf[sz + 1]; + read_cmdline(buf, sz, p->pid, p->comm); + puts(buf); } - free(p->cmd); } + if (ENABLE_FEATURE_CLEAN_UP) + clear_username_cache(); return EXIT_SUCCESS; } +#endif /* ENABLE_DESKTOP */ diff --git a/release/src/router/busybox/procps/ps.posix b/release/src/router/busybox/procps/ps.posix new file mode 100644 index 00000000..57f4fa8a --- /dev/null +++ b/release/src/router/busybox/procps/ps.posix @@ -0,0 +1,175 @@ +This is what POSIX 2003 says about ps: + +By default, ps shall select all processes with the same effective user +ID as the current user and the same controlling terminal as the invoker + +ps [-aA][-defl][-G grouplist][-o format]...[-p proclist][-t termlist] +[-U userlist][-g grouplist][-n namelist][-u userlist] + +-a Write information for all processes associated with terminals. + Implementations may omit session leaders from this list. + +-A Write information for all processes. + +-d Write information for all processes, except session leaders. + +-e Write information for all processes. (Equivalent to -A.) + +-f Generate a full listing. (See the STDOUT section for the con- + tents of a full listing.) + +-g grouplist + Write information for processes whose session leaders are given + in grouplist. The application shall ensure that the grouplist is + a single argument in the form of a <blank> or comma-separated + list. + +-G grouplist + Write information for processes whose real group ID numbers are + given in grouplist. The application shall ensure that the grou- + plist is a single argument in the form of a <blank> or comma- + separated list. + +-l Generate a long listing. (See STDOUT for the contents of a long + listing.) + +-n namelist + Specify the name of an alternative system namelist file in place + of the default. The name of the default file and the format of a + namelist file are unspecified. + +-o format + Write information according to the format specification given in + format. Multiple -o options can be specified; the format speci- + fication shall be interpreted as the <space>-separated concate- + nation of all the format option-arguments. + +-p proclist + Write information for processes whose process ID numbers are + given in proclist. The application shall ensure that the pro- + clist is a single argument in the form of a <blank> or comma- + separated list. + +-t termlist + Write information for processes associated with terminals given + in termlist. The application shall ensure that the termlist is a + single argument in the form of a <blank> or comma-separated + list. Terminal identifiers shall be given in an implementation- + defined format. On XSI-conformant systems, they shall be + given in one of two forms: the device's filename (for example, + tty04) or, if the device's filename starts with tty, just the + identifier following the characters tty (for example, "04" ). + +-u userlist + Write information for processes whose user ID numbers or login + names are given in userlist. The application shall ensure that + the userlist is a single argument in the form of a <blank> or + comma-separated list. In the listing, the numerical user ID + shall be written unless the -f option is used, in which case the + login name shall be written. + +-U userlist + Write information for processes whose real user ID numbers or + login names are given in userlist. The application shall ensure + that the userlist is a single argument in the form of a <blank> + or comma-separated list. + +With the exception of -o format, all of the options shown are used to +select processes. If any are specified, the default list shall be +ignored and ps shall select the processes represented by the inclusive +OR of all the selection-criteria options. + +The -o option allows the output format to be specified under user con- +trol. + +The application shall ensure that the format specification is a list of +names presented as a single argument, <blank> or comma-separated. Each +variable has a default header. The default header can be overridden by +appending an equals sign and the new text of the header. The rest of +the characters in the argument shall be used as the header text. The +fields specified shall be written in the order specified on the command +line, and should be arranged in columns in the output. The field widths +shall be selected by the system to be at least as wide as the header +text (default or overridden value). If the header text is null, such as +-o user=, the field width shall be at least as wide as the default +header text. If all header text fields are null, no header line shall +be written. + +ruser The real user ID of the process. This shall be the textual user + ID, if it can be obtained and the field width permits, or a dec- + imal representation otherwise. + +user The effective user ID of the process. This shall be the textual + user ID, if it can be obtained and the field width permits, or a + decimal representation otherwise. + +rgroup The real group ID of the process. This shall be the textual + group ID, if it can be obtained and the field width permits, or + a decimal representation otherwise. + +group The effective group ID of the process. This shall be the textual + group ID, if it can be obtained and the field width permits, or + a decimal representation otherwise. + +pid The decimal value of the process ID. + +ppid The decimal value of the parent process ID. + +pgid The decimal value of the process group ID. + +pcpu The ratio of CPU time used recently to CPU time available in the + same period, expressed as a percentage. The meaning of + "recently" in this context is unspecified. The CPU time avail- + able is determined in an unspecified manner. + +vsz The size of the process in (virtual) memory in 1024 byte units + as a decimal integer. + +nice The decimal value of the nice value of the process; see nice() . + +etime In the POSIX locale, the elapsed time since the process was + started, in the form: [[dd-]hh:]mm:ss + +time In the POSIX locale, the cumulative CPU time of the process in + the form: [dd-]hh:mm:ss + +tty The name of the controlling terminal of the process (if any) in + the same format used by the who utility. + +comm The name of the command being executed ( argv[0] value) as a + string. + +args The command with all its arguments as a string. The implementa- + tion may truncate this value to the field width; it is implemen- + tation-defined whether any further truncation occurs. It is + unspecified whether the string represented is a version of the + argument list as it was passed to the command when it started, + or is a version of the arguments as they may have been modified + by the application. Applications cannot depend on being able to + modify their argument list and having that modification be + reflected in the output of ps. + +Any field need not be meaningful in all implementations. In such a case +a hyphen ( '-' ) should be output in place of the field value. + +Only comm and args shall be allowed to contain <blank>s; all others +shall not. + +The following table specifies the default header to be used in the +POSIX locale corresponding to each format specifier. + + Format Specifier Default Header Format Specifier Default Header + args COMMAND ppid PPID + comm COMMAND rgroup RGROUP + etime ELAPSED ruser RUSER + group GROUP time TIME + nice NI tty TT + pcpu %CPU user USER + pgid PGID vsz VSZ + pid PID + +There is no special quoting mechanism for header text. The header text +is the rest of the argument. If multiple header changes are needed, +multiple -o options can be used, such as: + + ps -o "user=User Name" -o pid=Process\ ID diff --git a/release/src/router/busybox/procps/renice.c b/release/src/router/busybox/procps/renice.c index a81156ee..ea5fc703 100644 --- a/release/src/router/busybox/procps/renice.c +++ b/release/src/router/busybox/procps/renice.c @@ -1,54 +1,128 @@ +/* vi: set sw=4 ts=4: */ /* - * Mini renice implementation for busybox + * renice implementation for busybox * + * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org> * - * Copyright (C) 2000 Dave 'Kill a Cop' Cinege <dcinege@psychosis.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +/* Notes: + * Setting an absolute priority was obsoleted in SUSv2 and removed + * in SUSv3. However, the common linux version of renice does + * absolute and not relative. So we'll continue supporting absolute, + * although the stdout logging has been removed since both SUSv2 and + * SUSv3 specify that stdout isn't used. * + * This version is lenient in that it doesn't require any IDs. The + * options -p, -g, and -u are treated as mode switches for the + * following IDs (if any). Multiple switches are allowed. */ -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include <sys/time.h> +#include "libbb.h" #include <sys/resource.h> -#include "busybox.h" +void BUG_bad_PRIO_PROCESS(void); +void BUG_bad_PRIO_PGRP(void); +void BUG_bad_PRIO_USER(void); -extern int renice_main(int argc, char **argv) +int renice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int renice_main(int argc UNUSED_PARAM, char **argv) { - int prio, status = EXIT_SUCCESS; - - if (argc < 3) bb_show_usage(); - - prio = atoi(*++argv); - if (prio > 20) prio = 20; - if (prio < -20) prio = -20; - - while (*++argv) { - int ps = atoi(*argv); - int oldp = getpriority(PRIO_PROCESS, ps); - - if (setpriority(PRIO_PROCESS, ps, prio) == 0) { - printf("%d: old priority %d, new priority %d\n", ps, oldp, prio ); + static const char Xetpriority_msg[] ALIGN1 = "%cetpriority"; + + int retval = EXIT_SUCCESS; + int which = PRIO_PROCESS; /* Default 'which' value. */ + int use_relative = 0; + int adjustment, new_priority; + unsigned who; + char *arg; + + /* Yes, they are not #defines in glibc 2.4! #if won't work */ + if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX) + BUG_bad_PRIO_PROCESS(); + if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX) + BUG_bad_PRIO_PGRP(); + if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX) + BUG_bad_PRIO_USER(); + + arg = *++argv; + + /* Check if we are using a relative adjustment. */ + if (arg && arg[0] == '-' && arg[1] == 'n') { + use_relative = 1; + if (!arg[2]) + arg = *++argv; + else + arg += 2; + } + + if (!arg) { /* No args? Then show usage. */ + bb_show_usage(); + } + + /* Get the priority adjustment (absolute or relative). */ + adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2); + + while ((arg = *++argv) != NULL) { + /* Check for a mode switch. */ + if (arg[0] == '-' && arg[1]) { + static const char opts[] ALIGN1 = { + 'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER + }; + const char *p = strchr(opts, arg[1]); + if (p) { + which = p[4]; + if (!arg[2]) + continue; + arg += 2; + } + } + + /* Process an ID arg. */ + if (which == PRIO_USER) { + struct passwd *p; + p = getpwnam(arg); + if (!p) { + bb_error_msg("unknown user %s", arg); + goto HAD_ERROR; + } + who = p->pw_uid; + } else { + who = bb_strtou(arg, NULL, 10); + if (errno) { + bb_error_msg("bad value: %s", arg); + goto HAD_ERROR; + } + } + + /* Get priority to use, and set it. */ + if (use_relative) { + int old_priority; + + errno = 0; /* Needed for getpriority error detection. */ + old_priority = getpriority(which, who); + if (errno) { + bb_perror_msg(Xetpriority_msg, 'g'); + goto HAD_ERROR; + } + + new_priority = old_priority + adjustment; } else { - bb_perror_msg("%d: setpriority", ps); - status = EXIT_FAILURE; + new_priority = adjustment; + } + + if (setpriority(which, who, new_priority) == 0) { + continue; } + + bb_perror_msg(Xetpriority_msg, 's'); + HAD_ERROR: + retval = EXIT_FAILURE; } - return status; + /* No need to check for errors outputing to stderr since, if it + * was used, the HAD_ERROR label was reached and retval was set. */ + + return retval; } diff --git a/release/src/router/busybox/procps/sysctl.c b/release/src/router/busybox/procps/sysctl.c new file mode 100644 index 00000000..c9063bf0 --- /dev/null +++ b/release/src/router/busybox/procps/sysctl.c @@ -0,0 +1,258 @@ +/* vi: set sw=4 ts=4: */ +/* + * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters + * + * Copyright 1999 George Staikos + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Changelog: + * v1.01 - added -p <preload> to preload values from a file + * v1.01.1 - busybox applet aware by <solar@gentoo.org> + */ + +#include "libbb.h" + +enum { + FLAG_SHOW_KEYS = 1 << 0, + FLAG_SHOW_KEY_ERRORS = 1 << 1, + FLAG_TABLE_FORMAT = 1 << 2, /* not implemented */ + FLAG_SHOW_ALL = 1 << 3, + FLAG_PRELOAD_FILE = 1 << 4, + FLAG_WRITE = 1 << 5, +}; +#define OPTION_STR "neAapw" + +static void sysctl_dots_to_slashes(char *name) +{ + char *cptr, *last_good, *end; + + /* Convert minimum number of '.' to '/' so that + * we end up with existing file's name. + * + * Example from bug 3894: + * net.ipv4.conf.eth0.100.mc_forwarding -> + * net/ipv4/conf/eth0.100/mc_forwarding + * NB: net/ipv4/conf/eth0/mc_forwarding *also exists*, + * therefore we must start from the end, and if + * we replaced even one . -> /, start over again, + * but never replace dots before the position + * where last replacement occurred. + * + * Another bug we later had is that + * net.ipv4.conf.eth0.100 + * (without .mc_forwarding) was mishandled. + * + * To set up testing: modprobe 8021q; vconfig add eth0 100 + */ + end = name + strlen(name); + last_good = name - 1; + *end = '.'; /* trick the loop into trying full name too */ + + again: + cptr = end; + while (cptr > last_good) { + if (*cptr == '.') { + *cptr = '\0'; + //bb_error_msg("trying:'%s'", name); + if (access(name, F_OK) == 0) { + if (cptr != end) /* prevent trailing '/' */ + *cptr = '/'; + //bb_error_msg("replaced:'%s'", name); + last_good = cptr; + goto again; + } + *cptr = '.'; + } + cptr--; + } + *end = '\0'; +} + +static int sysctl_act_on_setting(char *setting) +{ + int fd, retval = EXIT_SUCCESS; + char *cptr, *outname; + char *value = value; /* for compiler */ + + outname = xstrdup(setting); + + cptr = outname; + while (*cptr) { + if (*cptr == '/') + *cptr = '.'; + cptr++; + } + + if (option_mask32 & FLAG_WRITE) { + cptr = strchr(setting, '='); + if (cptr == NULL) { + bb_error_msg("error: '%s' must be of the form name=value", + outname); + retval = EXIT_FAILURE; + goto end; + } + value = cptr + 1; /* point to the value in name=value */ + if (setting == cptr || !*value) { + bb_error_msg("error: malformed setting '%s'", outname); + retval = EXIT_FAILURE; + goto end; + } + *cptr = '\0'; + outname[cptr - setting] = '\0'; + /* procps 3.2.7 actually uses these flags */ + fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } else { + fd = open(setting, O_RDONLY); + } + + if (fd < 0) { + switch (errno) { + case ENOENT: + if (option_mask32 & FLAG_SHOW_KEY_ERRORS) + bb_error_msg("error: '%s' is an unknown key", outname); + break; + default: + bb_perror_msg("error %sing key '%s'", + option_mask32 & FLAG_WRITE ? + "sett" : "read", + outname); + break; + } + retval = EXIT_FAILURE; + goto end; + } + + if (option_mask32 & FLAG_WRITE) { +//TODO: procps 3.2.7 writes "value\n", note trailing "\n" + xwrite_str(fd, value); + close(fd); + if (option_mask32 & FLAG_SHOW_KEYS) + printf("%s = ", outname); + puts(value); + } else { + char c; + + value = cptr = xmalloc_read(fd, NULL); + close(fd); + if (value == NULL) { + bb_perror_msg("error reading key '%s'", outname); + goto end; + } + + /* dev.cdrom.info and sunrpc.transports, for example, + * are multi-line. Try "sysctl sunrpc.transports" + */ + while ((c = *cptr) != '\0') { + if (option_mask32 & FLAG_SHOW_KEYS) + printf("%s = ", outname); + while (1) { + fputc(c, stdout); + cptr++; + if (c == '\n') + break; + c = *cptr; + if (c == '\0') + break; + } + } + free(value); + } + end: + free(outname); + return retval; +} + +static int sysctl_act_recursive(const char *path) +{ + DIR *dirp; + struct stat buf; + struct dirent *entry; + char *next; + int retval = 0; + + stat(path, &buf); + if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) { + dirp = opendir(path); + if (dirp == NULL) + return -1; + while ((entry = readdir(dirp)) != NULL) { + next = concat_subpath_file(path, entry->d_name); + if (next == NULL) + continue; /* d_name is "." or ".." */ + /* if path was ".", drop "./" prefix: */ + retval |= sysctl_act_recursive((next[0] == '.' && next[1] == '/') ? + next + 2 : next); + free(next); + } + closedir(dirp); + } else { + char *name = xstrdup(path); + retval |= sysctl_act_on_setting(name); + free(name); + } + + return retval; +} + +/* Set sysctl's from a conf file. Format example: + * # Controls IP packet forwarding + * net.ipv4.ip_forward = 0 + */ +static int sysctl_handle_preload_file(const char *filename) +{ + char *token[2]; + parser_t *parser; + + parser = config_open(filename); + /* Must do it _after_ config_open(): */ + xchdir("/proc/sys"); + /* xchroot(".") - if you are paranoid */ + +//TODO: ';' is comment char too +//TODO: comment may be only at line start. "var=1 #abc" - "1 #abc" is the value +// (but _whitespace_ from ends should be trimmed first (and we do it right)) +//TODO: "var==1" is mishandled (must use "=1" as a value, but uses "1") + while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { + char *tp; + sysctl_dots_to_slashes(token[0]); + tp = xasprintf("%s=%s", token[0], token[1]); + sysctl_act_recursive(tp); + free(tp); + } + if (ENABLE_FEATURE_CLEAN_UP) + config_close(parser); + return 0; +} + +int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sysctl_main(int argc UNUSED_PARAM, char **argv) +{ + int retval; + int opt; + + opt = getopt32(argv, "+" OPTION_STR); /* '+' - stop on first non-option */ + argv += optind; + opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS); + option_mask32 = opt; + + if (opt & FLAG_PRELOAD_FILE) { + option_mask32 |= FLAG_WRITE; + /* xchdir("/proc/sys") is inside */ + return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf"); + } + xchdir("/proc/sys"); + /* xchroot(".") - if you are paranoid */ + if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) { + return sysctl_act_recursive("."); + } + + retval = 0; + while (*argv) { + sysctl_dots_to_slashes(*argv); + retval |= sysctl_act_recursive(*argv); + argv++; + } + + return retval; +} diff --git a/release/src/router/busybox/procps/top.c b/release/src/router/busybox/procps/top.c index 2e1bd328..b5951422 100644 --- a/release/src/router/busybox/procps/top.c +++ b/release/src/router/busybox/procps/top.c @@ -1,583 +1,1120 @@ +/* vi: set sw=4 ts=4: */ /* * A tiny 'top' utility. * * This is written specifically for the linux /proc/<PID>/stat(m) * files format. - + * * This reads the PIDs of all processes and their status and shows * the status of processes (first ones that fit to screen) at given * intervals. - * + * * NOTES: * - At startup this changes to /proc, all the reads are then * relative to that. - * + * * (C) Eero Tamminen <oak at welho dot com> * - * Rewroted by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru> - */ - -/* Original code Copyrights */ -/* + * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru> + * + * Sept 2008: Vineet Gupta <vineet.gupta@arc.com> + * Added Support for reporting SMP Information + * - CPU where Process was last seen running + * (to see effect of sched_setaffinity() etc) + * - CPU Time Split (idle/IO/wait etc) PER CPU + * * Copyright (c) 1992 Branko Lankester * Copyright (c) 1992 Roger Binns * Copyright (C) 1994-1996 Charles L. Blake. * Copyright (C) 1992-1998 Michael K. Johnson - * May be distributed under the conditions of the - * GNU Library General Public License + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. */ -#include <sys/types.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <sys/ioctl.h> -/* get page info */ -#include <asm/page.h> -#include "busybox.h" +#include "libbb.h" -//#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */ -#ifdef FEATURE_CPU_USAGE_PERCENTAGE -#include <time.h> -#include <sys/time.h> -#include <fcntl.h> -#include <netinet/in.h> /* htons */ +typedef struct top_status_t { + unsigned long vsz; +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + unsigned long ticks; + unsigned pcpu; /* delta of ticks */ +#endif + unsigned pid, ppid; + unsigned uid; + char state[4]; + char comm[COMM_LEN]; +#if ENABLE_FEATURE_TOP_SMP_PROCESS + int last_seen_on_cpu; #endif +} top_status_t; +typedef struct jiffy_counts_t { + /* Linux 2.4.x has only first four */ + unsigned long long usr, nic, sys, idle; + unsigned long long iowait, irq, softirq, steal; + unsigned long long total; + unsigned long long busy; +} jiffy_counts_t; -typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q); +/* This structure stores some critical information from one frame to + the next. Used for finding deltas. */ +typedef struct save_hist { + unsigned long ticks; + pid_t pid; +} save_hist; -static procps_status_t *top; /* Hehe */ -static int ntop; +typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q); -static int pid_sort (procps_status_t *P, procps_status_t *Q) -{ - int p = P->pid; - int q = Q->pid; +enum { SORT_DEPTH = 3 }; - if( p < q ) return -1; - if( p > q ) return 1; - return 0; -} -static int mem_sort (procps_status_t *P, procps_status_t *Q) +struct globals { + top_status_t *top; + int ntop; +#if ENABLE_FEATURE_TOPMEM + smallint sort_field; + smallint inverted; +#endif +#if ENABLE_FEATURE_TOP_SMP_CPU + smallint smp_cpu_info; /* one/many cpu info lines? */ +#endif +#if ENABLE_FEATURE_USE_TERMIOS + struct termios initial_settings; +#endif +#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + cmp_funcp sort_function[1]; +#else + cmp_funcp sort_function[SORT_DEPTH]; + struct save_hist *prev_hist; + int prev_hist_count; + jiffy_counts_t cur_jif, prev_jif; + /* int hist_iterations; */ + unsigned total_pcpu; + /* unsigned long total_vsz; */ +#endif +#if ENABLE_FEATURE_TOP_SMP_CPU + /* Per CPU samples: current and last */ + jiffy_counts_t *cpu_jif, *cpu_prev_jif; + int num_cpus; +#endif + char line_buf[80]; +}; + +enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) }; + +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { \ + struct G_sizecheck { \ + char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ + }; \ +} while (0) +#define top (G.top ) +#define ntop (G.ntop ) +#define sort_field (G.sort_field ) +#define inverted (G.inverted ) +#define smp_cpu_info (G.smp_cpu_info ) +#define initial_settings (G.initial_settings ) +#define sort_function (G.sort_function ) +#define prev_hist (G.prev_hist ) +#define prev_hist_count (G.prev_hist_count ) +#define cur_jif (G.cur_jif ) +#define prev_jif (G.prev_jif ) +#define cpu_jif (G.cpu_jif ) +#define cpu_prev_jif (G.cpu_prev_jif ) +#define num_cpus (G.num_cpus ) +#define total_pcpu (G.total_pcpu ) +#define line_buf (G.line_buf ) + +enum { + OPT_d = (1 << 0), + OPT_n = (1 << 1), + OPT_b = (1 << 2), + OPT_EOF = (1 << 3), /* pseudo: "we saw EOF in stdin" */ +}; +#define OPT_BATCH_MODE (option_mask32 & OPT_b) + + +#if ENABLE_FEATURE_USE_TERMIOS +static int pid_sort(top_status_t *P, top_status_t *Q) { - long p = P->rss; - long q = Q->rss; + /* Buggy wrt pids with high bit set */ + /* (linux pids are in [1..2^15-1]) */ + return (Q->pid - P->pid); +} +#endif - if( p > q ) return -1; - if( p < q ) return 1; - return 0; +static int mem_sort(top_status_t *P, top_status_t *Q) +{ + /* We want to avoid unsigned->signed and truncation errors */ + if (Q->vsz < P->vsz) return -1; + return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ } -#ifdef FEATURE_CPU_USAGE_PERCENTAGE -#define sort_depth 3 -static cmp_t sort_function[sort_depth]; +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE -static int pcpu_sort (procps_status_t *P, procps_status_t *Q) +static int pcpu_sort(top_status_t *P, top_status_t *Q) { - int p = P->pcpu; - int q = Q->pcpu; + /* Buggy wrt ticks with high bit set */ + /* Affects only processes for which ticks overflow */ + return (int)Q->pcpu - (int)P->pcpu; +} - if( p > q ) return -1; - if( p < q ) return 1; - return 0; +static int time_sort(top_status_t *P, top_status_t *Q) +{ + /* We want to avoid unsigned->signed and truncation errors */ + if (Q->ticks < P->ticks) return -1; + return Q->ticks != P->ticks; /* 0 if ==, 1 if > */ } -static int time_sort (procps_status_t *P, procps_status_t *Q) +static int mult_lvl_cmp(void* a, void* b) { - long p = P->stime; - long q = Q->stime; - - p += P->utime; - q += Q->utime; - if( p > q ) return -1; - if( p < q ) return 1; - return 0; + int i, cmp_val; + + for (i = 0; i < SORT_DEPTH; i++) { + cmp_val = (*sort_function[i])(a, b); + if (cmp_val != 0) + return cmp_val; + } + return 0; } -int mult_lvl_cmp(void* a, void* b) { - int i, cmp_val; +static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) +{ +#if !ENABLE_FEATURE_TOP_SMP_CPU + static const char fmt[] = "cpu %llu %llu %llu %llu %llu %llu %llu %llu"; +#else + static const char fmt[] = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu"; +#endif + int ret; + + if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) + return 0; + ret = sscanf(line_buf, fmt, + &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, + &p_jif->iowait, &p_jif->irq, &p_jif->softirq, + &p_jif->steal); + if (ret >= 4) { + p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle + + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal; + /* procps 2.x does not count iowait as busy time */ + p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait; + } - for(i = 0; i < sort_depth; i++) { - cmp_val = (*sort_function[i])(a, b); - if (cmp_val != 0) - return cmp_val; - } - return 0; + return ret; } -/* This structure stores some critical information from one frame to - the next. mostly used for sorting. Added cumulative and resident fields. */ -struct save_hist { - int ticks; - int pid; - int utime; - int stime; -}; +static void get_jiffy_counts(void) +{ + FILE* fp = xfopen_for_read("stat"); -/* - * Calculates percent cpu usage for each task. - */ + /* We need to parse cumulative counts even if SMP CPU display is on, + * they are used to calculate per process CPU% */ + prev_jif = cur_jif; + if (read_cpu_jiffy(fp, &cur_jif) < 4) + bb_error_msg_and_die("can't read /proc/stat"); -static struct save_hist *save_history; +#if !ENABLE_FEATURE_TOP_SMP_CPU + fclose(fp); + return; +#else + if (!smp_cpu_info) { + fclose(fp); + return; + } -static unsigned long Hertz; + if (!num_cpus) { + /* First time here. How many CPUs? + * There will be at least 1 /proc/stat line with cpu%d + */ + while (1) { + cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus); + if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4) + break; + num_cpus++; + } + if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */ + smp_cpu_info = 0; -/*********************************************************************** - * Some values in /proc are expressed in units of 1/HZ seconds, where HZ - * is the kernel clock tick rate. One of these units is called a jiffy. - * The HZ value used in the kernel may vary according to hacker desire. - * According to Linus Torvalds, this is not true. He considers the values - * in /proc as being in architecture-dependant units that have no relation - * to the kernel clock tick rate. Examination of the kernel source code - * reveals that opinion as wishful thinking. - * - * In any case, we need the HZ constant as used in /proc. (the real HZ value - * may differ, but we don't care) There are several ways we could get HZ: - * - * 1. Include the kernel header file. If it changes, recompile this library. - * 2. Use the sysconf() function. When HZ changes, recompile the C library! - * 3. Ask the kernel. This is obviously correct... - * - * Linus Torvalds won't let us ask the kernel, because he thinks we should - * not know the HZ value. Oh well, we don't have to listen to him. - * Someone smuggled out the HZ value. :-) - * - * This code should work fine, even if Linus fixes the kernel to match his - * stated behavior. The code only fails in case of a partial conversion. - * - */ + cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus); + + /* Otherwise the first per cpu display shows all 100% idles */ + usleep(50000); + } else { /* Non first time invocation */ + jiffy_counts_t *tmp; + int i; + + /* First switch the sample pointers: no need to copy */ + tmp = cpu_prev_jif; + cpu_prev_jif = cpu_jif; + cpu_jif = tmp; -#define FILE_TO_BUF(filename, fd) do{ \ - if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \ - bb_perror_msg_and_die("/proc not be mounted?"); \ - } \ - lseek(fd, 0L, SEEK_SET); \ - if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ - bb_perror_msg_and_die("%s", filename); \ - } \ - buf[local_n] = '\0'; \ -}while(0) - -#define FILE_TO_BUF2(filename, fd) do{ \ - lseek(fd, 0L, SEEK_SET); \ - if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ - bb_perror_msg_and_die("%s", filename); \ - } \ - buf[local_n] = '\0'; \ -}while(0) - -static void init_Hertz_value(void) { - unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ - double up_1, up_2, seconds; - unsigned long jiffies, h; - char buf[80]; - int uptime_fd = -1; - int stat_fd = -1; - - long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF); - - if(smp_num_cpus<1) smp_num_cpus=1; - do { - int local_n; - - FILE_TO_BUF("uptime", uptime_fd); - up_1 = strtod(buf, 0); - FILE_TO_BUF("stat", stat_fd); - sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j); - FILE_TO_BUF2("uptime", uptime_fd); - up_2 = strtod(buf, 0); - } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ - - close(uptime_fd); - close(stat_fd); - - jiffies = user_j + nice_j + sys_j + other_j; - seconds = (up_1 + up_2) / 2; - h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus ); - /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ - switch(h){ - case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ - case 48 ... 52 : Hertz = 50; break; - case 58 ... 62 : Hertz = 60; break; - case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */ - case 95 ... 105 : Hertz = 100; break; /* normal Linux */ - case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */ - case 195 ... 204 : Hertz = 200; break; /* normal << 1 */ - case 253 ... 260 : Hertz = 256; break; - case 295 ... 304 : Hertz = 300; break; /* 3 cpus */ - case 393 ... 408 : Hertz = 400; break; /* normal << 2 */ - case 495 ... 504 : Hertz = 500; break; /* 5 cpus */ - case 595 ... 604 : Hertz = 600; break; /* 6 cpus */ - case 695 ... 704 : Hertz = 700; break; /* 7 cpus */ - case 790 ... 808 : Hertz = 800; break; /* normal << 3 */ - case 895 ... 904 : Hertz = 900; break; /* 9 cpus */ - case 990 ... 1010 : Hertz = 1000; break; /* ARM */ - case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */ - case 1095 ... 1104 : Hertz = 1100; break; /* 11 cpus */ - case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */ - default: - /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ - Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; - } + /* Get the new samples */ + for (i = 0; i < num_cpus; i++) + read_cpu_jiffy(fp, &cpu_jif[i]); + } +#endif + fclose(fp); } static void do_stats(void) { - struct timeval t; - static struct timeval oldtime; - struct timezone timez; - float elapsed_time; - - procps_status_t *cur; - int total_time, i, n; - static int prev_count; - int systime, usrtime, pid; - - struct save_hist *New_save_hist; - - /* - * Finds the current time (in microseconds) and calculates the time - * elapsed since the last update. - */ - gettimeofday(&t, &timez); - elapsed_time = (t.tv_sec - oldtime.tv_sec) - + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0; - oldtime.tv_sec = t.tv_sec; - oldtime.tv_usec = t.tv_usec; - - New_save_hist = alloca(sizeof(struct save_hist)*ntop); - /* - * Make a pass through the data to get stats. - */ - for(n = 0; n < ntop; n++) { - cur = top + n; - + top_status_t *cur; + pid_t pid; + int i, last_i, n; + struct save_hist *new_hist; + + get_jiffy_counts(); + total_pcpu = 0; + /* total_vsz = 0; */ + new_hist = xmalloc(sizeof(new_hist[0]) * ntop); /* - * Calculate time in cur process. Time is sum of user time - * (usrtime) plus system time (systime). + * Make a pass through the data to get stats. */ - systime = cur->stime; - usrtime = cur->utime; - pid = cur->pid; - total_time = systime + usrtime; - New_save_hist[n].ticks = total_time; - New_save_hist[n].pid = pid; - New_save_hist[n].stime = systime; - New_save_hist[n].utime = usrtime; - - /* find matching entry from previous pass */ - for (i = 0; i < prev_count; i++) { - if (save_history[i].pid == pid) { - total_time -= save_history[i].ticks; - systime -= save_history[i].stime; - usrtime -= save_history[i].utime; - break; - } + /* hist_iterations = 0; */ + i = 0; + for (n = 0; n < ntop; n++) { + cur = top + n; + + /* + * Calculate time in cur process. Time is sum of user time + * and system time + */ + pid = cur->pid; + new_hist[n].ticks = cur->ticks; + new_hist[n].pid = pid; + + /* find matching entry from previous pass */ + cur->pcpu = 0; + /* do not start at index 0, continue at last used one + * (brought hist_iterations from ~14000 down to 172) */ + last_i = i; + if (prev_hist_count) do { + if (prev_hist[i].pid == pid) { + cur->pcpu = cur->ticks - prev_hist[i].ticks; + total_pcpu += cur->pcpu; + break; + } + i = (i+1) % prev_hist_count; + /* hist_iterations++; */ + } while (i != last_i); + /* total_vsz += cur->vsz; */ } /* - * Calculate percent cpu time for cur task. + * Save cur frame's information. */ - i = (total_time * 10 * 100/Hertz) / elapsed_time; - if (i > 999) - i = 999; - cur->pcpu = i; - - } - - /* - * Save cur frame's information. - */ - free(save_history); - save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist, - sizeof(struct save_hist)*n); - prev_count = n; - qsort(top, n, sizeof(procps_status_t), (void*)mult_lvl_cmp); + free(prev_hist); + prev_hist = new_hist; + prev_hist_count = ntop; } + +#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ + +#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS +/* formats 7 char string (8 with terminating NUL) */ +static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) +{ + unsigned t; + if (value >= total) { /* 100% ? */ + strcpy(pbuf, " 100% "); + return pbuf; + } + /* else generate " [N/space]N.N% " string */ + value = 1000 * value / total; + t = value / 100; + value = value % 100; + pbuf[0] = ' '; + pbuf[1] = t ? t + '0' : ' '; + pbuf[2] = '0' + (value / 10); + pbuf[3] = '.'; + pbuf[4] = '0' + (value % 10); + pbuf[5] = '%'; + pbuf[6] = ' '; + pbuf[7] = '\0'; + return pbuf; +} +#endif + +#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS +static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) +{ + /* + * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% + */ + unsigned total_diff; + jiffy_counts_t *p_jif, *p_prev_jif; + int i; + +#if ENABLE_FEATURE_TOP_SMP_CPU + int n_cpu_lines; +#endif + + /* using (unsigned) casts to make operations cheaper */ +#define CALC_TOT_DIFF ((unsigned)(p_jif->total - p_prev_jif->total) ? : 1) + +#if ENABLE_FEATURE_TOP_DECIMALS +#define CALC_STAT(xxx) char xxx[8] +#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff) +#define FMT "%s" #else -static cmp_t sort_function; -#endif /* FEATURE_CPU_USAGE_PERCENTAGE */ +#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff +#define SHOW_STAT(xxx) xxx +#define FMT "%4u%% " +#endif + +#if !ENABLE_FEATURE_TOP_SMP_CPU + { + i = 1; + p_jif = &cur_jif; + p_prev_jif = &prev_jif; +#else + /* Loop thru CPU(s) */ + n_cpu_lines = smp_cpu_info ? num_cpus : 1; + if (n_cpu_lines > *lines_rem_p) + n_cpu_lines = *lines_rem_p; + + for (i = 0; i < n_cpu_lines; i++) { + p_jif = &cpu_jif[i]; + p_prev_jif = &cpu_prev_jif[i]; +#endif + total_diff = CALC_TOT_DIFF; + + { /* Need a block: CALC_STAT are declarations */ + CALC_STAT(usr); + CALC_STAT(sys); + CALC_STAT(nic); + CALC_STAT(idle); + CALC_STAT(iowait); + CALC_STAT(irq); + CALC_STAT(softirq); + /*CALC_STAT(steal);*/ + + snprintf(scrbuf, scr_width, + /* Barely fits in 79 chars when in "decimals" mode. */ +#if ENABLE_FEATURE_TOP_SMP_CPU + "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", + (smp_cpu_info ? utoa(i) : ""), +#else + "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", +#endif + SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle), + SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq) + /*, SHOW_STAT(steal) - what is this 'steal' thing? */ + /* I doubt anyone wants to know it */ + ); + puts(scrbuf); + } + } +#undef SHOW_STAT +#undef CALC_STAT +#undef FMT + *lines_rem_p -= i; +} +#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ +#define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) +#endif -/* display generic info (meminfo / loadavg) */ -static unsigned long display_generic(void) +static unsigned long display_header(int scr_width, int *lines_rem_p) { FILE *fp; char buf[80]; - float avg1, avg2, avg3; + char scrbuf[80]; unsigned long total, used, mfree, shared, buffers, cached; /* read memory info */ - fp = bb_xfopen("meminfo", "r"); - fgets(buf, sizeof(buf), fp); /* skip first line */ + fp = xfopen_for_read("meminfo"); - if (fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", - &total, &used, &mfree, &shared, &buffers, &cached) != 6) { - bb_error_msg_and_die("failed to read '%s'", "meminfo"); - } - fclose(fp); - - /* read load average */ - fp = bb_xfopen("loadavg", "r"); - if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) { - bb_error_msg_and_die("failed to read '%s'", "loadavg"); + /* + * Old kernels (such as 2.4.x) had a nice summary of memory info that + * we could parse, however this is gone entirely in 2.6. Try parsing + * the old way first, and if that fails, parse each field manually. + * + * First, we read in the first line. Old kernels will have bogus + * strings we don't care about, whereas new kernels will start right + * out with MemTotal: + * -- PFM. + */ + if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) { + fgets(buf, sizeof(buf), fp); /* skip first line */ + + fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", + &total, &used, &mfree, &shared, &buffers, &cached); + /* convert to kilobytes */ + used /= 1024; + mfree /= 1024; + shared /= 1024; + buffers /= 1024; + cached /= 1024; + total /= 1024; + } else { + /* + * Revert to manual parsing, which incidentally already has the + * sizes in kilobytes. This should be safe for both 2.4 and + * 2.6. + */ + fscanf(fp, "MemFree: %lu %s\n", &mfree, buf); + + /* + * MemShared: is no longer present in 2.6. Report this as 0, + * to maintain consistent behavior with normal procps. + */ + if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2) + shared = 0; + + fscanf(fp, "Buffers: %lu %s\n", &buffers, buf); + fscanf(fp, "Cached: %lu %s\n", &cached, buf); + + used = total - mfree; } fclose(fp); - /* convert to kilobytes */ - used /= 1024; - mfree /= 1024; - shared /= 1024; - buffers /= 1024; - cached /= 1024; - - /* output memory info and load average */ + /* output memory info */ + if (scr_width > (int)sizeof(scrbuf)) + scr_width = sizeof(scrbuf); + snprintf(scrbuf, scr_width, + "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", + used, mfree, shared, buffers, cached); /* clear screen & go to top */ - printf("\e[H\e[J" "Mem: " - "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n", - used, mfree, shared, buffers, cached); - printf("Load average: %.2f, %.2f, %.2f " - "(State: S=sleeping R=running, W=waiting)\n", - avg1, avg2, avg3); - return total / 1024; -} + printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf); + (*lines_rem_p)--; + /* Display CPU time split as percentage of total time + * This displays either a cumulative line or one line per CPU + */ + display_cpus(scr_width, scrbuf, lines_rem_p); + + /* read load average as a string */ + buf[0] = '\0'; + open_read_close("loadavg", buf, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\n'; + *strchr(buf, '\n') = '\0'; + snprintf(scrbuf, scr_width, "Load average: %s", buf); + puts(scrbuf); + (*lines_rem_p)--; + + return total; +} -/* display process statuses */ -static void display_status(int count, int col) +static NOINLINE void display_process_list(int lines_rem, int scr_width) { - procps_status_t *s = top; - char rss_str_buf[8]; - unsigned long total_memory = display_generic(); - -#ifdef FEATURE_CPU_USAGE_PERCENTAGE + enum { + BITS_PER_INT = sizeof(int) * 8 + }; + + top_status_t *s; + char vsz_str_buf[8]; + unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ + /* xxx_shift and xxx_scale variables allow us to replace + * expensive divides with multiply and shift */ + unsigned pmem_shift, pmem_scale, pmem_half; +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + unsigned pcpu_shift, pcpu_scale, pcpu_half; + unsigned busy_jifs; +#endif + /* what info of the processes is shown */ - printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n"); + printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, + " PID PPID USER STAT VSZ %MEM" +#if ENABLE_FEATURE_TOP_SMP_PROCESS + " CPU" +#endif +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + " %CPU" +#endif + " COMMAND"); + lines_rem--; + +#if ENABLE_FEATURE_TOP_DECIMALS +#define UPSCALE 1000 +#define CALC_STAT(name, val) div_t name = div((val), 10) +#define SHOW_STAT(name) name.quot, '0'+name.rem +#define FMT "%3u.%c" #else - printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n"); +#define UPSCALE 100 +#define CALC_STAT(name, val) unsigned name = (val) +#define SHOW_STAT(name) name +#define FMT "%4u%%" #endif + /* + * MEM% = s->vsz/MemTotal + */ + pmem_shift = BITS_PER_INT-11; + pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; + /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ + while (pmem_scale >= 512) { + pmem_scale /= 4; + pmem_shift -= 2; + } + pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2); +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + busy_jifs = cur_jif.busy - prev_jif.busy; + /* This happens if there were lots of short-lived processes + * between two top updates (e.g. compilation) */ + if (total_pcpu < busy_jifs) total_pcpu = busy_jifs; - while (count--) { - char *namecmd = s->short_cmd; - int pmem; + /* + * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks + * (pcpu is delta of sys+user time between samples) + */ + /* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are + * in 0..~64000 range (HZ*update_interval). + * we assume that unsigned is at least 32-bit. + */ + pcpu_shift = 6; + pcpu_scale = (UPSCALE*64 * (uint16_t)busy_jifs ? : 1); + while (pcpu_scale < (1U << (BITS_PER_INT-2))) { + pcpu_scale *= 4; + pcpu_shift += 2; + } + pcpu_scale /= ( (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu ? : 1); + /* we want (s->pcpu * pcpu_scale) to never overflow */ + while (pcpu_scale >= 1024) { + pcpu_scale /= 4; + pcpu_shift -= 2; + } + pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2); + /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ +#endif - pmem = 1000.0 * s->rss / total_memory; - if (pmem > 999) pmem = 999; - - if(s->rss > 10*1024) - sprintf(rss_str_buf, "%6ldM", s->rss/1024); + /* Ok, all preliminary data is ready, go through the list */ + scr_width += 2; /* account for leading '\n' and trailing NUL */ + if (lines_rem > ntop) + lines_rem = ntop; + s = top; + while (--lines_rem >= 0) { + unsigned col; + CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); +#endif + + if (s->vsz >= 100000) + sprintf(vsz_str_buf, "%6ldm", s->vsz/1024); else - sprintf(rss_str_buf, "%7ld", s->rss); -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ", -#else - printf("%5d %-8s %s %s %5d %2u.%u ", + sprintf(vsz_str_buf, "%7ld", s->vsz); + /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */ + col = snprintf(line_buf, scr_width, + "\n" "%5u%6u %-8.8s %s%s" FMT +#if ENABLE_FEATURE_TOP_SMP_PROCESS + " %3d" #endif - s->pid, s->user, s->state, rss_str_buf, s->ppid, -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - s->pcpu/10, s->pcpu%10, +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + FMT #endif - pmem/10, pmem%10); - if(strlen(namecmd) > col) - namecmd[col] = 0; - printf("%s\n", namecmd); + " ", + s->pid, s->ppid, get_cached_username(s->uid), + s->state, vsz_str_buf, + SHOW_STAT(pmem) +#if ENABLE_FEATURE_TOP_SMP_PROCESS + , s->last_seen_on_cpu +#endif +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + , SHOW_STAT(pcpu) +#endif + ); + if ((int)(col + 1) < scr_width) + read_cmdline(line_buf + col, scr_width - col - 1, s->pid, s->comm); + fputs(line_buf, stdout); + /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, + cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ s++; } + /* printf(" %d", hist_iterations); */ + bb_putchar(OPT_BATCH_MODE ? '\n' : '\r'); + fflush(stdout); } +#undef UPSCALE +#undef SHOW_STAT +#undef CALC_STAT +#undef FMT static void clearmems(void) { + clear_username_cache(); free(top); - top = 0; + top = NULL; ntop = 0; } -#if defined CONFIG_FEATURE_USE_TERMIOS -#include <termios.h> -#include <sys/time.h> -#include <signal.h> - - -static struct termios initial_settings; +#if ENABLE_FEATURE_USE_TERMIOS static void reset_term(void) { - tcsetattr(0, TCSANOW, (void *) &initial_settings); -#ifdef CONFIG_FEATURE_CLEAN_UP - clearmems(); -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - free(save_history); + tcsetattr_stdin_TCSANOW(&initial_settings); + if (ENABLE_FEATURE_CLEAN_UP) { + clearmems(); +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + free(prev_hist); #endif -#endif /* CONFIG_FEATURE_CLEAN_UP */ + } } - -static void sig_catcher (int sig) + +static void sig_catcher(int sig UNUSED_PARAM) { reset_term(); + exit(EXIT_FAILURE); } -#endif /* CONFIG_FEATURE_USE_TERMIOS */ +#endif /* FEATURE_USE_TERMIOS */ + +/* + * TOPMEM support + */ + +typedef unsigned long mem_t; + +typedef struct topmem_status_t { + unsigned pid; + char comm[COMM_LEN]; + /* vsz doesn't count /dev/xxx mappings except /dev/zero */ + mem_t vsz ; + mem_t vszrw ; + mem_t rss ; + mem_t rss_sh ; + mem_t dirty ; + mem_t dirty_sh; + mem_t stack ; +} topmem_status_t; + +enum { NUM_SORT_FIELD = 7 }; + +#define topmem ((topmem_status_t*)top) +#if ENABLE_FEATURE_TOPMEM -int top_main(int argc, char **argv) +static int topmem_sort(char *a, char *b) { - int opt, interval, lines, col; -#if defined CONFIG_FEATURE_USE_TERMIOS - struct termios new_settings; - struct timeval tv; - fd_set readfds; - unsigned char c; - struct sigaction sa; -#if defined CONFIG_FEATURE_AUTOWIDTH - struct winsize win = { 0, 0, 0, 0 }; -#endif -#endif /* CONFIG_FEATURE_USE_TERMIOS */ - - /* Default update rate is 5 seconds */ - interval = 5; - - /* do normal option parsing */ - while ((opt = getopt(argc, argv, "d:")) > 0) { - switch (opt) { - case 'd': - interval = atoi(optarg); - break; - default: - bb_show_usage(); - } + int n; + mem_t l, r; + + n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t)); + l = *(mem_t*)(a + n); + r = *(mem_t*)(b + n); +// if (l == r) { +// l = a->mapped_rw; +// r = b->mapped_rw; +// } + /* We want to avoid unsigned->signed and truncation errors */ + /* l>r: -1, l=r: 0, l<r: 1 */ + n = (l > r) ? -1 : (l != r); + return inverted ? -n : n; +} + +/* Cut "NNNN " out of " NNNN kb" */ +static char *grab_number(char *str, const char *match, unsigned sz) +{ + if (strncmp(str, match, sz) == 0) { + str = skip_whitespace(str + sz); + (skip_non_whitespace(str))[1] = '\0'; + return xstrdup(str); } + return NULL; +} - /* Default to 25 lines - 5 lines for status */ - lines = 25 - 5; - /* Default CMD format size */ -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - col = 35 - 6; -#else - col = 35; -#endif - /* change to /proc */ - if (chdir("/proc") < 0) { - bb_perror_msg_and_die("chdir('/proc')"); +/* display header info (meminfo / loadavg) */ +static void display_topmem_header(int scr_width, int *lines_rem_p) +{ + char linebuf[128]; + unsigned i; + FILE *fp; + union { + struct { + /* 1 */ char *total; + /* 2 */ char *mfree; + /* 3 */ char *buf; + /* 4 */ char *cache; + /* 5 */ char *swaptotal; + /* 6 */ char *swapfree; + /* 7 */ char *dirty; + /* 8 */ char *mwrite; + /* 9 */ char *anon; + /* 10 */ char *map; + /* 11 */ char *slab; + } u; + char *str[11]; + } Z; +#define total Z.u.total +#define mfree Z.u.mfree +#define buf Z.u.buf +#define cache Z.u.cache +#define swaptotal Z.u.swaptotal +#define swapfree Z.u.swapfree +#define dirty Z.u.dirty +#define mwrite Z.u.mwrite +#define anon Z.u.anon +#define map Z.u.map +#define slab Z.u.slab +#define str Z.str + + memset(&Z, 0, sizeof(Z)); + + /* read memory info */ + fp = xfopen_for_read("meminfo"); + while (fgets(linebuf, sizeof(linebuf), fp)) { + char *p; + +#define SCAN(match, name) \ + p = grab_number(linebuf, match, sizeof(match)-1); \ + if (p) { name = p; continue; } + + SCAN("MemTotal:", total); + SCAN("MemFree:", mfree); + SCAN("Buffers:", buf); + SCAN("Cached:", cache); + SCAN("SwapTotal:", swaptotal); + SCAN("SwapFree:", swapfree); + SCAN("Dirty:", dirty); + SCAN("Writeback:", mwrite); + SCAN("AnonPages:", anon); + SCAN("Mapped:", map); + SCAN("Slab:", slab); +#undef SCAN } -#if defined CONFIG_FEATURE_USE_TERMIOS - tcgetattr(0, (void *) &initial_settings); - memcpy(&new_settings, &initial_settings, sizeof(struct termios)); - new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */ - /* Turn off echoing */ - new_settings.c_lflag &= ~(ECHO | ECHONL); - - signal (SIGTERM, sig_catcher); - sigaction (SIGTERM, (struct sigaction *) 0, &sa); - sa.sa_flags |= SA_RESTART; - sa.sa_flags &= ~SA_INTERRUPT; - sigaction (SIGTERM, &sa, (struct sigaction *) 0); - sigaction (SIGINT, &sa, (struct sigaction *) 0); - tcsetattr(0, TCSANOW, (void *) &new_settings); - atexit(reset_term); -#if defined CONFIG_FEATURE_AUTOWIDTH - ioctl(0, TIOCGWINSZ, &win); - if (win.ws_row > 4) { - lines = win.ws_row - 5; -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - col = win.ws_col - 80 + 35 - 6; + fclose(fp); + +#define S(s) (s ? s : "0 ") + snprintf(linebuf, sizeof(linebuf), + "Mem %stotal %sanon %smap %sfree", + S(total), S(anon), S(map), S(mfree)); + printf(OPT_BATCH_MODE ? "%.*s\n" : "\e[H\e[J%.*s\n", scr_width, linebuf); + + snprintf(linebuf, sizeof(linebuf), + " %sslab %sbuf %scache %sdirty %swrite", + S(slab), S(buf), S(cache), S(dirty), S(mwrite)); + printf("%.*s\n", scr_width, linebuf); + + snprintf(linebuf, sizeof(linebuf), + "Swap %stotal %sfree", // TODO: % used? + S(swaptotal), S(swapfree)); + printf("%.*s\n", scr_width, linebuf); + + (*lines_rem_p) -= 3; +#undef S + + for (i = 0; i < ARRAY_SIZE(str); i++) + free(str[i]); +#undef total +#undef free +#undef buf +#undef cache +#undef swaptotal +#undef swapfree +#undef dirty +#undef write +#undef anon +#undef map +#undef slab +#undef str +} + +static void ulltoa6_and_space(unsigned long long ul, char buf[6]) +{ + /* see http://en.wikipedia.org/wiki/Tera */ + smart_ulltoa5(ul, buf, " mgtpezy"); + buf[5] = ' '; +} + +static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) +{ +#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK" +#define MIN_WIDTH sizeof(HDR_STR) + const topmem_status_t *s = topmem; + + display_topmem_header(scr_width, &lines_rem); + strcpy(line_buf, HDR_STR " COMMAND"); + line_buf[5 + sort_field * 6] = '*'; + printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); + lines_rem--; + + if (lines_rem > ntop) + lines_rem = ntop; + while (--lines_rem >= 0) { + /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ + ulltoa6_and_space(s->pid , &line_buf[0*6]); + ulltoa6_and_space(s->vsz , &line_buf[1*6]); + ulltoa6_and_space(s->vszrw , &line_buf[2*6]); + ulltoa6_and_space(s->rss , &line_buf[3*6]); + ulltoa6_and_space(s->rss_sh , &line_buf[4*6]); + ulltoa6_and_space(s->dirty , &line_buf[5*6]); + ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]); + ulltoa6_and_space(s->stack , &line_buf[7*6]); + line_buf[8*6] = '\0'; + if (scr_width > (int)MIN_WIDTH) { + read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm); + } + printf("\n""%.*s", scr_width, line_buf); + s++; + } + bb_putchar(OPT_BATCH_MODE ? '\n' : '\r'); + fflush(stdout); +#undef HDR_STR +#undef MIN_WIDTH +} + #else - col = win.ws_col - 80 + 35; +void display_topmem_process_list(int lines_rem, int scr_width); +int topmem_sort(char *a, char *b); +#endif /* TOPMEM */ + +/* + * end TOPMEM support + */ + +enum { + TOP_MASK = 0 + | PSSCAN_PID + | PSSCAN_PPID + | PSSCAN_VSZ + | PSSCAN_STIME + | PSSCAN_UTIME + | PSSCAN_STATE + | PSSCAN_COMM +#if ENABLE_FEATURE_TOP_SMP_PROCESS + | PSSCAN_CPU #endif - } + | PSSCAN_UIDGID, + TOPMEM_MASK = 0 + | PSSCAN_PID + | PSSCAN_SMAPS + | PSSCAN_COMM, +}; + +int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int top_main(int argc UNUSED_PARAM, char **argv) +{ + int iterations; + unsigned lines, col; + int lines_rem; + unsigned interval; + char *str_interval, *str_iterations; + SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK; +#if ENABLE_FEATURE_USE_TERMIOS + struct termios new_settings; + struct pollfd pfd[1]; + unsigned char c; + + pfd[0].fd = 0; + pfd[0].events = POLLIN; +#endif /* FEATURE_USE_TERMIOS */ + + INIT_G(); + + interval = 5; /* default update interval is 5 seconds */ + iterations = 0; /* infinite */ +#if ENABLE_FEATURE_TOP_SMP_CPU + /*num_cpus = 0;*/ + /*smp_cpu_info = 0;*/ /* to start with show aggregate */ + cpu_jif = &cur_jif; + cpu_prev_jif = &prev_jif; #endif -#endif /* CONFIG_FEATURE_USE_TERMIOS */ -#ifdef FEATURE_CPU_USAGE_PERCENTAGE + + /* all args are options; -n NUM */ + opt_complementary = "-"; + col = getopt32(argv, "d:n:b", &str_interval, &str_iterations); + if (col & OPT_d) { + /* work around for "-d 1" -> "-d -1" done by getopt32 */ + if (str_interval[0] == '-') + str_interval++; + /* Need to limit it to not overflow poll timeout */ + interval = xatou16(str_interval); + } + if (col & OPT_n) { + if (str_iterations[0] == '-') + str_iterations++; + iterations = xatou(str_iterations); + } + + /* change to /proc */ + xchdir("/proc"); +#if ENABLE_FEATURE_USE_TERMIOS + tcgetattr(0, (void *) &initial_settings); + memcpy(&new_settings, &initial_settings, sizeof(new_settings)); + /* unbuffered input, turn off echo */ + new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); + + bb_signals(BB_FATAL_SIGS, sig_catcher); + tcsetattr_stdin_TCSANOW(&new_settings); +#endif /* FEATURE_USE_TERMIOS */ + +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE sort_function[0] = pcpu_sort; sort_function[1] = mem_sort; sort_function[2] = time_sort; #else - sort_function = mem_sort; -#endif + sort_function[0] = mem_sort; +#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ + while (1) { - /* read process IDs & status for all the processes */ - procps_status_t * p; + procps_status_t *p = NULL; + + lines = 24; /* default */ + col = 79; +#if ENABLE_FEATURE_USE_TERMIOS + /* We output to stdout, we need size of stdout (not stdin)! */ + get_terminal_width_height(STDOUT_FILENO, &col, &lines); + if (lines < 5 || col < 10) { + sleep(interval); + continue; + } +#endif /* FEATURE_USE_TERMIOS */ + if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ + col = LINE_BUF_SIZE-2; -#ifdef CONFIG_SELINUX - while ((p = procps_scan(0, 0, NULL) ) != 0) { -#else - while ((p = procps_scan(0)) != 0) { + /* read process IDs & status for all the processes */ + while ((p = procps_scan(p, scan_mask)) != NULL) { + int n; + if (scan_mask == TOP_MASK) { + n = ntop; + top = xrealloc_vector(top, 6, ntop++); + top[n].pid = p->pid; + top[n].ppid = p->ppid; + top[n].vsz = p->vsz; +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + top[n].ticks = p->stime + p->utime; #endif - int n = ntop; - - top = xrealloc(top, (++ntop)*sizeof(procps_status_t)); - memcpy(top + n, p, sizeof(procps_status_t)); - } + top[n].uid = p->uid; + strcpy(top[n].state, p->state); + strcpy(top[n].comm, p->comm); +#if ENABLE_FEATURE_TOP_SMP_PROCESS + top[n].last_seen_on_cpu = p->last_seen_on_cpu; +#endif + } else { /* TOPMEM */ +#if ENABLE_FEATURE_TOPMEM + if (!(p->mapped_ro | p->mapped_rw)) + continue; /* kernel threads are ignored */ + n = ntop; + /* No bug here - top and topmem are the same */ + top = xrealloc_vector(topmem, 6, ntop++); + strcpy(topmem[n].comm, p->comm); + topmem[n].pid = p->pid; + topmem[n].vsz = p->mapped_rw + p->mapped_ro; + topmem[n].vszrw = p->mapped_rw; + topmem[n].rss_sh = p->shared_clean + p->shared_dirty; + topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; + topmem[n].dirty = p->private_dirty + p->shared_dirty; + topmem[n].dirty_sh = p->shared_dirty; + topmem[n].stack = p->stack; +#endif + } + } /* end of "while we read /proc" */ if (ntop == 0) { - bb_perror_msg_and_die("scandir('/proc')"); - } -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - if(!Hertz) { - init_Hertz_value(); + bb_error_msg("no process info in /proc"); + break; + } + + if (scan_mask == TOP_MASK) { +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + if (!prev_hist_count) { + do_stats(); + usleep(100000); + clearmems(); + continue; + } do_stats(); - sleep(1); - clearmems(); - continue; - } - do_stats(); + /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */ + qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp); #else - qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function); + qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0])); +#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ + } +#if ENABLE_FEATURE_TOPMEM + else { /* TOPMEM */ + qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); + } #endif - opt = lines; - if (opt > ntop) { - opt = ntop; + lines_rem = lines; + if (OPT_BATCH_MODE) { + lines_rem = INT_MAX; } - /* show status for each of the processes */ - display_status(opt, col); -#if defined CONFIG_FEATURE_USE_TERMIOS - tv.tv_sec = interval; - tv.tv_usec = 0; - FD_ZERO (&readfds); - FD_SET (0, &readfds); - select (1, &readfds, NULL, NULL, &tv); - if (FD_ISSET (0, &readfds)) { - if (read (0, &c, 1) <= 0) { /* signal */ - return EXIT_FAILURE; - } - if(c == 'q' || c == initial_settings.c_cc[VINTR]) - return EXIT_SUCCESS; - if(c == 'M') { -#ifdef FEATURE_CPU_USAGE_PERCENTAGE + if (scan_mask == TOP_MASK) + display_process_list(lines_rem, col); +#if ENABLE_FEATURE_TOPMEM + else + display_topmem_process_list(lines_rem, col); +#endif + clearmems(); + if (iterations >= 0 && !--iterations) + break; +#if !ENABLE_FEATURE_USE_TERMIOS + sleep(interval); +#else + if (option_mask32 & (OPT_b|OPT_EOF)) + /* batch mode, or EOF on stdin ("top </dev/null") */ + sleep(interval); + else if (safe_poll(pfd, 1, interval * 1000) > 0) { + if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ + option_mask32 |= OPT_EOF; + continue; + } + if (c == initial_settings.c_cc[VINTR]) + break; + c |= 0x20; /* lowercase */ + if (c == 'q') + break; + if (c == 'n') { + USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) + sort_function[0] = pid_sort; + } + if (c == 'm') { + USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) sort_function[0] = mem_sort; +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE sort_function[1] = pcpu_sort; sort_function[2] = time_sort; -#else - sort_function = mem_sort; #endif } -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - if(c == 'P') { +#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + if (c == 'p') { + USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) sort_function[0] = pcpu_sort; sort_function[1] = mem_sort; sort_function[2] = time_sort; } - if(c == 'T') { + if (c == 't') { + USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;) sort_function[0] = time_sort; sort_function[1] = mem_sort; sort_function[2] = pcpu_sort; } +#if ENABLE_FEATURE_TOPMEM + if (c == 's') { + scan_mask = TOPMEM_MASK; + free(prev_hist); + prev_hist = NULL; + prev_hist_count = 0; + sort_field = (sort_field + 1) % NUM_SORT_FIELD; + } + if (c == 'r') + inverted ^= 1; #endif - if(c == 'N') { -#ifdef FEATURE_CPU_USAGE_PERCENTAGE - sort_function[0] = pid_sort; -#else - sort_function = pid_sort; -#endif +#if ENABLE_FEATURE_TOP_SMP_CPU + /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ + if (c == 'c' || c == '1') { + /* User wants to toggle per cpu <> aggregate */ + if (smp_cpu_info) { + free(cpu_prev_jif); + free(cpu_jif); + cpu_jif = &cur_jif; + cpu_prev_jif = &prev_jif; + } else { + /* Prepare for xrealloc() */ + cpu_jif = cpu_prev_jif = NULL; + } + num_cpus = 0; + smp_cpu_info = !smp_cpu_info; + get_jiffy_counts(); } +#endif +#endif } -#else - sleep(interval); -#endif /* CONFIG_FEATURE_USE_TERMIOS */ - clearmems(); - } - +#endif /* FEATURE_USE_TERMIOS */ + } /* end of "while (1)" */ + + bb_putchar('\n'); +#if ENABLE_FEATURE_USE_TERMIOS + reset_term(); +#endif return EXIT_SUCCESS; } diff --git a/release/src/router/busybox/procps/uptime.c b/release/src/router/busybox/procps/uptime.c index a974313d..d9aa1d03 100644 --- a/release/src/router/busybox/procps/uptime.c +++ b/release/src/router/busybox/procps/uptime.c @@ -2,22 +2,9 @@ /* * Mini uptime implementation for busybox * - * Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> * + * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ /* This version of uptime doesn't display the number of users on the system, @@ -28,20 +15,18 @@ /* getopt not needed */ +#include "libbb.h" -#include <stdio.h> -#include <time.h> -#include <errno.h> -#include <stdlib.h> -#include "busybox.h" - -static const int FSHIFT = 16; /* nr of bits of precision */ +#ifndef FSHIFT +# define FSHIFT 16 /* nr of bits of precision */ +#endif #define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */ #define LOAD_INT(x) ((x) >> FSHIFT) #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) -extern int uptime_main(int argc, char **argv) +int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int updays, uphours, upminutes; struct sysinfo info; @@ -53,23 +38,22 @@ extern int uptime_main(int argc, char **argv) sysinfo(&info); - printf(" %2d:%02d%s up ", - current_time->tm_hour%12 ? current_time->tm_hour%12 : 12, - current_time->tm_min, current_time->tm_hour > 11 ? "pm" : "am"); + printf(" %02d:%02d:%02d up ", + current_time->tm_hour, current_time->tm_min, current_time->tm_sec); updays = (int) info.uptime / (60*60*24); if (updays) printf("%d day%s, ", updays, (updays != 1) ? "s" : ""); upminutes = (int) info.uptime / 60; uphours = (upminutes / 60) % 24; upminutes %= 60; - if(uphours) + if (uphours) printf("%2d:%02d, ", uphours, upminutes); else printf("%d min, ", upminutes); - printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n", - LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]), - LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]), + printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n", + LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]), + LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]), LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2])); return EXIT_SUCCESS; diff --git a/release/src/router/busybox/procps/watch.c b/release/src/router/busybox/procps/watch.c new file mode 100644 index 00000000..5fd05107 --- /dev/null +++ b/release/src/router/busybox/procps/watch.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini watch implementation for busybox + * + * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de> + * Copyrigjt (C) Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A */ +/* BB_AUDIT GNU defects -- only option -n is supported. */ + +#include "libbb.h" + +// procps 2.0.18: +// watch [-d] [-n seconds] +// [--differences[=cumulative]] [--interval=seconds] command +// +// procps-3.2.3: +// watch [-dt] [-n seconds] +// [--differences[=cumulative]] [--interval=seconds] [--no-title] command +// +// (procps 3.x and procps 2.x are forks, not newer/older versions of the same) + +int watch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int watch_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opt; + unsigned period = 2; + unsigned width, new_width; + char *header; + char *cmd; + + opt_complementary = "-1:n+"; // at least one param; -n NUM + // "+": stop at first non-option (procps 3.x only) + opt = getopt32(argv, "+dtn:", &period); + argv += optind; + + // watch from both procps 2.x and 3.x does concatenation. Example: + // watch ls -l "a /tmp" "2>&1" -- ls won't see "a /tmp" as one param + cmd = *argv; + while (*++argv) + cmd = xasprintf("%s %s", cmd, *argv); // leaks cmd + + width = (unsigned)-1; // make sure first time new_width != width + header = NULL; + while (1) { + printf("\033[H\033[J"); + if (!(opt & 0x2)) { // no -t + const unsigned time_len = sizeof("1234-67-90 23:56:89"); + time_t t; + + get_terminal_width_height(STDIN_FILENO, &new_width, NULL); + if (new_width != width) { + width = new_width; + free(header); + header = xasprintf("Every %us: %-*s", period, (int)width, cmd); + } + time(&t); + if (time_len < width) + strftime(header + width - time_len, time_len, + "%Y-%m-%d %H:%M:%S", localtime(&t)); + + puts(header); + } + fflush(stdout); + // TODO: 'real' watch pipes cmd's output to itself + // and does not allow it to overflow the screen + // (taking into account linewrap!) + system(cmd); + sleep(period); + } + return 0; // gcc thinks we can reach this :) +} |