blob: e0bdfe13c4196908852d1beb906eab3c4714fcee [file] [log] [blame]
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00001/* vi: set sw=4 ts=4: */
Eric Andersenc3657422001-11-30 07:54:32 +00002/* `time' utility to display resource usage of processes.
3 Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
4
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00005 Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
6*/
Eric Andersenc3657422001-11-30 07:54:32 +00007/* Originally written by David Keppel <pardo@cs.washington.edu>.
Eric Andersenc7bda1c2004-03-15 08:29:22 +00008 Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
Eric Andersenc3657422001-11-30 07:54:32 +00009 Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000010*/
Eric Andersenc3657422001-11-30 07:54:32 +000011
Bernhard Reutner-Fischerc89982d2006-06-03 19:49:21 +000012#include "busybox.h"
Eric Andersenc3657422001-11-30 07:54:32 +000013#include <stdlib.h>
14#include <stdio.h>
15#include <signal.h>
16#include <errno.h>
17#include <getopt.h>
18#include <string.h>
19#include <limits.h>
20#include <unistd.h>
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000021#include <sys/types.h> /* For pid_t. */
Eric Andersenc3657422001-11-30 07:54:32 +000022#include <sys/wait.h>
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000023#include <sys/param.h> /* For getpagesize, maybe. */
Eric Andersenc3657422001-11-30 07:54:32 +000024
25#define TV_MSEC tv_usec / 1000
26#include <sys/resource.h>
Eric Andersenc3657422001-11-30 07:54:32 +000027
28/* Information on the resources used by a child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000029typedef struct {
30 int waitstatus;
31 struct rusage ru;
32 struct timeval start, elapsed; /* Wallclock time of process. */
Eric Andersenc3657422001-11-30 07:54:32 +000033} resource_t;
34
35/* msec = milliseconds = 1/1,000 (1*10e-3) second.
36 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
37
38#ifndef TICKS_PER_SEC
39#define TICKS_PER_SEC 100
40#endif
41
42/* The number of milliseconds in one `tick' used by the `rusage' structure. */
43#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
44
45/* Return the number of clock ticks that occur in M milliseconds. */
46#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
47
48#define UL unsigned long
49
50static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
51
52/* The output format for the -p option .*/
53static const char *const posix_format = "real %e\nuser %U\nsys %S";
54
55
56/* Format string for printing all statistics verbosely.
57 Keep this output to 24 lines so users on terminals can see it all.*/
58static const char *const long_format =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000059 "\tCommand being timed: \"%C\"\n"
60 "\tUser time (seconds): %U\n"
61 "\tSystem time (seconds): %S\n"
62 "\tPercent of CPU this job got: %P\n"
63 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
64 "\tAverage shared text size (kbytes): %X\n"
65 "\tAverage unshared data size (kbytes): %D\n"
66 "\tAverage stack size (kbytes): %p\n"
67 "\tAverage total size (kbytes): %K\n"
68 "\tMaximum resident set size (kbytes): %M\n"
69 "\tAverage resident set size (kbytes): %t\n"
70 "\tMajor (requiring I/O) page faults: %F\n"
71 "\tMinor (reclaiming a frame) page faults: %R\n"
72 "\tVoluntary context switches: %w\n"
73 "\tInvoluntary context switches: %c\n"
74 "\tSwaps: %W\n"
75 "\tFile system inputs: %I\n"
76 "\tFile system outputs: %O\n"
77 "\tSocket messages sent: %s\n"
78 "\tSocket messages received: %r\n"
79 "\tSignals delivered: %k\n"
80 "\tPage size (bytes): %Z\n" "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000081
82
83 /* Wait for and fill in data on child process PID.
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000084 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000085
86/* pid_t is short on BSDI, so don't try to promote it. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000087static int resuse_end(pid_t pid, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +000088{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000089 int status;
Eric Andersenc3657422001-11-30 07:54:32 +000090
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000091 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000092
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000093 /* Ignore signals, but don't ignore the children. When wait3
94 returns the child process, set the time the command finished. */
95 while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
96 if (caught == -1)
97 return 0;
98 }
Eric Andersenc3657422001-11-30 07:54:32 +000099
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000100 gettimeofday(&resp->elapsed, (struct timezone *) 0);
101 resp->elapsed.tv_sec -= resp->start.tv_sec;
102 if (resp->elapsed.tv_usec < resp->start.tv_usec) {
103 /* Manually carry a one from the seconds field. */
104 resp->elapsed.tv_usec += 1000000;
105 --resp->elapsed.tv_sec;
106 }
107 resp->elapsed.tv_usec -= resp->start.tv_usec;
Eric Andersenc3657422001-11-30 07:54:32 +0000108
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000109 resp->waitstatus = status;
Eric Andersenc3657422001-11-30 07:54:32 +0000110
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000111 return 1;
Eric Andersenc3657422001-11-30 07:54:32 +0000112}
113
114/* Print ARGV to FP, with each entry in ARGV separated by FILLER. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000115static void fprintargv(FILE * fp, char *const *argv, const char *filler)
Eric Andersenc3657422001-11-30 07:54:32 +0000116{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000117 char *const *av;
Eric Andersenc3657422001-11-30 07:54:32 +0000118
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000119 av = argv;
120 fputs(*av, fp);
121 while (*++av) {
122 fputs(filler, fp);
123 fputs(*av, fp);
124 }
125 if (ferror(fp))
126 bb_error_msg_and_die(bb_msg_write_error);
Eric Andersenc3657422001-11-30 07:54:32 +0000127}
128
129/* Return the number of kilobytes corresponding to a number of pages PAGES.
130 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
131
132 Try to do arithmetic so that the risk of overflow errors is minimized.
133 This is funky since the pagesize could be less than 1K.
134 Note: Some machines express getrusage statistics in terms of K,
135 others in terms of pages. */
136
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000137static unsigned long ptok(unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +0000138{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000139 static unsigned long ps = 0;
140 unsigned long tmp;
141 static long size = LONG_MAX;
Eric Andersenc3657422001-11-30 07:54:32 +0000142
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000143 /* Initialization. */
144 if (ps == 0)
145 ps = (long) getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000146
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000147 /* Conversion. */
148 if (pages > (LONG_MAX / ps)) { /* Could overflow. */
149 tmp = pages / 1024; /* Smaller first, */
150 size = tmp * ps; /* then larger. */
151 } else { /* Could underflow. */
152 tmp = pages * ps; /* Larger first, */
153 size = tmp / 1024; /* then smaller. */
154 }
155 return size;
Eric Andersenc3657422001-11-30 07:54:32 +0000156}
157
158/* summarize: Report on the system use of a command.
159
160 Copy the FMT argument to FP except that `%' sequences
161 have special meaning, and `\n' and `\t' are translated into
162 newline and tab, respectively, and `\\' is translated into `\'.
163
164 The character following a `%' can be:
165 (* means the tcsh time builtin also recognizes it)
166 % == a literal `%'
167 C == command name and arguments
168* D == average unshared data size in K (ru_idrss+ru_isrss)
169* E == elapsed real (wall clock) time in [hour:]min:sec
170* F == major page faults (required physical I/O) (ru_majflt)
171* I == file system inputs (ru_inblock)
172* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
173* M == maximum resident set size in K (ru_maxrss)
174* O == file system outputs (ru_oublock)
175* P == percent of CPU this job got (total cpu time / elapsed time)
176* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
177* S == system (kernel) time (seconds) (ru_stime)
178* T == system time in [hour:]min:sec
179* U == user time (seconds) (ru_utime)
180* u == user time in [hour:]min:sec
181* W == times swapped out (ru_nswap)
182* X == average amount of shared text in K (ru_ixrss)
183 Z == page size
184* c == involuntary context switches (ru_nivcsw)
185 e == elapsed real time in seconds
186* k == signals delivered (ru_nsignals)
187 p == average unshared stack size in K (ru_isrss)
188* r == socket messages received (ru_msgrcv)
189* s == socket messages sent (ru_msgsnd)
190 t == average resident set size in K (ru_idrss)
191* w == voluntary context switches (ru_nvcsw)
192 x == exit status of command
193
194 Various memory usages are found by converting from page-seconds
195 to kbytes by multiplying by the page size, dividing by 1024,
196 and dividing by elapsed real time.
197
198 FP is the stream to print to.
199 FMT is the format string, interpreted as described above.
200 COMMAND is the command and args that are being summarized.
201 RESP is resource information on the command. */
202
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000203static void summarize(FILE * fp, const char *fmt, char **command,
204 resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000205{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000206 unsigned long r; /* Elapsed real milliseconds. */
207 unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
Eric Andersenc3657422001-11-30 07:54:32 +0000208
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000209 if (WIFSTOPPED(resp->waitstatus))
210 fprintf(fp, "Command stopped by signal %d\n",
211 WSTOPSIG(resp->waitstatus));
212 else if (WIFSIGNALED(resp->waitstatus))
213 fprintf(fp, "Command terminated by signal %d\n",
214 WTERMSIG(resp->waitstatus));
215 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
216 fprintf(fp, "Command exited with non-zero status %d\n",
217 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000218
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000219 /* Convert all times to milliseconds. Occasionally, one of these values
220 comes out as zero. Dividing by zero causes problems, so we first
221 check the time value. If it is zero, then we take `evasive action'
222 instead of calculating a value. */
Eric Andersenc3657422001-11-30 07:54:32 +0000223
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000224 r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000225
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000226 v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
227 resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
Eric Andersenc3657422001-11-30 07:54:32 +0000228
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000229 while (*fmt) {
230 switch (*fmt) {
231 case '%':
232 switch (*++fmt) {
233 case '%': /* Literal '%'. */
234 putc('%', fp);
235 break;
236 case 'C': /* The command that got timed. */
237 fprintargv(fp, command, " ");
238 break;
239 case 'D': /* Average unshared data size. */
240 fprintf(fp, "%lu",
241 MSEC_TO_TICKS(v) == 0 ? 0 :
242 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
243 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
244 break;
245 case 'E': /* Elapsed real (wall clock) time. */
246 if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
247 fprintf(fp, "%ldh %ldm %02lds",
248 resp->elapsed.tv_sec / 3600,
249 (resp->elapsed.tv_sec % 3600) / 60,
250 resp->elapsed.tv_sec % 60);
251 else
252 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */
253 resp->elapsed.tv_sec / 60,
254 resp->elapsed.tv_sec % 60,
255 resp->elapsed.tv_usec / 10000);
256 break;
257 case 'F': /* Major page faults. */
258 fprintf(fp, "%ld", resp->ru.ru_majflt);
259 break;
260 case 'I': /* Inputs. */
261 fprintf(fp, "%ld", resp->ru.ru_inblock);
262 break;
263 case 'K': /* Average mem usage == data+stack+text. */
264 fprintf(fp, "%lu",
265 MSEC_TO_TICKS(v) == 0 ? 0 :
266 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
267 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) +
268 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
269 break;
270 case 'M': /* Maximum resident set size. */
271 fprintf(fp, "%lu", ptok((UL) resp->ru.ru_maxrss));
272 break;
273 case 'O': /* Outputs. */
274 fprintf(fp, "%ld", resp->ru.ru_oublock);
275 break;
276 case 'P': /* Percent of CPU this job got. */
277 /* % cpu is (total cpu time)/(elapsed time). */
278 if (r > 0)
279 fprintf(fp, "%lu%%", (v * 100 / r));
280 else
281 fprintf(fp, "?%%");
282 break;
283 case 'R': /* Minor page faults (reclaims). */
284 fprintf(fp, "%ld", resp->ru.ru_minflt);
285 break;
286 case 'S': /* System time. */
287 fprintf(fp, "%ld.%02ld",
288 resp->ru.ru_stime.tv_sec,
289 resp->ru.ru_stime.TV_MSEC / 10);
290 break;
291 case 'T': /* System time. */
292 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
293 fprintf(fp, "%ldh %ldm %02lds",
294 resp->ru.ru_stime.tv_sec / 3600,
295 (resp->ru.ru_stime.tv_sec % 3600) / 60,
296 resp->ru.ru_stime.tv_sec % 60);
297 else
298 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */
299 resp->ru.ru_stime.tv_sec / 60,
300 resp->ru.ru_stime.tv_sec % 60,
301 resp->ru.ru_stime.tv_usec / 10000);
302 break;
303 case 'U': /* User time. */
304 fprintf(fp, "%ld.%02ld",
305 resp->ru.ru_utime.tv_sec,
306 resp->ru.ru_utime.TV_MSEC / 10);
307 break;
308 case 'u': /* User time. */
309 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
310 fprintf(fp, "%ldh %ldm %02lds",
311 resp->ru.ru_utime.tv_sec / 3600,
312 (resp->ru.ru_utime.tv_sec % 3600) / 60,
313 resp->ru.ru_utime.tv_sec % 60);
314 else
315 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */
316 resp->ru.ru_utime.tv_sec / 60,
317 resp->ru.ru_utime.tv_sec % 60,
318 resp->ru.ru_utime.tv_usec / 10000);
319 break;
320 case 'W': /* Times swapped out. */
321 fprintf(fp, "%ld", resp->ru.ru_nswap);
322 break;
323 case 'X': /* Average shared text size. */
324 fprintf(fp, "%lu",
325 MSEC_TO_TICKS(v) == 0 ? 0 :
326 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
327 break;
328 case 'Z': /* Page size. */
329 fprintf(fp, "%d", getpagesize());
330 break;
331 case 'c': /* Involuntary context switches. */
332 fprintf(fp, "%ld", resp->ru.ru_nivcsw);
333 break;
334 case 'e': /* Elapsed real time in seconds. */
335 fprintf(fp, "%ld.%02ld",
336 resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000);
337 break;
338 case 'k': /* Signals delivered. */
339 fprintf(fp, "%ld", resp->ru.ru_nsignals);
340 break;
341 case 'p': /* Average stack segment. */
342 fprintf(fp, "%lu",
343 MSEC_TO_TICKS(v) == 0 ? 0 :
344 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
345 break;
346 case 'r': /* Incoming socket messages received. */
347 fprintf(fp, "%ld", resp->ru.ru_msgrcv);
348 break;
349 case 's': /* Outgoing socket messages sent. */
350 fprintf(fp, "%ld", resp->ru.ru_msgsnd);
351 break;
352 case 't': /* Average resident set size. */
353 fprintf(fp, "%lu",
354 MSEC_TO_TICKS(v) == 0 ? 0 :
355 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v));
356 break;
357 case 'w': /* Voluntary context switches. */
358 fprintf(fp, "%ld", resp->ru.ru_nvcsw);
359 break;
360 case 'x': /* Exit status. */
361 fprintf(fp, "%d", WEXITSTATUS(resp->waitstatus));
362 break;
363 case '\0':
364 putc('?', fp);
365 return;
366 default:
367 putc('?', fp);
368 putc(*fmt, fp);
369 }
370 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000371 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000372
373 case '\\': /* Format escape. */
374 switch (*++fmt) {
375 case 't':
376 putc('\t', fp);
377 break;
378 case 'n':
379 putc('\n', fp);
380 break;
381 case '\\':
382 putc('\\', fp);
383 break;
384 default:
385 putc('?', fp);
386 putc('\\', fp);
387 putc(*fmt, fp);
388 }
389 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000390 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000391
392 default:
393 putc(*fmt++, fp);
Eric Andersenc3657422001-11-30 07:54:32 +0000394 }
Eric Andersenc3657422001-11-30 07:54:32 +0000395
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000396 if (ferror(fp))
397 bb_error_msg_and_die(bb_msg_write_error);
Eric Andersenc3657422001-11-30 07:54:32 +0000398 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000399 putc('\n', fp);
Eric Andersenc3657422001-11-30 07:54:32 +0000400
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000401 if (ferror(fp))
402 bb_error_msg_and_die(bb_msg_write_error);
Eric Andersenc3657422001-11-30 07:54:32 +0000403}
404
405/* Run command CMD and return statistics on it.
406 Put the statistics in *RESP. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000407static void run_command(char *const *cmd, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000408{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000409 pid_t pid; /* Pid of child. */
410 __sighandler_t interrupt_signal, quit_signal;
Eric Andersenc3657422001-11-30 07:54:32 +0000411
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000412 gettimeofday(&resp->start, (struct timezone *) 0);
413 pid = fork(); /* Run CMD as child process. */
414 if (pid < 0)
415 bb_error_msg_and_die("cannot fork");
416 else if (pid == 0) { /* If child. */
417 /* Don't cast execvp arguments; that causes errors on some systems,
418 versus merely warnings if the cast is left off. */
419 execvp(cmd[0], cmd);
420 bb_error_msg("cannot run %s", cmd[0]);
421 _exit(errno == ENOENT ? 127 : 126);
422 }
Eric Andersenc3657422001-11-30 07:54:32 +0000423
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000424 /* Have signals kill the child but not self (if possible). */
425 interrupt_signal = signal(SIGINT, SIG_IGN);
426 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000427
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000428 if (resuse_end(pid, resp) == 0)
429 bb_error_msg("error waiting for child process");
Eric Andersenc3657422001-11-30 07:54:32 +0000430
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000431 /* Re-enable signals. */
432 signal(SIGINT, interrupt_signal);
433 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000434}
435
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000436int time_main(int argc, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000437{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000438 int gotone;
439 resource_t res;
440 const char *output_format = default_format;
Eric Andersenc3657422001-11-30 07:54:32 +0000441
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000442 argc--;
443 argv++;
444 /* Parse any options -- don't use getopt() here so we don't
445 * consume the args of our client application... */
446 while (argc > 0 && **argv == '-') {
447 gotone = 0;
448 while (gotone == 0 && *++(*argv)) {
449 switch (**argv) {
450 case 'v':
451 output_format = long_format;
452 break;
453 case 'p':
454 output_format = posix_format;
455 break;
456 default:
457 bb_show_usage();
458 }
459 argc--;
460 argv++;
461 gotone = 1;
462 }
Eric Andersenc3657422001-11-30 07:54:32 +0000463 }
Eric Andersenc3657422001-11-30 07:54:32 +0000464
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000465 if (argv == NULL || *argv == NULL)
466 bb_show_usage();
Eric Andersenc3657422001-11-30 07:54:32 +0000467
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000468 run_command(argv, &res);
469 summarize(stderr, output_format, argv, &res);
470 fflush(stderr);
Eric Andersenc3657422001-11-30 07:54:32 +0000471
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000472 if (WIFSTOPPED(res.waitstatus))
473 exit(WSTOPSIG(res.waitstatus));
474 else if (WIFSIGNALED(res.waitstatus))
475 exit(WTERMSIG(res.waitstatus));
476 else if (WIFEXITED(res.waitstatus))
477 exit(WEXITSTATUS(res.waitstatus));
478 return 0;
Eric Andersenc3657422001-11-30 07:54:32 +0000479}