1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27/*	All Rights Reserved	*/
28
29#pragma ident	"%Z%%M%	%I%	%E% SMI"
30
31#include <sys/types.h>
32#include <sys/times.h>
33#include <sys/time.h>
34#include <sys/param.h>
35#include <sys/wait.h>
36#include <unistd.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <signal.h>
40#include <strings.h>
41#include <time.h>
42#include <errno.h>
43#include <pwd.h>
44
45#define	NSEC_TO_TICK(nsec)	((nsec) / nsec_per_tick)
46#define	NSEC_TO_TICK_ROUNDUP(nsec) NSEC_TO_TICK((nsec) + \
47	nsec_per_tick/2)
48
49char	fname[20];
50static int hz;
51static int nsec_per_tick;
52
53void printt(char *, hrtime_t);
54void hmstime(char[]);
55void usage();
56
57int
58main(int argc, char **argv)
59{
60	struct	tms buffer, obuffer;
61	int	status;
62	register pid_t	p;
63	int	c;
64	hrtime_t before, after, timediff;
65	char	stime[9], etime[9];
66	char	cmd[80];
67	int	pflg = 0, sflg = 0, oflg = 0;
68	char	aopt[25];
69	FILE	*pipin;
70	char	ttyid[12], line[150];
71	char	eol;
72	char	fld[20][12];
73	int	iline = 0, i, nfld;
74	int	ichar, iblok;
75	long	chars = 0, bloks = 0;
76
77	aopt[0] = '\0';
78
79	hz = sysconf(_SC_CLK_TCK);
80	nsec_per_tick = NANOSEC / hz;
81
82	/* check options; */
83	while ((c = getopt(argc, argv, "sopfhkmrt")) != EOF)
84		switch (c)  {
85		case 's':  sflg++;  break;
86		case 'o':  oflg++;  break;
87		case 'p':  pflg++;  break;
88
89		case 'f':  strcat(aopt, "-f ");  break;
90		case 'h':  strcat(aopt, "-h ");  break;
91		case 'k':  strcat(aopt, "-k ");  break;
92		case 'm':  strcat(aopt, "-m ");  break;
93		case 'r':  strcat(aopt, "-r ");  break;
94		case 't':  strcat(aopt, "-t ");  break;
95
96		case '?':  usage();
97				break;
98		}
99	if (optind >= argc) {
100		fprintf(stderr, "timex: Missing command\n");
101		usage();
102	}
103
104	/*
105	 * Check to see if accounting is installed and print a somewhat
106	 * meaninful message if not.
107	 */
108	if (((oflg+pflg) != 0) && (access("/usr/bin/acctcom", 01) == -1)) {
109		oflg = 0;
110		pflg = 0;
111		fprintf(stderr,
112		    "Information from -p and -o options not available\n");
113		fprintf(stderr,
114		    " because process accounting is not operational.\n");
115	}
116
117	if (sflg) {
118		sprintf(fname, "/tmp/tmx%ld", getpid());
119		sprintf(cmd, "/usr/lib/sa/sadc 1 1 %s", fname);
120		system(cmd);
121	}
122	if (pflg + oflg) hmstime(stime);
123	before = gethrtime();
124	(void) times(&obuffer);
125	if ((p = fork()) == (pid_t)-1) {
126		perror("Fork Failed");
127		(void) unlink(fname);
128		exit(EXIT_FAILURE);
129	}
130	if (p == 0) {
131		setgid(getgid());
132		execvp(*(argv+optind), (argv+optind));
133		fprintf(stderr, "%s: %s\n", *(argv+optind), strerror(errno));
134		exit(EXIT_FAILURE);
135	}
136	signal(SIGINT, SIG_IGN);
137	signal(SIGQUIT, SIG_IGN);
138	while (wait(&status) != p)
139		;
140	if ((status&0377) != 0)
141		fprintf(stderr, "Command terminated abnormally.\n");
142	signal(SIGINT, SIG_DFL);
143	signal(SIGQUIT, SIG_DFL);
144	(void) times(&buffer);
145	after = gethrtime();
146	timediff = after - before;
147	if (pflg + oflg) hmstime(etime);
148	if (sflg) system(cmd);
149
150	fprintf(stderr, "\n");
151	printt("real", NSEC_TO_TICK_ROUNDUP(timediff));
152	printt("user", buffer.tms_cutime - obuffer.tms_cutime);
153	printt("sys ", buffer.tms_cstime - obuffer.tms_cstime);
154	fprintf(stderr, "\n");
155
156	if (oflg+pflg) {
157		if (isatty(0))
158			sprintf(ttyid, "-l %s", ttyname(0)+5);
159		sprintf(cmd, "acctcom -S %s -E %s -u %s %s -i %s",
160		    stime, etime, getpwuid(getuid())->pw_name, ttyid, aopt);
161		pipin = popen(cmd, "r");
162		while (fscanf(pipin, "%[^\n]%1c", line, &eol) > 1) {
163			if (pflg)
164				fprintf(stderr, "%s\n", line);
165			if (oflg)  {
166				nfld = sscanf(line,
167				    "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
168				    fld[0], fld[1], fld[2], fld[3], fld[4],
169				    fld[5], fld[6], fld[7], fld[8], fld[9],
170				    fld[10], fld[11], fld[12], fld[13], fld[14],
171				    fld[15], fld[16], fld[17], fld[18],
172				    fld[19]);
173				if (++iline == 3)
174					for (i = 0; i < nfld; i++)  {
175						if (strcmp(fld[i], "CHARS")
176						    == 0)
177							ichar = i+2;
178						if (strcmp(fld[i], "BLOCKS")
179						    == 0)
180							iblok = i+2;
181					}
182				if (iline > 4)  {
183					chars += atol(fld[ichar]);
184					bloks += atol(fld[iblok]);
185				}
186			}
187		}
188		pclose(pipin);
189
190		if (oflg)
191			if (iline > 4)
192				fprintf(stderr,
193				    "\nCHARS TRNSFD = %ld\n"
194				    "BLOCKS READ  = %ld\n", chars, bloks);
195			else
196				fprintf(stderr,
197				    "\nNo process records found!\n");
198	}
199
200	if (sflg)  {
201		sprintf(cmd, "/usr/bin/sar -ubdycwaqvmpgrk -f %s 1>&2", fname);
202		system(cmd);
203		unlink(fname);
204	}
205	exit(WEXITSTATUS(status));
206}
207
208void
209printt(char *label, hrtime_t ticks)
210{
211	long tk;	/* number of leftover ticks   */
212	long ss;	/* number of seconds */
213	long mm;	/* number of minutes */
214	long hh;	/* number of hours   */
215	longlong_t total = ticks;
216
217	tk	= total % hz;	/* ticks % hz		*/
218	total	/= hz;
219	ss	= total % 60;	/* ticks / hz % 60	*/
220	total	/= 60;
221	mm	= total % 60;	/* ticks / hz / 60 % 60 */
222	hh	= total / 60;	/* ticks / hz / 60 / 60 */
223
224	(void) fprintf(stderr, "%s ", label);
225
226	/* Display either padding or the elapsed hours */
227	if (hh == 0L) {
228		(void) fprintf(stderr, "%6c", ' ');
229	} else {
230		(void) fprintf(stderr, "%5ld:", hh);
231	}
232
233	/*
234	 * Display either nothing or the elapsed minutes, zero
235	 * padding (if hours > 0) or space padding (if not).
236	 */
237	if (mm == 0L && hh == 0L) {
238		(void) fprintf(stderr, "%3c", ' ');
239	} else if (mm != 0L && hh == 0L) {
240		(void) fprintf(stderr, "%2ld:", mm);
241	} else {
242		(void) fprintf(stderr, "%02ld:", mm);
243	}
244
245	/*
246	 * Display the elapsed seconds; seconds are always
247	 * zero padded.
248	 */
249	if (hh == 0L && mm == 0L) {
250		(void) fprintf(stderr, "%2ld.", ss);
251	} else {
252		(void) fprintf(stderr, "%02ld.", ss);
253	}
254
255	/* Display hundredths of a second. */
256	(void) fprintf(stderr, "%02ld\n", tk * 100/hz);
257}
258
259/*
260 * hmstime() sets current time in hh:mm:ss string format in stime;
261 */
262
263void
264hmstime(char stime[])
265{
266	char	*ltime;
267	time_t tme;
268
269	tme = time((time_t *)0);
270	ltime = ctime(&tme);
271	strncpy(stime, ltime+11, 8);
272	stime[8] = '\0';
273}
274
275void
276usage()
277{
278	fprintf(stderr, "Usage: timex [-o] [-p [-fhkmrt]] [-s] command\n");
279	unlink(fname);
280	exit(EXIT_FAILURE);
281}
282