[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit
Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/busybox/src/procps/Config.src b/ap/app/busybox/src/procps/Config.src
new file mode 100644
index 0000000..527d9ee
--- /dev/null
+++ b/ap/app/busybox/src/procps/Config.src
@@ -0,0 +1,158 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Process Utilities"
+
+INSERT
+
+config FREE
+ bool "free"
+ default y
+ select PLATFORM_LINUX #sysinfo()
+ help
+ free displays the total amount of free and used physical and swap
+ memory in the system, as well as the buffers used by the kernel.
+ The shared memory column should be ignored; it is obsolete.
+
+config FUSER
+ bool "fuser"
+ default y
+ 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 y
+ help
+ The command kill sends the specified signal to the specified
+ process or process group. If no signal is specified, the TERM
+ signal is sent.
+
+config KILLALL
+ bool "killall"
+ default y
+ 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
+ sent.
+
+config KILLALL5
+ bool "killall5"
+ default y
+ depends on KILL
+
+config PGREP
+ bool "pgrep"
+ default y
+ help
+ Look for processes by name.
+
+config PIDOF
+ bool "pidof"
+ default y
+ help
+ Pidof finds the process id's (pids) of the named programs. It prints
+ those id's on the standard output.
+
+config FEATURE_PIDOF_SINGLE
+ bool "Enable argument for single shot (-s)"
+ default y
+ 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 y
+ 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 y
+ help
+ Send signals to processes by name.
+
+config PS
+ bool "ps"
+ default y
+ help
+ ps gives a snapshot of the current processes.
+
+config FEATURE_PS_WIDE
+ bool "Enable wide output option (-w)"
+ default y
+ depends on PS && !DESKTOP
+ help
+ Support argument 'w' for wide output.
+ If given once, 132 chars are printed, and if given more
+ than once, the length is unlimited.
+
+config FEATURE_PS_LONG
+ bool "Enable long output option (-l)"
+ default y
+ depends on PS && !DESKTOP
+ help
+ Support argument 'l' for long output.
+ Adds fields PPID, RSS, START, TIME & TTY
+
+config FEATURE_PS_TIME
+ bool "Enable time and elapsed time output"
+ default y
+ depends on PS && DESKTOP
+ select PLATFORM_LINUX
+ help
+ Support -o time and -o etime output specifiers.
+
+config FEATURE_PS_ADDITIONAL_COLUMNS
+ bool "Enable additional ps columns"
+ default y
+ depends on PS && DESKTOP
+ help
+ Support -o rgroup, -o ruser, -o nice 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 y
+ help
+ Renice alters the scheduling priority of one or more running
+ processes.
+
+config BB_SYSCTL
+ bool "sysctl"
+ default y
+ help
+ Configure kernel parameters at runtime.
+
+config FEATURE_SHOW_THREADS
+ bool "Support for showing threads in ps/pstree/top"
+ default y
+ depends on PS || TOP || PSTREE
+ help
+ Enables the ps -T option, showing of threads in pstree,
+ and 'h' command in top.
+
+config WATCH
+ bool "watch"
+ default y
+ help
+ watch is used to execute a program periodically, showing
+ output to the screen.
+
+endmenu
diff --git a/ap/app/busybox/src/procps/Kbuild.src b/ap/app/busybox/src/procps/Kbuild.src
new file mode 100644
index 0000000..89b1cc0
--- /dev/null
+++ b/ap/app/busybox/src/procps/Kbuild.src
@@ -0,0 +1,22 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+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_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/ap/app/busybox/src/procps/free.c b/ap/app/busybox/src/procps/free.c
new file mode 100644
index 0000000..73a69c6
--- /dev/null
+++ b/ap/app/busybox/src/procps/free.c
@@ -0,0 +1,135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini free implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* getopt not needed */
+
+//usage:#define free_trivial_usage
+//usage: "" IF_DESKTOP("[-b/k/m/g]")
+//usage:#define free_full_usage "\n\n"
+//usage: "Display the amount of free and used system memory"
+//usage:
+//usage:#define free_example_usage
+//usage: "$ free\n"
+//usage: " total used free shared buffers\n"
+//usage: " Mem: 257628 248724 8904 59644 93124\n"
+//usage: " Swap: 128516 8404 120112\n"
+//usage: "Total: 386144 257128 129016\n"
+
+#include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
+struct globals {
+ unsigned mem_unit;
+#if ENABLE_DESKTOP
+ uint8_t unit_steps;
+# define G_unit_steps g->unit_steps
+#else
+# define G_unit_steps 10
+#endif
+};
+/* Because of NOFORK, "globals" are not in global data */
+
+static unsigned long long scale(struct globals *g, unsigned long d)
+{
+ return ((unsigned long long)d * g->mem_unit) >> G_unit_steps;
+}
+
+/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */
+static NOINLINE unsigned long parse_cached_kb(void)
+{
+ char buf[60]; /* actual lines we expect are ~30 chars or less */
+ FILE *fp;
+ unsigned long cached = 0;
+
+ fp = xfopen_for_read("/proc/meminfo");
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (sscanf(buf, "Cached: %lu %*s\n", &cached) == 1)
+ break;
+ }
+ /* Have to close because of NOFORK */
+ fclose(fp);
+
+ return cached;
+}
+
+int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
+{
+ struct globals G;
+ struct sysinfo info;
+ unsigned long long cached;
+
+#if ENABLE_DESKTOP
+ G.unit_steps = 10;
+ if (argv[1] && argv[1][0] == '-') {
+ switch (argv[1][1]) {
+ case 'b':
+ G.unit_steps = 0;
+ break;
+ case 'k': /* 2^10 */
+ /* G.unit_steps = 10; - already is */
+ break;
+ case 'm': /* 2^(2*10) */
+ G.unit_steps = 20;
+ break;
+ case 'g': /* 2^(3*10) */
+ G.unit_steps = 30;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+#endif
+ printf(" %11s%11s%11s%11s%11s%11s\n"
+ "Mem: ",
+ "total",
+ "used",
+ "free",
+ "shared", "buffers", "cached" /* swap and total don't have these columns */
+ );
+
+ sysinfo(&info);
+ /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
+ G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
+ /* Extract cached from /proc/meminfo and convert to mem_units */
+ cached = ((unsigned long long) parse_cached_kb() * 1024) / G.mem_unit;
+
+#define FIELDS_6 "%11llu%11llu%11llu%11llu%11llu%11llu\n"
+#define FIELDS_3 (FIELDS_6 + 3*6)
+#define FIELDS_2 (FIELDS_6 + 4*6)
+
+ printf(FIELDS_6,
+ scale(&G, info.totalram), //total
+ scale(&G, info.totalram - info.freeram), //used
+ scale(&G, info.freeram), //free
+ scale(&G, info.sharedram), //shared
+ scale(&G, info.bufferram), //buffers
+ scale(&G, cached) //cached
+ );
+ /* Show alternate, more meaningful busy/free numbers by counting
+ * buffer cache as free memory. */
+ printf("-/+ buffers/cache:");
+ cached += info.freeram;
+ cached += info.bufferram;
+ printf(FIELDS_2,
+ scale(&G, info.totalram - cached), //used
+ scale(&G, cached) //free
+ );
+#if BB_MMU
+ printf("Swap: ");
+ printf(FIELDS_3,
+ scale(&G, info.totalswap), //total
+ scale(&G, info.totalswap - info.freeswap), //used
+ scale(&G, info.freeswap) //free
+ );
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/fuser.c b/ap/app/busybox/src/procps/fuser.c
new file mode 100644
index 0000000..05b52ab
--- /dev/null
+++ b/ap/app/busybox/src/procps/fuser.c
@@ -0,0 +1,321 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny fuser implementation
+ *
+ * Copyright 2004 Tony J. White
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define fuser_trivial_usage
+//usage: "[OPTIONS] FILE or PORT/PROTO"
+//usage:#define fuser_full_usage "\n\n"
+//usage: "Find processes which use FILEs or PORTs\n"
+//usage: "\n -m Find processes which use same fs as FILEs"
+//usage: "\n -4,-6 Search only IPv4/IPv6 space"
+//usage: "\n -s Don't display PIDs"
+//usage: "\n -k Kill found processes"
+//usage: "\n -SIGNAL Signal to send (default: KILL)"
+
+#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;
+
+struct globals {
+ int recursion_depth;
+ pid_t mypid;
+ inode_list *inode_list_head;
+ smallint kill_failed;
+ int killsig;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+ G.mypid = getpid(); \
+ G.killsig = SIGKILL; \
+} while (0)
+
+static void add_inode(const struct stat *st)
+{
+ inode_list **curr = &G.inode_list_head;
+
+ while (*curr) {
+ if ((*curr)->dev == st->st_dev
+ && (*curr)->inode == st->st_ino
+ ) {
+ return;
+ }
+ curr = &(*curr)->next;
+ }
+
+ *curr = xzalloc(sizeof(inode_list));
+ (*curr)->dev = st->st_dev;
+ (*curr)->inode = st->st_ino;
+}
+
+static smallint search_dev_inode(const struct stat *st)
+{
+ inode_list *ilist = G.inode_list_head;
+
+ while (ilist) {
+ if (ilist->dev == st->st_dev) {
+ if (option_mask32 & OPT_MOUNT)
+ return 1;
+ if (ilist->inode == st->st_ino)
+ return 1;
+ }
+ ilist = ilist->next;
+ }
+ return 0;
+}
+
+enum {
+ PROC_NET = 0,
+ PROC_DIR,
+ PROC_DIR_LINKS,
+ PROC_SUBDIR_LINKS,
+};
+
+static smallint scan_proc_net_or_maps(const char *path, unsigned port)
+{
+ FILE *f;
+ char line[MAX_LINE + 1], addr[68];
+ int major, minor, r;
+ long long uint64_inode;
+ unsigned tmp_port;
+ smallint retval;
+ struct stat statbuf;
+ const char *fmt;
+ void *fag, *sag;
+
+ f = fopen_for_read(path);
+ if (!f)
+ return 0;
+
+ if (G.recursion_depth == PROC_NET) {
+ int fd;
+
+ /* find socket dev */
+ statbuf.st_dev = 0;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd >= 0) {
+ fstat(fd, &statbuf);
+ close(fd);
+ }
+
+ fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
+ "%*x:%*x %*x:%*x %*x %*d %*d %llu";
+ fag = addr;
+ sag = &tmp_port;
+ } else {
+ fmt = "%*s %*s %*s %x:%x %llu";
+ fag = &major;
+ sag = &minor;
+ }
+
+ retval = 0;
+ while (fgets(line, MAX_LINE, f)) {
+ r = sscanf(line, fmt, fag, sag, &uint64_inode);
+ if (r != 3)
+ continue;
+
+ statbuf.st_ino = uint64_inode;
+ if (G.recursion_depth == PROC_NET) {
+ r = strlen(addr);
+ if (r == 8 && (option_mask32 & OPT_IP6))
+ continue;
+ if (r > 8 && (option_mask32 & OPT_IP4))
+ continue;
+ if (tmp_port == port)
+ add_inode(&statbuf);
+ } else {
+ if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
+ statbuf.st_dev = makedev(major, minor);
+ retval = search_dev_inode(&statbuf);
+ if (retval)
+ break;
+ }
+ }
+ }
+ fclose(f);
+
+ return retval;
+}
+
+static smallint scan_recursive(const char *path)
+{
+ DIR *d;
+ struct dirent *d_ent;
+ smallint stop_scan;
+ smallint retval;
+
+ d = opendir(path);
+ if (d == NULL)
+ return 0;
+
+ G.recursion_depth++;
+ retval = 0;
+ stop_scan = 0;
+ while (!stop_scan && (d_ent = readdir(d)) != NULL) {
+ struct stat statbuf;
+ pid_t pid;
+ char *subpath;
+
+ subpath = concat_subpath_file(path, d_ent->d_name);
+ if (subpath == NULL)
+ continue; /* . or .. */
+
+ switch (G.recursion_depth) {
+ case PROC_DIR:
+ pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
+ if (errno != 0
+ || pid == G.mypid
+ /* "this PID doesn't use specified FILEs or PORT/PROTO": */
+ || scan_recursive(subpath) == 0
+ ) {
+ break;
+ }
+ if (option_mask32 & OPT_KILL) {
+ if (kill(pid, G.killsig) != 0) {
+ bb_perror_msg("kill pid %s", d_ent->d_name);
+ G.kill_failed = 1;
+ }
+ }
+ if (!(option_mask32 & OPT_SILENT))
+ printf("%s ", d_ent->d_name);
+ retval = 1;
+ break;
+
+ case PROC_DIR_LINKS:
+ switch (
+ index_in_substrings(
+ "cwd" "\0" "exe" "\0"
+ "root" "\0" "fd" "\0"
+ "lib" "\0" "mmap" "\0"
+ "maps" "\0",
+ d_ent->d_name
+ )
+ ) {
+ enum {
+ CWD_LINK,
+ EXE_LINK,
+ ROOT_LINK,
+ FD_DIR_LINKS,
+ LIB_DIR_LINKS,
+ MMAP_DIR_LINKS,
+ MAPS,
+ };
+ case CWD_LINK:
+ case EXE_LINK:
+ case ROOT_LINK:
+ goto scan_link;
+ case FD_DIR_LINKS:
+ case LIB_DIR_LINKS:
+ case MMAP_DIR_LINKS:
+ stop_scan = scan_recursive(subpath);
+ if (stop_scan)
+ retval = stop_scan;
+ break;
+ case MAPS:
+ stop_scan = scan_proc_net_or_maps(subpath, 0);
+ if (stop_scan)
+ retval = stop_scan;
+ default:
+ break;
+ }
+ break;
+ case PROC_SUBDIR_LINKS:
+ scan_link:
+ if (stat(subpath, &statbuf) < 0)
+ break;
+ stop_scan = search_dev_inode(&statbuf);
+ if (stop_scan)
+ retval = stop_scan;
+ default:
+ break;
+ }
+ free(subpath);
+ }
+ closedir(d);
+ G.recursion_depth--;
+ return retval;
+}
+
+int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fuser_main(int argc UNUSED_PARAM, char **argv)
+{
+ char **pp;
+
+ INIT_G();
+
+ /* Handle -SIGNAL. Oh my... */
+ pp = argv;
+ while (*++pp) {
+ int sig;
+ 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" */
+ sig = get_signum(&arg[1]);
+ if (sig < 0)
+ continue;
+ /* "-SIGNAL" option found. Remove it and bail out */
+ G.killsig = sig;
+ do {
+ pp[0] = arg = pp[1];
+ pp++;
+ } while (arg);
+ break;
+ }
+
+ opt_complementary = "-1"; /* at least one param */
+ getopt32(argv, OPTION_STRING);
+ argv += optind;
+
+ pp = argv;
+ while (*pp) {
+ /* parse net arg */
+ unsigned port;
+ char path[sizeof("/proc/net/TCP6")];
+
+ strcpy(path, "/proc/net/");
+ if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
+ && access(path, R_OK) == 0
+ ) {
+ /* PORT/PROTO */
+ scan_proc_net_or_maps(path, port);
+ } else {
+ /* FILE */
+ struct stat statbuf;
+ xstat(*pp, &statbuf);
+ add_inode(&statbuf);
+ }
+ pp++;
+ }
+
+ if (scan_recursive("/proc")) {
+ if (!(option_mask32 & OPT_SILENT))
+ bb_putchar('\n');
+ return G.kill_failed;
+ }
+
+ return EXIT_FAILURE;
+}
diff --git a/ap/app/busybox/src/procps/iostat.c b/ap/app/busybox/src/procps/iostat.c
new file mode 100644
index 0000000..978d234
--- /dev/null
+++ b/ap/app/busybox/src/procps/iostat.c
@@ -0,0 +1,535 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Report CPU and I/O stats, based on sysstat version 9.1.2 by Sebastien Godard
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config IOSTAT
+//config: bool "iostat"
+//config: default y
+//config: help
+//config: Report CPU and I/O statistics
+
+//applet:IF_IOSTAT(APPLET(iostat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IOSTAT) += iostat.o
+
+#include "libbb.h"
+#include <sys/utsname.h> /* struct utsname */
+
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
+
+#define MAX_DEVICE_NAME 12
+#define MAX_DEVICE_NAME_STR "12"
+
+#if 1
+typedef unsigned long long cputime_t;
+typedef long long icputime_t;
+# define FMT_DATA "ll"
+# define CPUTIME_MAX (~0ULL)
+#else
+typedef unsigned long cputime_t;
+typedef long icputime_t;
+# define FMT_DATA "l"
+# define CPUTIME_MAX (~0UL)
+#endif
+
+enum {
+ STATS_CPU_USER,
+ STATS_CPU_NICE,
+ STATS_CPU_SYSTEM,
+ STATS_CPU_IDLE,
+ STATS_CPU_IOWAIT,
+ STATS_CPU_IRQ,
+ STATS_CPU_SOFTIRQ,
+ STATS_CPU_STEAL,
+ STATS_CPU_GUEST,
+
+ GLOBAL_UPTIME,
+ SMP_UPTIME,
+
+ N_STATS_CPU,
+};
+
+typedef struct {
+ cputime_t vector[N_STATS_CPU];
+} stats_cpu_t;
+
+typedef struct {
+ stats_cpu_t *prev;
+ stats_cpu_t *curr;
+ cputime_t itv;
+} stats_cpu_pair_t;
+
+typedef struct {
+ unsigned long long rd_sectors;
+ unsigned long long wr_sectors;
+ unsigned long rd_ops;
+ unsigned long wr_ops;
+} stats_dev_data_t;
+
+typedef struct stats_dev {
+ struct stats_dev *next;
+ char dname[MAX_DEVICE_NAME + 1];
+ stats_dev_data_t prev_data;
+ stats_dev_data_t curr_data;
+} stats_dev_t;
+
+/* Globals. Sort by size and access frequency. */
+struct globals {
+ smallint show_all;
+ unsigned total_cpus; /* Number of CPUs */
+ unsigned clk_tck; /* Number of clock ticks per second */
+ llist_t *dev_name_list; /* List of devices entered on the command line */
+ stats_dev_t *stats_dev_list;
+ struct tm tmtime;
+ struct {
+ const char *str;
+ unsigned div;
+ } unit;
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+ G.unit.str = "Blk"; \
+ G.unit.div = 1; \
+} while (0)
+
+/* Must match option string! */
+enum {
+ OPT_c = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_t = 1 << 2,
+ OPT_z = 1 << 3,
+ OPT_k = 1 << 4,
+ OPT_m = 1 << 5,
+};
+
+static ALWAYS_INLINE unsigned get_user_hz(void)
+{
+ return sysconf(_SC_CLK_TCK);
+}
+
+static ALWAYS_INLINE int this_is_smp(void)
+{
+ return (G.total_cpus > 1);
+}
+
+static void print_header(void)
+{
+ char buf[32];
+ struct utsname uts;
+
+ uname(&uts); /* never fails */
+
+ /* Date representation for the current locale */
+ strftime(buf, sizeof(buf), "%x", &G.tmtime);
+
+ printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n",
+ uts.sysname, uts.release, uts.nodename,
+ buf, uts.machine, G.total_cpus);
+}
+
+static void get_localtime(struct tm *ptm)
+{
+ time_t timer;
+ time(&timer);
+ localtime_r(&timer, ptm);
+}
+
+static void print_timestamp(void)
+{
+ char buf[64];
+ /* %x: date representation for the current locale */
+ /* %X: time representation for the current locale */
+ strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
+ printf("%s\n", buf);
+}
+
+static cputime_t get_smp_uptime(void)
+{
+ FILE *fp;
+ unsigned long sec, dec;
+
+ fp = xfopen_for_read("/proc/uptime");
+
+ if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2)
+ bb_error_msg_and_die("can't read '%s'", "/proc/uptime");
+
+ fclose(fp);
+
+ return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
+}
+
+/* Fetch CPU statistics from /proc/stat */
+static void get_cpu_statistics(stats_cpu_t *sc)
+{
+ FILE *fp;
+ char buf[1024];
+
+ fp = xfopen_for_read("/proc/stat");
+
+ memset(sc, 0, sizeof(*sc));
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ int i;
+ char *ibuf;
+
+ /* Does the line start with "cpu "? */
+ if (!starts_with_cpu(buf) || buf[3] != ' ') {
+ continue;
+ }
+ ibuf = buf + 4;
+ for (i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) {
+ ibuf = skip_whitespace(ibuf);
+ sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]);
+ if (i != STATS_CPU_GUEST) {
+ sc->vector[GLOBAL_UPTIME] += sc->vector[i];
+ }
+ ibuf = skip_non_whitespace(ibuf);
+ }
+ break;
+ }
+
+ if (this_is_smp()) {
+ sc->vector[SMP_UPTIME] = get_smp_uptime();
+ }
+
+ fclose(fp);
+}
+
+static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new)
+{
+ cputime_t itv = new - old;
+
+ return (itv == 0) ? 1 : itv;
+}
+
+#if CPUTIME_MAX > 0xffffffff
+/*
+ * Handle overflow conditions properly for counters which can have
+ * less bits than cputime_t, depending on the kernel version.
+ */
+/* Surprisingly, on 32bit inlining is a size win */
+static ALWAYS_INLINE cputime_t overflow_safe_sub(cputime_t prev, cputime_t curr)
+{
+ cputime_t v = curr - prev;
+
+ if ((icputime_t)v < 0 /* curr < prev - counter overflow? */
+ && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */
+ ) {
+ /* Add 33th bit set to 1 to curr, compensating for the overflow */
+ /* double shift defeats "warning: left shift count >= width of type" */
+ v += ((cputime_t)1 << 16) << 16;
+ }
+ return v;
+}
+#else
+static ALWAYS_INLINE cputime_t overflow_safe_sub(cputime_t prev, cputime_t curr)
+{
+ return curr - prev;
+}
+#endif
+
+static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv)
+{
+ return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
+}
+
+static void print_stats_cpu_struct(stats_cpu_pair_t *stats)
+{
+ cputime_t *p = stats->prev->vector;
+ cputime_t *c = stats->curr->vector;
+ printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+ percent_value(p[STATS_CPU_USER] , c[STATS_CPU_USER] , stats->itv),
+ percent_value(p[STATS_CPU_NICE] , c[STATS_CPU_NICE] , stats->itv),
+ percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ],
+ c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv),
+ percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv),
+ percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv),
+ percent_value(p[STATS_CPU_IDLE] , c[STATS_CPU_IDLE] , stats->itv)
+ );
+}
+
+static void cpu_report(stats_cpu_pair_t *stats)
+{
+ /* Always print a header */
+ puts("avg-cpu: %user %nice %system %iowait %steal %idle");
+
+ /* Print current statistics */
+ print_stats_cpu_struct(stats);
+}
+
+static void print_stats_dev_struct(stats_dev_t *stats_dev, cputime_t itv)
+{
+ stats_dev_data_t *p = &stats_dev->prev_data;
+ stats_dev_data_t *c = &stats_dev->curr_data;
+ if (option_mask32 & OPT_z)
+ if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops)
+ return;
+
+ printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu\n",
+ stats_dev->dname,
+ percent_value(p->rd_ops + p->wr_ops, c->rd_ops + c->wr_ops, itv),
+ percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div,
+ percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div,
+ (c->rd_sectors - p->rd_sectors) / G.unit.div,
+ (c->wr_sectors - p->wr_sectors) / G.unit.div
+ );
+}
+
+static void print_devstat_header(void)
+{
+ printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n",
+ "tps",
+ G.unit.str, "_read", G.unit.str, "_wrtn",
+ G.unit.str, "_read", G.unit.str, "_wrtn"
+ );
+}
+
+/*
+ * Is input partition of format [sdaN]?
+ */
+static int is_partition(const char *dev)
+{
+ /* Ok, this is naive... */
+ return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]);
+}
+
+static stats_dev_t *stats_dev_find_or_new(const char *dev_name)
+{
+ stats_dev_t **curr = &G.stats_dev_list;
+
+ while (*curr != NULL) {
+ if (strcmp((*curr)->dname, dev_name) == 0)
+ return *curr;
+ curr = &(*curr)->next;
+ }
+
+ *curr = xzalloc(sizeof(stats_dev_t));
+ strncpy((*curr)->dname, dev_name, MAX_DEVICE_NAME);
+ return *curr;
+}
+
+static void stats_dev_free(stats_dev_t *stats_dev)
+{
+ if (stats_dev) {
+ stats_dev_free(stats_dev->next);
+ free(stats_dev);
+ }
+}
+
+static void do_disk_statistics(cputime_t itv)
+{
+ char buf[128];
+ char dev_name[MAX_DEVICE_NAME + 1];
+ unsigned long long rd_sec_or_dummy;
+ unsigned long long wr_sec_or_dummy;
+ stats_dev_data_t *curr_data;
+ stats_dev_t *stats_dev;
+ FILE *fp;
+ int rc;
+
+ fp = xfopen_for_read("/proc/diskstats");
+ /* Read and possibly print stats from /proc/diskstats */
+ while (fgets(buf, sizeof(buf), fp)) {
+ sscanf(buf, "%*s %*s %"MAX_DEVICE_NAME_STR"s", dev_name);
+ if (G.dev_name_list) {
+ /* Is device name in list? */
+ if (!llist_find_str(G.dev_name_list, dev_name))
+ continue;
+ } else if (is_partition(dev_name)) {
+ continue;
+ }
+
+ stats_dev = stats_dev_find_or_new(dev_name);
+ curr_data = &stats_dev->curr_data;
+
+ rc = sscanf(buf, "%*s %*s %*s %lu %llu %llu %llu %lu %*s %llu",
+ &curr_data->rd_ops,
+ &rd_sec_or_dummy,
+ &curr_data->rd_sectors,
+ &wr_sec_or_dummy,
+ &curr_data->wr_ops,
+ &curr_data->wr_sectors);
+ if (rc != 6) {
+ curr_data->rd_sectors = rd_sec_or_dummy;
+ curr_data->wr_sectors = wr_sec_or_dummy;
+ //curr_data->rd_ops = ;
+ curr_data->wr_ops = (unsigned long)curr_data->rd_sectors;
+ }
+
+ if (!G.dev_name_list /* User didn't specify device */
+ && !G.show_all
+ && curr_data->rd_ops == 0
+ && curr_data->wr_ops == 0
+ ) {
+ /* Don't print unused device */
+ continue;
+ }
+
+ /* Print current statistics */
+ print_stats_dev_struct(stats_dev, itv);
+ stats_dev->prev_data = *curr_data;
+ }
+
+ fclose(fp);
+}
+
+static void dev_report(cputime_t itv)
+{
+ /* Always print a header */
+ print_devstat_header();
+
+ /* Fetch current disk statistics */
+ do_disk_statistics(itv);
+}
+
+//usage:#define iostat_trivial_usage
+//usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]"
+//usage:#define iostat_full_usage "\n\n"
+//usage: "Report CPU and I/O statistics\n"
+//usage: "\n -c Show CPU utilization"
+//usage: "\n -d Show device utilization"
+//usage: "\n -t Print current time"
+//usage: "\n -z Omit devices with no activity"
+//usage: "\n -k Use kb/s"
+//usage: "\n -m Use Mb/s"
+
+int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iostat_main(int argc UNUSED_PARAM, char **argv)
+{
+ int opt;
+ unsigned interval;
+ int count;
+ stats_cpu_t stats_data[2];
+ smallint current_stats;
+
+ INIT_G();
+
+ memset(&stats_data, 0, sizeof(stats_data));
+
+ /* Get number of clock ticks per sec */
+ G.clk_tck = get_user_hz();
+
+ /* Determine number of CPUs */
+ G.total_cpus = get_cpu_count();
+ if (G.total_cpus == 0)
+ G.total_cpus = 1;
+
+ /* Parse and process arguments */
+ /* -k and -m are mutually exclusive */
+ opt_complementary = "k--m:m--k";
+ opt = getopt32(argv, "cdtzkm");
+ if (!(opt & (OPT_c + OPT_d)))
+ /* Default is -cd */
+ opt |= OPT_c + OPT_d;
+
+ argv += optind;
+
+ /* Store device names into device list */
+ while (*argv && !isdigit(*argv[0])) {
+ if (strcmp(*argv, "ALL") != 0) {
+ /* If not ALL, save device name */
+ char *dev_name = skip_dev_pfx(*argv);
+ if (!llist_find_str(G.dev_name_list, dev_name)) {
+ llist_add_to(&G.dev_name_list, dev_name);
+ }
+ } else {
+ G.show_all = 1;
+ }
+ argv++;
+ }
+
+ interval = 0;
+ count = 1;
+ if (*argv) {
+ /* Get interval */
+ interval = xatoi_positive(*argv);
+ count = (interval != 0 ? -1 : 1);
+ argv++;
+ if (*argv)
+ /* Get count value */
+ count = xatoi_positive(*argv);
+ }
+
+ if (opt & OPT_m) {
+ G.unit.str = " MB";
+ G.unit.div = 2048;
+ }
+
+ if (opt & OPT_k) {
+ G.unit.str = " kB";
+ G.unit.div = 2;
+ }
+
+ get_localtime(&G.tmtime);
+
+ /* Display header */
+ print_header();
+
+ current_stats = 0;
+ /* Main loop */
+ for (;;) {
+ stats_cpu_pair_t stats;
+
+ stats.prev = &stats_data[current_stats ^ 1];
+ stats.curr = &stats_data[current_stats];
+
+ /* Fill the time structure */
+ get_localtime(&G.tmtime);
+
+ /* Fetch current CPU statistics */
+ get_cpu_statistics(stats.curr);
+
+ /* Get interval */
+ stats.itv = get_interval(
+ stats.prev->vector[GLOBAL_UPTIME],
+ stats.curr->vector[GLOBAL_UPTIME]
+ );
+
+ if (opt & OPT_t)
+ print_timestamp();
+
+ if (opt & OPT_c) {
+ cpu_report(&stats);
+ if (opt & OPT_d)
+ /* Separate outputs by a newline */
+ bb_putchar('\n');
+ }
+
+ if (opt & OPT_d) {
+ if (this_is_smp()) {
+ stats.itv = get_interval(
+ stats.prev->vector[SMP_UPTIME],
+ stats.curr->vector[SMP_UPTIME]
+ );
+ }
+ dev_report(stats.itv);
+ }
+
+ bb_putchar('\n');
+
+ if (count > 0) {
+ if (--count == 0)
+ break;
+ }
+
+ /* Swap stats */
+ current_stats ^= 1;
+
+ sleep(interval);
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ llist_free(G.dev_name_list, NULL);
+ stats_dev_free(G.stats_dev_list);
+ free(&G);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/kill.c b/ap/app/busybox/src/procps/kill.c
new file mode 100644
index 0000000..cd189bc
--- /dev/null
+++ b/ap/app/busybox/src/procps/kill.c
@@ -0,0 +1,284 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini kill/killall[5] implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define kill_trivial_usage
+//usage: "[-l] [-SIG] PID..."
+//usage:#define kill_full_usage "\n\n"
+//usage: "Send a signal (default: TERM) to given PIDs\n"
+//usage: "\n -l List all signal names and numbers"
+/* //usage: "\n -s SIG Yet another way of specifying SIG" */
+//usage:
+//usage:#define kill_example_usage
+//usage: "$ ps | grep apache\n"
+//usage: "252 root root S [apache]\n"
+//usage: "263 www-data www-data S [apache]\n"
+//usage: "264 www-data www-data S [apache]\n"
+//usage: "265 www-data www-data S [apache]\n"
+//usage: "266 www-data www-data S [apache]\n"
+//usage: "267 www-data www-data S [apache]\n"
+//usage: "$ kill 252\n"
+//usage:
+//usage:#define killall_trivial_usage
+//usage: "[-l] [-q] [-SIG] PROCESS_NAME..."
+//usage:#define killall_full_usage "\n\n"
+//usage: "Send a signal (default: TERM) to given processes\n"
+//usage: "\n -l List all signal names and numbers"
+/* //usage: "\n -s SIG Yet another way of specifying SIG" */
+//usage: "\n -q Don't complain if no processes were killed"
+//usage:
+//usage:#define killall_example_usage
+//usage: "$ killall apache\n"
+//usage:
+//usage:#define killall5_trivial_usage
+//usage: "[-l] [-SIG] [-o PID]..."
+//usage:#define killall5_full_usage "\n\n"
+//usage: "Send a signal (default: TERM) to all processes outside current session\n"
+//usage: "\n -l List all signal names and numbers"
+//usage: "\n -o PID Don't signal this PID"
+/* //usage: "\n -s SIG Yet another way of specifying SIG" */
+
+#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 brings some complications:
+ *
+ * + 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
+ */
+
+int kill_main(int argc, char **argv)
+{
+ char *arg;
+ pid_t pid;
+ int signo = SIGTERM, errors = 0, quiet = 0;
+#if !ENABLE_KILLALL && !ENABLE_KILLALL5
+#define killall 0
+#define killall5 0
+#else
+/* 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 */
+ argc--;
+ arg = *++argv;
+
+ if (argc < 1 || arg[0] != '-') {
+ goto do_it_now;
+ }
+
+ /* 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 */
+ 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;
+ }
+ /* 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, we are all done */
+ return EXIT_SUCCESS;
+ }
+
+ /* The -q quiet option */
+ if (killall && arg[1] == 'q' && arg[2] == '\0') {
+ quiet = 1;
+ arg = *++argv;
+ argc--;
+ if (argc < 1)
+ bb_show_usage();
+ if (arg[0] != '-')
+ goto do_it_now;
+ }
+
+ arg++; /* skip '-' */
+
+ /* -o PID? (if present, it always is at the end of command line) */
+ if (killall5 && arg[0] == 'o')
+ goto do_it_now;
+
+ 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 */
+ if (signo != SIGSTOP && signo != SIGCONT)
+ kill(-1, SIGSTOP);
+ /* Signal all processes except those in our session */
+ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID)) != NULL) {
+ 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("invalid number '%s'", arg);
+ ret = 1;
+ goto resume;
+ }
+ if (p->pid == omit)
+ goto dont_kill;
+ }
+ kill(p->pid, signo);
+ dont_kill: ;
+ }
+ resume:
+ /* And let them continue */
+ if (signo != SIGSTOP && signo != SIGCONT)
+ kill(-1, SIGCONT);
+ return ret;
+ }
+
+ /* 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 (arg) {
+ pid_t* pidList;
+
+ pidList = find_pid_by_name(arg);
+ if (*pidList == 0) {
+ errors++;
+ if (!quiet)
+ bb_error_msg("%s: no process killed", arg);
+ } else {
+ pid_t *pl;
+
+ for (pl = pidList; *pl; pl++) {
+ if (*pl == pid)
+ continue;
+ if (kill(*pl, signo) == 0)
+ continue;
+ errors++;
+ if (!quiet)
+ bb_perror_msg("can't kill pid %d", (int)*pl);
+ }
+ }
+ free(pidList);
+ arg = *++argv;
+ }
+ return errors;
+ }
+
+ /* Looks like they want to do a kill. Do that */
+ while (arg) {
+#if ENABLE_ASH || ENABLE_HUSH
+ /*
+ * We need to support shell's "hack formats" of
+ * " -PRGP_ID" (yes, with a leading space)
+ * and " PID1 PID2 PID3" (with degenerate case "")
+ */
+ while (*arg != '\0') {
+ char *end;
+ if (*arg == ' ')
+ arg++;
+ pid = bb_strtoi(arg, &end, 10);
+ if (errno && (errno != EINVAL || *end != ' ')) {
+ bb_error_msg("invalid number '%s'", arg);
+ errors++;
+ break;
+ }
+ if (kill(pid, signo) != 0) {
+ bb_perror_msg("can't kill pid %d", (int)pid);
+ errors++;
+ }
+ arg = end; /* can only point to ' ' or '\0' now */
+ }
+#else
+ pid = bb_strtoi(arg, NULL, 10);
+ if (errno) {
+ bb_error_msg("invalid number '%s'", arg);
+ errors++;
+ } else if (kill(pid, signo) != 0) {
+ bb_perror_msg("can't kill pid %d", (int)pid);
+ errors++;
+ }
+#endif
+ arg = *++argv;
+ }
+ return errors;
+}
diff --git a/ap/app/busybox/src/procps/lsof.c b/ap/app/busybox/src/procps/lsof.c
new file mode 100644
index 0000000..7e0ffa4
--- /dev/null
+++ b/ap/app/busybox/src/procps/lsof.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini lsof implementation for busybox
+ *
+ * Copyright (C) 2012 by Sven Oliver 'SvOlli' Moll <svolli@svolli.de>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config LSOF
+//config: bool "lsof"
+//config: default y
+//config: help
+//config: Show open files in the format of:
+//config: PID <TAB> /path/to/executable <TAB> /path/to/opened/file
+
+//applet:IF_LSOF(APPLET(lsof, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LSOF) += lsof.o
+
+//usage:#define lsof_trivial_usage
+//usage: ""
+//usage:#define lsof_full_usage "\n\n"
+//usage: "Show all open files"
+
+#include "libbb.h"
+
+/*
+ * Examples of "standard" lsof output:
+ *
+ * COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
+ * init 1 root cwd DIR 8,5 4096 2 /
+ * init 1 root rtd DIR 8,5 4096 2 /
+ * init 1 root txt REG 8,5 872400 63408 /app/busybox-1.19.2/busybox
+ * rpc.portm 1064 root mem REG 8,5 43494 47451 /app/glibc-2.11/lib/libnss_files-2.11.so
+ * rpc.portm 1064 root 3u IPv4 2178 UDP *:111
+ * rpc.portm 1064 root 4u IPv4 1244 TCP *:111 (LISTEN)
+ * runsvdir 1116 root 0r CHR 1,3 1214 /dev/null
+ * runsvdir 1116 root 1w CHR 1,3 1214 /dev/null
+ * runsvdir 1116 root 2w CHR 1,3 1214 /dev/null
+ * runsvdir 1116 root 3r DIR 8,6 1560 58359 /.local/var/service
+ * gpm 1128 root 4u unix 0xffff88007c09ccc0 1302 /dev/gpmctl
+ */
+
+int lsof_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsof_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ procps_status_t *proc = NULL;
+
+ while ((proc = procps_scan(proc, PSSCAN_PID|PSSCAN_EXE)) != NULL) {
+ char name[sizeof("/proc/%u/fd/0123456789") + sizeof(int)*3];
+ unsigned baseofs;
+ DIR *d_fd;
+ char *fdlink;
+ struct dirent *entry;
+
+ if (getpid() == proc->pid)
+ continue;
+
+ baseofs = sprintf(name, "/proc/%u/fd/", proc->pid);
+ d_fd = opendir(name);
+ if (d_fd) {
+ while ((entry = readdir(d_fd)) != NULL) {
+ if (entry->d_type == DT_LNK) {
+ safe_strncpy(name + baseofs, entry->d_name, 10);
+ fdlink = xmalloc_readlink(name);
+ printf("%d\t%s\t%s\n", proc->pid, proc->exe, fdlink);
+ free(fdlink);
+ }
+ }
+ closedir(d_fd);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/mpstat.c b/ap/app/busybox/src/procps/mpstat.c
new file mode 100644
index 0000000..aa5a5c7
--- /dev/null
+++ b/ap/app/busybox/src/procps/mpstat.c
@@ -0,0 +1,977 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Per-processor statistics, based on sysstat version 9.1.2 by Sebastien Godard
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_MPSTAT(APPLET(mpstat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o
+
+//config:config MPSTAT
+//config: bool "mpstat"
+//config: default y
+//config: help
+//config: Per-processor statistics
+
+#include "libbb.h"
+#include <sys/utsname.h> /* struct utsname */
+
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
+
+/* Size of /proc/interrupts line, CPU data excluded */
+#define INTERRUPTS_LINE 64
+/* Maximum number of interrupts */
+#define NR_IRQS 256
+#define NR_IRQCPU_PREALLOC 3
+#define MAX_IRQNAME_LEN 16
+#define MAX_PF_NAME 512
+/* sysstat 9.0.6 uses width 8, but newer code which also prints /proc/softirqs
+ * data needs more: "interrupts" in /proc/softirqs have longer names,
+ * most are up to 8 chars, one (BLOCK_IOPOLL) is even longer.
+ * We are printing headers in the " IRQNAME/s" form, experimentally
+ * anything smaller than 10 chars looks ugly for /proc/softirqs stats.
+ */
+#define INTRATE_SCRWIDTH 10
+#define INTRATE_SCRWIDTH_STR "10"
+
+/* System files */
+#define PROCFS_STAT "/proc/stat"
+#define PROCFS_INTERRUPTS "/proc/interrupts"
+#define PROCFS_SOFTIRQS "/proc/softirqs"
+#define PROCFS_UPTIME "/proc/uptime"
+
+
+#if 1
+typedef unsigned long long data_t;
+typedef long long idata_t;
+#define FMT_DATA "ll"
+#define DATA_MAX ULLONG_MAX
+#else
+typedef unsigned long data_t;
+typedef long idata_t;
+#define FMT_DATA "l"
+#define DATA_MAX ULONG_MAX
+#endif
+
+
+struct stats_irqcpu {
+ unsigned interrupts;
+ char irq_name[MAX_IRQNAME_LEN];
+};
+
+struct stats_cpu {
+ data_t cpu_user;
+ data_t cpu_nice;
+ data_t cpu_system;
+ data_t cpu_idle;
+ data_t cpu_iowait;
+ data_t cpu_steal;
+ data_t cpu_irq;
+ data_t cpu_softirq;
+ data_t cpu_guest;
+};
+
+struct stats_irq {
+ data_t irq_nr;
+};
+
+
+/* Globals. Sort by size and access frequency. */
+struct globals {
+ int interval;
+ int count;
+ unsigned cpu_nr; /* Number of CPUs */
+ unsigned irqcpu_nr; /* Number of interrupts per CPU */
+ unsigned softirqcpu_nr; /* Number of soft interrupts per CPU */
+ unsigned options;
+ unsigned hz;
+ unsigned cpu_bitmap_len;
+ smallint p_option;
+ // 9.0.6 does not do it. Try "mpstat -A 1 2" - headers are repeated!
+ //smallint header_done;
+ //smallint avg_header_done;
+ unsigned char *cpu_bitmap; /* Bit 0: global, bit 1: 1st proc... */
+ data_t global_uptime[3];
+ data_t per_cpu_uptime[3];
+ struct stats_cpu *st_cpu[3];
+ struct stats_irq *st_irq[3];
+ struct stats_irqcpu *st_irqcpu[3];
+ struct stats_irqcpu *st_softirqcpu[3];
+ struct tm timestamp[3];
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+/* The selected interrupts statistics (bits in G.options) */
+enum {
+ D_CPU = 1 << 0,
+ D_IRQ_SUM = 1 << 1,
+ D_IRQ_CPU = 1 << 2,
+ D_SOFTIRQS = 1 << 3,
+};
+
+
+/* Is option on? */
+static ALWAYS_INLINE int display_opt(int opt)
+{
+ return (opt & G.options);
+}
+
+#if DATA_MAX > 0xffffffff
+/*
+ * Handle overflow conditions properly for counters which can have
+ * less bits than data_t, depending on the kernel version.
+ */
+/* Surprisingly, on 32bit inlining is a size win */
+static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr)
+{
+ data_t v = curr - prev;
+
+ if ((idata_t)v < 0 /* curr < prev - counter overflow? */
+ && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */
+ ) {
+ /* Add 33th bit set to 1 to curr, compensating for the overflow */
+ /* double shift defeats "warning: left shift count >= width of type" */
+ v += ((data_t)1 << 16) << 16;
+ }
+ return v;
+}
+#else
+static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr)
+{
+ return curr - prev;
+}
+#endif
+
+static double percent_value(data_t prev, data_t curr, data_t itv)
+{
+ return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
+}
+
+static double hz_value(data_t prev, data_t curr, data_t itv)
+{
+ //bb_error_msg("curr:%lld prev:%lld G.hz:%u", curr, prev, G.hz);
+ return ((double)overflow_safe_sub(prev, curr)) / itv * G.hz;
+}
+
+static ALWAYS_INLINE data_t jiffies_diff(data_t old, data_t new)
+{
+ data_t diff = new - old;
+ return (diff == 0) ? 1 : diff;
+}
+
+static int is_cpu_in_bitmap(unsigned cpu)
+{
+ return G.cpu_bitmap[cpu >> 3] & (1 << (cpu & 7));
+}
+
+static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[],
+ int total_irqs,
+ data_t itv,
+ int prev, int current,
+ const char *prev_str, const char *current_str)
+{
+ int j;
+ int offset, cpu;
+ struct stats_irqcpu *p0, *q0;
+
+ /* Check if number of IRQs has changed */
+ if (G.interval != 0) {
+ for (j = 0; j <= total_irqs; j++) {
+ p0 = &per_cpu_stats[current][j];
+ if (p0->irq_name[0] != '\0') {
+ q0 = &per_cpu_stats[prev][j];
+ if (strcmp(p0->irq_name, q0->irq_name) != 0) {
+ /* Strings are different */
+ break;
+ }
+ }
+ }
+ }
+
+ /* Print header */
+ printf("\n%-11s CPU", prev_str);
+ {
+ /* A bit complex code to "buy back" space if one header is too wide.
+ * Here's how it looks like. BLOCK_IOPOLL eats too much space,
+ * and latter headers use smaller width to compensate:
+ * ...BLOCK/s BLOCK_IOPOLL/s TASKLET/s SCHED/s HRTIMER/s RCU/s
+ * ... 2.32 0.00 0.01 17.58 0.14 141.96
+ */
+ int expected_len = 0;
+ int printed_len = 0;
+ for (j = 0; j < total_irqs; j++) {
+ p0 = &per_cpu_stats[current][j];
+ if (p0->irq_name[0] != '\0') {
+ int n = (INTRATE_SCRWIDTH-3) - (printed_len - expected_len);
+ printed_len += printf(" %*s/s", n > 0 ? n : 0, skip_whitespace(p0->irq_name));
+ expected_len += INTRATE_SCRWIDTH;
+ }
+ }
+ }
+ bb_putchar('\n');
+
+ for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
+ /* Check if we want stats about this CPU */
+ if (!is_cpu_in_bitmap(cpu) && G.p_option) {
+ continue;
+ }
+
+ printf("%-11s %4u", current_str, cpu - 1);
+
+ for (j = 0; j < total_irqs; j++) {
+ /* IRQ field set only for proc 0 */
+ p0 = &per_cpu_stats[current][j];
+
+ /*
+ * An empty string for irq name means that
+ * interrupt is no longer used.
+ */
+ if (p0->irq_name[0] != '\0') {
+ offset = j;
+ q0 = &per_cpu_stats[prev][offset];
+
+ /*
+ * If we want stats for the time since boot
+ * we have p0->irq != q0->irq.
+ */
+ if (strcmp(p0->irq_name, q0->irq_name) != 0
+ && G.interval != 0
+ ) {
+ if (j) {
+ offset = j - 1;
+ q0 = &per_cpu_stats[prev][offset];
+ }
+ if (strcmp(p0->irq_name, q0->irq_name) != 0
+ && (j + 1 < total_irqs)
+ ) {
+ offset = j + 1;
+ q0 = &per_cpu_stats[prev][offset];
+ }
+ }
+
+ if (strcmp(p0->irq_name, q0->irq_name) == 0
+ || G.interval == 0
+ ) {
+ struct stats_irqcpu *p, *q;
+ p = &per_cpu_stats[current][(cpu - 1) * total_irqs + j];
+ q = &per_cpu_stats[prev][(cpu - 1) * total_irqs + offset];
+ printf("%"INTRATE_SCRWIDTH_STR".2f",
+ (double)(p->interrupts - q->interrupts) / itv * G.hz);
+ } else {
+ printf(" N/A");
+ }
+ }
+ }
+ bb_putchar('\n');
+ }
+}
+
+static data_t get_per_cpu_interval(const struct stats_cpu *scc,
+ const struct stats_cpu *scp)
+{
+ return ((scc->cpu_user + scc->cpu_nice +
+ scc->cpu_system + scc->cpu_iowait +
+ scc->cpu_idle + scc->cpu_steal +
+ scc->cpu_irq + scc->cpu_softirq) -
+ (scp->cpu_user + scp->cpu_nice +
+ scp->cpu_system + scp->cpu_iowait +
+ scp->cpu_idle + scp->cpu_steal +
+ scp->cpu_irq + scp->cpu_softirq));
+}
+
+static void print_stats_cpu_struct(const struct stats_cpu *p,
+ const struct stats_cpu *c,
+ data_t itv)
+{
+ printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+ percent_value(p->cpu_user - p->cpu_guest,
+ /**/ c->cpu_user - c->cpu_guest, itv),
+ percent_value(p->cpu_nice , c->cpu_nice , itv),
+ percent_value(p->cpu_system , c->cpu_system , itv),
+ percent_value(p->cpu_iowait , c->cpu_iowait , itv),
+ percent_value(p->cpu_irq , c->cpu_irq , itv),
+ percent_value(p->cpu_softirq, c->cpu_softirq, itv),
+ percent_value(p->cpu_steal , c->cpu_steal , itv),
+ percent_value(p->cpu_guest , c->cpu_guest , itv),
+ percent_value(p->cpu_idle , c->cpu_idle , itv)
+ );
+}
+
+static void write_stats_core(int prev, int current,
+ const char *prev_str, const char *current_str)
+{
+ struct stats_cpu *scc, *scp;
+ data_t itv, global_itv;
+ int cpu;
+
+ /* Compute time interval */
+ itv = global_itv = jiffies_diff(G.global_uptime[prev], G.global_uptime[current]);
+
+ /* Reduce interval to one CPU */
+ if (G.cpu_nr > 1)
+ itv = jiffies_diff(G.per_cpu_uptime[prev], G.per_cpu_uptime[current]);
+
+ /* Print CPU stats */
+ if (display_opt(D_CPU)) {
+
+ ///* This is done exactly once */
+ //if (!G.header_done) {
+ printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq %%soft %%steal %%guest %%idle\n",
+ prev_str
+ );
+ // G.header_done = 1;
+ //}
+
+ for (cpu = 0; cpu <= G.cpu_nr; cpu++) {
+ data_t per_cpu_itv;
+
+ /* Print stats about this particular CPU? */
+ if (!is_cpu_in_bitmap(cpu))
+ continue;
+
+ scc = &G.st_cpu[current][cpu];
+ scp = &G.st_cpu[prev][cpu];
+ per_cpu_itv = global_itv;
+
+ printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1);
+ if (cpu) {
+ double idle;
+ /*
+ * If the CPU is offline, then it isn't in /proc/stat,
+ * so all values are 0.
+ * NB: Guest time is already included in user time.
+ */
+ if ((scc->cpu_user | scc->cpu_nice | scc->cpu_system |
+ scc->cpu_iowait | scc->cpu_idle | scc->cpu_steal |
+ scc->cpu_irq | scc->cpu_softirq) == 0
+ ) {
+ /*
+ * Set current struct fields to values from prev.
+ * iteration. Then their values won't jump from
+ * zero, when the CPU comes back online.
+ */
+ *scc = *scp;
+ idle = 0.0;
+ goto print_zeros;
+ }
+ /* Compute interval again for current proc */
+ per_cpu_itv = get_per_cpu_interval(scc, scp);
+ if (per_cpu_itv == 0) {
+ /*
+ * If the CPU is tickless then there is no change in CPU values
+ * but the sum of values is not zero.
+ */
+ idle = 100.0;
+ print_zeros:
+ printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle);
+ continue;
+ }
+ }
+ print_stats_cpu_struct(scp, scc, per_cpu_itv);
+ }
+ }
+
+ /* Print total number of IRQs per CPU */
+ if (display_opt(D_IRQ_SUM)) {
+
+ ///* Print average header, this is done exactly once */
+ //if (!G.avg_header_done) {
+ printf("\n%-11s CPU intr/s\n", prev_str);
+ // G.avg_header_done = 1;
+ //}
+
+ for (cpu = 0; cpu <= G.cpu_nr; cpu++) {
+ data_t per_cpu_itv;
+
+ /* Print stats about this CPU? */
+ if (!is_cpu_in_bitmap(cpu))
+ continue;
+
+ per_cpu_itv = itv;
+ printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1);
+ if (cpu) {
+ scc = &G.st_cpu[current][cpu];
+ scp = &G.st_cpu[prev][cpu];
+ /* Compute interval again for current proc */
+ per_cpu_itv = get_per_cpu_interval(scc, scp);
+ if (per_cpu_itv == 0) {
+ printf(" %9.2f\n", 0.0);
+ continue;
+ }
+ }
+ //bb_error_msg("G.st_irq[%u][%u].irq_nr:%lld - G.st_irq[%u][%u].irq_nr:%lld",
+ // current, cpu, G.st_irq[prev][cpu].irq_nr, prev, cpu, G.st_irq[current][cpu].irq_nr);
+ printf(" %9.2f\n", hz_value(G.st_irq[prev][cpu].irq_nr, G.st_irq[current][cpu].irq_nr, per_cpu_itv));
+ }
+ }
+
+ if (display_opt(D_IRQ_CPU)) {
+ write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr,
+ itv,
+ prev, current,
+ prev_str, current_str
+ );
+ }
+
+ if (display_opt(D_SOFTIRQS)) {
+ write_irqcpu_stats(G.st_softirqcpu, G.softirqcpu_nr,
+ itv,
+ prev, current,
+ prev_str, current_str
+ );
+ }
+}
+
+/*
+ * Print the statistics
+ */
+static void write_stats(int current)
+{
+ char prev_time[16];
+ char curr_time[16];
+
+ strftime(prev_time, sizeof(prev_time), "%X", &G.timestamp[!current]);
+ strftime(curr_time, sizeof(curr_time), "%X", &G.timestamp[current]);
+
+ write_stats_core(!current, current, prev_time, curr_time);
+}
+
+static void write_stats_avg(int current)
+{
+ write_stats_core(2, current, "Average:", "Average:");
+}
+
+/*
+ * Read CPU statistics
+ */
+static void get_cpu_statistics(struct stats_cpu *cpu, data_t *up, data_t *up0)
+{
+ FILE *fp;
+ char buf[1024];
+
+ fp = xfopen_for_read(PROCFS_STAT);
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ data_t sum;
+ unsigned cpu_number;
+ struct stats_cpu *cp;
+
+ if (!starts_with_cpu(buf))
+ continue; /* not "cpu" */
+
+ cp = cpu; /* for "cpu " case */
+ if (buf[3] != ' ') {
+ /* "cpuN " */
+ if (G.cpu_nr == 0
+ || sscanf(buf + 3, "%u ", &cpu_number) != 1
+ || cpu_number >= G.cpu_nr
+ ) {
+ continue;
+ }
+ cp = &cpu[cpu_number + 1];
+ }
+
+ /* Read the counters, save them */
+ /* Not all fields have to be present */
+ memset(cp, 0, sizeof(*cp));
+ sscanf(buf, "%*s"
+ " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u"
+ " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u"
+ " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u",
+ &cp->cpu_user, &cp->cpu_nice, &cp->cpu_system,
+ &cp->cpu_idle, &cp->cpu_iowait, &cp->cpu_irq,
+ &cp->cpu_softirq, &cp->cpu_steal, &cp->cpu_guest
+ );
+ /*
+ * Compute uptime in jiffies (1/HZ), it'll be the sum of
+ * individual CPU's uptimes.
+ * NB: We have to omit cpu_guest, because cpu_user includes it.
+ */
+ sum = cp->cpu_user + cp->cpu_nice + cp->cpu_system +
+ cp->cpu_idle + cp->cpu_iowait + cp->cpu_irq +
+ cp->cpu_softirq + cp->cpu_steal;
+
+ if (buf[3] == ' ') {
+ /* "cpu " */
+ *up = sum;
+ } else {
+ /* "cpuN " */
+ if (cpu_number == 0 && *up0 != 0) {
+ /* Compute uptime of single CPU */
+ *up0 = sum;
+ }
+ }
+ }
+ fclose(fp);
+}
+
+/*
+ * Read IRQs from /proc/stat
+ */
+static void get_irqs_from_stat(struct stats_irq *irq)
+{
+ FILE *fp;
+ char buf[1024];
+
+ fp = fopen_for_read(PROCFS_STAT);
+ if (!fp)
+ return;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ //bb_error_msg("/proc/stat:'%s'", buf);
+ if (strncmp(buf, "intr ", 5) == 0) {
+ /* Read total number of IRQs since system boot */
+ sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr);
+ }
+ }
+
+ fclose(fp);
+}
+
+/*
+ * Read stats from /proc/interrupts or /proc/softirqs
+ */
+static void get_irqs_from_interrupts(const char *fname,
+ struct stats_irqcpu *per_cpu_stats[],
+ int irqs_per_cpu, int current)
+{
+ FILE *fp;
+ struct stats_irq *irq_i;
+ struct stats_irqcpu *ic;
+ char *buf;
+ unsigned buflen;
+ unsigned cpu;
+ unsigned irq;
+ int cpu_index[G.cpu_nr];
+ int iindex;
+
+// Moved to caller.
+// Otherwise reading of /proc/softirqs
+// was resetting counts to 0 after we painstakingly collected them from
+// /proc/interrupts. Which resulted in:
+// 01:32:47 PM CPU intr/s
+// 01:32:47 PM all 591.47
+// 01:32:47 PM 0 0.00 <= ???
+// 01:32:47 PM 1 0.00 <= ???
+// for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
+// G.st_irq[current][cpu].irq_nr = 0;
+// //bb_error_msg("G.st_irq[%u][%u].irq_nr=0", current, cpu);
+// }
+
+ fp = fopen_for_read(fname);
+ if (!fp)
+ return;
+
+ buflen = INTERRUPTS_LINE + 16 * G.cpu_nr;
+ buf = xmalloc(buflen);
+
+ /* Parse header and determine, which CPUs are online */
+ iindex = 0;
+ while (fgets(buf, buflen, fp)) {
+ char *cp, *next;
+ next = buf;
+ while ((cp = strstr(next, "CPU")) != NULL
+ && iindex < G.cpu_nr
+ ) {
+ cpu = strtoul(cp + 3, &next, 10);
+ cpu_index[iindex++] = cpu;
+ }
+ if (iindex) /* We found header */
+ break;
+ }
+
+ irq = 0;
+ while (fgets(buf, buflen, fp)
+ && irq < irqs_per_cpu
+ ) {
+ int len;
+ char last_char;
+ char *cp;
+
+ /* Skip over "IRQNAME:" */
+ cp = strchr(buf, ':');
+ if (!cp)
+ continue;
+ last_char = cp[-1];
+
+ ic = &per_cpu_stats[current][irq];
+ len = cp - buf;
+ if (len >= sizeof(ic->irq_name)) {
+ len = sizeof(ic->irq_name) - 1;
+ }
+ safe_strncpy(ic->irq_name, buf, len + 1);
+ //bb_error_msg("%s: irq%d:'%s' buf:'%s'", fname, irq, ic->irq_name, buf);
+ cp++;
+
+ for (cpu = 0; cpu < iindex; cpu++) {
+ char *next;
+ ic = &per_cpu_stats[current][cpu_index[cpu] * irqs_per_cpu + irq];
+ irq_i = &G.st_irq[current][cpu_index[cpu] + 1];
+ ic->interrupts = strtoul(cp, &next, 10);
+ /* Count only numerical IRQs */
+ if (isdigit(last_char)) {
+ irq_i->irq_nr += ic->interrupts;
+ //bb_error_msg("G.st_irq[%u][%u].irq_nr + %u = %lld",
+ // current, cpu_index[cpu] + 1, ic->interrupts, irq_i->irq_nr);
+ }
+ cp = next;
+ }
+ irq++;
+ }
+ fclose(fp);
+ free(buf);
+
+ while (irq < irqs_per_cpu) {
+ /* Number of interrupts per CPU has changed */
+ ic = &per_cpu_stats[current][irq];
+ ic->irq_name[0] = '\0'; /* False interrupt */
+ irq++;
+ }
+}
+
+static void get_uptime(data_t *uptime)
+{
+ FILE *fp;
+ char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */
+ unsigned long uptime_sec, decimal;
+
+ fp = fopen_for_read(PROCFS_UPTIME);
+ if (!fp)
+ return;
+ if (fgets(buf, sizeof(buf), fp)) {
+ if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) {
+ *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100;
+ }
+ }
+
+ fclose(fp);
+}
+
+static void get_localtime(struct tm *tm)
+{
+ time_t timer;
+ time(&timer);
+ localtime_r(&timer, tm);
+}
+
+static void alarm_handler(int sig UNUSED_PARAM)
+{
+ signal(SIGALRM, alarm_handler);
+ alarm(G.interval);
+}
+
+static void main_loop(void)
+{
+ unsigned current;
+ unsigned cpus;
+
+ /* Read the stats */
+ if (G.cpu_nr > 1) {
+ G.per_cpu_uptime[0] = 0;
+ get_uptime(&G.per_cpu_uptime[0]);
+ }
+
+ get_cpu_statistics(G.st_cpu[0], &G.global_uptime[0], &G.per_cpu_uptime[0]);
+
+ if (display_opt(D_IRQ_SUM))
+ get_irqs_from_stat(G.st_irq[0]);
+
+ if (display_opt(D_IRQ_SUM | D_IRQ_CPU))
+ get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu,
+ G.irqcpu_nr, 0);
+
+ if (display_opt(D_SOFTIRQS))
+ get_irqs_from_interrupts(PROCFS_SOFTIRQS, G.st_softirqcpu,
+ G.softirqcpu_nr, 0);
+
+ if (G.interval == 0) {
+ /* Display since boot time */
+ cpus = G.cpu_nr + 1;
+ G.timestamp[1] = G.timestamp[0];
+ memset(G.st_cpu[1], 0, sizeof(G.st_cpu[1][0]) * cpus);
+ memset(G.st_irq[1], 0, sizeof(G.st_irq[1][0]) * cpus);
+ memset(G.st_irqcpu[1], 0, sizeof(G.st_irqcpu[1][0]) * cpus * G.irqcpu_nr);
+ memset(G.st_softirqcpu[1], 0, sizeof(G.st_softirqcpu[1][0]) * cpus * G.softirqcpu_nr);
+
+ write_stats(0);
+
+ /* And we're done */
+ return;
+ }
+
+ /* Set a handler for SIGALRM */
+ alarm_handler(0);
+
+ /* Save the stats we already have. We need them to compute the average */
+ G.timestamp[2] = G.timestamp[0];
+ G.global_uptime[2] = G.global_uptime[0];
+ G.per_cpu_uptime[2] = G.per_cpu_uptime[0];
+ cpus = G.cpu_nr + 1;
+ memcpy(G.st_cpu[2], G.st_cpu[0], sizeof(G.st_cpu[0][0]) * cpus);
+ memcpy(G.st_irq[2], G.st_irq[0], sizeof(G.st_irq[0][0]) * cpus);
+ memcpy(G.st_irqcpu[2], G.st_irqcpu[0], sizeof(G.st_irqcpu[0][0]) * cpus * G.irqcpu_nr);
+ if (display_opt(D_SOFTIRQS)) {
+ memcpy(G.st_softirqcpu[2], G.st_softirqcpu[0],
+ sizeof(G.st_softirqcpu[0][0]) * cpus * G.softirqcpu_nr);
+ }
+
+ current = 1;
+ while (1) {
+ /* Suspend until a signal is received */
+ pause();
+
+ /* Set structures to 0 to distinguish off/online CPUs */
+ memset(&G.st_cpu[current][/*cpu:*/ 1], 0, sizeof(G.st_cpu[0][0]) * G.cpu_nr);
+
+ get_localtime(&G.timestamp[current]);
+
+ /* Read stats */
+ if (G.cpu_nr > 1) {
+ G.per_cpu_uptime[current] = 0;
+ get_uptime(&G.per_cpu_uptime[current]);
+ }
+ get_cpu_statistics(G.st_cpu[current], &G.global_uptime[current], &G.per_cpu_uptime[current]);
+
+ if (display_opt(D_IRQ_SUM))
+ get_irqs_from_stat(G.st_irq[current]);
+
+ if (display_opt(D_IRQ_SUM | D_IRQ_CPU)) {
+ int cpu;
+ for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
+ G.st_irq[current][cpu].irq_nr = 0;
+ }
+ /* accumulates .irq_nr */
+ get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu,
+ G.irqcpu_nr, current);
+ }
+
+ if (display_opt(D_SOFTIRQS))
+ get_irqs_from_interrupts(PROCFS_SOFTIRQS,
+ G.st_softirqcpu,
+ G.softirqcpu_nr, current);
+
+ write_stats(current);
+
+ if (G.count > 0) {
+ if (--G.count == 0)
+ break;
+ }
+
+ current ^= 1;
+ }
+
+ /* Print average statistics */
+ write_stats_avg(current);
+}
+
+/* Initialization */
+
+/* Get number of clock ticks per sec */
+static ALWAYS_INLINE unsigned get_hz(void)
+{
+ return sysconf(_SC_CLK_TCK);
+}
+
+static void alloc_struct(int cpus)
+{
+ int i;
+ for (i = 0; i < 3; i++) {
+ G.st_cpu[i] = xzalloc(sizeof(G.st_cpu[i][0]) * cpus);
+ G.st_irq[i] = xzalloc(sizeof(G.st_irq[i][0]) * cpus);
+ G.st_irqcpu[i] = xzalloc(sizeof(G.st_irqcpu[i][0]) * cpus * G.irqcpu_nr);
+ G.st_softirqcpu[i] = xzalloc(sizeof(G.st_softirqcpu[i][0]) * cpus * G.softirqcpu_nr);
+ }
+ G.cpu_bitmap_len = (cpus >> 3) + 1;
+ G.cpu_bitmap = xzalloc(G.cpu_bitmap_len);
+}
+
+static void print_header(struct tm *t)
+{
+ char cur_date[16];
+ struct utsname uts;
+
+ /* Get system name, release number and hostname */
+ uname(&uts);
+
+ strftime(cur_date, sizeof(cur_date), "%x", t);
+
+ printf("%s %s (%s)\t%s\t_%s_\t(%u CPU)\n",
+ uts.sysname, uts.release, uts.nodename, cur_date, uts.machine, G.cpu_nr);
+}
+
+/*
+ * Get number of interrupts available per processor
+ */
+static int get_irqcpu_nr(const char *f, int max_irqs)
+{
+ FILE *fp;
+ char *line;
+ unsigned linelen;
+ unsigned irq;
+
+ fp = fopen_for_read(f);
+ if (!fp) /* No interrupts file */
+ return 0;
+
+ linelen = INTERRUPTS_LINE + 16 * G.cpu_nr;
+ line = xmalloc(linelen);
+
+ irq = 0;
+ while (fgets(line, linelen, fp)
+ && irq < max_irqs
+ ) {
+ int p = strcspn(line, ":");
+ if ((p > 0) && (p < 16))
+ irq++;
+ }
+
+ fclose(fp);
+ free(line);
+
+ return irq;
+}
+
+//usage:#define mpstat_trivial_usage
+//usage: "[-A] [-I SUM|CPU|ALL|SCPU] [-u] [-P num|ALL] [INTERVAL [COUNT]]"
+//usage:#define mpstat_full_usage "\n\n"
+//usage: "Per-processor statistics\n"
+//usage: "\n -A Same as -I ALL -u -P ALL"
+//usage: "\n -I SUM|CPU|ALL|SCPU Report interrupt statistics"
+//usage: "\n -P num|ALL Processor to monitor"
+//usage: "\n -u Report CPU utilization"
+
+int mpstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mpstat_main(int UNUSED_PARAM argc, char **argv)
+{
+ char *opt_irq_fmt;
+ char *opt_set_cpu;
+ int i, opt;
+ enum {
+ OPT_ALL = 1 << 0, /* -A */
+ OPT_INTS = 1 << 1, /* -I */
+ OPT_SETCPU = 1 << 2, /* -P */
+ OPT_UTIL = 1 << 3, /* -u */
+ };
+
+ /* Dont buffer data if redirected to a pipe */
+ setbuf(stdout, NULL);
+
+ INIT_G();
+
+ G.interval = -1;
+
+ /* Get number of processors */
+ G.cpu_nr = get_cpu_count();
+
+ /* Get number of clock ticks per sec */
+ G.hz = get_hz();
+
+ /* Calculate number of interrupts per processor */
+ G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC;
+
+ /* Calculate number of soft interrupts per processor */
+ G.softirqcpu_nr = get_irqcpu_nr(PROCFS_SOFTIRQS, NR_IRQS) + NR_IRQCPU_PREALLOC;
+
+ /* Allocate space for structures. + 1 for global structure. */
+ alloc_struct(G.cpu_nr + 1);
+
+ /* Parse and process arguments */
+ opt = getopt32(argv, "AI:P:u", &opt_irq_fmt, &opt_set_cpu);
+ argv += optind;
+
+ if (*argv) {
+ /* Get interval */
+ G.interval = xatoi_positive(*argv);
+ G.count = -1;
+ argv++;
+ if (*argv) {
+ /* Get count value */
+ if (G.interval == 0)
+ bb_show_usage();
+ G.count = xatoi_positive(*argv);
+ //if (*++argv)
+ // bb_show_usage();
+ }
+ }
+ if (G.interval < 0)
+ G.interval = 0;
+
+ if (opt & OPT_ALL) {
+ G.p_option = 1;
+ G.options |= D_CPU + D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS;
+ /* Select every CPU */
+ memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
+ }
+
+ if (opt & OPT_INTS) {
+ static const char v[] = {
+ D_IRQ_CPU, D_IRQ_SUM, D_SOFTIRQS,
+ D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS
+ };
+ i = index_in_strings("CPU\0SUM\0SCPU\0ALL\0", opt_irq_fmt);
+ if (i == -1)
+ bb_show_usage();
+ G.options |= v[i];
+ }
+
+ if ((opt & OPT_UTIL) /* -u? */
+ || G.options == 0 /* nothing? (use default then) */
+ ) {
+ G.options |= D_CPU;
+ }
+
+ if (opt & OPT_SETCPU) {
+ char *t;
+ G.p_option = 1;
+
+ for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) {
+ if (strcmp(t, "ALL") == 0) {
+ /* Select every CPU */
+ memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
+ } else {
+ /* Get CPU number */
+ unsigned n = xatoi_positive(t);
+ if (n >= G.cpu_nr)
+ bb_error_msg_and_die("not that many processors");
+ n++;
+ G.cpu_bitmap[n >> 3] |= 1 << (n & 7);
+ }
+ }
+ }
+
+ if (!G.p_option)
+ /* Display global stats */
+ G.cpu_bitmap[0] = 1;
+
+ /* Get time */
+ get_localtime(&G.timestamp[0]);
+
+ /* Display header */
+ print_header(&G.timestamp[0]);
+
+ /* The main loop */
+ main_loop();
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ /* Clean up */
+ for (i = 0; i < 3; i++) {
+ free(G.st_cpu[i]);
+ free(G.st_irq[i]);
+ free(G.st_irqcpu[i]);
+ free(G.st_softirqcpu[i]);
+ }
+ free(G.cpu_bitmap);
+ free(&G);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/nmeter.c b/ap/app/busybox/src/procps/nmeter.c
new file mode 100644
index 0000000..6a3b327
--- /dev/null
+++ b/ap/app/busybox/src/procps/nmeter.c
@@ -0,0 +1,977 @@
+/*
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * Based on nanotop.c from floppyfw project
+ *
+ * Contact me: vda.linux@googlemail.com
+ */
+
+//config:config NMETER
+//config: bool "nmeter"
+//config: default y
+//config: help
+//config: Prints selected system stats continuously, one line per update.
+
+//applet:IF_NMETER(APPLET(nmeter, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NMETER) += nmeter.o
+
+//usage:#define nmeter_trivial_usage
+//usage: "[-d MSEC] FORMAT_STRING"
+//usage:#define nmeter_full_usage "\n\n"
+//usage: "Monitor system in real time"
+//usage: "\n"
+//usage: "\n -d MSEC Milliseconds between updates (default:1000)"
+//usage: "\n"
+//usage: "\nFormat specifiers:"
+//usage: "\n %Nc or %[cN] CPU. N - bar size (default:10)"
+//usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
+//usage: "\n %[nINTERFACE] Network INTERFACE"
+//usage: "\n %m Allocated memory"
+//usage: "\n %[mf] Free memory"
+//usage: "\n %[mt] Total memory"
+//usage: "\n %s Allocated swap"
+//usage: "\n %f Number of used file descriptors"
+//usage: "\n %Ni Total/specific IRQ rate"
+//usage: "\n %x Context switch rate"
+//usage: "\n %p Forks"
+//usage: "\n %[pn] # of processes"
+//usage: "\n %b Block io"
+//usage: "\n %Nt Time (with N decimal points)"
+//usage: "\n %r Print <cr> instead of <lf> at EOL"
+
+//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
+// Linux 3.0 (maybe earlier) started printing full stats for hda1 too.
+// Had to add code which skips such devices.
+static int rdval_diskstats(const char* p, ullong *vec)
+{
+ char devname[32];
+ unsigned devname_len = 0;
+ int value_idx = 0;
+
+ vec[0] = 0;
+ vec[1] = 0;
+ while (1) {
+ value_idx++;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\0')
+ break;
+ if (*p == '\n') {
+ value_idx = 0;
+ p++;
+ continue;
+ }
+ if (value_idx == 3) {
+ char *end = strchrnul(p, ' ');
+ /* If this a hda1-like device (same prefix as last one + digit)? */
+ if (devname_len && strncmp(devname, p, devname_len) == 0 && isdigit(p[devname_len])) {
+ p = end;
+ goto skip_line; /* skip entire line */
+ }
+ /* It is not. Remember the name for future checks */
+ devname_len = end - p;
+ if (devname_len > sizeof(devname)-1)
+ devname_len = sizeof(devname)-1;
+ strncpy(devname, p, devname_len);
+ /* devname[devname_len] = '\0'; - not really needed */
+ p = end;
+ } else
+ if (value_idx == 6) {
+ // TODO: *sectorsize (don't know how to find out sectorsize)
+ vec[0] += strtoull(p, NULL, 10);
+ } else
+ if (value_idx == 10) {
+ // TODO: *sectorsize (don't know how to find out sectorsize)
+ vec[1] += strtoull(p, NULL, 10);
+ skip_line:
+ while (*p != '\n' && *p != '\0')
+ p++;
+ continue;
+ }
+ while ((unsigned char)(*p) > ' ') // skip over value
+ p++;
+ }
+ 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) FAST_FUNC; \
+ const char *label;
+#define S_STAT_END(a) } a;
+
+S_STAT(s_stat)
+S_STAT_END(s_stat)
+
+static void FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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_positive(param);
+ s->no = n + 2;
+ }
+ return (s_stat*)s;
+}
+
+
+S_STAT(ctx_stat)
+ ullong old;
+S_STAT_END(ctx_stat)
+
+static void FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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 FAST_FUNC 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);
+
+// Deprecated %NNNd is to be removed, -d MSEC supersedes it
+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 UNUSED_PARAM, char **argv)
+{
+ char buf[32];
+ s_stat *first = NULL;
+ s_stat *last = NULL;
+ s_stat *s;
+ char *opt_d;
+ char *cur, *prev;
+
+ INIT_G();
+
+ xchdir("/proc");
+
+ if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
+ buf[sizeof(buf)-1] = '\0';
+ is26 = (strstr(buf, " 2.4.") == NULL);
+ }
+
+ if (getopt32(argv, "d:", &opt_d))
+ init_delay(opt_d);
+ argv += optind;
+
+ if (!argv[0])
+ bb_show_usage();
+
+ // Can use argv[0] directly, but this will mess up
+ // parameters as seen by e.g. ps. Making a copy...
+ cur = xstrdup(argv[0]);
+ 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/ap/app/busybox/src/procps/pgrep.c b/ap/app/busybox/src/procps/pgrep.c
new file mode 100644
index 0000000..dc7ffff
--- /dev/null
+++ b/ap/app/busybox/src/procps/pgrep.c
@@ -0,0 +1,182 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini pgrep/pkill implementation for busybox
+ *
+ * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define pgrep_trivial_usage
+//usage: "[-flnovx] [-s SID|-P PPID|PATTERN]"
+//usage:#define pgrep_full_usage "\n\n"
+//usage: "Display process(es) selected by regex PATTERN\n"
+//usage: "\n -l Show command name too"
+//usage: "\n -f Match against entire command line"
+//usage: "\n -n Show the newest process only"
+//usage: "\n -o Show the oldest process only"
+//usage: "\n -v Negate the match"
+//usage: "\n -x Match whole name (not substring)"
+//usage: "\n -s Match session ID (0 for current)"
+//usage: "\n -P Match parent process ID"
+//usage:
+//usage:#define pkill_trivial_usage
+//usage: "[-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]"
+//usage:#define pkill_full_usage "\n\n"
+//usage: "Send a signal to process(es) selected by regex PATTERN\n"
+//usage: "\n -l List all signals"
+//usage: "\n -f Match against entire command line"
+//usage: "\n -n Signal the newest process only"
+//usage: "\n -o Signal the oldest process only"
+//usage: "\n -v Negate the match"
+//usage: "\n -x Match whole name (not substring)"
+//usage: "\n -s Match session ID (0 for current)"
+//usage: "\n -P Match parent process ID"
+
+#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 {
+ /* "vlfxons:P:" */
+ OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
+ OPTBIT_L,
+ OPTBIT_F,
+ OPTBIT_X,
+ OPTBIT_O,
+ OPTBIT_N,
+ OPTBIT_S,
+ OPTBIT_P,
+};
+
+#define OPT_INVERT (opt & (1 << OPTBIT_V))
+#define OPT_LIST (opt & (1 << OPTBIT_L))
+#define OPT_FULL (opt & (1 << OPTBIT_F))
+#define OPT_ANCHOR (opt & (1 << OPTBIT_X))
+#define OPT_FIRST (opt & (1 << OPTBIT_O))
+#define OPT_LAST (opt & (1 << OPTBIT_N))
+#define OPT_SID (opt & (1 << OPTBIT_S))
+#define OPT_PPID (opt & (1 << OPTBIT_P))
+
+static void act(unsigned pid, char *cmd, int signo)
+{
+ if (pgrep) {
+ if (option_mask32 & (1 << OPTBIT_L)) /* 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;
+ int signo;
+ unsigned opt;
+ int scan_mask;
+ int matched_pid;
+ int sid2match, ppid2match;
+ 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));
+
+ /* Parse -SIGNAL for pkill. Must be first option, if present */
+ signo = SIGTERM;
+ if (pkill && argv[1] && argv[1][0] == '-') {
+ int temp = get_signum(argv[1]+1);
+ if (temp != -1) {
+ signo = temp;
+ argv++;
+ }
+ }
+
+ /* Parse remaining options */
+ ppid2match = -1;
+ sid2match = -1;
+ opt_complementary = "s+:P+"; /* numeric opts */
+ opt = getopt32(argv, "vlfxons:P:", &sid2match, &ppid2match);
+ argv += optind;
+
+ if (pkill && OPT_LIST) { /* -l: print the whole signal list */
+ print_signames();
+ return 0;
+ }
+
+ pid = getpid();
+ if (sid2match == 0)
+ sid2match = getsid(pid);
+
+ scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
+ if (OPT_FULL)
+ scan_mask |= PSSCAN_ARGVN;
+
+ /* One pattern is required, if no -s and no -P */
+ if ((sid2match & ppid2match) < 0 && (!argv[0] || argv[1]))
+ bb_show_usage();
+
+ if (argv[0])
+ xregcomp(&re_buffer, argv[0], REG_EXTENDED | REG_NOSUB);
+
+ 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 >= 0) {
+ if ((unsigned char)cmd[i] < ' ')
+ cmd[i] = ' ';
+ }
+ }
+
+ if (ppid2match >= 0 && ppid2match != proc->ppid)
+ continue;
+ if (sid2match >= 0 && sid2match != proc->sid)
+ continue;
+
+ /* NB: OPT_INVERT is always 0 or 1 */
+ if (!argv[0]
+ || (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);
+ if (OPT_FIRST)
+ break;
+ }
+ }
+
+ if (cmd_last) {
+ act(matched_pid, cmd_last, signo);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(cmd_last);
+ }
+ return matched_pid == 0; /* return 1 if no processes listed/signaled */
+}
diff --git a/ap/app/busybox/src/procps/pidof.c b/ap/app/busybox/src/procps/pidof.c
new file mode 100644
index 0000000..6d7b591
--- /dev/null
+++ b/ap/app/busybox/src/procps/pidof.c
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pidof implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#if (ENABLE_FEATURE_PIDOF_SINGLE || ENABLE_FEATURE_PIDOF_OMIT)
+//usage:#define pidof_trivial_usage
+//usage: "[OPTIONS] [NAME]..."
+//usage:#define USAGE_PIDOF "\n"
+//usage:#else
+//usage:#define pidof_trivial_usage
+//usage: "[NAME]..."
+//usage:#define USAGE_PIDOF /* none */
+//usage:#endif
+//usage:#define pidof_full_usage "\n\n"
+//usage: "List PIDs of all processes with names that match NAMEs"
+//usage: USAGE_PIDOF
+//usage: IF_FEATURE_PIDOF_SINGLE(
+//usage: "\n -s Show only one PID"
+//usage: )
+//usage: IF_FEATURE_PIDOF_OMIT(
+//usage: "\n -o PID Omit given pid"
+//usage: "\n Use %PPID to omit pid of pidof's parent"
+//usage: )
+//usage:
+//usage:#define pidof_example_usage
+//usage: "$ pidof init\n"
+//usage: "1\n"
+//usage: IF_FEATURE_PIDOF_OMIT(
+//usage: "$ pidof /bin/sh\n20351 5973 5950\n")
+//usage: IF_FEATURE_PIDOF_OMIT(
+//usage: "$ pidof /bin/sh -o %PPID\n20351 5950")
+
+#include "libbb.h"
+
+enum {
+ IF_FEATURE_PIDOF_SINGLE(OPTBIT_SINGLE,)
+ IF_FEATURE_PIDOF_OMIT( OPTBIT_OMIT ,)
+ OPT_SINGLE = IF_FEATURE_PIDOF_SINGLE((1<<OPTBIT_SINGLE)) + 0,
+ OPT_OMIT = IF_FEATURE_PIDOF_OMIT( (1<<OPTBIT_OMIT )) + 0,
+};
+
+int pidof_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pidof_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned first = 1;
+ unsigned opt;
+#if ENABLE_FEATURE_PIDOF_OMIT
+ llist_t *omits = NULL; /* list of pids to omit */
+ opt_complementary = "o::";
+#endif
+
+ /* do unconditional option parsing */
+ opt = getopt32(argv, ""
+ IF_FEATURE_PIDOF_SINGLE ("s")
+ IF_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;
+ /* 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;
+
+ /* 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);
+ argv++;
+ }
+ if (!first)
+ bb_putchar('\n');
+
+#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/ap/app/busybox/src/procps/pmap.c b/ap/app/busybox/src/procps/pmap.c
new file mode 100644
index 0000000..fd995a5
--- /dev/null
+++ b/ap/app/busybox/src/procps/pmap.c
@@ -0,0 +1,111 @@
+/*
+ * pmap implementation for busybox
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Written by Alexander Shishkin <virtuoso@slind.org>
+ *
+ * Licensed under GPLv2 or later, see the LICENSE file in this source tree
+ * for details.
+ */
+
+//config:config PMAP
+//config: bool "pmap"
+//config: default y
+//config: help
+//config: Display processes' memory mappings.
+
+//applet:IF_PMAP(APPLET(pmap, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_PMAP) += pmap.o
+
+//usage:#define pmap_trivial_usage
+//usage: "[-xq] PID"
+//usage:#define pmap_full_usage "\n\n"
+//usage: "Display detailed process memory usage"
+//usage: "\n"
+//usage: "\n -x Show details"
+//usage: "\n -q Quiet"
+
+#include "libbb.h"
+
+#if ULONG_MAX == 0xffffffff
+# define TABS "\t"
+# define AFMT "8"
+# define DASHES ""
+#else
+# define TABS "\t\t"
+# define AFMT "16"
+# define DASHES "--------"
+#endif
+
+enum {
+ OPT_x = 1 << 0,
+ OPT_q = 1 << 1,
+};
+
+static void print_smaprec(struct smaprec *currec, void *data)
+{
+ unsigned opt = (uintptr_t)data;
+
+ printf("%0" AFMT "lx ", currec->smap_start);
+
+ if (opt & OPT_x)
+ printf("%7lu %7lu %7lu %7lu ",
+ currec->smap_size,
+ currec->smap_pss,
+ currec->private_dirty,
+ currec->smap_swap);
+ else
+ printf("%7luK", currec->smap_size);
+
+ printf(" %.4s %s\n", currec->smap_mode, currec->smap_name);
+}
+
+static int procps_get_maps(pid_t pid, unsigned opt)
+{
+ struct smaprec total;
+ int ret;
+ char buf[256];
+
+ read_cmdline(buf, sizeof(buf), pid, "no such process");
+ printf("%u: %s\n", (int)pid, buf);
+
+ if (!(opt & OPT_q) && (opt & OPT_x))
+ puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
+
+ memset(&total, 0, sizeof(total));
+
+ ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
+ if (ret)
+ return ret;
+
+ if (!(opt & OPT_q)) {
+ if (opt & OPT_x)
+ printf("--------" DASHES " ------ ------ ------ ------\n"
+ "total" TABS " %7lu %7lu %7lu %7lu\n",
+ total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
+ else
+ printf("mapped: %luK\n", total.smap_size);
+ }
+
+ return 0;
+}
+
+int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pmap_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opts;
+ int ret;
+
+ opts = getopt32(argv, "xq");
+ argv += optind;
+
+ ret = 0;
+ while (*argv) {
+ pid_t pid = xatoi_positive(*argv++);
+ /* GNU pmap returns 42 if any of the pids failed */
+ if (procps_get_maps(pid, opts) != 0)
+ ret = 42;
+ }
+
+ return ret;
+}
diff --git a/ap/app/busybox/src/procps/powertop.c b/ap/app/busybox/src/procps/powertop.c
new file mode 100644
index 0000000..71988a2
--- /dev/null
+++ b/ap/app/busybox/src/procps/powertop.c
@@ -0,0 +1,857 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A mini 'powertop' utility:
+ * Analyze power consumption on Intel-based laptops.
+ * Based on powertop 1.11.
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_POWERTOP(APPLET(powertop, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
+
+//config:config POWERTOP
+//config: bool "powertop"
+//config: default y
+//config: help
+//config: Analyze power consumption on Intel-based laptops
+
+// XXX This should be configurable
+#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
+
+#include "libbb.h"
+
+
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
+
+
+#define BLOATY_HPET_IRQ_NUM_DETECTION 0
+#define MAX_CSTATE_COUNT 8
+#define IRQCOUNT 40
+
+
+#define DEFAULT_SLEEP 10
+#define DEFAULT_SLEEP_STR "10"
+
+/* Frequency of the ACPI timer */
+#define FREQ_ACPI 3579.545
+#define FREQ_ACPI_1000 3579545
+
+/* Max filename length of entry in /sys/devices subsystem */
+#define BIG_SYSNAME_LEN 16
+
+typedef unsigned long long ullong;
+
+struct line {
+ char *string;
+ int count;
+ /*int disk_count;*/
+};
+
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+struct irqdata {
+ smallint active;
+ int number;
+ ullong count;
+ char irq_desc[32];
+};
+#endif
+
+struct globals {
+ struct line *lines; /* the most often used member */
+ int lines_cnt;
+ int lines_cumulative_count;
+ int maxcstate;
+ unsigned total_cpus;
+ smallint cant_enable_timer_stats;
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+ smallint scanned_timer_list;
+ int percpu_hpet_start;
+ int percpu_hpet_end;
+# endif
+ int interrupt_0;
+ int total_interrupt;
+ struct irqdata interrupts[IRQCOUNT];
+#endif
+ ullong start_usage[MAX_CSTATE_COUNT];
+ ullong last_usage[MAX_CSTATE_COUNT];
+ ullong start_duration[MAX_CSTATE_COUNT];
+ ullong last_duration[MAX_CSTATE_COUNT];
+#if ENABLE_FEATURE_USE_TERMIOS
+ struct termios init_settings;
+#endif
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static void reset_term(void)
+{
+ tcsetattr_stdin_TCSANOW(&G.init_settings);
+}
+
+static void sig_handler(int signo UNUSED_PARAM)
+{
+ reset_term();
+ _exit(EXIT_FAILURE);
+}
+#endif
+
+static int write_str_to_file(const char *fname, const char *str)
+{
+ FILE *fp = fopen_for_write(fname);
+ if (!fp)
+ return 1;
+ fputs(str, fp);
+ fclose(fp);
+ return 0;
+}
+
+/* Make it more readable */
+#define start_timer() write_str_to_file("/proc/timer_stats", "1\n")
+#define stop_timer() write_str_to_file("/proc/timer_stats", "0\n")
+
+static NOINLINE void clear_lines(void)
+{
+ int i;
+ if (G.lines) {
+ for (i = 0; i < G.lines_cnt; i++)
+ free(G.lines[i].string);
+ free(G.lines);
+ G.lines_cnt = 0;
+ G.lines = NULL;
+ }
+}
+
+static void update_lines_cumulative_count(void)
+{
+ int i;
+ for (i = 0; i < G.lines_cnt; i++)
+ G.lines_cumulative_count += G.lines[i].count;
+}
+
+static int line_compare(const void *p1, const void *p2)
+{
+ const struct line *a = p1;
+ const struct line *b = p2;
+ return (b->count /*+ 50 * b->disk_count*/) - (a->count /*+ 50 * a->disk_count*/);
+}
+
+static void sort_lines(void)
+{
+ qsort(G.lines, G.lines_cnt, sizeof(G.lines[0]), line_compare);
+}
+
+/* Save C-state usage and duration. Also update maxcstate. */
+static void read_cstate_counts(ullong *usage, ullong *duration)
+{
+ DIR *dir;
+ struct dirent *d;
+
+ dir = opendir("/proc/acpi/processor");
+ if (!dir)
+ return;
+
+ while ((d = readdir(dir)) != NULL) {
+ FILE *fp;
+ char buf[192];
+ int level;
+ int len;
+
+ len = strlen(d->d_name); /* "CPUnn" */
+ if (len < 3 || len > BIG_SYSNAME_LEN)
+ continue;
+
+ sprintf(buf, "%s/%s/power", "/proc/acpi/processor", d->d_name);
+ fp = fopen_for_read(buf);
+ if (!fp)
+ continue;
+
+// Example file contents:
+// active state: C0
+// max_cstate: C8
+// maximum allowed latency: 2000000000 usec
+// states:
+// C1: type[C1] promotion[--] demotion[--] latency[001] usage[00006173] duration[00000000000000000000]
+// C2: type[C2] promotion[--] demotion[--] latency[001] usage[00085191] duration[00000000000083024907]
+// C3: type[C3] promotion[--] demotion[--] latency[017] usage[01017622] duration[00000000017921327182]
+ level = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *p = strstr(buf, "age[");
+ if (!p)
+ continue;
+ p += 4;
+ usage[level] += bb_strtoull(p, NULL, 10) + 1;
+ p = strstr(buf, "ation[");
+ if (!p)
+ continue;
+ p += 6;
+ duration[level] += bb_strtoull(p, NULL, 10);
+
+ if (level >= MAX_CSTATE_COUNT-1)
+ break;
+ level++;
+ if (level > G.maxcstate) /* update maxcstate */
+ G.maxcstate = level;
+ }
+ fclose(fp);
+ }
+ closedir(dir);
+}
+
+/* Add line and/or update count */
+static void save_line(const char *string, int count)
+{
+ int i;
+ for (i = 0; i < G.lines_cnt; i++) {
+ if (strcmp(string, G.lines[i].string) == 0) {
+ /* It's already there, only update count */
+ G.lines[i].count += count;
+ return;
+ }
+ }
+
+ /* Add new line */
+ G.lines = xrealloc_vector(G.lines, 4, G.lines_cnt);
+ G.lines[G.lines_cnt].string = xstrdup(string);
+ G.lines[G.lines_cnt].count = count;
+ /*G.lines[G.lines_cnt].disk_count = 0;*/
+ G.lines_cnt++;
+}
+
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+static int is_hpet_irq(const char *name)
+{
+ char *p;
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+ long hpet_chan;
+
+ /* Learn the range of existing hpet timers. This is done once */
+ if (!G.scanned_timer_list) {
+ FILE *fp;
+ char buf[80];
+
+ G.scanned_timer_list = true;
+ fp = fopen_for_read("/proc/timer_list");
+ if (!fp)
+ return 0;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ p = strstr(buf, "Clock Event Device: hpet");
+ if (!p)
+ continue;
+ p += sizeof("Clock Event Device: hpet")-1;
+ if (!isdigit(*p))
+ continue;
+ hpet_chan = xatoi_positive(p);
+ if (hpet_chan < G.percpu_hpet_start)
+ G.percpu_hpet_start = hpet_chan;
+ if (hpet_chan > G.percpu_hpet_end)
+ G.percpu_hpet_end = hpet_chan;
+ }
+ fclose(fp);
+ }
+# endif
+//TODO: optimize
+ p = strstr(name, "hpet");
+ if (!p)
+ return 0;
+ p += 4;
+ if (!isdigit(*p))
+ return 0;
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+ hpet_chan = xatoi_positive(p);
+ if (hpet_chan < G.percpu_hpet_start || hpet_chan > G.percpu_hpet_end)
+ return 0;
+# endif
+ return 1;
+}
+
+/* Save new IRQ count, return delta from old one */
+static int save_irq_count(int irq, ullong count)
+{
+ int unused = IRQCOUNT;
+ int i;
+ for (i = 0; i < IRQCOUNT; i++) {
+ if (G.interrupts[i].active && G.interrupts[i].number == irq) {
+ ullong old = G.interrupts[i].count;
+ G.interrupts[i].count = count;
+ return count - old;
+ }
+ if (!G.interrupts[i].active && unused > i)
+ unused = i;
+ }
+ if (unused < IRQCOUNT) {
+ G.interrupts[unused].active = 1;
+ G.interrupts[unused].count = count;
+ G.interrupts[unused].number = irq;
+ }
+ return count;
+}
+
+/* Read /proc/interrupts, save IRQ counts and IRQ description */
+static void process_irq_counts(void)
+{
+ FILE *fp;
+ char buf[128];
+
+ /* Reset values */
+ G.interrupt_0 = 0;
+ G.total_interrupt = 0;
+
+ fp = xfopen_for_read("/proc/interrupts");
+ while (fgets(buf, sizeof(buf), fp)) {
+ char irq_desc[sizeof(" <kernel IPI> : ") + sizeof(buf)];
+ char *p;
+ const char *name;
+ int nr;
+ ullong count;
+ ullong delta;
+
+ p = strchr(buf, ':');
+ if (!p)
+ continue;
+ /* 0: 143646045 153901007 IO-APIC-edge timer
+ * ^
+ */
+ *p = '\0';
+ /* Deal with non-maskable interrupts -- make up fake numbers */
+ nr = index_in_strings("NMI\0RES\0CAL\0TLB\0TRM\0THR\0SPU\0", buf);
+ if (nr >= 0) {
+ nr += 20000;
+ } else {
+ /* bb_strtou doesn't eat leading spaces, using strtoul */
+ errno = 0;
+ nr = strtoul(buf, NULL, 10);
+ if (errno)
+ continue;
+ }
+ p++;
+ /* 0: 143646045 153901007 IO-APIC-edge timer
+ * ^
+ */
+ /* Sum counts for this IRQ */
+ count = 0;
+ while (1) {
+ char *tmp;
+ p = skip_whitespace(p);
+ if (!isdigit(*p))
+ break;
+ count += bb_strtoull(p, &tmp, 10);
+ p = tmp;
+ }
+ /* 0: 143646045 153901007 IO-APIC-edge timer
+ * NMI: 1 2 Non-maskable interrupts
+ * ^
+ */
+ if (nr < 20000) {
+ /* Skip to the interrupt name, e.g. 'timer' */
+ p = strchr(p, ' ');
+ if (!p)
+ continue;
+ p = skip_whitespace(p);
+ }
+
+ name = p;
+ strchrnul(name, '\n')[0] = '\0';
+ /* Save description of the interrupt */
+ if (nr >= 20000)
+ sprintf(irq_desc, " <kernel IPI> : %s", name);
+ else
+ sprintf(irq_desc, " <interrupt> : %s", name);
+
+ delta = save_irq_count(nr, count);
+
+ /* Skip per CPU timer interrupts */
+ if (is_hpet_irq(name))
+ continue;
+
+ if (nr != 0 && delta != 0)
+ save_line(irq_desc, delta);
+
+ if (nr == 0)
+ G.interrupt_0 = delta;
+ else
+ G.total_interrupt += delta;
+ }
+
+ fclose(fp);
+}
+#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
+# define process_irq_counts() ((void)0)
+#endif
+
+static NOINLINE int process_timer_stats(void)
+{
+ char buf[128];
+ char line[15 + 3 + 128];
+ int n;
+ FILE *fp;
+
+ buf[0] = '\0';
+
+ n = 0;
+ fp = NULL;
+ if (!G.cant_enable_timer_stats)
+ fp = fopen_for_read("/proc/timer_stats");
+ if (fp) {
+// Example file contents:
+// Timer Stats Version: v0.2
+// Sample period: 1.329 s
+// 76, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
+// 88, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
+// 24, 3787 firefox hrtimer_start_range_ns (hrtimer_wakeup)
+// 46D, 1136 kondemand/1 do_dbs_timer (delayed_work_timer_fn)
+// ...
+// 1, 1656 Xorg hrtimer_start_range_ns (hrtimer_wakeup)
+// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup)
+// 331 total events, 249.059 events/sec
+ while (fgets(buf, sizeof(buf), fp)) {
+ const char *count, *process, *func;
+ char *p;
+ int idx;
+ unsigned cnt;
+
+ count = skip_whitespace(buf);
+ p = strchr(count, ',');
+ if (!p)
+ continue;
+ *p++ = '\0';
+ cnt = bb_strtou(count, NULL, 10);
+ if (strcmp(skip_non_whitespace(count), " total events") == 0) {
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+ n = cnt / G.total_cpus;
+ if (n > 0 && n < G.interrupt_0) {
+ sprintf(line, " <interrupt> : %s", "extra timer interrupt");
+ save_line(line, G.interrupt_0 - n);
+ }
+#endif
+ break;
+ }
+ if (strchr(count, 'D'))
+ continue; /* deferred */
+ p = skip_whitespace(p); /* points to pid now */
+ process = NULL;
+ get_func_name:
+ p = strchr(p, ' ');
+ if (!p)
+ continue;
+ *p++ = '\0';
+ p = skip_whitespace(p);
+ if (process == NULL) {
+ process = p;
+ goto get_func_name;
+ }
+ func = p;
+
+ //if (strcmp(process, "swapper") == 0
+ // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
+ //) {
+ // process = "[kernel scheduler]";
+ // func = "Load balancing tick";
+ //}
+
+ if (strncmp(func, "tick_nohz_", 10) == 0)
+ continue;
+ if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
+ continue;
+ //if (strcmp(process, "powertop") == 0)
+ // continue;
+
+ idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
+ if (idx != -1) {
+ process = idx < 2 ? "[kernel module]" : "<kernel core>";
+ }
+
+ strchrnul(p, '\n')[0] = '\0';
+
+ // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
+ // ^ ^ ^
+ // count process func
+
+ //if (strchr(process, '['))
+ sprintf(line, "%15.15s : %s", process, func);
+ //else
+ // sprintf(line, "%s", process);
+ save_line(line, cnt);
+ }
+ fclose(fp);
+ }
+
+ return n;
+}
+
+#ifdef __i386__
+/*
+ * Get information about CPU using CPUID opcode.
+ */
+static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
+ unsigned int *edx)
+{
+ /* EAX value specifies what information to return */
+ __asm__(
+ " pushl %%ebx\n" /* Save EBX */
+ " cpuid\n"
+ " movl %%ebx, %1\n" /* Save content of EBX */
+ " popl %%ebx\n" /* Restore EBX */
+ : "=a"(*eax), /* Output */
+ "=r"(*ebx),
+ "=c"(*ecx),
+ "=d"(*edx)
+ : "0"(*eax), /* Input */
+ "1"(*ebx),
+ "2"(*ecx),
+ "3"(*edx)
+ /* No clobbered registers */
+ );
+}
+#endif
+
+#ifdef __i386__
+static NOINLINE void print_intel_cstates(void)
+{
+ int bios_table[8] = { 0 };
+ int nbios = 0;
+ DIR *cpudir;
+ struct dirent *d;
+ int i;
+ unsigned eax, ebx, ecx, edx;
+
+ cpudir = opendir("/sys/devices/system/cpu");
+ if (!cpudir)
+ return;
+
+ /* Loop over cpuN entries */
+ while ((d = readdir(cpudir)) != NULL) {
+ DIR *dir;
+ int len;
+ char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
+
+ len = strlen(d->d_name);
+ if (len < 3 || len > BIG_SYSNAME_LEN)
+ continue;
+
+ if (!isdigit(d->d_name[3]))
+ continue;
+
+ len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
+ dir = opendir(fname);
+ if (!dir)
+ continue;
+
+ /*
+ * Every C-state has its own stateN directory, that
+ * contains a 'time' and a 'usage' file.
+ */
+ while ((d = readdir(dir)) != NULL) {
+ FILE *fp;
+ char buf[64];
+ int n;
+
+ n = strlen(d->d_name);
+ if (n < 3 || n > BIG_SYSNAME_LEN)
+ continue;
+
+ sprintf(fname + len, "/%s/desc", d->d_name);
+ fp = fopen_for_read(fname);
+ if (fp) {
+ char *p = fgets(buf, sizeof(buf), fp);
+ fclose(fp);
+ if (!p)
+ break;
+ p = strstr(p, "MWAIT ");
+ if (p) {
+ int pos;
+ p += sizeof("MWAIT ") - 1;
+ pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
+ if (pos >= ARRAY_SIZE(bios_table))
+ continue;
+ bios_table[pos]++;
+ nbios++;
+ }
+ }
+ }
+ closedir(dir);
+ }
+ closedir(cpudir);
+
+ if (!nbios)
+ return;
+
+ eax = 5;
+ ebx = ecx = edx = 0;
+ cpuid(&eax, &ebx, &ecx, &edx);
+ if (!edx || !(ecx & 1))
+ return;
+
+ printf("Your CPU supports the following C-states: ");
+ i = 0;
+ while (edx) {
+ if (edx & 7)
+ printf("C%u ", i);
+ edx >>= 4;
+ i++;
+ }
+ bb_putchar('\n');
+
+ /* Print BIOS C-States */
+ printf("Your BIOS reports the following C-states: ");
+ for (i = 0; i < ARRAY_SIZE(bios_table); i++)
+ if (bios_table[i])
+ printf("C%u ", i);
+
+ bb_putchar('\n');
+}
+#else
+# define print_intel_cstates() ((void)0)
+#endif
+
+static void show_timerstats(void)
+{
+ unsigned lines;
+
+ /* Get terminal height */
+ get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
+
+ /* We don't have whole terminal just for timerstats */
+ lines -= 12;
+
+ if (!G.cant_enable_timer_stats) {
+ int i, n = 0;
+ char strbuf6[6];
+
+ strbuf6[5] = '\0';
+ puts("\nTop causes for wakeups:");
+ for (i = 0; i < G.lines_cnt; i++) {
+ if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
+ && n++ < lines
+ ) {
+ /* NB: upstream powertop prints "(wakeups/sec)",
+ * we print just "(wakeup counts)".
+ */
+ /*char c = ' ';
+ if (G.lines[i].disk_count)
+ c = 'D';*/
+ smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY");
+ printf(/*" %5.1f%% (%s)%c %s\n"*/
+ " %5.1f%% (%s) %s\n",
+ G.lines[i].count * 100.0 / G.lines_cumulative_count,
+ strbuf6, /*c,*/
+ G.lines[i].string);
+ }
+ }
+ } else {
+ bb_putchar('\n');
+ bb_error_msg("no stats available; run as root or"
+ " enable the timer_stats module");
+ }
+}
+
+// Example display from powertop version 1.11
+// Cn Avg residency P-states (frequencies)
+// C0 (cpu running) ( 0.5%) 2.00 Ghz 0.0%
+// polling 0.0ms ( 0.0%) 1.67 Ghz 0.0%
+// C1 mwait 0.0ms ( 0.0%) 1333 Mhz 0.1%
+// C2 mwait 0.1ms ( 0.1%) 1000 Mhz 99.9%
+// C3 mwait 12.1ms (99.4%)
+//
+// Wakeups-from-idle per second : 93.6 interval: 15.0s
+// no ACPI power usage estimate available
+//
+// Top causes for wakeups:
+// 32.4% ( 26.7) <interrupt> : extra timer interrupt
+// 29.0% ( 23.9) <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
+// 9.0% ( 7.5) <kernel core> : hrtimer_start (tick_sched_timer)
+// 6.5% ( 5.3) <interrupt> : ata_piix
+// 5.0% ( 4.1) inetd : hrtimer_start_range_ns (hrtimer_wakeup)
+
+//usage:#define powertop_trivial_usage
+//usage: ""
+//usage:#define powertop_full_usage "\n\n"
+//usage: "Analyze power consumption on Intel-based laptops\n"
+
+int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
+{
+ ullong cur_usage[MAX_CSTATE_COUNT];
+ ullong cur_duration[MAX_CSTATE_COUNT];
+ char cstate_lines[MAX_CSTATE_COUNT + 2][64];
+#if ENABLE_FEATURE_USE_TERMIOS
+ struct termios new_settings;
+ struct pollfd pfd[1];
+
+ pfd[0].fd = 0;
+ pfd[0].events = POLLIN;
+#endif
+
+ INIT_G();
+
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
+ G.percpu_hpet_start = INT_MAX;
+ G.percpu_hpet_end = INT_MIN;
+#endif
+
+ /* Print warning when we don't have superuser privileges */
+ if (geteuid() != 0)
+ bb_error_msg("run as root to collect enough information");
+
+ /* Get number of CPUs */
+ G.total_cpus = get_cpu_count();
+
+ printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
+
+#if ENABLE_FEATURE_USE_TERMIOS
+ tcgetattr(0, (void *)&G.init_settings);
+ memcpy(&new_settings, &G.init_settings, sizeof(new_settings));
+ /* Turn on unbuffered input, turn off echoing */
+ new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
+ /* So we don't forget to reset term settings */
+ atexit(reset_term);
+ bb_signals(BB_FATAL_SIGS, sig_handler);
+ tcsetattr_stdin_TCSANOW(&new_settings);
+#endif
+
+ /* Collect initial data */
+ process_irq_counts();
+
+ /* Read initial usage and duration */
+ read_cstate_counts(G.start_usage, G.start_duration);
+
+ /* Copy them to "last" */
+ memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
+ memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
+
+ /* Display C-states */
+ print_intel_cstates();
+
+ G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
+
+ /* The main loop */
+ for (;;) {
+ //double maxsleep = 0.0;
+ ullong totalticks, totalevents;
+ int i;
+
+ G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
+#if !ENABLE_FEATURE_USE_TERMIOS
+ sleep(DEFAULT_SLEEP);
+#else
+ if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
+ unsigned char c;
+ if (safe_read(STDIN_FILENO, &c, 1) != 1)
+ break; /* EOF/error */
+ if (c == G.init_settings.c_cc[VINTR])
+ break; /* ^C */
+ if ((c | 0x20) == 'q')
+ break;
+ }
+#endif
+ G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
+
+ clear_lines();
+ process_irq_counts();
+
+ /* Clear the stats */
+ memset(cur_duration, 0, sizeof(cur_duration));
+ memset(cur_usage, 0, sizeof(cur_usage));
+
+ /* Read them */
+ read_cstate_counts(cur_usage, cur_duration);
+
+ /* Count totalticks and totalevents */
+ totalticks = totalevents = 0;
+ for (i = 0; i < MAX_CSTATE_COUNT; i++) {
+ if (cur_usage[i] != 0) {
+ totalticks += cur_duration[i] - G.last_duration[i];
+ totalevents += cur_usage[i] - G.last_usage[i];
+ }
+ }
+
+ /* Clear the screen */
+ printf("\033[H\033[J");
+
+ /* Clear C-state lines */
+ memset(&cstate_lines, 0, sizeof(cstate_lines));
+
+ if (totalevents == 0 && G.maxcstate <= 1) {
+ /* This should not happen */
+ strcpy(cstate_lines[0], "C-state information is not available\n");
+ } else {
+ double percentage;
+ unsigned newticks;
+
+ newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
+ /* Handle rounding errors: do not display negative values */
+ if ((int)newticks < 0)
+ newticks = 0;
+
+ sprintf(cstate_lines[0], "Cn\t\t Avg residency\n");
+ percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
+ sprintf(cstate_lines[1], "C0 (cpu running) (%4.1f%%)\n", percentage);
+
+ /* Compute values for individual C-states */
+ for (i = 0; i < MAX_CSTATE_COUNT; i++) {
+ if (cur_usage[i] != 0) {
+ double slept;
+ slept = (cur_duration[i] - G.last_duration[i])
+ / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
+ percentage = (cur_duration[i] - G.last_duration[i]) * 100
+ / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
+ sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
+ i + 1, slept, percentage);
+ //if (maxsleep < slept)
+ // maxsleep = slept;
+ }
+ }
+ }
+
+ for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
+ if (cstate_lines[i][0])
+ fputs(cstate_lines[i], stdout);
+
+ i = process_timer_stats();
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+ if (totalevents == 0) {
+ /* No C-state info available, use timerstats */
+ totalevents = i * G.total_cpus + G.total_interrupt;
+ if (i < 0)
+ totalevents += G.interrupt_0 - i;
+ }
+#endif
+ /* Upstream powertop prints wakeups per sec per CPU,
+ * we print just raw wakeup counts.
+ */
+//TODO: show real seconds (think about manual refresh)
+ printf("\nWakeups-from-idle in %u seconds: %llu\n",
+ DEFAULT_SLEEP,
+ totalevents
+ );
+
+ update_lines_cumulative_count();
+ sort_lines();
+ show_timerstats();
+ fflush(stdout);
+
+ /* Clear the stats */
+ memset(cur_duration, 0, sizeof(cur_duration));
+ memset(cur_usage, 0, sizeof(cur_usage));
+
+ /* Get new values */
+ read_cstate_counts(cur_usage, cur_duration);
+
+ /* Save them */
+ memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
+ memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
+ } /* for (;;) */
+
+ bb_putchar('\n');
+
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/ps.c b/ap/app/busybox/src/procps/ps.c
new file mode 100644
index 0000000..efc087e
--- /dev/null
+++ b/ap/app/busybox/src/procps/ps.c
@@ -0,0 +1,799 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ps implementation(s) for busybox
+ *
+ * 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>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#if ENABLE_DESKTOP
+//usage:
+//usage:#define ps_trivial_usage
+//usage: "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
+//usage:#define ps_full_usage "\n\n"
+//usage: "Show list of processes\n"
+//usage: "\n -o COL1,COL2=HEADER Select columns for display"
+//usage: IF_FEATURE_SHOW_THREADS(
+//usage: "\n -T Show threads"
+//usage: )
+//usage:
+//usage:#else /* !ENABLE_DESKTOP */
+//usage:
+//usage:#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
+//usage:#define USAGE_PS "\nThis version of ps accepts no options"
+//usage:#else
+//usage:#define USAGE_PS ""
+//usage:#endif
+//usage:
+//usage:#define ps_trivial_usage
+//usage: ""
+//usage:#define ps_full_usage "\n\n"
+//usage: "Show list of processes\n"
+//usage: USAGE_PS
+//usage: IF_SELINUX(
+//usage: "\n -Z Show selinux context"
+//usage: )
+//usage: IF_FEATURE_PS_WIDE(
+//usage: "\n w Wide output"
+//usage: )
+//usage: IF_FEATURE_PS_LONG(
+//usage: "\n l Long output"
+//usage: )
+//usage: IF_FEATURE_SHOW_THREADS(
+//usage: "\n T Show threads"
+//usage: )
+//usage:
+//usage:#endif /* ENABLE_DESKTOP */
+//usage:
+//usage:#define ps_example_usage
+//usage: "$ ps\n"
+//usage: " PID Uid Gid State Command\n"
+//usage: " 1 root root S init\n"
+//usage: " 2 root root S [kflushd]\n"
+//usage: " 3 root root S [kupdate]\n"
+//usage: " 4 root root S [kpiod]\n"
+//usage: " 5 root root S [kswapd]\n"
+//usage: " 742 andersen andersen S [bash]\n"
+//usage: " 743 andersen andersen S -bash\n"
+//usage: " 745 root root S [getty]\n"
+//usage: " 2990 andersen andersen R ps\n"
+
+#include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
+/* Absolute maximum on output line length */
+enum { MAX_WIDTH = 2*1024 };
+
+#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
+static long get_uptime(void)
+{
+#ifdef __linux__
+ struct sysinfo info;
+ if (sysinfo(&info) < 0)
+ return 0;
+ return info.uptime;
+#elif 1
+ char buf[64];
+ long uptime;
+ if (open_read_close("/proc/uptime", buf, sizeof(buf)) <= 0)
+ bb_perror_msg_and_die("can't read %s", "/proc/uptime");
+ buf[sizeof(buf)-1] = '\0';
+ sscanf(buf, "%l", &uptime);
+ return uptime;
+#else
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+ return 0;
+ return ts.tv_sec;
+#endif
+}
+#endif
+
+#if ENABLE_DESKTOP
+
+#include <sys/times.h> /* for times() */
+#ifndef AT_CLKTCK
+# define AT_CLKTCK 17
+#endif
+
+/* TODO:
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
+ * specifies (for XSI-conformant systems) following default columns
+ * (l and f mark columns shown with -l and -f respectively):
+ * F l Flags (octal and additive) associated with the process (??)
+ * S l The state of the process
+ * UID f,l The user ID; the login name is printed with -f
+ * PID The process ID
+ * PPID f,l The parent process
+ * C f,l Processor utilization
+ * PRI l The priority of the process; higher numbers mean lower priority
+ * NI l Nice value
+ * ADDR l The address of the process
+ * SZ l The size in blocks of the core image of the process
+ * WCHAN l The event for which the process is waiting or sleeping
+ * STIME f Starting time of the process
+ * TTY The controlling terminal for the process
+ * TIME The cumulative execution time for the process
+ * CMD The command name; the full command line is shown with -f
+ */
+typedef struct {
+ uint16_t width;
+ char name6[6];
+ const char *header;
+ void (*f)(char *buf, int size, const procps_status_t *ps);
+ int ps_flags;
+} ps_out_t;
+
+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
+} FIX_ALIASING;
+#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 INIT_G() do { } while (0)
+
+#if ENABLE_FEATURE_PS_TIME
+/* for ELF executables, notes are pushed before environment and args */
+static ptrdiff_t find_elf_note(ptrdiff_t findme)
+{
+ ptrdiff_t *ep = (ptrdiff_t *) environ;
+
+ while (*ep++)
+ continue;
+ 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
+static inline unsigned get_HZ_by_waiting(void)
+{
+ /* Better method? */
+ return 100;
+}
+#endif
+
+static unsigned get_kernel_HZ(void)
+{
+
+ 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();
+
+ seconds_since_boot = get_uptime();
+
+ return kernel_HZ;
+}
+#endif
+
+/* 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
+}
+
+static void func_group(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, get_cached_groupname(ps->gid), size+1);
+}
+
+static void func_comm(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, ps->comm, size+1);
+}
+
+static void func_state(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, ps->state, size+1);
+}
+
+static void func_args(char *buf, int size, const procps_status_t *ps)
+{
+ read_cmdline(buf, size+1, 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_ADDITIONAL_COLUMNS
+
+static void func_rgroup(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, get_cached_groupname(ps->rgid), size+1);
+}
+
+static void func_ruser(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, get_cached_username(ps->ruid), size+1);
+}
+
+static void func_nice(char *buf, int size, const procps_status_t *ps)
+{
+ sprintf(buf, "%*d", size, ps->niceness);
+}
+
+#endif
+
+#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
+
+/*
+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 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */
+ { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID },
+ { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID },
+ { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM },
+ { MAX_WIDTH , "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
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+ { 5 , "nice" ,"NI" ,func_nice ,PSSCAN_NICE },
+ { 8 , "rgroup","RGROUP" ,func_rgroup,PSSCAN_RUIDGID },
+ { 8 , "ruser" ,"RUSER" ,func_ruser ,PSSCAN_RUIDGID },
+// { 5 , "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ },
+#endif
+#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, but useful: */
+ { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE },
+ { 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;
+ char buf[ARRAY_SIZE(out_spec)*7 + 1];
+ char *p = buf;
+
+ for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
+ if (strncmp(name, out_spec[i].name6, 6) == 0)
+ return &out_spec[i];
+ p += sprintf(p, "%.6s,", out_spec[i].name6);
+ }
+ p[-1] = '\0';
+ bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
+}
+
+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 alloc_line_buffer(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 ((int)(width - terminal_width) > 0) {
+ /* The rest does not fit on the screen */
+ //out[i].width -= (width - terminal_width - 1);
+ out_cnt = i + 1;
+ break;
+ }
+ }
+#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);
+}
+
+#if ENABLE_SELINUX
+# define SELINUX_O_PREFIX "label,"
+# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
+#else
+# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
+#endif
+
+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;
+ char default_o[sizeof(DEFAULT_O_STR)];
+ int opt;
+ enum {
+ OPT_Z = (1 << 0),
+ OPT_o = (1 << 1),
+ OPT_a = (1 << 2),
+ OPT_A = (1 << 3),
+ OPT_d = (1 << 4),
+ OPT_e = (1 << 5),
+ OPT_f = (1 << 6),
+ OPT_l = (1 << 7),
+ OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS,
+ };
+
+ INIT_G();
+
+ // 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.
+ * -T is picked for threads (POSIX hasn't it standardized).
+ * procps v3.2.7 supports -T and shows tids as SPID column,
+ * it also supports -L where it shows tids as LWP column.
+ */
+ opt_complementary = "o::";
+ opt = getopt32(argv, "Zo:aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
+ if (opt_o) {
+ do {
+ parse_o(llist_pop(&opt_o));
+ } while (opt_o);
+ } else {
+ /* Below: parse_o() needs char*, NOT const char*, can't give it default_o */
+#if ENABLE_SELINUX
+ if (!(opt & OPT_Z) || !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
+ {
+ strcpy(default_o, DEFAULT_O_STR);
+ }
+ parse_o(default_o);
+ }
+#if ENABLE_FEATURE_SHOW_THREADS
+ if (opt & OPT_T)
+ need_flags |= PSSCAN_TASKS;
+#endif
+
+ /* 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;
+ }
+ alloc_line_buffer();
+ format_header();
+
+ p = NULL;
+ while ((p = procps_scan(p, need_flags)) != NULL) {
+ 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;
+ int psscan_flags = PSSCAN_PID | PSSCAN_UIDGID
+ | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_COMM;
+ unsigned terminal_width IF_NOT_FEATURE_PS_WIDE(= 79);
+ enum {
+ OPT_Z = (1 << 0) * ENABLE_SELINUX,
+ OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
+ OPT_l = (1 << ENABLE_SELINUX) * (1 << ENABLE_FEATURE_SHOW_THREADS) * ENABLE_FEATURE_PS_LONG,
+ };
+#if ENABLE_FEATURE_PS_LONG
+ time_t now = now;
+ long uptime;
+#endif
+ /* If we support any options, parse argv */
+#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_FEATURE_PS_LONG
+ int opts = 0;
+# if ENABLE_FEATURE_PS_WIDE
+ /* -w is a bit complicated */
+ int w_count = 0;
+ opt_complementary = "-:ww";
+ opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l")
+ "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
+ /* -w is not supported, only -Z and/or -T */
+ opt_complementary = "-";
+ opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l"));
+# endif
+
+# if ENABLE_SELINUX
+ if ((opts & OPT_Z) && is_selinux_enabled()) {
+ psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
+ | PSSCAN_STATE | PSSCAN_COMM;
+ puts(" PID CONTEXT STAT COMMAND");
+ } else
+# endif
+ if (opts & OPT_l) {
+ psscan_flags = PSSCAN_STATE | PSSCAN_UIDGID | PSSCAN_PID | PSSCAN_PPID
+ | PSSCAN_TTY | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_COMM
+ | PSSCAN_VSZ | PSSCAN_RSS;
+/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
+ * mandates for -l:
+ * -F Flags (?)
+ * S State
+ * UID,PID,PPID
+ * -C CPU usage
+ * -PRI The priority of the process; higher numbers mean lower priority
+ * -NI Nice value
+ * -ADDR The address of the process (?)
+ * SZ The size in blocks of the core image
+ * -WCHAN The event for which the process is waiting or sleeping
+ * TTY
+ * TIME The cumulative execution time
+ * CMD
+ * We don't show fields marked with '-'.
+ * We show VSZ and RSS instead of SZ.
+ * We also show STIME (standard says that -f shows it, -l doesn't).
+ */
+ puts("S UID PID PPID VSZ RSS TTY STIME TIME CMD");
+# if ENABLE_FEATURE_PS_LONG
+ now = time(NULL);
+ uptime = get_uptime();
+# endif
+ }
+ else {
+ puts(" PID USER VSZ STAT COMMAND");
+ }
+ if (opts & OPT_T) {
+ psscan_flags |= PSSCAN_TASKS;
+ }
+#endif
+
+ p = NULL;
+ while ((p = procps_scan(p, psscan_flags)) != NULL) {
+ int len;
+#if ENABLE_SELINUX
+ if (psscan_flags & PSSCAN_CONTEXT) {
+ len = printf("%5u %-32.32s %s ",
+ p->pid,
+ p->context ? p->context : "unknown",
+ p->state);
+ } else
+#endif
+ {
+ char buf6[6];
+ smart_ulltoa5(p->vsz, buf6, " mgtpezy");
+ buf6[5] = '\0';
+#if ENABLE_FEATURE_PS_LONG
+ if (opts & OPT_l) {
+ char bufr[6], stime_str[6];
+ char tty[2 * sizeof(int)*3 + 2];
+ char *endp;
+ unsigned sut = (p->stime + p->utime) / 100;
+ unsigned elapsed = uptime - (p->start_time / 100);
+ time_t start = now - elapsed;
+ struct tm *tm = localtime(&start);
+
+ smart_ulltoa5(p->rss, bufr, " mgtpezy");
+ bufr[5] = '\0';
+
+ if (p->tty_major == 136)
+ /* It should be pts/N, not ptsN, but N > 9
+ * will overflow field width...
+ */
+ endp = stpcpy(tty, "pts");
+ else
+ if (p->tty_major == 4) {
+ endp = stpcpy(tty, "tty");
+ if (p->tty_minor >= 64) {
+ p->tty_minor -= 64;
+ *endp++ = 'S';
+ }
+ }
+ else
+ endp = tty + sprintf(tty, "%d:", p->tty_major);
+ strcpy(endp, utoa(p->tty_minor));
+
+ strftime(stime_str, 6, (elapsed >= (24 * 60 * 60)) ? "%b%d" : "%H:%M", tm);
+ stime_str[5] = '\0';
+ // S UID PID PPID VSZ RSS TTY STIME TIME CMD
+ len = printf("%c %5u %5u %5u %5s %5s %-5s %s %02u:%02u:%02u ",
+ p->state[0], p->uid, p->pid, p->ppid, buf6, bufr, tty,
+ stime_str, sut / 3600, (sut % 3600) / 60, sut % 60);
+ } else
+#endif
+ {
+ const char *user = get_cached_username(p->uid);
+ len = printf("%5u %-8.8s %s %s ",
+ p->pid, user, buf6, p->state);
+ }
+ }
+
+ {
+ int sz = terminal_width - len;
+ char buf[sz + 1];
+ read_cmdline(buf, sz, p->pid, p->comm);
+ puts(buf);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ clear_username_cache();
+ return EXIT_SUCCESS;
+}
+
+#endif /* !ENABLE_DESKTOP */
diff --git a/ap/app/busybox/src/procps/ps.posix b/ap/app/busybox/src/procps/ps.posix
new file mode 100644
index 0000000..57f4fa8
--- /dev/null
+++ b/ap/app/busybox/src/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/ap/app/busybox/src/procps/pstree.c b/ap/app/busybox/src/procps/pstree.c
new file mode 100644
index 0000000..8ba3079
--- /dev/null
+++ b/ap/app/busybox/src/procps/pstree.c
@@ -0,0 +1,407 @@
+/*
+ * pstree.c - display process tree
+ *
+ * Copyright (C) 1993-2002 Werner Almesberger
+ * Copyright (C) 2002-2009 Craig Small
+ * Copyright (C) 2010 Lauri Kasanen
+ *
+ * Based on pstree (PSmisc) 22.13.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config PSTREE
+//config: bool "pstree"
+//config: default y
+//config: help
+//config: Display a tree of processes.
+
+//applet:IF_PSTREE(APPLET(pstree, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PSTREE) += pstree.o
+
+//usage:#define pstree_trivial_usage
+//usage: "[-p] [PID|USER]"
+//usage:#define pstree_full_usage "\n\n"
+//usage: "Display process tree, optionally start from USER or PID\n"
+//usage: "\n -p Show pids"
+
+#include "libbb.h"
+
+#define PROC_BASE "/proc"
+
+#define OPT_PID (1 << 0)
+
+struct child;
+
+typedef struct proc {
+ char comm[COMM_LEN + 1];
+// char flags; - unused, delete?
+ pid_t pid;
+ uid_t uid;
+ struct child *children;
+ struct proc *parent;
+ struct proc *next;
+} PROC;
+
+/* For flags above */
+//#define PFLAG_THREAD 0x01
+
+typedef struct child {
+ PROC *child;
+ struct child *next;
+} CHILD;
+
+#define empty_2 " "
+#define branch_2 "|-"
+#define vert_2 "| "
+#define last_2 "`-"
+#define single_3 "---"
+#define first_3 "-+-"
+
+struct globals {
+ /* 0-based. IOW: the number of chars we printed on current line */
+ unsigned cur_x;
+ unsigned output_width;
+
+ /* The buffers will be dynamically increased in size as needed */
+ unsigned capacity;
+ unsigned *width;
+ uint8_t *more;
+
+ PROC *list;
+
+ smallint dumped; /* used by dump_by_user */
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/*
+ * Allocates additional buffer space for width and more as needed.
+ * The first call will allocate the first buffer.
+ *
+ * bufindex the index that will be used after the call to this function.
+ */
+static void ensure_buffer_capacity(int bufindex)
+{
+ if (bufindex >= G.capacity) {
+ G.capacity += 0x100;
+ G.width = xrealloc(G.width, G.capacity * sizeof(G.width[0]));
+ G.more = xrealloc(G.more, G.capacity * sizeof(G.more[0]));
+ }
+}
+
+/* NB: this function is never called with "bad" chars
+ * (control chars or chars >= 0x7f)
+ */
+static void out_char(char c)
+{
+ G.cur_x++;
+ if (G.cur_x > G.output_width)
+ return;
+ if (G.cur_x == G.output_width)
+ c = '+';
+ putchar(c);
+}
+
+/* NB: this function is never called with "bad" chars
+ * (control chars or chars >= 0x7f)
+ */
+static void out_string(const char *str)
+{
+ while (*str)
+ out_char(*str++);
+}
+
+static void out_newline(void)
+{
+ putchar('\n');
+ G.cur_x = 0;
+}
+
+static PROC *find_proc(pid_t pid)
+{
+ PROC *walk;
+
+ for (walk = G.list; walk; walk = walk->next)
+ if (walk->pid == pid)
+ break;
+
+ return walk;
+}
+
+static PROC *new_proc(const char *comm, pid_t pid, uid_t uid)
+{
+ PROC *new = xzalloc(sizeof(*new));
+
+ strcpy(new->comm, comm);
+ new->pid = pid;
+ new->uid = uid;
+ new->next = G.list;
+
+ G.list = new;
+ return G.list;
+}
+
+static void add_child(PROC *parent, PROC *child)
+{
+ CHILD *new, **walk;
+ int cmp;
+
+ new = xmalloc(sizeof(*new));
+
+ new->child = child;
+ for (walk = &parent->children; *walk; walk = &(*walk)->next) {
+ cmp = strcmp((*walk)->child->comm, child->comm);
+ if (cmp > 0)
+ break;
+ if (cmp == 0 && (*walk)->child->uid > child->uid)
+ break;
+ }
+ new->next = *walk;
+ *walk = new;
+}
+
+static void add_proc(const char *comm, pid_t pid, pid_t ppid,
+ uid_t uid /*, char isthread*/)
+{
+ PROC *this, *parent;
+
+ this = find_proc(pid);
+ if (!this)
+ this = new_proc(comm, pid, uid);
+ else {
+ strcpy(this->comm, comm);
+ this->uid = uid;
+ }
+
+ if (pid == ppid)
+ ppid = 0;
+// if (isthread)
+// this->flags |= PFLAG_THREAD;
+
+ parent = find_proc(ppid);
+ if (!parent)
+ parent = new_proc("?", ppid, 0);
+
+ add_child(parent, this);
+ this->parent = parent;
+}
+
+static int tree_equal(const PROC *a, const PROC *b)
+{
+ const CHILD *walk_a, *walk_b;
+
+ if (strcmp(a->comm, b->comm) != 0)
+ return 0;
+ if ((option_mask32 /*& OPT_PID*/) && a->pid != b->pid)
+ return 0;
+
+ for (walk_a = a->children, walk_b = b->children;
+ walk_a && walk_b;
+ walk_a = walk_a->next, walk_b = walk_b->next
+ ) {
+ if (!tree_equal(walk_a->child, walk_b->child))
+ return 0;
+ }
+
+ return !(walk_a || walk_b);
+}
+
+static int out_args(const char *mystr)
+{
+ const char *here;
+ int strcount = 0;
+ char tmpstr[5];
+
+ for (here = mystr; *here; here++) {
+ if (*here == '\\') {
+ out_string("\\\\");
+ strcount += 2;
+ } else if (*here >= ' ' && *here < 0x7f) {
+ out_char(*here);
+ strcount++;
+ } else {
+ sprintf(tmpstr, "\\%03o", (unsigned char) *here);
+ out_string(tmpstr);
+ strcount += 4;
+ }
+ }
+
+ return strcount;
+}
+
+static void
+dump_tree(PROC *current, int level, int rep, int leaf, int last, int closing)
+{
+ CHILD *walk, *next, **scan;
+ int lvl, i, add, offset, count, comm_len, first;
+ char tmp[sizeof(int)*3 + 4];
+
+ if (!current)
+ return;
+
+ if (!leaf) {
+ for (lvl = 0; lvl < level; lvl++) {
+ i = G.width[lvl] + 1;
+ while (--i >= 0)
+ out_char(' ');
+
+ if (lvl == level - 1) {
+ if (last) {
+ out_string(last_2);
+ } else {
+ out_string(branch_2);
+ }
+ } else {
+ if (G.more[lvl + 1]) {
+ out_string(vert_2);
+ } else {
+ out_string(empty_2);
+ }
+ }
+ }
+ }
+
+ add = 0;
+ if (rep > 1) {
+ add += sprintf(tmp, "%d*[", rep);
+ out_string(tmp);
+ }
+ comm_len = out_args(current->comm);
+ if (option_mask32 /*& OPT_PID*/) {
+ comm_len += sprintf(tmp, "(%d)", (int)current->pid);
+ out_string(tmp);
+ }
+ offset = G.cur_x;
+
+ if (!current->children) {
+ while (closing--)
+ out_char(']');
+ out_newline();
+ }
+ ensure_buffer_capacity(level);
+ G.more[level] = !last;
+
+ G.width[level] = comm_len + G.cur_x - offset + add;
+ if (G.cur_x >= G.output_width) {
+ //out_string(first_3); - why? it won't print anything
+ //out_char('+');
+ out_newline();
+ return;
+ }
+
+ first = 1;
+ for (walk = current->children; walk; walk = next) {
+ count = 0;
+ next = walk->next;
+ scan = &walk->next;
+ while (*scan) {
+ if (!tree_equal(walk->child, (*scan)->child))
+ scan = &(*scan)->next;
+ else {
+ if (next == *scan)
+ next = (*scan)->next;
+ count++;
+ *scan = (*scan)->next;
+ }
+ }
+ if (first) {
+ out_string(next ? first_3 : single_3);
+ first = 0;
+ }
+
+ dump_tree(walk->child, level + 1, count + 1,
+ walk == current->children, !next,
+ closing + (count ? 1 : 0));
+ }
+}
+
+static void dump_by_user(PROC *current, uid_t uid)
+{
+ const CHILD *walk;
+
+ if (!current)
+ return;
+
+ if (current->uid == uid) {
+ if (G.dumped)
+ putchar('\n');
+ dump_tree(current, 0, 1, 1, 1, 0);
+ G.dumped = 1;
+ return;
+ }
+ for (walk = current->children; walk; walk = walk->next)
+ dump_by_user(walk->child, uid);
+}
+
+#if ENABLE_FEATURE_SHOW_THREADS
+static void handle_thread(const char *comm, pid_t pid, pid_t ppid, uid_t uid)
+{
+ char threadname[COMM_LEN + 2];
+ sprintf(threadname, "{%.*s}", COMM_LEN - 2, comm);
+ add_proc(threadname, pid, ppid, uid/*, 1*/);
+}
+#endif
+
+static void mread_proc(void)
+{
+ procps_status_t *p = NULL;
+ pid_t parent = 0;
+ int flags = PSSCAN_COMM | PSSCAN_PID | PSSCAN_PPID | PSSCAN_UIDGID | PSSCAN_TASKS;
+
+ while ((p = procps_scan(p, flags)) != NULL) {
+#if ENABLE_FEATURE_SHOW_THREADS
+ if (p->pid != p->main_thread_pid)
+ handle_thread(p->comm, p->pid, parent, p->uid);
+ else
+#endif
+ {
+ add_proc(p->comm, p->pid, p->ppid, p->uid/*, 0*/);
+ parent = p->pid;
+ }
+ }
+}
+
+int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pstree_main(int argc UNUSED_PARAM, char **argv)
+{
+ pid_t pid = 1;
+ long uid = 0;
+
+ INIT_G();
+
+ get_terminal_width_height(0, &G.output_width, NULL);
+
+ opt_complementary = "?1";
+ getopt32(argv, "p");
+ argv += optind;
+
+ if (argv[0]) {
+ if (argv[0][0] >= '0' && argv[0][0] <= '9') {
+ pid = xatoi(argv[0]);
+ } else {
+ uid = xuname2uid(argv[0]);
+ }
+ }
+
+ mread_proc();
+
+ if (!uid)
+ dump_tree(find_proc(pid), 0, 1, 1, 1, 0);
+ else {
+ dump_by_user(find_proc(1), uid);
+ if (!G.dumped) {
+ bb_error_msg_and_die("no processes found");
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(G.width);
+ free(G.more);
+ }
+ return 0;
+}
diff --git a/ap/app/busybox/src/procps/pwdx.c b/ap/app/busybox/src/procps/pwdx.c
new file mode 100644
index 0000000..7818104
--- /dev/null
+++ b/ap/app/busybox/src/procps/pwdx.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pwdx implementation for busybox
+ *
+ * Copyright (c) 2004 Nicholas Miell
+ * ported from procps by Pere Orga <gotrunks@gmail.com> 2011
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config PWDX
+//config: bool "pwdx"
+//config: default y
+//config: help
+//config: Report current working directory of a process
+
+//applet:IF_PWDX(APPLET(pwdx, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PWDX) += pwdx.o
+
+//usage:#define pwdx_trivial_usage
+//usage: "PID..."
+//usage:#define pwdx_full_usage "\n\n"
+//usage: "Show current directory for PIDs\n"
+
+#include "libbb.h"
+
+int pwdx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pwdx_main(int argc UNUSED_PARAM, char **argv)
+{
+ opt_complementary = "-1";
+ getopt32(argv, "");
+ argv += optind;
+
+ do {
+ char buf[sizeof("/proc/%u/cwd") + sizeof(int)*3];
+ unsigned pid;
+ char *s;
+ char *arg = *argv;
+
+ // Allowed on the command line:
+ // /proc/NUM
+ // NUM
+ if (strncmp(arg, "/proc/", 6) == 0)
+ arg += 6;
+
+ pid = bb_strtou(arg, NULL, 10);
+ if (errno)
+ bb_error_msg_and_die("invalid process id: '%s'", arg);
+
+ sprintf(buf, "/proc/%u/cwd", pid);
+
+ s = xmalloc_readlink(buf);
+ // "pwdx /proc/1" says "/proc/1: DIR", not "1: DIR"
+ printf("%s: %s\n", *argv, s ? s : strerror(errno == ENOENT ? ESRCH : errno));
+ free(s);
+ } while (*++argv);
+
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/renice.c b/ap/app/busybox/src/procps/renice.c
new file mode 100644
index 0000000..77f400a
--- /dev/null
+++ b/ap/app/busybox/src/procps/renice.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * renice implementation for busybox
+ *
+ * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* 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.
+ */
+
+//usage:#define renice_trivial_usage
+//usage: "{{-n INCREMENT} | PRIORITY} [[-p | -g | -u] ID...]"
+//usage:#define renice_full_usage "\n\n"
+//usage: "Change scheduling priority for a running process\n"
+//usage: "\n -n Adjust current nice value (smaller is faster)"
+//usage: "\n -p Process id(s) (default)"
+//usage: "\n -g Process group id(s)"
+//usage: "\n -u Process user name(s) and/or id(s)"
+
+#include "libbb.h"
+#include <sys/resource.h>
+
+void BUG_bad_PRIO_PROCESS(void);
+void BUG_bad_PRIO_PGRP(void);
+void BUG_bad_PRIO_USER(void);
+
+int renice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int renice_main(int argc UNUSED_PARAM, char **argv)
+{
+ 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("invalid number '%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 {
+ new_priority = adjustment;
+ }
+
+ if (setpriority(which, who, new_priority) == 0) {
+ continue;
+ }
+
+ bb_perror_msg(Xetpriority_msg, 's');
+ HAD_ERROR:
+ retval = EXIT_FAILURE;
+ }
+
+ /* 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/ap/app/busybox/src/procps/smemcap.c b/ap/app/busybox/src/procps/smemcap.c
new file mode 100644
index 0000000..9d1126a
--- /dev/null
+++ b/ap/app/busybox/src/procps/smemcap.c
@@ -0,0 +1,132 @@
+/*
+ smemcap - a tool for meaningful memory reporting
+
+ Copyright 2008-2009 Matt Mackall <mpm@selenic.com>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License version 2 or later, incorporated
+ herein by reference.
+*/
+
+//applet:IF_SMEMCAP(APPLET(smemcap, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
+
+//config:config SMEMCAP
+//config: bool "smemcap"
+//config: default y
+//config: help
+//config: smemcap is a tool for capturing process data for smem,
+//config: a memory usage statistic tool.
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+struct fileblock {
+ struct fileblock *next;
+ char data[TAR_BLOCK_SIZE];
+};
+
+static void writeheader(const char *path, struct stat *sb, int type)
+{
+ struct tar_header_t header;
+ int i, sum;
+
+ memset(&header, 0, TAR_BLOCK_SIZE);
+ strcpy(header.name, path);
+ sprintf(header.mode, "%o", sb->st_mode & 0777);
+ /* careful to not overflow fields! */
+ sprintf(header.uid, "%o", sb->st_uid & 07777777);
+ sprintf(header.gid, "%o", sb->st_gid & 07777777);
+ sprintf(header.size, "%o", (unsigned)sb->st_size);
+ sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
+ header.typeflag = type;
+ strcpy(header.magic, "ustar "); /* like GNU tar */
+
+ /* Calculate and store the checksum (the sum of all of the bytes of
+ * the header). The checksum field must be filled with blanks for the
+ * calculation. The checksum field is formatted differently from the
+ * other fields: it has 6 digits, a NUL, then a space -- rather than
+ * digits, followed by a NUL like the other fields... */
+ header.chksum[7] = ' ';
+ sum = ' ' * 7;
+ for (i = 0; i < TAR_BLOCK_SIZE; i++)
+ sum += ((unsigned char*)&header)[i];
+ sprintf(header.chksum, "%06o", sum);
+
+ xwrite(STDOUT_FILENO, &header, TAR_BLOCK_SIZE);
+}
+
+static void archivefile(const char *path)
+{
+ struct fileblock *start, *cur;
+ struct fileblock **prev = &start;
+ int fd, r;
+ unsigned size = 0;
+ struct stat s;
+
+ /* buffer the file */
+ fd = xopen(path, O_RDONLY);
+ do {
+ cur = xzalloc(sizeof(*cur));
+ *prev = cur;
+ prev = &cur->next;
+ r = full_read(fd, cur->data, TAR_BLOCK_SIZE);
+ if (r > 0)
+ size += r;
+ } while (r == TAR_BLOCK_SIZE);
+
+ /* write archive header */
+ fstat(fd, &s);
+ close(fd);
+ s.st_size = size;
+ writeheader(path, &s, '0');
+
+ /* dump file contents */
+ for (cur = start; (int)size > 0; size -= TAR_BLOCK_SIZE) {
+ xwrite(STDOUT_FILENO, cur->data, TAR_BLOCK_SIZE);
+ start = cur;
+ cur = cur->next;
+ free(start);
+ }
+}
+
+static void archivejoin(const char *sub, const char *name)
+{
+ char path[sizeof(long long)*3 + sizeof("/cmdline")];
+ sprintf(path, "%s/%s", sub, name);
+ archivefile(path);
+}
+
+//usage:#define smemcap_trivial_usage ">SMEMDATA.TAR"
+//usage:#define smemcap_full_usage "\n\n"
+//usage: "Collect memory usage data in /proc and write it to stdout"
+
+int smemcap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int smemcap_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ DIR *d;
+ struct dirent *de;
+
+ xchdir("/proc");
+ d = xopendir(".");
+
+ archivefile("meminfo");
+ archivefile("version");
+ while ((de = readdir(d)) != NULL) {
+ if (isdigit(de->d_name[0])) {
+ struct stat s;
+ memset(&s, 0, sizeof(s));
+ s.st_mode = 0555;
+ writeheader(de->d_name, &s, '5');
+ archivejoin(de->d_name, "smaps");
+ archivejoin(de->d_name, "cmdline");
+ archivejoin(de->d_name, "stat");
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ closedir(d);
+
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/sysctl.c b/ap/app/busybox/src/procps/sysctl.c
new file mode 100644
index 0000000..c6a1de2
--- /dev/null
+++ b/ap/app/busybox/src/procps/sysctl.c
@@ -0,0 +1,282 @@
+/* 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 source tree.
+ *
+ * Changelog:
+ * v1.01 - added -p <preload> to preload values from a file
+ * v1.01.1 - busybox applet aware by <solar@gentoo.org>
+ */
+
+//usage:#define sysctl_trivial_usage
+//usage: "[OPTIONS] [KEY[=VALUE]]..."
+//usage:#define sysctl_full_usage "\n\n"
+//usage: "Show/set kernel parameters\n"
+//usage: "\n -e Don't warn about unknown keys"
+//usage: "\n -n Don't show key names"
+//usage: "\n -a Show all values"
+/* Same as -a, no need to show it */
+/* //usage: "\n -A Show all values in table form" */
+//usage: "\n -w Set values"
+//usage: "\n -p FILE Set values from FILE (default /etc/sysctl.conf)"
+//usage: "\n -q Set values silently"
+//usage:
+//usage:#define sysctl_example_usage
+//usage: "sysctl [-n] [-e] variable...\n"
+//usage: "sysctl [-n] [-e] [-q] -w variable=value...\n"
+//usage: "sysctl [-n] [-e] -a\n"
+//usage: "sysctl [-n] [-e] [-q] -p file (default /etc/sysctl.conf)\n"
+//usage: "sysctl [-n] [-e] -A\n"
+
+#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,
+/* TODO: procps 3.2.8 seems to not require -w for KEY=VAL to work: */
+ FLAG_WRITE = 1 << 5,
+ FLAG_QUIET = 1 << 6,
+};
+#define OPTION_STR "neAapwq"
+
+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) {
+ *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_QUIET)) {
+ 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("/proc/sys") - 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")
+// can it be fixed by removing PARSE_COLLAPSE bit?
+ 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("/proc/sys") - 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/ap/app/busybox/src/procps/top.c b/ap/app/busybox/src/procps/top.c
new file mode 100644
index 0000000..2908bd3
--- /dev/null
+++ b/ap/app/busybox/src/procps/top.c
@@ -0,0 +1,1291 @@
+/* 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>
+ *
+ * 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
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+/* How to snapshot /proc for debugging top problems:
+ * for f in /proc/[0-9]*""/stat; do
+ * n=${f#/proc/}
+ * n=${n%/stat}_stat
+ * cp $f $n
+ * done
+ * cp /proc/stat /proc/meminfo /proc/loadavg .
+ * top -bn1 >top.out
+ *
+ * ...and how to run top on it on another machine:
+ * rm -rf proc; mkdir proc
+ * for f in [0-9]*_stat; do
+ * p=${f%_stat}
+ * mkdir -p proc/$p
+ * cp $f proc/$p/stat
+ * done
+ * cp stat meminfo loadavg proc
+ * chroot . ./top -bn1 >top1.out
+ */
+
+//config:config TOP
+//config: bool "top"
+//config: default y
+//config: help
+//config: The top program provides a dynamic real-time view of a running
+//config: system.
+//config:
+//config:config FEATURE_TOP_CPU_USAGE_PERCENTAGE
+//config: bool "Show CPU per-process usage percentage"
+//config: default y
+//config: depends on TOP
+//config: help
+//config: Make top display CPU usage for each process.
+//config: This adds about 2k.
+//config:
+//config:config FEATURE_TOP_CPU_GLOBAL_PERCENTS
+//config: bool "Show CPU global usage percentage"
+//config: default y
+//config: depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
+//config: help
+//config: Makes top display "CPU: NN% usr NN% sys..." line.
+//config: This adds about 0.5k.
+//config:
+//config:config FEATURE_TOP_SMP_CPU
+//config: bool "SMP CPU usage display ('c' key)"
+//config: default y
+//config: depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
+//config: help
+//config: Allow 'c' key to switch between individual/cumulative CPU stats
+//config: This adds about 0.5k.
+//config:
+//config:config FEATURE_TOP_DECIMALS
+//config: bool "Show 1/10th of a percent in CPU/mem statistics"
+//config: default y
+//config: depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
+//config: help
+//config: Show 1/10th of a percent in CPU/mem statistics.
+//config: This adds about 0.3k.
+//config:
+//config:config FEATURE_TOP_SMP_PROCESS
+//config: bool "Show CPU process runs on ('j' field)"
+//config: default y
+//config: depends on TOP
+//config: help
+//config: Show CPU where process was last found running on.
+//config: This is the 'j' field.
+//config:
+//config:config FEATURE_TOPMEM
+//config: bool "Topmem command ('s' key)"
+//config: default y
+//config: depends on TOP
+//config: help
+//config: Enable 's' in top (gives lots of memory info).
+
+#include "libbb.h"
+
+
+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;
+
+/* 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;
+
+typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
+
+
+enum { SORT_DEPTH = 3 };
+
+
+struct globals {
+ top_status_t *top;
+ int ntop;
+ smallint inverted;
+#if ENABLE_FEATURE_TOPMEM
+ smallint sort_field;
+#endif
+#if ENABLE_FEATURE_TOP_SMP_CPU
+ smallint smp_cpu_info; /* one/many cpu info lines? */
+#endif
+ unsigned lines; /* screen height */
+#if ENABLE_FEATURE_USE_TERMIOS
+ struct termios initial_settings;
+ int scroll_ofs;
+#define G_scroll_ofs G.scroll_ofs
+#else
+#define G_scroll_ofs 0
+#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
+#if ENABLE_FEATURE_USE_TERMIOS
+ char kbd_input[KEYCODE_BUFFER_SIZE];
+#endif
+ char line_buf[80];
+}; //FIX_ALIASING; - large code growth
+enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
+#define G (*(struct globals*)&bb_common_bufsiz1)
+struct BUG_bad_size {
+ char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+ char BUG_line_buf_too_small[LINE_BUF_SIZE > 80 ? 1 : -1];
+};
+#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 )
+#define INIT_G() do { } while (0)
+
+enum {
+ OPT_d = (1 << 0),
+ OPT_n = (1 << 1),
+ OPT_b = (1 << 2),
+ OPT_m = (1 << 3),
+ OPT_EOF = (1 << 4), /* 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)
+{
+ /* Buggy wrt pids with high bit set */
+ /* (linux pids are in [1..2^15-1]) */
+ return (Q->pid - P->pid);
+}
+#endif
+
+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 > */
+}
+
+
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+
+static int pcpu_sort(top_status_t *P, top_status_t *Q)
+{
+ /* Buggy wrt ticks with high bit set */
+ /* Affects only processes for which ticks overflow */
+ return (int)Q->pcpu - (int)P->pcpu;
+}
+
+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 mult_lvl_cmp(void* a, void* b)
+{
+ int i, cmp_val;
+
+ for (i = 0; i < SORT_DEPTH; i++) {
+ cmp_val = (*sort_function[i])(a, b);
+ if (cmp_val != 0)
+ break;
+ }
+ return inverted ? -cmp_val : 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;
+ }
+
+ return ret;
+}
+
+static void get_jiffy_counts(void)
+{
+ FILE* fp = xfopen_for_read("stat");
+
+ /* 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");
+
+#if !ENABLE_FEATURE_TOP_SMP_CPU
+ fclose(fp);
+ return;
+#else
+ if (!smp_cpu_info) {
+ fclose(fp);
+ return;
+ }
+
+ 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;
+
+ 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;
+
+ /* 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)
+{
+ 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);
+ /*
+ * Make a pass through the data to get stats.
+ */
+ /* 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; */
+ }
+
+ /*
+ * Save cur frame's information.
+ */
+ 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_TOTAL_DIFF do { \
+ total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
+ if (total_diff == 0) total_diff = 1; \
+} while (0)
+
+# 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
+# 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
+ CALC_TOTAL_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
+
+static unsigned long display_header(int scr_width, int *lines_rem_p)
+{
+ FILE *fp;
+ char buf[80];
+ char scrbuf[80];
+ unsigned long total, used, mfree, shared, buffers, cached;
+
+ /* read memory info */
+ fp = xfopen_for_read("meminfo");
+
+ /*
+ * 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);
+
+ /* 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);
+ /* go to top & clear to the end of screen */
+ printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[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;
+}
+
+static NOINLINE void display_process_list(int lines_rem, int scr_width)
+{
+ 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 tmp_unsigned;
+ unsigned pcpu_shift, pcpu_scale, pcpu_half;
+ unsigned busy_jifs;
+#endif
+
+ /* what info of the processes is shown */
+ printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
+ " PID PPID USER STAT VSZ %VSZ"
+ IF_FEATURE_TOP_SMP_PROCESS(" CPU")
+ IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
+ " 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
+# define UPSCALE 100
+# define CALC_STAT(name, val) unsigned name = (val)
+# define SHOW_STAT(name) name
+# define FMT "%4u%%"
+#endif
+ /*
+ * %VSZ = 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;
+
+ /*
+ * 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;
+ if (pcpu_scale == 0)
+ pcpu_scale = 1;
+ while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
+ pcpu_scale *= 4;
+ pcpu_shift += 2;
+ }
+ tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
+ if (tmp_unsigned != 0)
+ pcpu_scale /= tmp_unsigned;
+ /* 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
+
+ /* Ok, all preliminary data is ready, go through the list */
+ scr_width += 2; /* account for leading '\n' and trailing NUL */
+ if (lines_rem > ntop - G_scroll_ofs)
+ lines_rem = ntop - G_scroll_ofs;
+ s = top + G_scroll_ofs;
+ 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(vsz_str_buf, "%7ld", s->vsz);
+ /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
+ col = snprintf(line_buf, scr_width,
+ "\n" "%5u%6u %-8.8s %s%s" FMT
+ IF_FEATURE_TOP_SMP_PROCESS(" %3d")
+ IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
+ " ",
+ s->pid, s->ppid, get_cached_username(s->uid),
+ s->state, vsz_str_buf,
+ SHOW_STAT(pmem)
+ IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
+ IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
+ );
+ if ((int)(col + 1) < scr_width)
+ read_cmdline(line_buf + col, scr_width - col, 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_all();
+}
+#undef UPSCALE
+#undef SHOW_STAT
+#undef CALC_STAT
+#undef FMT
+
+static void clearmems(void)
+{
+ clear_username_cache();
+ free(top);
+ top = NULL;
+}
+
+#if ENABLE_FEATURE_USE_TERMIOS
+
+static void reset_term(void)
+{
+ if (!OPT_BATCH_MODE)
+ tcsetattr_stdin_TCSANOW(&initial_settings);
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ clearmems();
+# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ free(prev_hist);
+# endif
+ }
+}
+
+static void sig_catcher(int sig)
+{
+ reset_term();
+ kill_myself_with_sig(sig);
+}
+
+#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
+
+static int topmem_sort(char *a, char *b)
+{
+ 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 = ((topmem_status_t*)a)->dirty;
+ r = ((topmem_status_t*)b)->dirty;
+ }
+ /* 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;
+}
+
+/* display header info (meminfo / loadavg) */
+static void display_topmem_header(int scr_width, int *lines_rem_p)
+{
+ enum {
+ TOTAL = 0, MFREE, BUF, CACHE,
+ SWAPTOTAL, SWAPFREE, DIRTY,
+ MWRITE, ANON, MAP, SLAB,
+ NUM_FIELDS
+ };
+ static const char match[NUM_FIELDS][12] = {
+ "\x09" "MemTotal:", // TOTAL
+ "\x08" "MemFree:", // MFREE
+ "\x08" "Buffers:", // BUF
+ "\x07" "Cached:", // CACHE
+ "\x0a" "SwapTotal:", // SWAPTOTAL
+ "\x09" "SwapFree:", // SWAPFREE
+ "\x06" "Dirty:", // DIRTY
+ "\x0a" "Writeback:", // MWRITE
+ "\x0a" "AnonPages:", // ANON
+ "\x07" "Mapped:", // MAP
+ "\x05" "Slab:", // SLAB
+ };
+ char meminfo_buf[4 * 1024];
+ const char *Z[NUM_FIELDS];
+ unsigned i;
+ int sz;
+
+ for (i = 0; i < NUM_FIELDS; i++)
+ Z[i] = "?";
+
+ /* read memory info */
+ sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1);
+ if (sz >= 0) {
+ char *p = meminfo_buf;
+ meminfo_buf[sz] = '\0';
+ /* Note that fields always appear in the match[] order */
+ for (i = 0; i < NUM_FIELDS; i++) {
+ char *found = strstr(p, match[i] + 1);
+ if (found) {
+ /* Cut "NNNN" out of " NNNN kb" */
+ char *s = skip_whitespace(found + match[i][0]);
+ p = skip_non_whitespace(s);
+ *p++ = '\0';
+ Z[i] = s;
+ }
+ }
+ }
+
+ snprintf(line_buf, LINE_BUF_SIZE,
+ "Mem total:%s anon:%s map:%s free:%s",
+ Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]);
+ printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf);
+
+ snprintf(line_buf, LINE_BUF_SIZE,
+ " slab:%s buf:%s cache:%s dirty:%s write:%s",
+ Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]);
+ printf("%.*s\n", scr_width, line_buf);
+
+ snprintf(line_buf, LINE_BUF_SIZE,
+ "Swap total:%s free:%s", // TODO: % used?
+ Z[SWAPTOTAL], Z[SWAPFREE]);
+ printf("%.*s\n", scr_width, line_buf);
+
+ (*lines_rem_p) -= 3;
+}
+
+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 + G_scroll_ofs;
+
+ display_topmem_header(scr_width, &lines_rem);
+ strcpy(line_buf, HDR_STR " COMMAND");
+ line_buf[11 + sort_field * 6] = "^_"[inverted];
+ printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
+ lines_rem--;
+
+ if (lines_rem > ntop - G_scroll_ofs)
+ lines_rem = ntop - G_scroll_ofs;
+ 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_all();
+#undef HDR_STR
+#undef MIN_WIDTH
+}
+
+#else
+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
+ | PSSCAN_CPU
+ | PSSCAN_UIDGID,
+ TOPMEM_MASK = 0
+ | PSSCAN_PID
+ | PSSCAN_SMAPS
+ | PSSCAN_COMM,
+ EXIT_MASK = (unsigned)-1,
+};
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static unsigned handle_input(unsigned scan_mask, unsigned interval)
+{
+ struct pollfd pfd[1];
+
+ if (option_mask32 & OPT_EOF) {
+ /* EOF on stdin ("top </dev/null") */
+ sleep(interval);
+ return scan_mask;
+ }
+
+ pfd[0].fd = 0;
+ pfd[0].events = POLLIN;
+
+ while (1) {
+ int32_t c;
+
+ c = read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
+ if (c == -1 && errno != EAGAIN) {
+ /* error/EOF */
+ option_mask32 |= OPT_EOF;
+ break;
+ }
+ interval = 0;
+
+ if (c == initial_settings.c_cc[VINTR])
+ return EXIT_MASK;
+ if (c == initial_settings.c_cc[VEOF])
+ return EXIT_MASK;
+
+ if (c == KEYCODE_UP) {
+ G_scroll_ofs--;
+ goto normalize_ofs;
+ }
+ if (c == KEYCODE_DOWN) {
+ G_scroll_ofs++;
+ goto normalize_ofs;
+ }
+ if (c == KEYCODE_HOME) {
+ G_scroll_ofs = 0;
+ break;
+ }
+ if (c == KEYCODE_END) {
+ G_scroll_ofs = ntop - G.lines / 2;
+ goto normalize_ofs;
+ }
+ if (c == KEYCODE_PAGEUP) {
+ G_scroll_ofs -= G.lines / 2;
+ goto normalize_ofs;
+ }
+ if (c == KEYCODE_PAGEDOWN) {
+ G_scroll_ofs += G.lines / 2;
+ normalize_ofs:
+ if (G_scroll_ofs >= ntop)
+ G_scroll_ofs = ntop - 1;
+ if (G_scroll_ofs < 0)
+ G_scroll_ofs = 0;
+ break;
+ }
+
+ c |= 0x20; /* lowercase */
+ if (c == 'q')
+ return EXIT_MASK;
+
+ if (c == 'n') {
+ IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+ sort_function[0] = pid_sort;
+ continue;
+ }
+ if (c == 'm') {
+ IF_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;
+# endif
+ continue;
+ }
+# if ENABLE_FEATURE_SHOW_THREADS
+ if (c == 'h'
+ IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
+ ) {
+ scan_mask ^= PSSCAN_TASKS;
+ continue;
+ }
+# endif
+# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ if (c == 'p') {
+ IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+ sort_function[0] = pcpu_sort;
+ sort_function[1] = mem_sort;
+ sort_function[2] = time_sort;
+ continue;
+ }
+ if (c == 't') {
+ IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+ sort_function[0] = time_sort;
+ sort_function[1] = mem_sort;
+ sort_function[2] = pcpu_sort;
+ continue;
+ }
+# 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;
+ continue;
+ }
+# endif
+ if (c == 'r') {
+ inverted ^= 1;
+ continue;
+ }
+# 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();
+ continue;
+ }
+# endif
+# endif
+ break; /* unknown key -> force refresh */
+ }
+
+ return scan_mask;
+}
+#endif
+
+//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
+//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
+//usage:#else
+//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...)
+//usage:#endif
+//usage:#define top_trivial_usage
+//usage: "[-b] [-nCOUNT] [-dSECONDS]" IF_FEATURE_TOPMEM(" [-m]")
+//usage:#define top_full_usage "\n\n"
+//usage: "Provide a view of process activity in real time."
+//usage: "\n""Read the status of all processes from /proc each SECONDS"
+//usage: "\n""and display a screenful of them."
+//usage: "\n""Keys:"
+//usage: "\n"" N/M"
+//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
+//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
+//usage: ": " IF_FEATURE_TOPMEM("show CPU usage, ") "sort by pid/mem"
+//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
+//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
+//usage: IF_FEATURE_TOPMEM(
+//usage: "\n"" S: show memory"
+//usage: )
+//usage: "\n"" R: reverse sort"
+//usage: IF_SHOW_THREADS_OR_TOP_SMP(
+//usage: "\n"" "
+//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads")
+//usage: IF_FEATURE_SHOW_THREADS(IF_FEATURE_TOP_SMP_CPU(", "))
+//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
+//usage: )
+//usage: "\n"" Q,^C: exit"
+//usage: "\n"
+//usage: "\n""Options:"
+//usage: "\n"" -b Batch mode"
+//usage: "\n"" -n N Exit after N iterations"
+//usage: "\n"" -d N Delay between updates"
+//usage: IF_FEATURE_TOPMEM(
+//usage: "\n"" -m Same as 's' key"
+//usage: )
+
+/* Interactive testing:
+ * echo sss | ./busybox top
+ * - shows memory screen
+ * echo sss | ./busybox top -bn1 >mem
+ * - saves memory screen - the *whole* list, not first NROWS processes!
+ * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
+ * - saves several different screens, and exits
+ *
+ * TODO: -i STRING param as a better alternative?
+ */
+
+int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int top_main(int argc UNUSED_PARAM, char **argv)
+{
+ int iterations;
+ unsigned col;
+ unsigned interval;
+ char *str_interval, *str_iterations;
+ unsigned scan_mask = TOP_MASK;
+#if ENABLE_FEATURE_USE_TERMIOS
+ struct termios new_settings;
+#endif
+
+ 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
+
+ /* all args are options; -n NUM */
+ opt_complementary = "-"; /* options can be specified w/o dash */
+ col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations);
+#if ENABLE_FEATURE_TOPMEM
+ if (col & OPT_m) /* -m (busybox specific) */
+ scan_mask = TOPMEM_MASK;
+#endif
+ if (col & OPT_d) {
+ /* work around for "-d 1" -> "-d -1" done by getopt32
+ * (opt_complementary == "-" does this) */
+ 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_TOP_CPU_USAGE_PERCENTAGE
+ sort_function[0] = pcpu_sort;
+ sort_function[1] = mem_sort;
+ sort_function[2] = time_sort;
+#else
+ sort_function[0] = mem_sort;
+#endif
+
+ if (OPT_BATCH_MODE) {
+ option_mask32 |= OPT_EOF;
+ }
+#if ENABLE_FEATURE_USE_TERMIOS
+ else {
+ 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);
+ tcsetattr_stdin_TCSANOW(&new_settings);
+ }
+
+ bb_signals(BB_FATAL_SIGS, sig_catcher);
+
+ /* Eat initial input, if any */
+ scan_mask = handle_input(scan_mask, 0);
+#endif
+
+ while (scan_mask != EXIT_MASK) {
+ procps_status_t *p = NULL;
+
+ if (OPT_BATCH_MODE) {
+ G.lines = INT_MAX;
+ col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
+ } else {
+ G.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, &G.lines);
+ if (G.lines < 5 || col < 10) {
+ sleep(interval);
+ continue;
+ }
+#endif
+ if (col > LINE_BUF_SIZE - 2)
+ col = LINE_BUF_SIZE - 2;
+ }
+
+ /* read process IDs & status for all the processes */
+ ntop = 0;
+ while ((p = procps_scan(p, scan_mask)) != NULL) {
+ int n;
+#if ENABLE_FEATURE_TOPMEM
+ if (scan_mask != TOPMEM_MASK)
+#endif
+ {
+ 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
+ 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
+ }
+#if ENABLE_FEATURE_TOPMEM
+ else { /* TOPMEM */
+ if (!(p->smaps.mapped_ro | p->smaps.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->smaps.mapped_rw + p->smaps.mapped_ro;
+ topmem[n].vszrw = p->smaps.mapped_rw;
+ topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
+ topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
+ topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
+ topmem[n].dirty_sh = p->smaps.shared_dirty;
+ topmem[n].stack = p->smaps.stack;
+ }
+#endif
+ } /* end of "while we read /proc" */
+ if (ntop == 0) {
+ bb_error_msg("no process info in /proc");
+ break;
+ }
+
+ if (scan_mask != TOPMEM_MASK) {
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ if (!prev_hist_count) {
+ do_stats();
+ usleep(100000);
+ 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(top_status_t), (void*)(sort_function[0]));
+#endif
+ }
+#if ENABLE_FEATURE_TOPMEM
+ else { /* TOPMEM */
+ qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
+ }
+#endif
+ if (scan_mask != TOPMEM_MASK)
+ display_process_list(G.lines, col);
+#if ENABLE_FEATURE_TOPMEM
+ else
+ display_topmem_process_list(G.lines, col);
+#endif
+ clearmems();
+ if (iterations >= 0 && !--iterations)
+ break;
+#if !ENABLE_FEATURE_USE_TERMIOS
+ sleep(interval);
+#else
+ scan_mask = handle_input(scan_mask, interval);
+#endif /* FEATURE_USE_TERMIOS */
+ } /* end of "while (not Q)" */
+
+ bb_putchar('\n');
+#if ENABLE_FEATURE_USE_TERMIOS
+ reset_term();
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/ap/app/busybox/src/procps/uptime.c b/ap/app/busybox/src/procps/uptime.c
new file mode 100644
index 0000000..778812a
--- /dev/null
+++ b/ap/app/busybox/src/procps/uptime.c
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini uptime implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* 2011 Pere Orga <gotrunks@gmail.com>
+ *
+ * Added FEATURE_UPTIME_UTMP_SUPPORT flag.
+ */
+
+/* getopt not needed */
+
+//config:config UPTIME
+//config: bool "uptime"
+//config: default y
+//config: select PLATFORM_LINUX #sysinfo()
+//config: help
+//config: uptime gives a one line display of the current time, how long
+//config: the system has been running, how many users are currently logged
+//config: on, and the system load averages for the past 1, 5, and 15 minutes.
+//config:
+//config:config FEATURE_UPTIME_UTMP_SUPPORT
+//config: bool "Support for showing the number of users"
+//config: default y
+//config: depends on UPTIME && FEATURE_UTMP
+//config: help
+//config: Makes uptime display the number of users currently logged on.
+
+//usage:#define uptime_trivial_usage
+//usage: ""
+//usage:#define uptime_full_usage "\n\n"
+//usage: "Display the time since the last boot"
+//usage:
+//usage:#define uptime_example_usage
+//usage: "$ uptime\n"
+//usage: " 1:55pm up 2:30, load average: 0.09, 0.04, 0.00\n"
+
+#include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
+
+#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) (unsigned)((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1 - 1)) * 100)
+
+
+int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ unsigned updays, uphours, upminutes;
+ struct sysinfo info;
+ struct tm *current_time;
+ time_t current_secs;
+
+ time(¤t_secs);
+ current_time = localtime(¤t_secs);
+
+ sysinfo(&info);
+
+ printf(" %02u:%02u:%02u up ",
+ current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
+ updays = (unsigned) info.uptime / (unsigned)(60*60*24);
+ if (updays)
+ printf("%u day%s, ", updays, (updays != 1) ? "s" : "");
+ upminutes = (unsigned) info.uptime / (unsigned)60;
+ uphours = (upminutes / (unsigned)60) % (unsigned)24;
+ upminutes %= 60;
+ if (uphours)
+ printf("%2u:%02u", uphours, upminutes);
+ else
+ printf("%u min", upminutes);
+
+#if ENABLE_FEATURE_UPTIME_UTMP_SUPPORT
+ {
+ struct utmp *ut;
+ unsigned users = 0;
+ while ((ut = getutent()) != NULL) {
+ if ((ut->ut_type == USER_PROCESS) && (ut->ut_name[0] != '\0'))
+ users++;
+ }
+ printf(", %u users", users);
+ }
+#endif
+
+ printf(", load average: %u.%02u, %u.%02u, %u.%02u\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/ap/app/busybox/src/procps/watch.c b/ap/app/busybox/src/procps/watch.c
new file mode 100644
index 0000000..36af1cc
--- /dev/null
+++ b/ap/app/busybox/src/procps/watch.c
@@ -0,0 +1,98 @@
+/* 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 source tree.
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+/* BB_AUDIT GNU defects -- only option -n is supported. */
+
+//usage:#define watch_trivial_usage
+//usage: "[-n SEC] [-t] PROG ARGS"
+//usage:#define watch_full_usage "\n\n"
+//usage: "Run PROG periodically\n"
+//usage: "\n -n Loop period in seconds (default 2)"
+//usage: "\n -t Don't print header"
+//usage:
+//usage:#define watch_example_usage
+//usage: "$ watch date\n"
+//usage: "Mon Dec 17 10:31:40 GMT 2000\n"
+//usage: "Mon Dec 17 10:31:42 GMT 2000\n"
+//usage: "Mon Dec 17 10:31:44 GMT 2000"
+
+#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;
+
+#if 0 // maybe ENABLE_DESKTOP?
+ // procps3 compat - "echo TEST | watch cat" doesn't show TEST:
+ close(STDIN_FILENO);
+ xopen("/dev/null", O_RDONLY);
+#endif
+
+ 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) {
+ /* home; clear to the end of screen */
+ printf("\033[H""\033[J");
+ if (!(opt & 0x2)) { // no -t
+ const unsigned time_len = sizeof("1234-67-90 23:56:89");
+ time_t t;
+
+ // STDERR_FILENO is procps3 compat:
+ // "watch ls 2>/dev/null" does not detect tty size
+ get_terminal_width_height(STDERR_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));
+
+ // compat: empty line between header and cmd output
+ printf("%s\n\n", header);
+ }
+ fflush_all();
+ // 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 :)
+}