[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(&current_secs);
+	current_time = localtime(&current_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 :)
+}