diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-05 00:45:47 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-05 00:45:47 +0000 |
commit | 9494919ea5116075e16daac1355265b023419cdc (patch) | |
tree | a6743775e25864bf7f1e656283752df7d988de36 | |
parent | aae0311356b4458e3a62fb19af656e00323e3bf1 (diff) | |
download | busybox-w32-9494919ea5116075e16daac1355265b023419cdc.tar.gz busybox-w32-9494919ea5116075e16daac1355265b023419cdc.tar.bz2 busybox-w32-9494919ea5116075e16daac1355265b023419cdc.zip |
ps: implement POSIX-like options, most notably -o
(activated by CONFIG_DESKTOP)
-rw-r--r-- | include/usage.h | 15 | ||||
-rw-r--r-- | procps/ps.c | 275 |
2 files changed, 290 insertions, 0 deletions
diff --git a/include/usage.h b/include/usage.h index 46bb9eca4..7174ad414 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -2377,6 +2377,18 @@ USE_FEATURE_MDEV_CONFIG( \ | |||
2377 | "$ printf \"Val=%d\\n\" 5\n" \ | 2377 | "$ printf \"Val=%d\\n\" 5\n" \ |
2378 | "Val=5\n" | 2378 | "Val=5\n" |
2379 | 2379 | ||
2380 | |||
2381 | #if ENABLE_DESKTOP | ||
2382 | |||
2383 | #define ps_trivial_usage \ | ||
2384 | "" | ||
2385 | #define ps_full_usage \ | ||
2386 | "Report process status\n" \ | ||
2387 | "\nOptions:" \ | ||
2388 | "\n\t-o col1,col2=header\tSelect columns for display" \ | ||
2389 | |||
2390 | #else /* !ENABLE_DESKTOP */ | ||
2391 | |||
2380 | #if !defined CONFIG_SELINUX && !ENABLE_FEATURE_PS_WIDE | 2392 | #if !defined CONFIG_SELINUX && !ENABLE_FEATURE_PS_WIDE |
2381 | #define USAGE_PS "\n\tThis version of ps accepts no options." | 2393 | #define USAGE_PS "\n\tThis version of ps accepts no options." |
2382 | #else | 2394 | #else |
@@ -2396,6 +2408,8 @@ USE_FEATURE_MDEV_CONFIG( \ | |||
2396 | USE_SELINUX("\n\t-c\tshow SE Linux context") \ | 2408 | USE_SELINUX("\n\t-c\tshow SE Linux context") \ |
2397 | USAGE_PS_WIDE("\n\tw\twide output") | 2409 | USAGE_PS_WIDE("\n\tw\twide output") |
2398 | 2410 | ||
2411 | #endif /* ENABLE_DESKTOP */ | ||
2412 | |||
2399 | #define ps_example_usage \ | 2413 | #define ps_example_usage \ |
2400 | "$ ps\n" \ | 2414 | "$ ps\n" \ |
2401 | " PID Uid Gid State Command\n" \ | 2415 | " PID Uid Gid State Command\n" \ |
@@ -2409,6 +2423,7 @@ USE_FEATURE_MDEV_CONFIG( \ | |||
2409 | " 745 root root S [getty]\n" \ | 2423 | " 745 root root S [getty]\n" \ |
2410 | " 2990 andersen andersen R ps\n" | 2424 | " 2990 andersen andersen R ps\n" |
2411 | 2425 | ||
2426 | |||
2412 | #define pwd_trivial_usage \ | 2427 | #define pwd_trivial_usage \ |
2413 | "" | 2428 | "" |
2414 | #define pwd_full_usage \ | 2429 | #define pwd_full_usage \ |
diff --git a/procps/ps.c b/procps/ps.c index 2ff6e77d4..3cb86e49c 100644 --- a/procps/ps.c +++ b/procps/ps.c | |||
@@ -9,6 +9,279 @@ | |||
9 | 9 | ||
10 | #include "busybox.h" | 10 | #include "busybox.h" |
11 | 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 | |||
12 | int ps_main(int argc, char **argv) | 285 | int ps_main(int argc, char **argv) |
13 | { | 286 | { |
14 | procps_status_t *p = NULL; | 287 | procps_status_t *p = NULL; |
@@ -111,3 +384,5 @@ int ps_main(int argc, char **argv) | |||
111 | clear_username_cache(); | 384 | clear_username_cache(); |
112 | return EXIT_SUCCESS; | 385 | return EXIT_SUCCESS; |
113 | } | 386 | } |
387 | |||
388 | #endif | ||