1/*
2 * Copyright (c) 1987, 1988, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1987, 1988, 1993\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)time.c	8.1 (Berkeley) 6/6/93";
39#endif
40static const char rcsid[] =
41  "$FreeBSD: releng/11.0/usr.bin/time/time.c 283073 2015-05-18 19:18:42Z jmg $";
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/resource.h>
46#include <sys/signal.h>
47#include <sys/sysctl.h>
48#include <sys/time.h>
49#include <sys/wait.h>
50
51#include <err.h>
52#include <errno.h>
53#include <locale.h>
54#include <signal.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <stdint.h>
58#include <string.h>
59#include <unistd.h>
60
61static int getstathz(void);
62static void humantime(FILE *, long, long);
63static void showtime(FILE *, struct timeval *, struct timeval *,
64    struct rusage *);
65static void siginfo(int);
66static void usage(void);
67
68static sig_atomic_t siginfo_recvd;
69static char decimal_point;
70static struct timeval before_tv;
71static int hflag, pflag;
72
73int
74main(int argc, char **argv)
75{
76	int aflag, ch, lflag, status;
77	int exitonsig;
78	pid_t pid;
79	struct rlimit rl;
80	struct rusage ru;
81	struct timeval after;
82	char *ofn = NULL;
83	FILE *out = stderr;
84
85	(void) setlocale(LC_NUMERIC, "");
86	decimal_point = localeconv()->decimal_point[0];
87
88	aflag = hflag = lflag = pflag = 0;
89	while ((ch = getopt(argc, argv, "ahlo:p")) != -1)
90		switch((char)ch) {
91		case 'a':
92			aflag = 1;
93			break;
94		case 'h':
95			hflag = 1;
96			break;
97		case 'l':
98			lflag = 1;
99			break;
100		case 'o':
101			ofn = optarg;
102			break;
103		case 'p':
104			pflag = 1;
105			break;
106		case '?':
107		default:
108			usage();
109		}
110
111	if (!(argc -= optind))
112		exit(0);
113	argv += optind;
114
115	if (ofn) {
116	        if ((out = fopen(ofn, aflag ? "ae" : "we")) == NULL)
117		        err(1, "%s", ofn);
118		setvbuf(out, (char *)NULL, _IONBF, (size_t)0);
119	}
120
121	(void)gettimeofday(&before_tv, NULL);
122	switch(pid = fork()) {
123	case -1:			/* error */
124		err(1, "time");
125		/* NOTREACHED */
126	case 0:				/* child */
127		execvp(*argv, argv);
128		err(errno == ENOENT ? 127 : 126, "%s", *argv);
129		/* NOTREACHED */
130	}
131	/* parent */
132	(void)signal(SIGINT, SIG_IGN);
133	(void)signal(SIGQUIT, SIG_IGN);
134	siginfo_recvd = 0;
135	(void)signal(SIGINFO, siginfo);
136	(void)siginterrupt(SIGINFO, 1);
137	while (wait4(pid, &status, 0, &ru) != pid) {
138		if (siginfo_recvd) {
139			siginfo_recvd = 0;
140			(void)gettimeofday(&after, NULL);
141			getrusage(RUSAGE_CHILDREN, &ru);
142			showtime(stdout, &before_tv, &after, &ru);
143		}
144	}
145	(void)gettimeofday(&after, NULL);
146	if ( ! WIFEXITED(status))
147		warnx("command terminated abnormally");
148	exitonsig = WIFSIGNALED(status) ? WTERMSIG(status) : 0;
149	showtime(out, &before_tv, &after, &ru);
150	if (lflag) {
151		int hz = getstathz();
152		u_long ticks;
153
154		ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) +
155		     hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000;
156
157		/*
158		 * If our round-off on the tick calculation still puts us at 0,
159		 * then always assume at least one tick.
160		 */
161		if (ticks == 0)
162			ticks = 1;
163
164		fprintf(out, "%10ld  %s\n",
165			ru.ru_maxrss, "maximum resident set size");
166		fprintf(out, "%10ld  %s\n",
167			ru.ru_ixrss / ticks, "average shared memory size");
168		fprintf(out, "%10ld  %s\n",
169			ru.ru_idrss / ticks, "average unshared data size");
170		fprintf(out, "%10ld  %s\n",
171			ru.ru_isrss / ticks, "average unshared stack size");
172		fprintf(out, "%10ld  %s\n",
173			ru.ru_minflt, "page reclaims");
174		fprintf(out, "%10ld  %s\n",
175			ru.ru_majflt, "page faults");
176		fprintf(out, "%10ld  %s\n",
177			ru.ru_nswap, "swaps");
178		fprintf(out, "%10ld  %s\n",
179			ru.ru_inblock, "block input operations");
180		fprintf(out, "%10ld  %s\n",
181			ru.ru_oublock, "block output operations");
182		fprintf(out, "%10ld  %s\n",
183			ru.ru_msgsnd, "messages sent");
184		fprintf(out, "%10ld  %s\n",
185			ru.ru_msgrcv, "messages received");
186		fprintf(out, "%10ld  %s\n",
187			ru.ru_nsignals, "signals received");
188		fprintf(out, "%10ld  %s\n",
189			ru.ru_nvcsw, "voluntary context switches");
190		fprintf(out, "%10ld  %s\n",
191			ru.ru_nivcsw, "involuntary context switches");
192	}
193	/*
194	 * If the child has exited on a signal, exit on the same
195	 * signal, too, in order to reproduce the child's exit status.
196	 * However, avoid actually dumping core from the current process.
197	 */
198	if (exitonsig) {
199		if (signal(exitonsig, SIG_DFL) == SIG_ERR)
200			warn("signal");
201		else {
202			rl.rlim_max = rl.rlim_cur = 0;
203			if (setrlimit(RLIMIT_CORE, &rl) == -1)
204				warn("setrlimit");
205			kill(getpid(), exitonsig);
206		}
207	}
208	exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
209}
210
211static void
212usage(void)
213{
214	fprintf(stderr,
215	    "usage: time [-al] [-h | -p] [-o file] utility [argument ...]\n");
216	exit(1);
217}
218
219/*
220 * Return the frequency of the kernel's statistics clock.
221 */
222static int
223getstathz(void)
224{
225	int mib[2];
226	size_t size;
227	struct clockinfo clockrate;
228
229	mib[0] = CTL_KERN;
230	mib[1] = KERN_CLOCKRATE;
231	size = sizeof clockrate;
232	if (sysctl(mib, 2, &clockrate, &size, NULL, 0) == -1)
233		err(1, "sysctl kern.clockrate");
234	return clockrate.stathz;
235}
236
237static void
238humantime(FILE *out, long sec, long usec)
239{
240	long days, hrs, mins;
241
242	days = sec / (60L * 60 * 24);
243	sec %= (60L * 60 * 24);
244	hrs = sec / (60L * 60);
245	sec %= (60L * 60);
246	mins = sec / 60;
247	sec %= 60;
248
249	fprintf(out, "\t");
250	if (days)
251		fprintf(out, "%ldd", days);
252	if (hrs)
253		fprintf(out, "%ldh", hrs);
254	if (mins)
255		fprintf(out, "%ldm", mins);
256	fprintf(out, "%ld%c%02lds", sec, decimal_point, usec);
257}
258
259static void
260showtime(FILE *out, struct timeval *before, struct timeval *after,
261    struct rusage *ru)
262{
263
264	after->tv_sec -= before->tv_sec;
265	after->tv_usec -= before->tv_usec;
266	if (after->tv_usec < 0)
267		after->tv_sec--, after->tv_usec += 1000000;
268
269	if (pflag) {
270		/* POSIX wants output that must look like
271		"real %f\nuser %f\nsys %f\n" and requires
272		at least two digits after the radix. */
273		fprintf(out, "real %jd%c%02ld\n",
274			(intmax_t)after->tv_sec, decimal_point,
275			after->tv_usec/10000);
276		fprintf(out, "user %jd%c%02ld\n",
277			(intmax_t)ru->ru_utime.tv_sec, decimal_point,
278			ru->ru_utime.tv_usec/10000);
279		fprintf(out, "sys %jd%c%02ld\n",
280			(intmax_t)ru->ru_stime.tv_sec, decimal_point,
281			ru->ru_stime.tv_usec/10000);
282	} else if (hflag) {
283		humantime(out, after->tv_sec, after->tv_usec/10000);
284		fprintf(out, " real\t");
285		humantime(out, ru->ru_utime.tv_sec, ru->ru_utime.tv_usec/10000);
286		fprintf(out, " user\t");
287		humantime(out, ru->ru_stime.tv_sec, ru->ru_stime.tv_usec/10000);
288		fprintf(out, " sys\n");
289	} else {
290		fprintf(out, "%9jd%c%02ld real ",
291			(intmax_t)after->tv_sec, decimal_point,
292			after->tv_usec/10000);
293		fprintf(out, "%9jd%c%02ld user ",
294			(intmax_t)ru->ru_utime.tv_sec, decimal_point,
295			ru->ru_utime.tv_usec/10000);
296		fprintf(out, "%9jd%c%02ld sys\n",
297			(intmax_t)ru->ru_stime.tv_sec, decimal_point,
298			ru->ru_stime.tv_usec/10000);
299	}
300}
301
302static void
303siginfo(int sig __unused)
304{
305
306	siginfo_recvd = 1;
307}
308