blob: 3f1451c8308232693437619e70fc6c50fe7f4276 [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
Rob Landleye9a7a622006-09-22 02:52:41 +00005 Licensed under GPL version 2, see file LICENSE in this tarball for details.
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00006*/
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
14#define TV_MSEC tv_usec / 1000
Eric Andersenc3657422001-11-30 07:54:32 +000015
16/* Information on the resources used by a child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000017typedef struct {
18 int waitstatus;
19 struct rusage ru;
20 struct timeval start, elapsed; /* Wallclock time of process. */
Eric Andersenc3657422001-11-30 07:54:32 +000021} resource_t;
22
23/* msec = milliseconds = 1/1,000 (1*10e-3) second.
24 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
25
26#ifndef TICKS_PER_SEC
27#define TICKS_PER_SEC 100
28#endif
29
30/* The number of milliseconds in one `tick' used by the `rusage' structure. */
31#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
32
33/* Return the number of clock ticks that occur in M milliseconds. */
34#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
35
36#define UL unsigned long
37
38static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
39
40/* The output format for the -p option .*/
41static const char *const posix_format = "real %e\nuser %U\nsys %S";
42
43
44/* Format string for printing all statistics verbosely.
45 Keep this output to 24 lines so users on terminals can see it all.*/
46static const char *const long_format =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000047 "\tCommand being timed: \"%C\"\n"
48 "\tUser time (seconds): %U\n"
49 "\tSystem time (seconds): %S\n"
50 "\tPercent of CPU this job got: %P\n"
51 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
52 "\tAverage shared text size (kbytes): %X\n"
53 "\tAverage unshared data size (kbytes): %D\n"
54 "\tAverage stack size (kbytes): %p\n"
55 "\tAverage total size (kbytes): %K\n"
56 "\tMaximum resident set size (kbytes): %M\n"
57 "\tAverage resident set size (kbytes): %t\n"
58 "\tMajor (requiring I/O) page faults: %F\n"
59 "\tMinor (reclaiming a frame) page faults: %R\n"
60 "\tVoluntary context switches: %w\n"
61 "\tInvoluntary context switches: %c\n"
62 "\tSwaps: %W\n"
63 "\tFile system inputs: %I\n"
64 "\tFile system outputs: %O\n"
65 "\tSocket messages sent: %s\n"
66 "\tSocket messages received: %r\n"
67 "\tSignals delivered: %k\n"
68 "\tPage size (bytes): %Z\n" "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000069
70
71 /* Wait for and fill in data on child process PID.
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000072 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000073
74/* pid_t is short on BSDI, so don't try to promote it. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000075static int resuse_end(pid_t pid, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +000076{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000077 int status;
Eric Andersenc3657422001-11-30 07:54:32 +000078
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000079 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000080
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000081 /* Ignore signals, but don't ignore the children. When wait3
82 returns the child process, set the time the command finished. */
83 while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
84 if (caught == -1)
85 return 0;
86 }
Eric Andersenc3657422001-11-30 07:54:32 +000087
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000088 gettimeofday(&resp->elapsed, (struct timezone *) 0);
89 resp->elapsed.tv_sec -= resp->start.tv_sec;
90 if (resp->elapsed.tv_usec < resp->start.tv_usec) {
91 /* Manually carry a one from the seconds field. */
92 resp->elapsed.tv_usec += 1000000;
93 --resp->elapsed.tv_sec;
94 }
95 resp->elapsed.tv_usec -= resp->start.tv_usec;
Eric Andersenc3657422001-11-30 07:54:32 +000096
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000097 resp->waitstatus = status;
Eric Andersenc3657422001-11-30 07:54:32 +000098
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000099 return 1;
Eric Andersenc3657422001-11-30 07:54:32 +0000100}
101
102/* Print ARGV to FP, with each entry in ARGV separated by FILLER. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000103static void fprintargv(FILE * fp, char *const *argv, const char *filler)
Eric Andersenc3657422001-11-30 07:54:32 +0000104{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000105 char *const *av;
Eric Andersenc3657422001-11-30 07:54:32 +0000106
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000107 av = argv;
108 fputs(*av, fp);
109 while (*++av) {
110 fputs(filler, fp);
111 fputs(*av, fp);
112 }
113 if (ferror(fp))
114 bb_error_msg_and_die(bb_msg_write_error);
Eric Andersenc3657422001-11-30 07:54:32 +0000115}
116
117/* Return the number of kilobytes corresponding to a number of pages PAGES.
118 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
119
120 Try to do arithmetic so that the risk of overflow errors is minimized.
121 This is funky since the pagesize could be less than 1K.
122 Note: Some machines express getrusage statistics in terms of K,
123 others in terms of pages. */
124
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000125static unsigned long ptok(unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +0000126{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000127 static unsigned long ps = 0;
128 unsigned long tmp;
129 static long size = LONG_MAX;
Eric Andersenc3657422001-11-30 07:54:32 +0000130
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000131 /* Initialization. */
132 if (ps == 0)
133 ps = (long) getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000134
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000135 /* Conversion. */
136 if (pages > (LONG_MAX / ps)) { /* Could overflow. */
137 tmp = pages / 1024; /* Smaller first, */
138 size = tmp * ps; /* then larger. */
139 } else { /* Could underflow. */
140 tmp = pages * ps; /* Larger first, */
141 size = tmp / 1024; /* then smaller. */
142 }
143 return size;
Eric Andersenc3657422001-11-30 07:54:32 +0000144}
145
146/* summarize: Report on the system use of a command.
147
148 Copy the FMT argument to FP except that `%' sequences
149 have special meaning, and `\n' and `\t' are translated into
150 newline and tab, respectively, and `\\' is translated into `\'.
151
152 The character following a `%' can be:
153 (* means the tcsh time builtin also recognizes it)
154 % == a literal `%'
155 C == command name and arguments
156* D == average unshared data size in K (ru_idrss+ru_isrss)
157* E == elapsed real (wall clock) time in [hour:]min:sec
158* F == major page faults (required physical I/O) (ru_majflt)
159* I == file system inputs (ru_inblock)
160* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
161* M == maximum resident set size in K (ru_maxrss)
162* O == file system outputs (ru_oublock)
163* P == percent of CPU this job got (total cpu time / elapsed time)
164* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
165* S == system (kernel) time (seconds) (ru_stime)
166* T == system time in [hour:]min:sec
167* U == user time (seconds) (ru_utime)
168* u == user time in [hour:]min:sec
169* W == times swapped out (ru_nswap)
170* X == average amount of shared text in K (ru_ixrss)
171 Z == page size
172* c == involuntary context switches (ru_nivcsw)
173 e == elapsed real time in seconds
174* k == signals delivered (ru_nsignals)
175 p == average unshared stack size in K (ru_isrss)
176* r == socket messages received (ru_msgrcv)
177* s == socket messages sent (ru_msgsnd)
178 t == average resident set size in K (ru_idrss)
179* w == voluntary context switches (ru_nvcsw)
180 x == exit status of command
181
182 Various memory usages are found by converting from page-seconds
183 to kbytes by multiplying by the page size, dividing by 1024,
184 and dividing by elapsed real time.
185
186 FP is the stream to print to.
187 FMT is the format string, interpreted as described above.
188 COMMAND is the command and args that are being summarized.
189 RESP is resource information on the command. */
190
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000191static void summarize(FILE * fp, const char *fmt, char **command,
192 resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000193{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000194 unsigned long r; /* Elapsed real milliseconds. */
195 unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
Eric Andersenc3657422001-11-30 07:54:32 +0000196
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000197 if (WIFSTOPPED(resp->waitstatus))
198 fprintf(fp, "Command stopped by signal %d\n",
199 WSTOPSIG(resp->waitstatus));
200 else if (WIFSIGNALED(resp->waitstatus))
201 fprintf(fp, "Command terminated by signal %d\n",
202 WTERMSIG(resp->waitstatus));
203 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
204 fprintf(fp, "Command exited with non-zero status %d\n",
205 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000206
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000207 /* Convert all times to milliseconds. Occasionally, one of these values
208 comes out as zero. Dividing by zero causes problems, so we first
209 check the time value. If it is zero, then we take `evasive action'
210 instead of calculating a value. */
Eric Andersenc3657422001-11-30 07:54:32 +0000211
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000212 r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000213
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000214 v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
215 resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
Eric Andersenc3657422001-11-30 07:54:32 +0000216
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000217 while (*fmt) {
218 switch (*fmt) {
219 case '%':
220 switch (*++fmt) {
221 case '%': /* Literal '%'. */
222 putc('%', fp);
223 break;
224 case 'C': /* The command that got timed. */
225 fprintargv(fp, command, " ");
226 break;
227 case 'D': /* Average unshared data size. */
228 fprintf(fp, "%lu",
229 MSEC_TO_TICKS(v) == 0 ? 0 :
230 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
231 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
232 break;
233 case 'E': /* Elapsed real (wall clock) time. */
234 if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
235 fprintf(fp, "%ldh %ldm %02lds",
236 resp->elapsed.tv_sec / 3600,
237 (resp->elapsed.tv_sec % 3600) / 60,
238 resp->elapsed.tv_sec % 60);
239 else
240 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */
241 resp->elapsed.tv_sec / 60,
242 resp->elapsed.tv_sec % 60,
243 resp->elapsed.tv_usec / 10000);
244 break;
245 case 'F': /* Major page faults. */
246 fprintf(fp, "%ld", resp->ru.ru_majflt);
247 break;
248 case 'I': /* Inputs. */
249 fprintf(fp, "%ld", resp->ru.ru_inblock);
250 break;
251 case 'K': /* Average mem usage == data+stack+text. */
252 fprintf(fp, "%lu",
253 MSEC_TO_TICKS(v) == 0 ? 0 :
254 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
255 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) +
256 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
257 break;
258 case 'M': /* Maximum resident set size. */
259 fprintf(fp, "%lu", ptok((UL) resp->ru.ru_maxrss));
260 break;
261 case 'O': /* Outputs. */
262 fprintf(fp, "%ld", resp->ru.ru_oublock);
263 break;
264 case 'P': /* Percent of CPU this job got. */
265 /* % cpu is (total cpu time)/(elapsed time). */
266 if (r > 0)
267 fprintf(fp, "%lu%%", (v * 100 / r));
268 else
269 fprintf(fp, "?%%");
270 break;
271 case 'R': /* Minor page faults (reclaims). */
272 fprintf(fp, "%ld", resp->ru.ru_minflt);
273 break;
274 case 'S': /* System time. */
275 fprintf(fp, "%ld.%02ld",
276 resp->ru.ru_stime.tv_sec,
277 resp->ru.ru_stime.TV_MSEC / 10);
278 break;
279 case 'T': /* System time. */
280 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
281 fprintf(fp, "%ldh %ldm %02lds",
282 resp->ru.ru_stime.tv_sec / 3600,
283 (resp->ru.ru_stime.tv_sec % 3600) / 60,
284 resp->ru.ru_stime.tv_sec % 60);
285 else
286 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */
287 resp->ru.ru_stime.tv_sec / 60,
288 resp->ru.ru_stime.tv_sec % 60,
289 resp->ru.ru_stime.tv_usec / 10000);
290 break;
291 case 'U': /* User time. */
292 fprintf(fp, "%ld.%02ld",
293 resp->ru.ru_utime.tv_sec,
294 resp->ru.ru_utime.TV_MSEC / 10);
295 break;
296 case 'u': /* User time. */
297 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
298 fprintf(fp, "%ldh %ldm %02lds",
299 resp->ru.ru_utime.tv_sec / 3600,
300 (resp->ru.ru_utime.tv_sec % 3600) / 60,
301 resp->ru.ru_utime.tv_sec % 60);
302 else
303 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */
304 resp->ru.ru_utime.tv_sec / 60,
305 resp->ru.ru_utime.tv_sec % 60,
306 resp->ru.ru_utime.tv_usec / 10000);
307 break;
308 case 'W': /* Times swapped out. */
309 fprintf(fp, "%ld", resp->ru.ru_nswap);
310 break;
311 case 'X': /* Average shared text size. */
312 fprintf(fp, "%lu",
313 MSEC_TO_TICKS(v) == 0 ? 0 :
314 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
315 break;
316 case 'Z': /* Page size. */
317 fprintf(fp, "%d", getpagesize());
318 break;
319 case 'c': /* Involuntary context switches. */
320 fprintf(fp, "%ld", resp->ru.ru_nivcsw);
321 break;
322 case 'e': /* Elapsed real time in seconds. */
323 fprintf(fp, "%ld.%02ld",
324 resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000);
325 break;
326 case 'k': /* Signals delivered. */
327 fprintf(fp, "%ld", resp->ru.ru_nsignals);
328 break;
329 case 'p': /* Average stack segment. */
330 fprintf(fp, "%lu",
331 MSEC_TO_TICKS(v) == 0 ? 0 :
332 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
333 break;
334 case 'r': /* Incoming socket messages received. */
335 fprintf(fp, "%ld", resp->ru.ru_msgrcv);
336 break;
337 case 's': /* Outgoing socket messages sent. */
338 fprintf(fp, "%ld", resp->ru.ru_msgsnd);
339 break;
340 case 't': /* Average resident set size. */
341 fprintf(fp, "%lu",
342 MSEC_TO_TICKS(v) == 0 ? 0 :
343 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v));
344 break;
345 case 'w': /* Voluntary context switches. */
346 fprintf(fp, "%ld", resp->ru.ru_nvcsw);
347 break;
348 case 'x': /* Exit status. */
349 fprintf(fp, "%d", WEXITSTATUS(resp->waitstatus));
350 break;
351 case '\0':
352 putc('?', fp);
353 return;
354 default:
355 putc('?', fp);
356 putc(*fmt, fp);
357 }
358 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000359 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000360
361 case '\\': /* Format escape. */
362 switch (*++fmt) {
363 case 't':
364 putc('\t', fp);
365 break;
366 case 'n':
367 putc('\n', fp);
368 break;
369 case '\\':
370 putc('\\', fp);
371 break;
372 default:
373 putc('?', fp);
374 putc('\\', fp);
375 putc(*fmt, fp);
376 }
377 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000378 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000379
380 default:
381 putc(*fmt++, fp);
Eric Andersenc3657422001-11-30 07:54:32 +0000382 }
Eric Andersenc3657422001-11-30 07:54:32 +0000383
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000384 if (ferror(fp))
385 bb_error_msg_and_die(bb_msg_write_error);
Eric Andersenc3657422001-11-30 07:54:32 +0000386 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000387 putc('\n', fp);
Eric Andersenc3657422001-11-30 07:54:32 +0000388
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000389 if (ferror(fp))
390 bb_error_msg_and_die(bb_msg_write_error);
Eric Andersenc3657422001-11-30 07:54:32 +0000391}
392
393/* Run command CMD and return statistics on it.
394 Put the statistics in *RESP. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000395static void run_command(char *const *cmd, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000396{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000397 pid_t pid; /* Pid of child. */
398 __sighandler_t interrupt_signal, quit_signal;
Eric Andersenc3657422001-11-30 07:54:32 +0000399
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000400 gettimeofday(&resp->start, (struct timezone *) 0);
Mike Frysinger8e640a12006-06-06 06:08:34 +0000401 pid = vfork(); /* Run CMD as child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000402 if (pid < 0)
403 bb_error_msg_and_die("cannot fork");
404 else if (pid == 0) { /* If child. */
405 /* Don't cast execvp arguments; that causes errors on some systems,
406 versus merely warnings if the cast is left off. */
407 execvp(cmd[0], cmd);
408 bb_error_msg("cannot run %s", cmd[0]);
409 _exit(errno == ENOENT ? 127 : 126);
410 }
Eric Andersenc3657422001-11-30 07:54:32 +0000411
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000412 /* Have signals kill the child but not self (if possible). */
413 interrupt_signal = signal(SIGINT, SIG_IGN);
414 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000415
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000416 if (resuse_end(pid, resp) == 0)
417 bb_error_msg("error waiting for child process");
Eric Andersenc3657422001-11-30 07:54:32 +0000418
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000419 /* Re-enable signals. */
420 signal(SIGINT, interrupt_signal);
421 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000422}
423
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000424int time_main(int argc, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000425{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000426 int gotone;
427 resource_t res;
428 const char *output_format = default_format;
Eric Andersenc3657422001-11-30 07:54:32 +0000429
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000430 argc--;
431 argv++;
432 /* Parse any options -- don't use getopt() here so we don't
433 * consume the args of our client application... */
434 while (argc > 0 && **argv == '-') {
435 gotone = 0;
436 while (gotone == 0 && *++(*argv)) {
437 switch (**argv) {
438 case 'v':
439 output_format = long_format;
440 break;
441 case 'p':
442 output_format = posix_format;
443 break;
444 default:
445 bb_show_usage();
446 }
447 argc--;
448 argv++;
449 gotone = 1;
450 }
Eric Andersenc3657422001-11-30 07:54:32 +0000451 }
Eric Andersenc3657422001-11-30 07:54:32 +0000452
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000453 if (argv == NULL || *argv == NULL)
454 bb_show_usage();
Eric Andersenc3657422001-11-30 07:54:32 +0000455
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000456 run_command(argv, &res);
457 summarize(stderr, output_format, argv, &res);
458 fflush(stderr);
Eric Andersenc3657422001-11-30 07:54:32 +0000459
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000460 if (WIFSTOPPED(res.waitstatus))
461 exit(WSTOPSIG(res.waitstatus));
462 else if (WIFSIGNALED(res.waitstatus))
463 exit(WTERMSIG(res.waitstatus));
464 else if (WIFEXITED(res.waitstatus))
465 exit(WEXITSTATUS(res.waitstatus));
466 return 0;
Eric Andersenc3657422001-11-30 07:54:32 +0000467}