lastcomm.c revision 169235
1/*
2 * Copyright (c) 1980, 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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1980, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)lastcomm.c	8.1 (Berkeley) 6/6/93";
43#endif
44#endif /* not lint */
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD: head/usr.bin/lastcomm/lastcomm.c 169235 2007-05-03 13:57:19Z dwmalone $");
47
48#include <sys/param.h>
49#include <sys/stat.h>
50#include <sys/acct.h>
51
52#include <ctype.h>
53#include <err.h>
54#include <fcntl.h>
55#include <grp.h>
56#include <pwd.h>
57#include <stdbool.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62#include <utmp.h>
63#include "pathnames.h"
64
65/*XXX*/#include <inttypes.h>
66
67time_t	 expand(u_int);
68char	*flagbits(int);
69const	 char *getdev(dev_t);
70int	 requested(char *[], struct acct *);
71static	 void usage(void);
72static void export_record(struct acct *acp);
73
74#define AC_UTIME 1 /* user */
75#define AC_STIME 2 /* system */
76#define AC_ETIME 4 /* elapsed */
77#define AC_CTIME 8 /* user + system time, default */
78
79#define AC_BTIME 16 /* starting time */
80#define AC_FTIME 32 /* exit time (starting time + elapsed time )*/
81
82#define AC_HZ ((double)AHZ)
83
84int
85main(int argc, char *argv[])
86{
87	char *p;
88	struct acct ab;
89	struct stat sb;
90	FILE *fp;
91	off_t size;
92	time_t t;
93	int ch;
94	const char *acctfile;
95	int flags = 0;
96	bool export_text = false;
97
98	acctfile = _PATH_ACCT;
99	while ((ch = getopt(argc, argv, "f:usecSEX")) != -1)
100		switch((char)ch) {
101		case 'f':
102			acctfile = optarg;
103			break;
104		case 'u':
105			flags |= AC_UTIME; /* user time */
106			break;
107		case 'X':
108			export_text = true; /* export */
109			break;
110		case 's':
111			flags |= AC_STIME; /* system time */
112			break;
113		case 'e':
114			flags |= AC_ETIME; /* elapsed time */
115			break;
116        	case 'c':
117                        flags |= AC_CTIME; /* user + system time */
118			break;
119
120        	case 'S':
121                        flags |= AC_BTIME; /* starting time */
122			break;
123        	case 'E':
124			/* exit time (starting time + elapsed time )*/
125                        flags |= AC_FTIME;
126			break;
127
128		case '?':
129		default:
130			usage();
131		}
132
133	/* default user + system time and starting time */
134	if (!flags && !export_text)
135	    flags = AC_CTIME | AC_BTIME;
136
137	if (flags && export_text)
138		usage();
139
140	argc -= optind;
141	argv += optind;
142
143	if (strcmp(acctfile, "-") == 0) {
144		fp = stdin;
145		size = sizeof(struct acct); /* Always one more to read. */
146	} else {
147		/* Open the file. */
148		if ((fp = fopen(acctfile, "r")) == NULL ||
149		    fstat(fileno(fp), &sb))
150			err(1, "could not open %s", acctfile);
151
152		/*
153		 * Round off to integral number of accounting records,
154		 * probably not necessary, but it doesn't hurt.
155		 */
156		size = sb.st_size - sb.st_size % sizeof(struct acct);
157
158		/* Check if any records to display. */
159		if ((unsigned)size < sizeof(struct acct))
160			exit(0);
161	}
162
163	do {
164		int rv;
165
166		if (fp != stdin && !export_text) {
167			size -= sizeof(struct acct);
168			if (fseeko(fp, size, SEEK_SET) == -1)
169				err(1, "seek %s failed", acctfile);
170		}
171
172		if ((rv = fread(&ab, sizeof(struct acct), 1, fp)) != 1) {
173			if (feof(fp))
174				break;
175			else
176				err(1, "read %s returned %d", acctfile, rv);
177		}
178
179		if (ab.ac_comm[0] == '\0') {
180			ab.ac_comm[0] = '?';
181			ab.ac_comm[1] = '\0';
182		} else
183			for (p = &ab.ac_comm[0];
184			    p < &ab.ac_comm[AC_COMM_LEN] && *p; ++p)
185				if (!isprint(*p))
186					*p = '?';
187		if (*argv && !requested(argv, &ab))
188			continue;
189
190		if (export_text) {
191			export_record(&ab);
192			continue;
193		}
194
195		(void)printf("%-*.*s %-7s %-*s %-*s",
196			     AC_COMM_LEN, AC_COMM_LEN, ab.ac_comm,
197			     flagbits(ab.ac_flag),
198			     UT_NAMESIZE, user_from_uid(ab.ac_uid, 0),
199			     UT_LINESIZE, getdev(ab.ac_tty));
200
201
202		/* user + system time */
203		if (flags & AC_CTIME) {
204			(void)printf(" %6.2f secs",
205				     (expand(ab.ac_utime) +
206				      expand(ab.ac_stime))/AC_HZ);
207		}
208
209		/* usr time */
210		if (flags & AC_UTIME) {
211			(void)printf(" %6.2f us", expand(ab.ac_utime)/AC_HZ);
212		}
213
214		/* system time */
215		if (flags & AC_STIME) {
216			(void)printf(" %6.2f sy", expand(ab.ac_stime)/AC_HZ);
217		}
218
219		/* elapsed time */
220		if (flags & AC_ETIME) {
221			(void)printf(" %8.2f es", expand(ab.ac_etime)/AC_HZ);
222		}
223
224		/* starting time */
225		if (flags & AC_BTIME) {
226			(void)printf(" %.16s", ctime(&ab.ac_btime));
227		}
228
229		/* exit time (starting time + elapsed time )*/
230		if (flags & AC_FTIME) {
231			t = ab.ac_btime;
232			t += (time_t)(expand(ab.ac_etime)/AC_HZ);
233			(void)printf(" %.16s", ctime(&t));
234		}
235		printf("\n");
236
237 	} while (size > 0);
238
239	if (fflush(stdout))
240		err(1, "stdout");
241 	exit(0);
242}
243
244time_t
245expand(u_int t)
246{
247	time_t nt;
248
249	nt = t & 017777;
250	t >>= 13;
251	while (t) {
252		t--;
253		nt <<= 3;
254	}
255	return (nt);
256}
257
258char *
259flagbits(int f)
260{
261	static char flags[20] = "-";
262	char *p;
263
264#define	BIT(flag, ch)	if (f & flag) *p++ = ch
265
266	p = flags + 1;
267	BIT(ASU, 'S');
268	BIT(AFORK, 'F');
269	BIT(ACOMPAT, 'C');
270	BIT(ACORE, 'D');
271	BIT(AXSIG, 'X');
272	*p = '\0';
273	return (flags);
274}
275
276int
277requested(char *argv[], struct acct *acp)
278{
279	const char *p;
280
281	do {
282		p = user_from_uid(acp->ac_uid, 0);
283		if (!strcmp(p, *argv))
284			return (1);
285		if ((p = getdev(acp->ac_tty)) && !strcmp(p, *argv))
286			return (1);
287		if (!strncmp(acp->ac_comm, *argv, AC_COMM_LEN))
288			return (1);
289	} while (*++argv);
290	return (0);
291}
292
293const char *
294getdev(dev_t dev)
295{
296	static dev_t lastdev = (dev_t)-1;
297	static const char *lastname;
298
299	if (dev == NODEV)			/* Special case. */
300		return ("__");
301	if (dev == lastdev)			/* One-element cache. */
302		return (lastname);
303	lastdev = dev;
304	lastname = devname(dev, S_IFCHR);
305	return (lastname);
306}
307
308static void
309usage(void)
310{
311	(void)fprintf(stderr,
312"usage: lastcomm [[-EScesu] | [-X]] [-f file] [command ...] [user ...] [terminal ...]\n");
313	exit(1);
314}
315
316static void
317export_record(struct acct *acp)
318{
319	(void)printf("%s %g %g %g",
320	    acp->ac_comm,
321	    expand(acp->ac_utime) / AC_HZ,
322	    expand(acp->ac_stime) / AC_HZ,
323	    expand(acp->ac_etime) / AC_HZ);
324
325	/* See if time_t is signed and use appropriate format. */
326	if ((time_t)-1 < 0)
327		(void)printf(" %ld", (long)(acp->ac_btime));
328	else
329		(void)printf(" %lu", (unsigned long)(acp->ac_btime));
330
331	(void)printf(" %s %s %d %g %s %s\n",
332	    user_from_uid(acp->ac_uid, 0),
333	    group_from_gid(acp->ac_gid, 0),
334	    acp->ac_mem,
335	    expand(acp->ac_io) / AC_HZ,
336	    getdev(acp->ac_tty),
337	    flagbits(acp->ac_flag));
338}
339