summaryrefslogtreecommitdiff
path: root/release/src/router/busybox/procps
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/router/busybox/procps')
-rw-r--r--[-rwxr-xr-x]release/src/router/busybox/procps/Config.in158
-rw-r--r--release/src/router/busybox/procps/Kbuild21
-rw-r--r--release/src/router/busybox/procps/Makefile30
-rwxr-xr-xrelease/src/router/busybox/procps/Makefile.in38
-rw-r--r--release/src/router/busybox/procps/free.c38
-rw-r--r--release/src/router/busybox/procps/fuser.c345
-rw-r--r--release/src/router/busybox/procps/kill.c278
-rw-r--r--release/src/router/busybox/procps/nmeter.c910
-rw-r--r--release/src/router/busybox/procps/pgrep.c144
-rw-r--r--release/src/router/busybox/procps/pidof.c115
-rw-r--r--release/src/router/busybox/procps/ps.c623
-rw-r--r--release/src/router/busybox/procps/ps.posix175
-rw-r--r--release/src/router/busybox/procps/renice.c152
-rw-r--r--release/src/router/busybox/procps/sysctl.c258
-rw-r--r--release/src/router/busybox/procps/top.c1419
-rw-r--r--release/src/router/busybox/procps/uptime.c44
-rw-r--r--release/src/router/busybox/procps/watch.c75
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 :)
+}