diff options
Diffstat (limited to 'procps')
-rw-r--r-- | procps/Config.in | 121 | ||||
-rw-r--r-- | procps/Kbuild | 16 | ||||
-rw-r--r-- | procps/free.c | 68 | ||||
-rw-r--r-- | procps/fuser.c | 366 | ||||
-rw-r--r-- | procps/kill.c | 155 | ||||
-rw-r--r-- | procps/pidof.c | 90 | ||||
-rw-r--r-- | procps/ps.c | 388 | ||||
-rw-r--r-- | procps/ps.posix | 175 | ||||
-rw-r--r-- | procps/renice.c | 126 | ||||
-rw-r--r-- | procps/sysctl.c | 326 | ||||
-rw-r--r-- | procps/top.c | 566 | ||||
-rw-r--r-- | procps/uptime.c | 59 |
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 | |||
6 | menu "Process Utilities" | ||
7 | |||
8 | config 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 | |||
16 | config 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 | |||
24 | config 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 | |||
32 | config 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 | |||
41 | config KILLALL5 | ||
42 | bool "killall5" | ||
43 | default n | ||
44 | depends on KILL | ||
45 | |||
46 | config 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 | |||
53 | config 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 | |||
60 | config 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 | |||
69 | config PS | ||
70 | bool "ps" | ||
71 | default n | ||
72 | help | ||
73 | ps gives a snapshot of the current processes. | ||
74 | |||
75 | config 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 | |||
84 | config RENICE | ||
85 | bool "renice" | ||
86 | default n | ||
87 | help | ||
88 | Renice alters the scheduling priority of one or more running | ||
89 | processes. | ||
90 | |||
91 | config BB_SYSCTL | ||
92 | bool "sysctl" | ||
93 | default n | ||
94 | help | ||
95 | Configure kernel parameters at runtime. | ||
96 | |||
97 | config 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 | |||
104 | config 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 | |||
111 | config 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 | |||
120 | endmenu | ||
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 | |||
7 | lib-y:= | ||
8 | lib-$(CONFIG_FREE) += free.o | ||
9 | lib-$(CONFIG_KILL) += kill.o | ||
10 | lib-$(CONFIG_PIDOF) += pidof.o | ||
11 | lib-$(CONFIG_PS) += ps.o | ||
12 | lib-$(CONFIG_RENICE) += renice.o | ||
13 | lib-$(CONFIG_BB_SYSCTL) += sysctl.o | ||
14 | lib-$(CONFIG_TOP) += top.o | ||
15 | lib-$(CONFIG_UPTIME) += uptime.o | ||
16 | lib-$(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 | |||
14 | int 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 | |||
22 | typedef struct inode_list { | ||
23 | ino_t inode; | ||
24 | dev_t dev; | ||
25 | struct inode_list *next; | ||
26 | } inode_list; | ||
27 | |||
28 | typedef struct pid_list { | ||
29 | pid_t pid; | ||
30 | struct pid_list *next; | ||
31 | } pid_list; | ||
32 | |||
33 | static 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 | |||
55 | static 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 | |||
65 | static 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 | |||
78 | static 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 | |||
90 | static 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 | |||
108 | static 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 | |||
130 | static 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 | |||
165 | static 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 | |||
181 | static 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 | |||
207 | static 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 | |||
219 | static 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 | |||
241 | static 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 | |||
271 | static 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 | |||
284 | static 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 | |||
304 | int 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 | |||
13 | int 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 | |||
81 | do_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 | |||
12 | enum { | ||
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 | |||
19 | int 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 | |||
16 | void 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 | |||
21 | void func_comm(char *buf, int size, const procps_status_t *ps) | ||
22 | { | ||
23 | safe_strncpy(buf, ps->comm, size+1); | ||
24 | } | ||
25 | |||
26 | void 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 | |||
35 | void func_pid(char *buf, int size, const procps_status_t *ps) | ||
36 | { | ||
37 | snprintf(buf, size+1, "%*u", size, ps->pid); | ||
38 | } | ||
39 | |||
40 | void func_ppid(char *buf, int size, const procps_status_t *ps) | ||
41 | { | ||
42 | snprintf(buf, size+1, "%*u", size, ps->ppid); | ||
43 | } | ||
44 | |||
45 | void func_pgid(char *buf, int size, const procps_status_t *ps) | ||
46 | { | ||
47 | snprintf(buf, size+1, "%*u", size, ps->pgid); | ||
48 | } | ||
49 | |||
50 | void 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 | /* | ||
58 | void func_nice(char *buf, int size, const procps_status_t *ps) | ||
59 | { | ||
60 | ps->??? | ||
61 | } | ||
62 | |||
63 | void func_etime(char *buf, int size, const procps_status_t *ps) | ||
64 | { | ||
65 | elapled time [[dd-]hh:]mm:ss | ||
66 | } | ||
67 | |||
68 | void func_time(char *buf, int size, const procps_status_t *ps) | ||
69 | { | ||
70 | cumulative time [[dd-]hh:]mm:ss | ||
71 | } | ||
72 | |||
73 | void func_pcpu(char *buf, int size, const procps_status_t *ps) | ||
74 | { | ||
75 | } | ||
76 | |||
77 | void func_tty(char *buf, int size, const procps_status_t *ps) | ||
78 | { | ||
79 | } | ||
80 | */ | ||
81 | |||
82 | typedef 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 | |||
90 | static 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 | |||
113 | static ps_out_t* out; | ||
114 | static int out_cnt; | ||
115 | static int print_header; | ||
116 | static int ps_flags; | ||
117 | static char *buffer; | ||
118 | static unsigned terminal_width; | ||
119 | |||
120 | |||
121 | static 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 | |||
128 | static 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 | |||
138 | static 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 | |||
175 | static 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 | |||
189 | static 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 | |||
209 | static 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 */ | ||
236 | static char default_o[] = "pid,user" /* TODO: ,vsz,stat */ ",args"; | ||
237 | |||
238 | int 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 | |||
285 | int 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 @@ | |||
1 | This is what POSIX 2003 says about ps: | ||
2 | |||
3 | By default, ps shall select all processes with the same effective user | ||
4 | ID as the current user and the same controlling terminal as the invoker | ||
5 | |||
6 | ps [-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 | |||
77 | With the exception of -o format, all of the options shown are used to | ||
78 | select processes. If any are specified, the default list shall be | ||
79 | ignored and ps shall select the processes represented by the inclusive | ||
80 | OR of all the selection-criteria options. | ||
81 | |||
82 | The -o option allows the output format to be specified under user con- | ||
83 | trol. | ||
84 | |||
85 | The application shall ensure that the format specification is a list of | ||
86 | names presented as a single argument, <blank> or comma-separated. Each | ||
87 | variable has a default header. The default header can be overridden by | ||
88 | appending an equals sign and the new text of the header. The rest of | ||
89 | the characters in the argument shall be used as the header text. The | ||
90 | fields specified shall be written in the order specified on the command | ||
91 | line, and should be arranged in columns in the output. The field widths | ||
92 | shall be selected by the system to be at least as wide as the header | ||
93 | text (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 | ||
95 | header text. If all header text fields are null, no header line shall | ||
96 | be written. | ||
97 | |||
98 | ruser 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 | |||
102 | user 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 | |||
106 | rgroup 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 | |||
110 | group 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 | |||
114 | pid The decimal value of the process ID. | ||
115 | |||
116 | ppid The decimal value of the parent process ID. | ||
117 | |||
118 | pgid The decimal value of the process group ID. | ||
119 | |||
120 | pcpu 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 | |||
125 | vsz The size of the process in (virtual) memory in 1024 byte units | ||
126 | as a decimal integer. | ||
127 | |||
128 | nice The decimal value of the nice value of the process; see nice() . | ||
129 | |||
130 | etime In the POSIX locale, the elapsed time since the process was | ||
131 | started, in the form: [[dd-]hh:]mm:ss | ||
132 | |||
133 | time In the POSIX locale, the cumulative CPU time of the process in | ||
134 | the form: [dd-]hh:mm:ss | ||
135 | |||
136 | tty The name of the controlling terminal of the process (if any) in | ||
137 | the same format used by the who utility. | ||
138 | |||
139 | comm The name of the command being executed ( argv[0] value) as a | ||
140 | string. | ||
141 | |||
142 | args 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 | |||
152 | Any field need not be meaningful in all implementations. In such a case | ||
153 | a hyphen ( '-' ) should be output in place of the field value. | ||
154 | |||
155 | Only comm and args shall be allowed to contain <blank>s; all others | ||
156 | shall not. | ||
157 | |||
158 | The following table specifies the default header to be used in the | ||
159 | POSIX 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 | |||
171 | There is no special quoting mechanism for header text. The header text | ||
172 | is the rest of the argument. If multiple header changes are needed, | ||
173 | multiple -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 | |||
25 | void BUG_bad_PRIO_PROCESS(void); | ||
26 | void BUG_bad_PRIO_PGRP(void); | ||
27 | void BUG_bad_PRIO_USER(void); | ||
28 | |||
29 | int 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 | */ | ||
22 | static int sysctl_read_setting(const char *setting, int output); | ||
23 | static int sysctl_write_setting(const char *setting, int output); | ||
24 | static int sysctl_preload_file(const char *filename, int output); | ||
25 | static int sysctl_display_all(const char *path, int output, int show_table); | ||
26 | |||
27 | /* | ||
28 | * Globals... | ||
29 | */ | ||
30 | static const char PROC_PATH[] = "/proc/sys/"; | ||
31 | static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf"; | ||
32 | |||
33 | /* error messages */ | ||
34 | static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n"; | ||
35 | static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n"; | ||
36 | static const char ERR_NO_EQUALS[] = | ||
37 | "error: '%s' must be of the form name=value\n"; | ||
38 | static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n"; | ||
39 | static const char ERR_UNKNOWN_WRITING[] = | ||
40 | "error: unknown error %d setting key '%s'\n"; | ||
41 | static const char ERR_UNKNOWN_READING[] = | ||
42 | "error: unknown error %d reading key '%s'\n"; | ||
43 | static const char ERR_PERMISSION_DENIED[] = | ||
44 | "error: permission denied on key '%s'\n"; | ||
45 | static const char ERR_PRELOAD_FILE[] = | ||
46 | "error: cannot open preload file '%s'\n"; | ||
47 | static const char WARN_BAD_LINE[] = | ||
48 | "warning: %s(%d): invalid syntax, continuing...\n"; | ||
49 | |||
50 | |||
51 | static void dwrite_str(int fd, const char *buf) | ||
52 | { | ||
53 | write(fd, buf, strlen(buf)); | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * sysctl_main()... | ||
58 | */ | ||
59 | int 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 | |||
119 | int 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 | */ | ||
171 | int 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 | */ | ||
239 | int 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 | */ | ||
293 | int 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 | |||
34 | typedef 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; | ||
45 | static top_status_t *top; | ||
46 | static int ntop; | ||
47 | /* This structure stores some critical information from one frame to | ||
48 | the next. Used for finding deltas. */ | ||
49 | struct save_hist { | ||
50 | unsigned long ticks; | ||
51 | unsigned pid; | ||
52 | }; | ||
53 | static struct save_hist *prev_hist; | ||
54 | static int prev_hist_count; | ||
55 | /* static int hist_iterations; */ | ||
56 | static 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 | ||
63 | static 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 | |||
71 | static 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 | |||
79 | typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q); | ||
80 | |||
81 | #if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
82 | |||
83 | static cmp_funcp sort_function; | ||
84 | |||
85 | #else | ||
86 | |||
87 | enum { SORT_DEPTH = 3 }; | ||
88 | |||
89 | static cmp_funcp sort_function[SORT_DEPTH]; | ||
90 | |||
91 | static 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 | |||
98 | static 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 | |||
105 | static 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 | |||
117 | typedef 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; | ||
122 | static jiffy_counts_t jif, prev_jif; | ||
123 | static 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 | |||
140 | static 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) */ | ||
195 | static 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 */ | ||
282 | static 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 | |||
379 | static 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 | |||
392 | static struct termios initial_settings; | ||
393 | |||
394 | static 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 | |||
405 | static void sig_catcher(int sig ATTRIBUTE_UNUSED) | ||
406 | { | ||
407 | reset_term(); | ||
408 | exit(1); | ||
409 | } | ||
410 | #endif /* FEATURE_USE_TERMIOS */ | ||
411 | |||
412 | |||
413 | int 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 | |||
28 | int 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(¤t_secs); | ||
36 | current_time = localtime(¤t_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 | } | ||