aboutsummaryrefslogtreecommitdiff
path: root/procps
diff options
context:
space:
mode:
Diffstat (limited to 'procps')
-rw-r--r--procps/Config.in121
-rw-r--r--procps/Kbuild16
-rw-r--r--procps/free.c68
-rw-r--r--procps/fuser.c366
-rw-r--r--procps/kill.c155
-rw-r--r--procps/pidof.c90
-rw-r--r--procps/ps.c388
-rw-r--r--procps/ps.posix175
-rw-r--r--procps/renice.c126
-rw-r--r--procps/sysctl.c326
-rw-r--r--procps/top.c566
-rw-r--r--procps/uptime.c59
12 files changed, 2456 insertions, 0 deletions
diff --git a/procps/Config.in b/procps/Config.in
new file mode 100644
index 000000000..20d5f9bf2
--- /dev/null
+++ b/procps/Config.in
@@ -0,0 +1,121 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Process Utilities"
7
8config FREE
9 bool "free"
10 default n
11 help
12 free displays the total amount of free and used physical and swap
13 memory in the system, as well as the buffers used by the kernel.
14 The shared memory column should be ignored; it is obsolete.
15
16config FUSER
17 bool "fuser"
18 default n
19 help
20 fuser lists all PIDs (Process IDs) that currently have a given
21 file open. fuser can also list all PIDs that have a given network
22 (TCP or UDP) port open.
23
24config KILL
25 bool "kill"
26 default n
27 help
28 The command kill sends the specified signal to the specified
29 process or process group. If no signal is specified, the TERM
30 signal is sent.
31
32config KILLALL
33 bool "killall"
34 default n
35 depends on KILL
36 help
37 killall sends a signal to all processes running any of the
38 specified commands. If no signal name is specified, SIGTERM is
39 sent.
40
41config KILLALL5
42 bool "killall5"
43 default n
44 depends on KILL
45
46config PIDOF
47 bool "pidof"
48 default n
49 help
50 Pidof finds the process id's (pids) of the named programs. It prints
51 those id's on the standard output.
52
53config FEATURE_PIDOF_SINGLE
54 bool "Enable argument for single shot (-s)"
55 default n
56 depends on PIDOF
57 help
58 Support argument '-s' for returning only the first pid found.
59
60config FEATURE_PIDOF_OMIT
61 bool "Enable argument for omitting pids (-o)"
62 default n
63 depends on PIDOF
64 help
65 Support argument '-o' for omitting the given pids in output.
66 The special pid %PPID can be used to name the parent process
67 of the pidof, in other words the calling shell or shell script.
68
69config PS
70 bool "ps"
71 default n
72 help
73 ps gives a snapshot of the current processes.
74
75config FEATURE_PS_WIDE
76 bool "Enable argument for wide output (-w)"
77 default n
78 depends on PS
79 help
80 Support argument 'w' for wide output.
81 If given once, 132 chars are printed and given more than
82 one, the length is unlimited.
83
84config RENICE
85 bool "renice"
86 default n
87 help
88 Renice alters the scheduling priority of one or more running
89 processes.
90
91config BB_SYSCTL
92 bool "sysctl"
93 default n
94 help
95 Configure kernel parameters at runtime.
96
97config TOP
98 bool "top"
99 default n
100 help
101 The top program provides a dynamic real-time view of a running
102 system.
103
104config FEATURE_TOP_CPU_USAGE_PERCENTAGE
105 bool "Support showing CPU usage percentage (add 2k bytes)"
106 default y
107 depends on TOP
108 help
109 Make top display CPU usage.
110
111config UPTIME
112 bool "uptime"
113 default n
114 help
115 uptime gives a one line display of the current time, how long
116 the system has been running, how many users are currently logged
117 on, and the system load averages for the past 1, 5, and 15 minutes.
118
119
120endmenu
121
diff --git a/procps/Kbuild b/procps/Kbuild
new file mode 100644
index 000000000..6a9a86637
--- /dev/null
+++ b/procps/Kbuild
@@ -0,0 +1,16 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
4#
5# Licensed under the GPL v2, see the file LICENSE in this tarball.
6
7lib-y:=
8lib-$(CONFIG_FREE) += free.o
9lib-$(CONFIG_KILL) += kill.o
10lib-$(CONFIG_PIDOF) += pidof.o
11lib-$(CONFIG_PS) += ps.o
12lib-$(CONFIG_RENICE) += renice.o
13lib-$(CONFIG_BB_SYSCTL) += sysctl.o
14lib-$(CONFIG_TOP) += top.o
15lib-$(CONFIG_UPTIME) += uptime.o
16lib-$(CONFIG_FUSER) += fuser.o
diff --git a/procps/free.c b/procps/free.c
new file mode 100644
index 000000000..84432e0d1
--- /dev/null
+++ b/procps/free.c
@@ -0,0 +1,68 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini free implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
8 */
9
10/* getopt not needed */
11
12#include "busybox.h"
13
14int free_main(int argc, char **argv)
15{
16 struct sysinfo info;
17 sysinfo(&info);
18
19 /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
20 if (info.mem_unit==0) {
21 info.mem_unit=1;
22 }
23 if ( info.mem_unit == 1 ) {
24 info.mem_unit=1024;
25
26 /* TODO: Make all this stuff not overflow when mem >= 4 Gib */
27 info.totalram/=info.mem_unit;
28 info.freeram/=info.mem_unit;
29#ifndef __uClinux__
30 info.totalswap/=info.mem_unit;
31 info.freeswap/=info.mem_unit;
32#endif
33 info.sharedram/=info.mem_unit;
34 info.bufferram/=info.mem_unit;
35 } else {
36 info.mem_unit/=1024;
37 /* TODO: Make all this stuff not overflow when mem >= 4 Gib */
38 info.totalram*=info.mem_unit;
39 info.freeram*=info.mem_unit;
40#ifndef __uClinux__
41 info.totalswap*=info.mem_unit;
42 info.freeswap*=info.mem_unit;
43#endif
44 info.sharedram*=info.mem_unit;
45 info.bufferram*=info.mem_unit;
46 }
47
48 if (argc > 1 && **(argv + 1) == '-')
49 bb_show_usage();
50
51 printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free",
52 "shared", "buffers");
53
54 printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram,
55 info.totalram-info.freeram, info.freeram,
56 info.sharedram, info.bufferram);
57
58#ifndef __uClinux__
59 printf("%6s%13ld%13ld%13ld\n", "Swap:", info.totalswap,
60 info.totalswap-info.freeswap, info.freeswap);
61
62 printf("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap,
63 (info.totalram-info.freeram)+(info.totalswap-info.freeswap),
64 info.freeram+info.freeswap);
65#endif
66 return EXIT_SUCCESS;
67}
68
diff --git a/procps/fuser.c b/procps/fuser.c
new file mode 100644
index 000000000..4628cdf5c
--- /dev/null
+++ b/procps/fuser.c
@@ -0,0 +1,366 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * tiny fuser implementation
4 *
5 * Copyright 2004 Tony J. White
6 *
7 * May be distributed under the conditions of the
8 * GNU Library General Public License
9 */
10
11#include "busybox.h"
12
13#define FUSER_PROC_DIR "/proc"
14#define FUSER_MAX_LINE 255
15
16#define FUSER_OPT_MOUNT 1
17#define FUSER_OPT_KILL 2
18#define FUSER_OPT_SILENT 4
19#define FUSER_OPT_IP6 8
20#define FUSER_OPT_IP4 16
21
22typedef struct inode_list {
23 ino_t inode;
24 dev_t dev;
25 struct inode_list *next;
26} inode_list;
27
28typedef struct pid_list {
29 pid_t pid;
30 struct pid_list *next;
31} pid_list;
32
33static int fuser_option(char *option)
34{
35 int opt = 0;
36
37 if(!(strlen(option))) return 0;
38 if(option[0] != '-') return 0;
39 ++option;
40 while(*option != '\0') {
41 if(*option == 'm') opt |= FUSER_OPT_MOUNT;
42 else if(*option == 'k') opt |= FUSER_OPT_KILL;
43 else if(*option == 's') opt |= FUSER_OPT_SILENT;
44 else if(*option == '6') opt |= FUSER_OPT_IP6;
45 else if(*option == '4') opt |= FUSER_OPT_IP4;
46 else {
47 bb_error_msg_and_die(
48 "Unsupported option '%c'", *option);
49 }
50 ++option;
51 }
52 return opt;
53}
54
55static int fuser_file_to_dev_inode(const char *filename,
56 dev_t *dev, ino_t *inode)
57{
58 struct stat f_stat;
59 if((stat(filename, &f_stat)) < 0) return 0;
60 *inode = f_stat.st_ino;
61 *dev = f_stat.st_dev;
62 return 1;
63}
64
65static int fuser_find_socket_dev(dev_t *dev)
66{
67 int fd = socket(PF_INET, SOCK_DGRAM,0);
68 struct stat buf;
69
70 if (fd >= 0 && (fstat(fd, &buf)) == 0) {
71 *dev = buf.st_dev;
72 close(fd);
73 return 1;
74 }
75 return 0;
76}
77
78static int fuser_parse_net_arg(const char *filename,
79 const char **proto, int *port)
80{
81 char path[sizeof(FUSER_PROC_DIR)+12], tproto[5];
82
83 if((sscanf(filename, "%d/%4s", port, tproto)) != 2) return 0;
84 sprintf(path, "%s/net/%s", FUSER_PROC_DIR, tproto);
85 if((access(path, R_OK)) != 0) return 0;
86 *proto = xstrdup(tproto);
87 return 1;
88}
89
90static int fuser_add_pid(pid_list *plist, pid_t pid)
91{
92 pid_list *curr = NULL, *last = NULL;
93
94 if(plist->pid == 0) plist->pid = pid;
95 curr = plist;
96 while(curr != NULL) {
97 if(curr->pid == pid) return 1;
98 last = curr;
99 curr = curr->next;
100 }
101 curr = xmalloc(sizeof(pid_list));
102 last->next = curr;
103 curr->pid = pid;
104 curr->next = NULL;
105 return 1;
106}
107
108static int fuser_add_inode(inode_list *ilist, dev_t dev, ino_t inode)
109{
110 inode_list *curr = NULL, *last = NULL;
111
112 if(!ilist->inode && !ilist->dev) {
113 ilist->dev = dev;
114 ilist->inode = inode;
115 }
116 curr = ilist;
117 while(curr != NULL) {
118 if(curr->inode == inode && curr->dev == dev) return 1;
119 last = curr;
120 curr = curr->next;
121 }
122 curr = xmalloc(sizeof(inode_list));
123 last->next = curr;
124 curr->dev = dev;
125 curr->inode = inode;
126 curr->next = NULL;
127 return 1;
128}
129
130static int fuser_scan_proc_net(int opts, const char *proto,
131 int port, inode_list *ilist)
132{
133 char path[sizeof(FUSER_PROC_DIR)+12], line[FUSER_MAX_LINE+1];
134 char addr[128];
135 ino_t tmp_inode;
136 dev_t tmp_dev;
137 long long uint64_inode;
138 int tmp_port;
139 FILE *f;
140
141 if(!fuser_find_socket_dev(&tmp_dev)) tmp_dev = 0;
142 sprintf(path, "%s/net/%s", FUSER_PROC_DIR, proto);
143
144 if (!(f = fopen(path, "r"))) return 0;
145 while(fgets(line, FUSER_MAX_LINE, f)) {
146 if(sscanf(line,
147 "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
148 "%*x:%*x %*x %*d %*d %llu",
149 addr, &tmp_port, &uint64_inode) == 3) {
150 if((strlen(addr) == 8) &&
151 (opts & FUSER_OPT_IP6)) continue;
152 else if((strlen(addr) > 8) &&
153 (opts & FUSER_OPT_IP4)) continue;
154 if(tmp_port == port) {
155 tmp_inode = uint64_inode;
156 fuser_add_inode(ilist, tmp_dev, tmp_inode);
157 }
158 }
159
160 }
161 fclose(f);
162 return 1;
163}
164
165static int fuser_search_dev_inode(int opts, inode_list *ilist,
166 dev_t dev, ino_t inode)
167{
168 inode_list *curr;
169 curr = ilist;
170
171 while(curr) {
172 if((opts & FUSER_OPT_MOUNT) && curr->dev == dev)
173 return 1;
174 if(curr->inode == inode && curr->dev == dev)
175 return 1;
176 curr = curr->next;
177 }
178 return 0;
179}
180
181static int fuser_scan_pid_maps(int opts, const char *fname, pid_t pid,
182 inode_list *ilist, pid_list *plist)
183{
184 FILE *file;
185 char line[FUSER_MAX_LINE + 1];
186 int major, minor;
187 ino_t inode;
188 long long uint64_inode;
189 dev_t dev;
190
191 if (!(file = fopen(fname, "r"))) return 0;
192 while (fgets(line, FUSER_MAX_LINE, file)) {
193 if(sscanf(line, "%*s %*s %*s %x:%x %llu",
194 &major, &minor, &uint64_inode) != 3) continue;
195 inode = uint64_inode;
196 if(major == 0 && minor == 0 && inode == 0) continue;
197 dev = makedev(major, minor);
198 if(fuser_search_dev_inode(opts, ilist, dev, inode)) {
199 fuser_add_pid(plist, pid);
200 }
201
202 }
203 fclose(file);
204 return 1;
205}
206
207static int fuser_scan_link(int opts, const char *lname, pid_t pid,
208 inode_list *ilist, pid_list *plist)
209{
210 ino_t inode;
211 dev_t dev;
212
213 if(!fuser_file_to_dev_inode(lname, &dev, &inode)) return 0;
214 if(fuser_search_dev_inode(opts, ilist, dev, inode))
215 fuser_add_pid(plist, pid);
216 return 1;
217}
218
219static int fuser_scan_dir_links(int opts, const char *dname, pid_t pid,
220 inode_list *ilist, pid_list *plist)
221{
222 DIR *d;
223 struct dirent *de;
224 char *lname;
225
226 if((d = opendir(dname))) {
227 while((de = readdir(d)) != NULL) {
228 lname = concat_subpath_file(dname, de->d_name);
229 if(lname == NULL)
230 continue;
231 fuser_scan_link(opts, lname, pid, ilist, plist);
232 free(lname);
233 }
234 closedir(d);
235 }
236 else return 0;
237 return 1;
238
239}
240
241static int fuser_scan_proc_pids(int opts, inode_list *ilist, pid_list *plist)
242{
243 DIR *d;
244 struct dirent *de;
245 pid_t pid;
246 char *dname;
247
248 if(!(d = opendir(FUSER_PROC_DIR))) return 0;
249 while((de = readdir(d)) != NULL) {
250 pid = (pid_t)atoi(de->d_name);
251 if(!pid) continue;
252 dname = concat_subpath_file(FUSER_PROC_DIR, de->d_name);
253 if(chdir(dname) < 0) {
254 free(dname);
255 continue;
256 }
257 free(dname);
258 fuser_scan_link(opts, "cwd", pid, ilist, plist);
259 fuser_scan_link(opts, "exe", pid, ilist, plist);
260 fuser_scan_link(opts, "root", pid, ilist, plist);
261 fuser_scan_dir_links(opts, "fd", pid, ilist, plist);
262 fuser_scan_dir_links(opts, "lib", pid, ilist, plist);
263 fuser_scan_dir_links(opts, "mmap", pid, ilist, plist);
264 fuser_scan_pid_maps(opts, "maps", pid, ilist, plist);
265 chdir("..");
266 }
267 closedir(d);
268 return 1;
269}
270
271static int fuser_print_pid_list(pid_list *plist)
272{
273 pid_list *curr = plist;
274
275 if(plist == NULL) return 0;
276 while(curr != NULL) {
277 if(curr->pid > 0) printf("%d ", curr->pid);
278 curr = curr->next;
279 }
280 puts("");
281 return 1;
282}
283
284static int fuser_kill_pid_list(pid_list *plist, int sig)
285{
286 pid_list *curr = plist;
287 pid_t mypid = getpid();
288 int success = 1;
289
290 if(plist == NULL) return 0;
291 while(curr != NULL) {
292 if(curr->pid > 0 && curr->pid != mypid) {
293 if (kill(curr->pid, sig) != 0) {
294 bb_perror_msg(
295 "cannot kill pid '%d'", curr->pid);
296 success = 0;
297 }
298 }
299 curr = curr->next;
300 }
301 return success;
302}
303
304int fuser_main(int argc, char **argv)
305{
306 int port, i, optn;
307 int* fni; /* file name indexes of argv */
308 int fnic = 0; /* file name index count */
309 const char *proto;
310 static int opt = 0; /* FUSER_OPT_ */
311 dev_t dev;
312 ino_t inode;
313 pid_list *pids;
314 inode_list *inodes;
315 int killsig = SIGTERM;
316 int success = 1;
317
318 if (argc < 2)
319 bb_show_usage();
320
321 fni = xmalloc(sizeof(int));
322 for(i=1;i<argc;i++) {
323 optn = fuser_option(argv[i]);
324 if(optn) opt |= optn;
325 else if(argv[i][0] == '-') {
326 if(0>(killsig = get_signum(argv[i]+1)))
327 killsig = SIGTERM;
328 }
329 else {
330 fni = xrealloc(fni, sizeof(int) * (fnic+2));
331 fni[fnic++] = i;
332 }
333 }
334 if(!fnic) return 1;
335
336 inodes = xmalloc(sizeof(inode_list));
337 for(i=0;i<fnic;i++) {
338 if(fuser_parse_net_arg(argv[fni[i]], &proto, &port)) {
339 fuser_scan_proc_net(opt, proto, port, inodes);
340 }
341 else {
342 if(!fuser_file_to_dev_inode(
343 argv[fni[i]], &dev, &inode)) {
344 if (ENABLE_FEATURE_CLEAN_UP) free(inodes);
345 bb_perror_msg_and_die("cannot open '%s'", argv[fni[i]]);
346 }
347 fuser_add_inode(inodes, dev, inode);
348 }
349 }
350 pids = xmalloc(sizeof(pid_list));
351 success = fuser_scan_proc_pids(opt, inodes, pids);
352 /* if the first pid in the list is 0, none have been found */
353 if(pids->pid == 0) success = 0;
354 if(success) {
355 if(opt & FUSER_OPT_KILL) {
356 success = fuser_kill_pid_list(pids, killsig);
357 }
358 else if(!(opt & FUSER_OPT_SILENT)) {
359 success = fuser_print_pid_list(pids);
360 }
361 }
362 free(pids);
363 free(inodes);
364 /* return 0 on (success == 1) 1 otherwise */
365 return (success != 1);
366}
diff --git a/procps/kill.c b/procps/kill.c
new file mode 100644
index 000000000..18121f06f
--- /dev/null
+++ b/procps/kill.c
@@ -0,0 +1,155 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini kill/killall implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9 */
10
11#include "busybox.h"
12
13int kill_main(int argc, char **argv)
14{
15 char *arg;
16 pid_t pid;
17 int signo = SIGTERM, errors = 0, quiet = 0;
18 const int killall = (ENABLE_KILLALL && applet_name[4] == 'a'
19 && (!ENABLE_KILLALL5 || applet_name[7] != '5'));
20 const int killall5 = (ENABLE_KILLALL5 && applet_name[4] == 'a'
21 && (!ENABLE_KILLALL || applet_name[7] == '5'));
22
23 /* Parse any options */
24 argc--;
25 arg = *++argv;
26
27 if (argc < 1 || arg[0] != '-') {
28 goto do_it_now;
29 }
30
31 /* The -l option, which prints out signal names. */
32 if (arg[1] == 'l' && arg[2] == '\0') {
33 const char *name;
34 if (argc == 1) {
35 /* Print the whole signal list */
36 int col = 0;
37 for (signo = 1; signo < 32; signo++) {
38 name = get_signame(signo);
39 if (isdigit(name[0])) continue;
40 if (col > 66) {
41 puts("");
42 col = 0;
43 }
44 col += printf("%2d) %-6s", signo, name);
45 }
46 puts("");
47 } else { /* -l <sig list> */
48 while ((arg = *++argv)) {
49 if (isdigit(arg[0])) {
50 signo = xatoi_u(arg);
51 name = get_signame(signo);
52 } else {
53 signo = get_signum(arg);
54 if (signo < 0)
55 bb_error_msg_and_die("unknown signal '%s'", arg);
56 name = get_signame(signo);
57 }
58 printf("%2d) %s\n", signo, name);
59 }
60 }
61 /* If they specified -l, we are all done */
62 return EXIT_SUCCESS;
63 }
64
65 /* The -q quiet option */
66 if (killall && arg[1] == 'q' && arg[2] == '\0') {
67 quiet = 1;
68 arg = *++argv;
69 argc--;
70 if (argc < 1) bb_show_usage();
71 if (arg[0] != '-') goto do_it_now;
72 }
73
74 /* -SIG */
75 signo = get_signum(&arg[1]);
76 if (signo < 0)
77 bb_error_msg_and_die("bad signal name '%s'", &arg[1]);
78 arg = *++argv;
79 argc--;
80
81do_it_now:
82
83 if (killall5) {
84 pid_t sid;
85 procps_status_t* p = NULL;
86
87// Cannot happen anyway? We don't TERM ourself, we STOP
88// /* kill(-1, sig) on Linux (at least 2.1.x)
89// * might send signal to the calling process too */
90// signal(SIGTERM, SIG_IGN);
91 /* Now stop all processes */
92 kill(-1, SIGSTOP);
93 /* Find out our own session id */
94 pid = getpid();
95 sid = getsid(pid);
96 /* Now kill all processes except our session */
97 while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
98 if (p->sid != sid && p->pid != pid && p->pid != 1)
99 kill(p->pid, signo);
100 }
101 /* And let them continue */
102 kill(-1, SIGCONT);
103 return 0;
104 }
105
106 /* Pid or name required for kill/killall */
107 if (argc < 1)
108 bb_show_usage();
109
110 if (killall) {
111 /* Looks like they want to do a killall. Do that */
112 pid = getpid();
113 while (arg) {
114 pid_t* pidList;
115
116 pidList = find_pid_by_name(arg);
117 if (*pidList == 0) {
118 errors++;
119 if (!quiet)
120 bb_error_msg("%s: no process killed", arg);
121 } else {
122 pid_t *pl;
123
124 for (pl = pidList; *pl; pl++) {
125 if (*pl == pid)
126 continue;
127 if (kill(*pl, signo) == 0)
128 continue;
129 errors++;
130 if (!quiet)
131 bb_perror_msg("cannot kill pid %u", (unsigned)*pl);
132 }
133 }
134 free(pidList);
135 arg = *++argv;
136 }
137 return errors;
138 }
139
140 /* Looks like they want to do a kill. Do that */
141 while (arg) {
142 /* Huh?
143 if (!isdigit(arg[0]) && arg[0] != '-')
144 bb_error_msg_and_die("bad pid '%s'", arg);
145 */
146 pid = xatou(arg);
147 /* FIXME: better overflow check? */
148 if (kill(pid, signo) != 0) {
149 bb_perror_msg("cannot kill pid %u", (unsigned)pid);
150 errors++;
151 }
152 arg = *++argv;
153 }
154 return errors;
155}
diff --git a/procps/pidof.c b/procps/pidof.c
new file mode 100644
index 000000000..1d9718945
--- /dev/null
+++ b/procps/pidof.c
@@ -0,0 +1,90 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * pidof implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
8 */
9
10#include "busybox.h"
11
12enum {
13 USE_FEATURE_PIDOF_SINGLE(OPTBIT_SINGLE,)
14 USE_FEATURE_PIDOF_OMIT( OPTBIT_OMIT ,)
15 OPT_SINGLE = USE_FEATURE_PIDOF_SINGLE((1<<OPTBIT_SINGLE)) + 0,
16 OPT_OMIT = USE_FEATURE_PIDOF_OMIT( (1<<OPTBIT_OMIT )) + 0,
17};
18
19int pidof_main(int argc, char **argv)
20{
21 unsigned first = 1;
22 unsigned fail = 1;
23 unsigned opt;
24#if ENABLE_FEATURE_PIDOF_OMIT
25 llist_t *omits = NULL; /* list of pids to omit */
26 opt_complementary = "o::";
27#endif
28
29 /* do unconditional option parsing */
30 opt = getopt32(argc, argv, ""
31 USE_FEATURE_PIDOF_SINGLE ("s")
32 USE_FEATURE_PIDOF_OMIT("o:", &omits));
33
34#if ENABLE_FEATURE_PIDOF_OMIT
35 /* fill omit list. */
36 {
37 char getppid_str[sizeof(int)*3 + 1];
38 llist_t * omits_p = omits;
39 while (omits_p) {
40 /* are we asked to exclude the parent's process ID? */
41 if (!strncmp(omits_p->data, "%PPID", 5)) {
42 llist_pop(&omits_p);
43 snprintf(getppid_str, sizeof(getppid_str), "%u", (unsigned)getppid());
44 llist_add_to(&omits_p, getppid_str);
45 }
46 omits_p = omits_p->link;
47 }
48 }
49#endif
50 /* Looks like everything is set to go. */
51 while (optind < argc) {
52 pid_t *pidList;
53 pid_t *pl;
54
55 /* reverse the pidlist like GNU pidof does. */
56 pidList = pidlist_reverse(find_pid_by_name(argv[optind]));
57 for (pl = pidList; *pl; pl++) {
58 SKIP_FEATURE_PIDOF_OMIT(const) unsigned omitted = 0;
59#if ENABLE_FEATURE_PIDOF_OMIT
60 if (opt & OPT_OMIT) {
61 llist_t *omits_p = omits;
62 while (omits_p) {
63 if (xatoul(omits_p->data) == *pl) {
64 omitted = 1;
65 break;
66 } else
67 omits_p = omits_p->link;
68 }
69 }
70#endif
71 if (!omitted) {
72 printf(" %u" + first, (unsigned)*pl);
73 first = 0;
74 }
75 fail = (!ENABLE_FEATURE_PIDOF_OMIT && omitted);
76
77 if (ENABLE_FEATURE_PIDOF_SINGLE && (opt & OPT_SINGLE))
78 break;
79 }
80 free(pidList);
81 optind++;
82 }
83 putchar('\n');
84
85#if ENABLE_FEATURE_PIDOF_OMIT
86 if (ENABLE_FEATURE_CLEAN_UP)
87 llist_free(omits, NULL);
88#endif
89 return fail ? EXIT_FAILURE : EXIT_SUCCESS;
90}
diff --git a/procps/ps.c b/procps/ps.c
new file mode 100644
index 000000000..e18bd2a58
--- /dev/null
+++ b/procps/ps.c
@@ -0,0 +1,388 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini ps implementation(s) for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
8 */
9
10#include "busybox.h"
11
12#if ENABLE_DESKTOP
13
14/* Print value to buf, max size+1 chars (including trailing '\0') */
15
16void func_user(char *buf, int size, const procps_status_t *ps)
17{
18 safe_strncpy(buf, get_cached_username(ps->uid), size+1);
19}
20
21void func_comm(char *buf, int size, const procps_status_t *ps)
22{
23 safe_strncpy(buf, ps->comm, size+1);
24}
25
26void func_args(char *buf, int size, const procps_status_t *ps)
27{
28 buf[0] = '\0';
29 if (ps->cmd)
30 safe_strncpy(buf, ps->cmd, size+1);
31 else if (size >= 2)
32 snprintf(buf, size+1, "[%.*s]", size-2, ps->comm);
33}
34
35void func_pid(char *buf, int size, const procps_status_t *ps)
36{
37 snprintf(buf, size+1, "%*u", size, ps->pid);
38}
39
40void func_ppid(char *buf, int size, const procps_status_t *ps)
41{
42 snprintf(buf, size+1, "%*u", size, ps->ppid);
43}
44
45void func_pgid(char *buf, int size, const procps_status_t *ps)
46{
47 snprintf(buf, size+1, "%*u", size, ps->pgid);
48}
49
50void func_rss(char *buf, int size, const procps_status_t *ps)
51{
52 char buf5[5];
53 smart_ulltoa5( ((unsigned long long)ps->rss) << 10, buf5);
54 snprintf(buf, size+1, "%.*s", size, buf5);
55}
56
57/*
58void func_nice(char *buf, int size, const procps_status_t *ps)
59{
60 ps->???
61}
62
63void func_etime(char *buf, int size, const procps_status_t *ps)
64{
65 elapled time [[dd-]hh:]mm:ss
66}
67
68void func_time(char *buf, int size, const procps_status_t *ps)
69{
70 cumulative time [[dd-]hh:]mm:ss
71}
72
73void func_pcpu(char *buf, int size, const procps_status_t *ps)
74{
75}
76
77void func_tty(char *buf, int size, const procps_status_t *ps)
78{
79}
80*/
81
82typedef struct {
83 char name[8];
84 const char *header;
85 void (*f)(char *buf, int size, const procps_status_t *ps);
86 int ps_flags;
87 int width;
88} ps_out_t;
89
90static const ps_out_t out_spec[] = {
91// Mandated by POSIX:
92 { "user" ,"USER" ,func_user ,PSSCAN_UIDGID,8 },
93 { "comm" ,"COMMAND",func_comm ,PSSCAN_COMM ,16 },
94 { "args" ,"COMMAND",func_args ,PSSCAN_CMD|PSSCAN_COMM,256 },
95 { "pid" ,"PID" ,func_pid ,PSSCAN_PID ,5 },
96 { "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID ,5 },
97 { "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID ,5 },
98// { "etime" ,"ELAPSED",func_etime ,PSSCAN_ ,sizeof("ELAPSED")-1 },
99// { "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID,sizeof("GROUP" )-1 },
100// { "nice" ,"NI" ,func_nice ,PSSCAN_ ,sizeof("NI" )-1 },
101// { "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ ,sizeof("%CPU" )-1 },
102// { "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID,sizeof("RGROUP" )-1 },
103// { "ruser" ,"RUSER" ,func_ruser ,PSSCAN_UIDGID,sizeof("RUSER" )-1 },
104// { "time" ,"TIME" ,func_time ,PSSCAN_ ,sizeof("TIME" )-1 },
105// { "tty" ,"TT" ,func_tty ,PSSCAN_ ,sizeof("TT" )-1 },
106// { "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ ,4 },
107// Not mandated by POSIX:
108 { "rss" ,"RSS" ,func_rss ,PSSCAN_RSS ,4 },
109};
110
111#define VEC_SIZE(v) ( sizeof(v) / sizeof((v)[0]) )
112
113static ps_out_t* out;
114static int out_cnt;
115static int print_header;
116static int ps_flags;
117static char *buffer;
118static unsigned terminal_width;
119
120
121static ps_out_t* new_out_t(void)
122{
123 int i = out_cnt++;
124 out = xrealloc(out, out_cnt * sizeof(*out));
125 return &out[i];
126}
127
128static const ps_out_t* find_out_spec(const char *name)
129{
130 int i;
131 for (i = 0; i < VEC_SIZE(out_spec); i++) {
132 if (!strcmp(name, out_spec[i].name))
133 return &out_spec[i];
134 }
135 bb_error_msg_and_die("bad -o argument '%s'", name);
136}
137
138static void parse_o(char* opt)
139{
140 ps_out_t* new;
141 // POSIX: "-o is blank- or comma-separated list" (FIXME)
142 char *comma, *equal;
143 while (1) {
144 comma = strchr(opt, ',');
145 equal = strchr(opt, '=');
146 if (comma && (!equal || equal > comma)) {
147 *comma = '\0';
148 *new_out_t() = *find_out_spec(opt);
149 *comma = ',';
150 opt = comma + 1;
151 continue;
152 }
153 break;
154 }
155 new = new_out_t();
156 if (equal)
157 *equal = '\0';
158 *new = *find_out_spec(opt);
159 if (equal) {
160 *equal = '=';
161 new->header = equal + 1;
162 // POSIX: the field widths shall be ... at least as wide as
163 // the header text (default or overridden value).
164 // If the header text is null, such as -o user=,
165 // the field width shall be at least as wide as the
166 // default header text
167 if (new->header[0]) {
168 new->width = strlen(new->header);
169 print_header = 1;
170 }
171 } else
172 print_header = 1;
173}
174
175static void post_process(void)
176{
177 int i;
178 int width = 0;
179 for (i = 0; i < out_cnt; i++) {
180 ps_flags |= out[i].ps_flags;
181 if (out[i].header[0]) {
182 print_header = 1;
183 }
184 width += out[i].width + 1; /* "FIELD " */
185 }
186 buffer = xmalloc(width + 1); /* for trailing \0 */
187}
188
189static void format_header(void)
190{
191 int i;
192 ps_out_t* op;
193 char *p = buffer;
194 if (!print_header)
195 return;
196 i = 0;
197 if (out_cnt) {
198 while (1) {
199 op = &out[i];
200 if (++i == out_cnt) /* do not pad last field */
201 break;
202 p += sprintf(p, "%-*s ", op->width, op->header);
203 }
204 strcpy(p, op->header);
205 }
206 printf("%.*s\n", terminal_width, buffer);
207}
208
209static void format_process(const procps_status_t *ps)
210{
211 int i, len;
212 char *p = buffer;
213 i = 0;
214 if (out_cnt) while (1) {
215 out[i].f(p, out[i].width, ps);
216 // POSIX: Any field need not be meaningful in all
217 // implementations. In such a case a hyphen ( '-' )
218 // should be output in place of the field value.
219 if (!*p) {
220 *p++ = '-';
221 *p = '\0';
222 }
223 len = strlen(p);
224 p += len;
225 len = out[i].width - len + 1;
226 if (++i == out_cnt) /* do not pad last field */
227 break;
228 while (len--)
229 *p++ = ' ';
230 *p = '\0';
231 }
232 printf("%.*s\n", terminal_width, buffer);
233}
234
235/* Cannot be const: parse_o() will choke */
236static char default_o[] = "pid,user" /* TODO: ,vsz,stat */ ",args";
237
238int ps_main(int argc, char **argv)
239{
240 procps_status_t *p;
241 llist_t* opt_o = NULL;
242
243 // POSIX:
244 // -a Write information for all processes associated with terminals
245 // Implementations may omit session leaders from this list
246 // -A Write information for all processes
247 // -d Write information for all processes, except session leaders
248 // -e Write information for all processes (equivalent to -A.)
249 // -f Generate a full listing
250 // -l Generate a long listing
251 // -o col1,col2,col3=header
252 // Select which columns to distplay
253 /* We allow (and ignore) most of the above. FIXME */
254 opt_complementary = "o::";
255 getopt32(argc, argv, "o:aAdefl", &opt_o);
256 if (opt_o) {
257 opt_o = rev_llist(opt_o);
258 do {
259 parse_o(opt_o->data);
260 opt_o = opt_o->link;
261 } while (opt_o);
262 } else
263 parse_o(default_o);
264 post_process();
265
266 terminal_width = INT_MAX;
267 if (isatty(1)) {
268 get_terminal_width_height(1, &terminal_width, NULL);
269 terminal_width--;
270 }
271 format_header();
272
273 p = NULL;
274 while ((p = procps_scan(p, ps_flags))) {
275 format_process(p);
276 }
277
278 return EXIT_SUCCESS;
279}
280
281
282#else /* !ENABLE_DESKTOP */
283
284
285int ps_main(int argc, char **argv)
286{
287 procps_status_t *p = NULL;
288 int i, len;
289 SKIP_SELINUX(const) int use_selinux = 0;
290 USE_SELINUX(security_context_t sid = NULL;)
291#if !ENABLE_FEATURE_PS_WIDE
292 enum { terminal_width = 79 };
293#else
294 int terminal_width;
295 int w_count = 0;
296#endif
297
298#if ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX
299#if ENABLE_FEATURE_PS_WIDE
300 opt_complementary = "-:ww";
301 USE_SELINUX(i =) getopt32(argc, argv, USE_SELINUX("c") "w", &w_count);
302 /* if w is given once, GNU ps sets the width to 132,
303 * if w is given more than once, it is "unlimited"
304 */
305 if (w_count) {
306 terminal_width = (w_count==1) ? 132 : INT_MAX;
307 } else {
308 get_terminal_width_height(1, &terminal_width, NULL);
309 /* Go one less... */
310 terminal_width--;
311 }
312#else /* only ENABLE_SELINUX */
313 i = getopt32(argc, argv, "c");
314#endif
315#if ENABLE_SELINUX
316 if ((i & 1) && is_selinux_enabled())
317 use_selinux = 1;
318#endif
319#endif /* ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX */
320
321 if (use_selinux)
322 puts(" PID Context Stat Command");
323 else
324 puts(" PID Uid VmSize Stat Command");
325
326 while ((p = procps_scan(p, 0
327 | PSSCAN_PID
328 | PSSCAN_UIDGID
329 | PSSCAN_STATE
330 | PSSCAN_RSS
331 | PSSCAN_CMD
332 ))) {
333 char *namecmd = p->cmd;
334#if ENABLE_SELINUX
335 if (use_selinux) {
336 char sbuf[128];
337 len = sizeof(sbuf);
338
339 if (is_selinux_enabled()) {
340 if (getpidcon(p->pid, &sid) < 0)
341 sid = NULL;
342 }
343
344 if (sid) {
345 /* I assume sid initialized with NULL */
346 len = strlen(sid) + 1;
347 safe_strncpy(sbuf, sid, len);
348 freecon(sid);
349 sid = NULL;
350 } else {
351 safe_strncpy(sbuf, "unknown", 7);
352 }
353 len = printf("%5u %-32s %s ", p->pid, sbuf, p->state);
354 } else
355#endif
356 {
357 const char *user = get_cached_username(p->uid);
358 if (p->rss == 0)
359 len = printf("%5u %-8s %s ",
360 p->pid, user, p->state);
361 else
362 len = printf("%5u %-8s %6ld %s ",
363 p->pid, user, p->rss, p->state);
364 }
365
366 i = terminal_width-len;
367
368 if (namecmd && namecmd[0]) {
369 if (i < 0)
370 i = 0;
371 if (strlen(namecmd) > (size_t)i)
372 namecmd[i] = 0;
373 puts(namecmd);
374 } else {
375 namecmd = p->comm;
376 if (i < 2)
377 i = 2;
378 if (strlen(namecmd) > ((size_t)i-2))
379 namecmd[i-2] = 0;
380 printf("[%s]\n", namecmd);
381 }
382 }
383 if (ENABLE_FEATURE_CLEAN_UP)
384 clear_username_cache();
385 return EXIT_SUCCESS;
386}
387
388#endif /* ENABLE_DESKTOP */
diff --git a/procps/ps.posix b/procps/ps.posix
new file mode 100644
index 000000000..57f4fa8af
--- /dev/null
+++ b/procps/ps.posix
@@ -0,0 +1,175 @@
1This is what POSIX 2003 says about ps:
2
3By default, ps shall select all processes with the same effective user
4ID as the current user and the same controlling terminal as the invoker
5
6ps [-aA][-defl][-G grouplist][-o format]...[-p proclist][-t termlist]
7[-U userlist][-g grouplist][-n namelist][-u userlist]
8
9-a Write information for all processes associated with terminals.
10 Implementations may omit session leaders from this list.
11
12-A Write information for all processes.
13
14-d Write information for all processes, except session leaders.
15
16-e Write information for all processes. (Equivalent to -A.)
17
18-f Generate a full listing. (See the STDOUT section for the con-
19 tents of a full listing.)
20
21-g grouplist
22 Write information for processes whose session leaders are given
23 in grouplist. The application shall ensure that the grouplist is
24 a single argument in the form of a <blank> or comma-separated
25 list.
26
27-G grouplist
28 Write information for processes whose real group ID numbers are
29 given in grouplist. The application shall ensure that the grou-
30 plist is a single argument in the form of a <blank> or comma-
31 separated list.
32
33-l Generate a long listing. (See STDOUT for the contents of a long
34 listing.)
35
36-n namelist
37 Specify the name of an alternative system namelist file in place
38 of the default. The name of the default file and the format of a
39 namelist file are unspecified.
40
41-o format
42 Write information according to the format specification given in
43 format. Multiple -o options can be specified; the format speci-
44 fication shall be interpreted as the <space>-separated concate-
45 nation of all the format option-arguments.
46
47-p proclist
48 Write information for processes whose process ID numbers are
49 given in proclist. The application shall ensure that the pro-
50 clist is a single argument in the form of a <blank> or comma-
51 separated list.
52
53-t termlist
54 Write information for processes associated with terminals given
55 in termlist. The application shall ensure that the termlist is a
56 single argument in the form of a <blank> or comma-separated
57 list. Terminal identifiers shall be given in an implementation-
58 defined format. On XSI-conformant systems, they shall be
59 given in one of two forms: the device's filename (for example,
60 tty04) or, if the device's filename starts with tty, just the
61 identifier following the characters tty (for example, "04" ).
62
63-u userlist
64 Write information for processes whose user ID numbers or login
65 names are given in userlist. The application shall ensure that
66 the userlist is a single argument in the form of a <blank> or
67 comma-separated list. In the listing, the numerical user ID
68 shall be written unless the -f option is used, in which case the
69 login name shall be written.
70
71-U userlist
72 Write information for processes whose real user ID numbers or
73 login names are given in userlist. The application shall ensure
74 that the userlist is a single argument in the form of a <blank>
75 or comma-separated list.
76
77With the exception of -o format, all of the options shown are used to
78select processes. If any are specified, the default list shall be
79ignored and ps shall select the processes represented by the inclusive
80OR of all the selection-criteria options.
81
82The -o option allows the output format to be specified under user con-
83trol.
84
85The application shall ensure that the format specification is a list of
86names presented as a single argument, <blank> or comma-separated. Each
87variable has a default header. The default header can be overridden by
88appending an equals sign and the new text of the header. The rest of
89the characters in the argument shall be used as the header text. The
90fields specified shall be written in the order specified on the command
91line, and should be arranged in columns in the output. The field widths
92shall be selected by the system to be at least as wide as the header
93text (default or overridden value). If the header text is null, such as
94-o user=, the field width shall be at least as wide as the default
95header text. If all header text fields are null, no header line shall
96be written.
97
98ruser The real user ID of the process. This shall be the textual user
99 ID, if it can be obtained and the field width permits, or a dec-
100 imal representation otherwise.
101
102user The effective user ID of the process. This shall be the textual
103 user ID, if it can be obtained and the field width permits, or a
104 decimal representation otherwise.
105
106rgroup The real group ID of the process. This shall be the textual
107 group ID, if it can be obtained and the field width permits, or
108 a decimal representation otherwise.
109
110group The effective group ID of the process. This shall be the textual
111 group ID, if it can be obtained and the field width permits, or
112 a decimal representation otherwise.
113
114pid The decimal value of the process ID.
115
116ppid The decimal value of the parent process ID.
117
118pgid The decimal value of the process group ID.
119
120pcpu The ratio of CPU time used recently to CPU time available in the
121 same period, expressed as a percentage. The meaning of
122 "recently" in this context is unspecified. The CPU time avail-
123 able is determined in an unspecified manner.
124
125vsz The size of the process in (virtual) memory in 1024 byte units
126 as a decimal integer.
127
128nice The decimal value of the nice value of the process; see nice() .
129
130etime In the POSIX locale, the elapsed time since the process was
131 started, in the form: [[dd-]hh:]mm:ss
132
133time In the POSIX locale, the cumulative CPU time of the process in
134 the form: [dd-]hh:mm:ss
135
136tty The name of the controlling terminal of the process (if any) in
137 the same format used by the who utility.
138
139comm The name of the command being executed ( argv[0] value) as a
140 string.
141
142args The command with all its arguments as a string. The implementa-
143 tion may truncate this value to the field width; it is implemen-
144 tation-defined whether any further truncation occurs. It is
145 unspecified whether the string represented is a version of the
146 argument list as it was passed to the command when it started,
147 or is a version of the arguments as they may have been modified
148 by the application. Applications cannot depend on being able to
149 modify their argument list and having that modification be
150 reflected in the output of ps.
151
152Any field need not be meaningful in all implementations. In such a case
153a hyphen ( '-' ) should be output in place of the field value.
154
155Only comm and args shall be allowed to contain <blank>s; all others
156shall not.
157
158The following table specifies the default header to be used in the
159POSIX locale corresponding to each format specifier.
160
161 Format Specifier Default Header Format Specifier Default Header
162 args COMMAND ppid PPID
163 comm COMMAND rgroup RGROUP
164 etime ELAPSED ruser RUSER
165 group GROUP time TIME
166 nice NI tty TT
167 pcpu %CPU user USER
168 pgid PGID vsz VSZ
169 pid PID
170
171There is no special quoting mechanism for header text. The header text
172is the rest of the argument. If multiple header changes are needed,
173multiple -o options can be used, such as:
174
175 ps -o "user=User Name" -o pid=Process\ ID
diff --git a/procps/renice.c b/procps/renice.c
new file mode 100644
index 000000000..08e0dc264
--- /dev/null
+++ b/procps/renice.c
@@ -0,0 +1,126 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * renice implementation for busybox
4 *
5 * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10/* Notes:
11 * Setting an absolute priority was obsoleted in SUSv2 and removed
12 * in SUSv3. However, the common linux version of renice does
13 * absolute and not relative. So we'll continue supporting absolute,
14 * although the stdout logging has been removed since both SUSv2 and
15 * SUSv3 specify that stdout isn't used.
16 *
17 * This version is lenient in that it doesn't require any IDs. The
18 * options -p, -g, and -u are treated as mode switches for the
19 * following IDs (if any). Multiple switches are allowed.
20 */
21
22#include "busybox.h"
23#include <sys/resource.h>
24
25void BUG_bad_PRIO_PROCESS(void);
26void BUG_bad_PRIO_PGRP(void);
27void BUG_bad_PRIO_USER(void);
28
29int renice_main(int argc, char **argv)
30{
31 static const char Xetpriority_msg[] = "%cetpriority";
32
33 int retval = EXIT_SUCCESS;
34 int which = PRIO_PROCESS; /* Default 'which' value. */
35 int use_relative = 0;
36 int adjustment, new_priority;
37 unsigned who;
38 char *arg;
39
40 /* Yes, they are not #defines in glibc 2.4! #if won't work */
41 if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX)
42 BUG_bad_PRIO_PROCESS();
43 if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX)
44 BUG_bad_PRIO_PGRP();
45 if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX)
46 BUG_bad_PRIO_USER();
47
48 arg = *++argv;
49
50 /* Check if we are using a relative adjustment. */
51 if (arg && arg[0] == '-' && arg[1] == 'n') {
52 use_relative = 1;
53 if (!arg[2])
54 arg = *++argv;
55 else
56 arg += 2;
57 }
58
59 if (!arg) { /* No args? Then show usage. */
60 bb_show_usage();
61 }
62
63 /* Get the priority adjustment (absolute or relative). */
64 adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2);
65
66 while ((arg = *++argv) != NULL) {
67 /* Check for a mode switch. */
68 if (arg[0] == '-' && arg[1]) {
69 static const char opts[]
70 = { 'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER };
71 const char *p = strchr(opts, arg[1]);
72 if (p) {
73 which = p[4];
74 if (!arg[2])
75 continue;
76 arg += 2;
77 }
78 }
79
80 /* Process an ID arg. */
81 if (which == PRIO_USER) {
82 struct passwd *p;
83 p = getpwnam(arg);
84 if (!p) {
85 bb_error_msg("unknown user: %s", arg);
86 goto HAD_ERROR;
87 }
88 who = p->pw_uid;
89 } else {
90 who = bb_strtou(arg, NULL, 10);
91 if (errno) {
92 bb_error_msg("bad value: %s", arg);
93 goto HAD_ERROR;
94 }
95 }
96
97 /* Get priority to use, and set it. */
98 if (use_relative) {
99 int old_priority;
100
101 errno = 0; /* Needed for getpriority error detection. */
102 old_priority = getpriority(which, who);
103 if (errno) {
104 bb_perror_msg(Xetpriority_msg, 'g');
105 goto HAD_ERROR;
106 }
107
108 new_priority = old_priority + adjustment;
109 } else {
110 new_priority = adjustment;
111 }
112
113 if (setpriority(which, who, new_priority) == 0) {
114 continue;
115 }
116
117 bb_perror_msg(Xetpriority_msg, 's');
118 HAD_ERROR:
119 retval = EXIT_FAILURE;
120 }
121
122 /* No need to check for errors outputing to stderr since, if it
123 * was used, the HAD_ERROR label was reached and retval was set. */
124
125 return retval;
126}
diff --git a/procps/sysctl.c b/procps/sysctl.c
new file mode 100644
index 000000000..49754489b
--- /dev/null
+++ b/procps/sysctl.c
@@ -0,0 +1,326 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
4 *
5 * Copyright 1999 George Staikos
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 *
9 * Changelog:
10 * v1.01:
11 * - added -p <preload> to preload values from a file
12 * v1.01.1
13 * - busybox applet aware by <solar@gentoo.org>
14 *
15 */
16
17#include "busybox.h"
18
19/*
20 * Function Prototypes
21 */
22static int sysctl_read_setting(const char *setting, int output);
23static int sysctl_write_setting(const char *setting, int output);
24static int sysctl_preload_file(const char *filename, int output);
25static int sysctl_display_all(const char *path, int output, int show_table);
26
27/*
28 * Globals...
29 */
30static const char PROC_PATH[] = "/proc/sys/";
31static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
32
33/* error messages */
34static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n";
35static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n";
36static const char ERR_NO_EQUALS[] =
37 "error: '%s' must be of the form name=value\n";
38static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n";
39static const char ERR_UNKNOWN_WRITING[] =
40 "error: unknown error %d setting key '%s'\n";
41static const char ERR_UNKNOWN_READING[] =
42 "error: unknown error %d reading key '%s'\n";
43static const char ERR_PERMISSION_DENIED[] =
44 "error: permission denied on key '%s'\n";
45static const char ERR_PRELOAD_FILE[] =
46 "error: cannot open preload file '%s'\n";
47static const char WARN_BAD_LINE[] =
48 "warning: %s(%d): invalid syntax, continuing...\n";
49
50
51static void dwrite_str(int fd, const char *buf)
52{
53 write(fd, buf, strlen(buf));
54}
55
56/*
57 * sysctl_main()...
58 */
59int sysctl_main(int argc, char **argv)
60{
61 int retval = 0;
62 int output = 1;
63 int write_mode = 0;
64 int switches_allowed = 1;
65
66 if (argc < 2)
67 bb_show_usage();
68
69 argv++;
70
71 for (; argv && *argv && **argv; argv++) {
72 if (switches_allowed && **argv == '-') { /* we have a switch */
73 switch ((*argv)[1]) {
74 case 'n':
75 output = 0;
76 break;
77 case 'w':
78 write_mode = 1;
79 switches_allowed = 0;
80 break;
81 case 'p':
82 argv++;
83 return
84 sysctl_preload_file(((argv && *argv
85 && **argv) ? *argv :
86 DEFAULT_PRELOAD), output);
87 case 'a':
88 case 'A':
89 switches_allowed = 0;
90 return sysctl_display_all(PROC_PATH, output,
91 ((*argv)[1] == 'a') ? 0 : 1);
92 case 'h':
93 case '?':
94 bb_show_usage();
95 default:
96 bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv);
97 bb_show_usage();
98 }
99 } else {
100 switches_allowed = 0;
101 if (write_mode)
102 retval = sysctl_write_setting(*argv, output);
103 else
104 sysctl_read_setting(*argv, output);
105 }
106 }
107 return retval;
108} /* end sysctl_main() */
109
110
111
112/*
113 * sysctl_preload_file
114 * preload the sysctl's from a conf file
115 * - we parse the file and then reform it (strip out whitespace)
116 */
117#define PRELOAD_BUF 256
118
119int sysctl_preload_file(const char *filename, int output)
120{
121 int lineno = 0;
122 char oneline[PRELOAD_BUF];
123 char buffer[PRELOAD_BUF];
124 char *name, *value, *ptr;
125 FILE *fp = NULL;
126
127 if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
128 bb_error_msg_and_die(ERR_PRELOAD_FILE, filename);
129 }
130
131 while (fgets(oneline, sizeof(oneline) - 1, fp)) {
132 oneline[sizeof(oneline) - 1] = '\0';
133 lineno++;
134 trim(oneline);
135 ptr = (char *) oneline;
136
137 if (*ptr == '#' || *ptr == ';')
138 continue;
139
140 if (strlen(ptr) < 2)
141 continue;
142
143 name = strtok(ptr, "=");
144 if (!name || !*name) {
145 bb_error_msg(WARN_BAD_LINE, filename, lineno);
146 continue;
147 }
148
149 trim(name);
150
151 value = strtok(NULL, "\n\r");
152 if (!value || !*value) {
153 bb_error_msg(WARN_BAD_LINE, filename, lineno);
154 continue;
155 }
156
157 while ((*value == ' ' || *value == '\t') && *value != 0)
158 value++;
159 /* safe because sizeof(oneline) == sizeof(buffer) */
160 sprintf(buffer, "%s=%s", name, value);
161 sysctl_write_setting(buffer, output);
162 }
163 fclose(fp);
164 return 0;
165} /* end sysctl_preload_file() */
166
167
168/*
169 * Write a single sysctl setting
170 */
171int sysctl_write_setting(const char *setting, int output)
172{
173 int retval = 0;
174 const char *name = setting;
175 const char *value;
176 const char *equals;
177 char *tmpname, *outname, *cptr;
178 int fd = -1;
179
180 if (!name) /* probably dont' want to display this err */
181 return 0;
182
183 if (!(equals = strchr(setting, '='))) {
184 bb_error_msg(ERR_NO_EQUALS, setting);
185 return -1;
186 }
187
188 value = equals + sizeof(char); /* point to the value in name=value */
189
190 if (!*name || !*value || name == equals) {
191 bb_error_msg(ERR_MALFORMED_SETTING, setting);
192 return -2;
193 }
194
195 tmpname = xasprintf("%s%.*s", PROC_PATH, (int)(equals - name), name);
196 outname = xstrdup(tmpname + strlen(PROC_PATH));
197
198 while ((cptr = strchr(tmpname, '.')) != NULL)
199 *cptr = '/';
200
201 while ((cptr = strchr(outname, '/')) != NULL)
202 *cptr = '.';
203
204 if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
205 switch (errno) {
206 case ENOENT:
207 bb_error_msg(ERR_INVALID_KEY, outname);
208 break;
209 case EACCES:
210 bb_perror_msg(ERR_PERMISSION_DENIED, outname);
211 break;
212 default:
213 bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
214 break;
215 }
216 retval = -1;
217 } else {
218 dwrite_str(fd, value);
219 close(fd);
220 if (output) {
221 dwrite_str(STDOUT_FILENO, outname);
222 dwrite_str(STDOUT_FILENO, " = ");
223 }
224 dwrite_str(STDOUT_FILENO, value);
225 dwrite_str(STDOUT_FILENO, "\n");
226 }
227
228 /* cleanup */
229 free(tmpname);
230 free(outname);
231 return retval;
232} /* end sysctl_write_setting() */
233
234
235/*
236 * Read a sysctl setting
237 *
238 */
239int sysctl_read_setting(const char *setting, int output)
240{
241 int retval = 0;
242 char *tmpname, *outname, *cptr;
243 char inbuf[1025];
244 const char *name = setting;
245 FILE *fp;
246
247 if (!setting || !*setting)
248 bb_error_msg(ERR_INVALID_KEY, setting);
249
250 tmpname = concat_path_file(PROC_PATH, name);
251 outname = xstrdup(tmpname + strlen(PROC_PATH));
252
253 while ((cptr = strchr(tmpname, '.')) != NULL)
254 *cptr = '/';
255 while ((cptr = strchr(outname, '/')) != NULL)
256 *cptr = '.';
257
258 if ((fp = fopen(tmpname, "r")) == NULL) {
259 switch (errno) {
260 case ENOENT:
261 bb_error_msg(ERR_INVALID_KEY, outname);
262 break;
263 case EACCES:
264 bb_error_msg(ERR_PERMISSION_DENIED, outname);
265 break;
266 default:
267 bb_error_msg(ERR_UNKNOWN_READING, errno, outname);
268 break;
269 }
270 retval = -1;
271 } else {
272 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
273 if (output) {
274 dwrite_str(STDOUT_FILENO, outname);
275 dwrite_str(STDOUT_FILENO, " = ");
276 }
277 dwrite_str(STDOUT_FILENO, inbuf);
278 }
279 fclose(fp);
280 }
281
282 free(tmpname);
283 free(outname);
284 return retval;
285} /* end sysctl_read_setting() */
286
287
288
289/*
290 * Display all the sysctl settings
291 *
292 */
293int sysctl_display_all(const char *path, int output, int show_table)
294{
295 int retval = 0;
296 int retval2;
297 DIR *dp;
298 struct dirent *de;
299 char *tmpdir;
300 struct stat ts;
301
302 if (!(dp = opendir(path))) {
303 retval = -1;
304 } else {
305 while ((de = readdir(dp)) != NULL) {
306 tmpdir = concat_subpath_file(path, de->d_name);
307 if(tmpdir == NULL)
308 continue;
309 if ((retval2 = stat(tmpdir, &ts)) != 0)
310 bb_perror_msg(tmpdir);
311 else {
312 if (S_ISDIR(ts.st_mode)) {
313 sysctl_display_all(tmpdir, output, show_table);
314 } else
315 retval |=
316 sysctl_read_setting(tmpdir + strlen(PROC_PATH),
317 output);
318
319 }
320 free(tmpdir);
321 } /* end while */
322 closedir(dp);
323 }
324
325 return retval;
326} /* end sysctl_display_all() */
diff --git a/procps/top.c b/procps/top.c
new file mode 100644
index 000000000..8d732d4b2
--- /dev/null
+++ b/procps/top.c
@@ -0,0 +1,566 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * A tiny 'top' utility.
4 *
5 * This is written specifically for the linux /proc/<PID>/stat(m)
6 * files format.
7
8 * This reads the PIDs of all processes and their status and shows
9 * the status of processes (first ones that fit to screen) at given
10 * intervals.
11 *
12 * NOTES:
13 * - At startup this changes to /proc, all the reads are then
14 * relative to that.
15 *
16 * (C) Eero Tamminen <oak at welho dot com>
17 *
18 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
19 */
20
21/* Original code Copyrights */
22/*
23 * Copyright (c) 1992 Branko Lankester
24 * Copyright (c) 1992 Roger Binns
25 * Copyright (C) 1994-1996 Charles L. Blake.
26 * Copyright (C) 1992-1998 Michael K. Johnson
27 * May be distributed under the conditions of the
28 * GNU Library General Public License
29 */
30
31#include "busybox.h"
32
33
34typedef struct {
35 unsigned long rss;
36#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
37 unsigned long ticks;
38 unsigned pcpu; /* delta of ticks */
39#endif
40 unsigned pid, ppid;
41 unsigned uid;
42 char state[4];
43 char comm[COMM_LEN];
44} top_status_t;
45static top_status_t *top;
46static int ntop;
47/* This structure stores some critical information from one frame to
48 the next. Used for finding deltas. */
49struct save_hist {
50 unsigned long ticks;
51 unsigned pid;
52};
53static struct save_hist *prev_hist;
54static int prev_hist_count;
55/* static int hist_iterations; */
56static unsigned total_pcpu;
57/* static unsigned long total_rss; */
58
59
60#define OPT_BATCH_MODE (option_mask32 & 0x4)
61
62#if ENABLE_FEATURE_USE_TERMIOS
63static int pid_sort(top_status_t *P, top_status_t *Q)
64{
65 /* Buggy wrt pids with high bit set */
66 /* (linux pids are in [1..2^15-1]) */
67 return (Q->pid - P->pid);
68}
69#endif
70
71static int mem_sort(top_status_t *P, top_status_t *Q)
72{
73 /* We want to avoid unsigned->signed and truncation errors */
74 if (Q->rss < P->rss) return -1;
75 return Q->rss != P->rss; /* 0 if ==, 1 if > */
76}
77
78
79typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
80
81#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
82
83static cmp_funcp sort_function;
84
85#else
86
87enum { SORT_DEPTH = 3 };
88
89static cmp_funcp sort_function[SORT_DEPTH];
90
91static int pcpu_sort(top_status_t *P, top_status_t *Q)
92{
93 /* Buggy wrt ticks with high bit set */
94 /* Affects only processes for which ticks overflow */
95 return (int)Q->pcpu - (int)P->pcpu;
96}
97
98static int time_sort(top_status_t *P, top_status_t *Q)
99{
100 /* We want to avoid unsigned->signed and truncation errors */
101 if (Q->ticks < P->ticks) return -1;
102 return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
103}
104
105static int mult_lvl_cmp(void* a, void* b) {
106 int i, cmp_val;
107
108 for (i = 0; i < SORT_DEPTH; i++) {
109 cmp_val = (*sort_function[i])(a, b);
110 if (cmp_val != 0)
111 return cmp_val;
112 }
113 return 0;
114}
115
116
117typedef struct {
118 unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
119 unsigned long long total;
120 unsigned long long busy;
121} jiffy_counts_t;
122static jiffy_counts_t jif, prev_jif;
123static void get_jiffy_counts(void)
124{
125 FILE* fp = xfopen("stat", "r");
126 prev_jif = jif;
127 if (fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
128 &jif.usr,&jif.nic,&jif.sys,&jif.idle,
129 &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) {
130 bb_error_msg_and_die("failed to read /proc/stat");
131 }
132 fclose(fp);
133 jif.total = jif.usr + jif.nic + jif.sys + jif.idle
134 + jif.iowait + jif.irq + jif.softirq + jif.steal;
135 /* procps 2.x does not count iowait as busy time */
136 jif.busy = jif.total - jif.idle - jif.iowait;
137}
138
139
140static void do_stats(void)
141{
142 top_status_t *cur;
143 pid_t pid;
144 int i, last_i, n;
145 struct save_hist *new_hist;
146
147 get_jiffy_counts();
148 total_pcpu = 0;
149 /* total_rss = 0; */
150 new_hist = xmalloc(sizeof(struct save_hist)*ntop);
151 /*
152 * Make a pass through the data to get stats.
153 */
154 /* hist_iterations = 0; */
155 i = 0;
156 for (n = 0; n < ntop; n++) {
157 cur = top + n;
158
159 /*
160 * Calculate time in cur process. Time is sum of user time
161 * and system time
162 */
163 pid = cur->pid;
164 new_hist[n].ticks = cur->ticks;
165 new_hist[n].pid = pid;
166
167 /* find matching entry from previous pass */
168 cur->pcpu = 0;
169 /* do not start at index 0, continue at last used one
170 * (brought hist_iterations from ~14000 down to 172) */
171 last_i = i;
172 if (prev_hist_count) do {
173 if (prev_hist[i].pid == pid) {
174 cur->pcpu = cur->ticks - prev_hist[i].ticks;
175 total_pcpu += cur->pcpu;
176 break;
177 }
178 i = (i+1) % prev_hist_count;
179 /* hist_iterations++; */
180 } while (i != last_i);
181 /* total_rss += cur->rss; */
182 }
183
184 /*
185 * Save cur frame's information.
186 */
187 free(prev_hist);
188 prev_hist = new_hist;
189 prev_hist_count = ntop;
190}
191#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
192
193
194/* display generic info (meminfo / loadavg) */
195static unsigned long display_generic(int scr_width)
196{
197 FILE *fp;
198 char buf[80];
199 char scrbuf[80];
200 char *end;
201 unsigned long total, used, mfree, shared, buffers, cached;
202 unsigned int needs_conversion = 1;
203
204 /* read memory info */
205 fp = xfopen("meminfo", "r");
206
207 /*
208 * Old kernels (such as 2.4.x) had a nice summary of memory info that
209 * we could parse, however this is gone entirely in 2.6. Try parsing
210 * the old way first, and if that fails, parse each field manually.
211 *
212 * First, we read in the first line. Old kernels will have bogus
213 * strings we don't care about, whereas new kernels will start right
214 * out with MemTotal:
215 * -- PFM.
216 */
217 if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
218 fgets(buf, sizeof(buf), fp); /* skip first line */
219
220 fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
221 &total, &used, &mfree, &shared, &buffers, &cached);
222 } else {
223 /*
224 * Revert to manual parsing, which incidentally already has the
225 * sizes in kilobytes. This should be safe for both 2.4 and
226 * 2.6.
227 */
228 needs_conversion = 0;
229
230 fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
231
232 /*
233 * MemShared: is no longer present in 2.6. Report this as 0,
234 * to maintain consistent behavior with normal procps.
235 */
236 if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
237 shared = 0;
238
239 fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
240 fscanf(fp, "Cached: %lu %s\n", &cached, buf);
241
242 used = total - mfree;
243 }
244 fclose(fp);
245
246 /* read load average as a string */
247 buf[0] = '\0';
248 open_read_close("loadavg", buf, sizeof(buf));
249 end = strchr(buf, ' ');
250 if (end) end = strchr(end+1, ' ');
251 if (end) end = strchr(end+1, ' ');
252 if (end) *end = '\0';
253
254 if (needs_conversion) {
255 /* convert to kilobytes */
256 used /= 1024;
257 mfree /= 1024;
258 shared /= 1024;
259 buffers /= 1024;
260 cached /= 1024;
261 total /= 1024;
262 }
263
264 /* output memory info and load average */
265 /* clear screen & go to top */
266 if (scr_width > sizeof(scrbuf))
267 scr_width = sizeof(scrbuf);
268 snprintf(scrbuf, scr_width,
269 "Mem: %ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached",
270 used, mfree, shared, buffers, cached);
271
272 printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);
273
274 snprintf(scrbuf, scr_width, "Load average: %s", buf);
275 printf("%s\n", scrbuf);
276
277 return total;
278}
279
280
281/* display process statuses */
282static void display_status(int count, int scr_width)
283{
284 enum {
285 bits_per_int = sizeof(int)*8
286 };
287
288 top_status_t *s = top;
289 char rss_str_buf[8];
290 unsigned long total_memory = display_generic(scr_width); /* or use total_rss? */
291 unsigned pmem_shift, pmem_scale;
292
293#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
294 unsigned pcpu_shift, pcpu_scale;
295 unsigned busy_jifs;
296
297 /* what info of the processes is shown */
298 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
299 " PID USER STATUS RSS PPID %CPU %MEM COMMAND");
300#define MIN_WIDTH \
301 sizeof( " PID USER STATUS RSS PPID %CPU %MEM C")
302#else
303 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
304 " PID USER STATUS RSS PPID %MEM COMMAND");
305#define MIN_WIDTH \
306 sizeof( " PID USER STATUS RSS PPID %MEM C")
307#endif
308
309 /*
310 * MEM% = s->rss/MemTotal
311 */
312 pmem_shift = bits_per_int-11;
313 pmem_scale = 1000*(1U<<(bits_per_int-11)) / total_memory;
314 /* s->rss is in kb. we want (s->rss * pmem_scale) to never overflow */
315 while (pmem_scale >= 512) {
316 pmem_scale /= 4;
317 pmem_shift -= 2;
318 }
319#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
320 busy_jifs = jif.busy - prev_jif.busy;
321 /* This happens if there were lots of short-lived processes
322 * between two top updates (e.g. compilation) */
323 if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
324
325 /*
326 * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
327 * (pcpu is delta of sys+user time between samples)
328 */
329 /* (jif.xxx - prev_jif.xxx) and s->pcpu are
330 * in 0..~64000 range (HZ*update_interval).
331 * we assume that unsigned is at least 32-bit.
332 */
333 pcpu_shift = 6;
334 pcpu_scale = (1000*64*(uint16_t)busy_jifs ? : 1);
335 while (pcpu_scale < (1U<<(bits_per_int-2))) {
336 pcpu_scale *= 4;
337 pcpu_shift += 2;
338 }
339 pcpu_scale /= ( (uint16_t)(jif.total-prev_jif.total)*total_pcpu ? : 1);
340 /* we want (s->pcpu * pcpu_scale) to never overflow */
341 while (pcpu_scale >= 1024) {
342 pcpu_scale /= 4;
343 pcpu_shift -= 2;
344 }
345 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
346#endif
347 while (count-- > 0) {
348 div_t pmem = div((s->rss*pmem_scale) >> pmem_shift, 10);
349 int col = scr_width+1;
350 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(div_t pcpu;)
351
352 if (s->rss >= 100*1024)
353 sprintf(rss_str_buf, "%6ldM", s->rss/1024);
354 else
355 sprintf(rss_str_buf, "%7ld", s->rss);
356 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(
357 pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);
358 )
359 col -= printf("\n%5u %-8s %s "
360 "%s%6u"
361 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c")
362 "%3u.%c ",
363 s->pid, get_cached_username(s->uid), s->state,
364 rss_str_buf, s->ppid,
365 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu.quot, '0'+pcpu.rem,)
366 pmem.quot, '0'+pmem.rem);
367 if (col > 0)
368 printf("%.*s", col, s->comm);
369 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
370 jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
371 s++;
372 }
373 /* printf(" %d", hist_iterations); */
374 putchar(OPT_BATCH_MODE ? '\n' : '\r');
375 fflush(stdout);
376}
377
378
379static void clearmems(void)
380{
381 clear_username_cache();
382 free(top);
383 top = 0;
384 ntop = 0;
385}
386
387
388#if ENABLE_FEATURE_USE_TERMIOS
389#include <termios.h>
390#include <signal.h>
391
392static struct termios initial_settings;
393
394static void reset_term(void)
395{
396 tcsetattr(0, TCSANOW, (void *) &initial_settings);
397#if ENABLE_FEATURE_CLEAN_UP
398 clearmems();
399#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
400 free(prev_hist);
401#endif
402#endif /* FEATURE_CLEAN_UP */
403}
404
405static void sig_catcher(int sig ATTRIBUTE_UNUSED)
406{
407 reset_term();
408 exit(1);
409}
410#endif /* FEATURE_USE_TERMIOS */
411
412
413int top_main(int argc, char **argv)
414{
415 int count, lines, col;
416 unsigned interval = 5; /* default update rate is 5 seconds */
417 unsigned iterations = UINT_MAX; /* 2^32 iterations by default :) */
418 char *sinterval, *siterations;
419#if ENABLE_FEATURE_USE_TERMIOS
420 struct termios new_settings;
421 struct timeval tv;
422 fd_set readfds;
423 unsigned char c;
424#endif /* FEATURE_USE_TERMIOS */
425
426 /* do normal option parsing */
427 interval = 5;
428 opt_complementary = "-";
429 getopt32(argc, argv, "d:n:b", &sinterval, &siterations);
430 if (option_mask32 & 0x1) interval = xatou(sinterval); // -d
431 if (option_mask32 & 0x2) iterations = xatou(siterations); // -n
432 //if (option_mask32 & 0x4) // -b
433
434 /* change to /proc */
435 xchdir("/proc");
436#if ENABLE_FEATURE_USE_TERMIOS
437 tcgetattr(0, (void *) &initial_settings);
438 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
439 /* unbuffered input, turn off echo */
440 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
441
442 signal(SIGTERM, sig_catcher);
443 signal(SIGINT, sig_catcher);
444 tcsetattr(0, TCSANOW, (void *) &new_settings);
445 atexit(reset_term);
446#endif /* FEATURE_USE_TERMIOS */
447
448#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
449 sort_function[0] = pcpu_sort;
450 sort_function[1] = mem_sort;
451 sort_function[2] = time_sort;
452#else
453 sort_function = mem_sort;
454#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
455
456 while (1) {
457 procps_status_t *p = NULL;
458
459 /* Default to 25 lines - 5 lines for status */
460 lines = 24 - 3;
461 col = 79;
462#if ENABLE_FEATURE_USE_TERMIOS
463 get_terminal_width_height(0, &col, &lines);
464 if (lines < 5 || col < MIN_WIDTH) {
465 sleep(interval);
466 continue;
467 }
468 lines -= 3;
469#endif /* FEATURE_USE_TERMIOS */
470
471 /* read process IDs & status for all the processes */
472 while ((p = procps_scan(p, 0
473 | PSSCAN_PID
474 | PSSCAN_PPID
475 | PSSCAN_RSS
476 | PSSCAN_STIME
477 | PSSCAN_UTIME
478 | PSSCAN_STATE
479 | PSSCAN_COMM
480 | PSSCAN_SID
481 | PSSCAN_UIDGID
482 ))) {
483 int n = ntop;
484 top = xrealloc(top, (++ntop)*sizeof(top_status_t));
485 top[n].pid = p->pid;
486 top[n].ppid = p->ppid;
487 top[n].rss = p->rss;
488 top[n].ticks = p->stime + p->utime;
489 top[n].uid = p->uid;
490 strcpy(top[n].state, p->state);
491 strcpy(top[n].comm, p->comm);
492 }
493 if (ntop == 0) {
494 bb_error_msg_and_die("can't find process info in /proc");
495 }
496#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
497 if (!prev_hist_count) {
498 do_stats();
499 sleep(1);
500 clearmems();
501 continue;
502 }
503 do_stats();
504 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
505#else
506 qsort(top, ntop, sizeof(top_status_t), (void*)sort_function);
507#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
508 count = lines;
509 if (OPT_BATCH_MODE || count > ntop) {
510 count = ntop;
511 }
512 /* show status for each of the processes */
513 display_status(count, col);
514#if ENABLE_FEATURE_USE_TERMIOS
515 tv.tv_sec = interval;
516 tv.tv_usec = 0;
517 FD_ZERO(&readfds);
518 FD_SET(0, &readfds);
519 select(1, &readfds, NULL, NULL, &tv);
520 if (FD_ISSET(0, &readfds)) {
521 if (read(0, &c, 1) <= 0) { /* signal */
522 return EXIT_FAILURE;
523 }
524 if (c == 'q' || c == initial_settings.c_cc[VINTR])
525 break;
526 if (c == 'M') {
527#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
528 sort_function[0] = mem_sort;
529 sort_function[1] = pcpu_sort;
530 sort_function[2] = time_sort;
531#else
532 sort_function = mem_sort;
533#endif
534 }
535#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
536 if (c == 'P') {
537 sort_function[0] = pcpu_sort;
538 sort_function[1] = mem_sort;
539 sort_function[2] = time_sort;
540 }
541 if (c == 'T') {
542 sort_function[0] = time_sort;
543 sort_function[1] = mem_sort;
544 sort_function[2] = pcpu_sort;
545 }
546#endif
547 if (c == 'N') {
548#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
549 sort_function[0] = pid_sort;
550#else
551 sort_function = pid_sort;
552#endif
553 }
554 }
555 if (!--iterations)
556 break;
557#else
558 sleep(interval);
559#endif /* FEATURE_USE_TERMIOS */
560 clearmems();
561 }
562 if (ENABLE_FEATURE_CLEAN_UP)
563 clearmems();
564 putchar('\n');
565 return EXIT_SUCCESS;
566}
diff --git a/procps/uptime.c b/procps/uptime.c
new file mode 100644
index 000000000..37c9d449a
--- /dev/null
+++ b/procps/uptime.c
@@ -0,0 +1,59 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini uptime implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
8 */
9
10/* This version of uptime doesn't display the number of users on the system,
11 * since busybox init doesn't mess with utmp. For folks using utmp that are
12 * just dying to have # of users reported, feel free to write it as some type
13 * of CONFIG_FEATURE_UTMP_SUPPORT #define
14 */
15
16/* getopt not needed */
17
18#include "busybox.h"
19
20#ifndef FSHIFT
21# define FSHIFT 16 /* nr of bits of precision */
22#endif
23#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
24#define LOAD_INT(x) ((x) >> FSHIFT)
25#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
26
27
28int uptime_main(int argc, char **argv)
29{
30 int updays, uphours, upminutes;
31 struct sysinfo info;
32 struct tm *current_time;
33 time_t current_secs;
34
35 time(&current_secs);
36 current_time = localtime(&current_secs);
37
38 sysinfo(&info);
39
40 printf(" %02d:%02d:%02d up ",
41 current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
42 updays = (int) info.uptime / (60*60*24);
43 if (updays)
44 printf("%d day%s, ", updays, (updays != 1) ? "s" : "");
45 upminutes = (int) info.uptime / 60;
46 uphours = (upminutes / 60) % 24;
47 upminutes %= 60;
48 if(uphours)
49 printf("%2d:%02d, ", uphours, upminutes);
50 else
51 printf("%d min, ", upminutes);
52
53 printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n",
54 LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]),
55 LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]),
56 LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2]));
57
58 return EXIT_SUCCESS;
59}