xref: /freebsd/usr.bin/lastcomm/lastcomm.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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
35 static 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
42 static char sccsid[] = "@(#)lastcomm.c	8.1 (Berkeley) 6/6/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
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 <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <struct.h>
59 #include <unistd.h>
60 #include <utmp.h>
61 #include "pathnames.h"
62 
63 time_t	 expand __P((u_int));
64 char	*flagbits __P((int));
65 char	*getdev __P((dev_t));
66 int	 requested __P((char *[], struct acct *));
67 static void	 usage __P((void));
68 char	*user_from_uid();
69 
70 #define AC_UTIME 1 /* user */
71 #define AC_STIME 2 /* system */
72 #define AC_ETIME 4 /* elapsed */
73 #define AC_CTIME 8 /* user + system time, default */
74 
75 #define AC_BTIME 16 /* starting time */
76 #define AC_FTIME 32 /* exit time (starting time + elapsed time )*/
77 
78 #define AC_HZ ((double)AHZ)
79 
80 int
81 main(argc, argv)
82 	int argc;
83 	char *argv[];
84 {
85 	register char *p;
86 	struct acct ab;
87 	struct stat sb;
88 	FILE *fp;
89 	off_t size;
90 	time_t t;
91 	int ch;
92 	char *acctfile;
93 	int time = 0;
94 
95 	acctfile = _PATH_ACCT;
96 	while ((ch = getopt(argc, argv, "f:usecSE")) != -1)
97 		switch((char)ch) {
98 		case 'f':
99 			acctfile = optarg;
100 			break;
101 
102 		case 'u':
103 			time |= AC_UTIME; /* user time */
104 			break;
105 		case 's':
106 			time |= AC_STIME; /* system time */
107 			break;
108 		case 'e':
109 			time |= AC_ETIME; /* elapsed time */
110 			break;
111         	case 'c':
112                         time |= AC_CTIME; /* user + system time */
113 			break;
114 
115         	case 'S':
116                         time |= AC_BTIME; /* starting time */
117 			break;
118         	case 'E':
119 			/* exit time (starting time + elapsed time )*/
120                         time |= AC_FTIME;
121 			break;
122 
123 		case '?':
124 		default:
125 			usage();
126 		}
127 
128 	/* default user + system time and starting time */
129 	if (!time) {
130 	    time = AC_CTIME | AC_BTIME;
131 	}
132 
133 	argc -= optind;
134 	argv += optind;
135 
136 	/* Open the file. */
137 	if ((fp = fopen(acctfile, "r")) == NULL || fstat(fileno(fp), &sb))
138 		err(1, "%s", acctfile);
139 
140 	/*
141 	 * Round off to integral number of accounting records, probably
142 	 * not necessary, but it doesn't hurt.
143 	 */
144 	size = sb.st_size - sb.st_size % sizeof(struct acct);
145 
146 	/* Check if any records to display. */
147 	if (size < sizeof(struct acct))
148 		exit(0);
149 
150 	/*
151 	 * Seek to before the last entry in the file; use lseek(2) in case
152 	 * the file is bigger than a "long".
153 	 */
154 	size -= sizeof(struct acct);
155 	if (lseek(fileno(fp), size, SEEK_SET) == -1)
156 		err(1, "%s", acctfile);
157 
158 	for (;;) {
159 		if (fread(&ab, sizeof(struct acct), 1, fp) != 1)
160 			err(1, "%s", acctfile);
161 
162 		if (fseek(fp, 2 * -(long)sizeof(struct acct), SEEK_CUR) == -1)
163 			err(1, "%s", acctfile);
164 
165 		if (size == 0)
166 			break;
167 		size -= sizeof(struct acct);
168 
169 		if (ab.ac_comm[0] == '\0') {
170 			ab.ac_comm[0] = '?';
171 			ab.ac_comm[1] = '\0';
172 		} else
173 			for (p = &ab.ac_comm[0];
174 			    p < &ab.ac_comm[fldsiz(acct, ac_comm)] && *p; ++p)
175 				if (!isprint(*p))
176 					*p = '?';
177 		if (*argv && !requested(argv, &ab))
178 			continue;
179 
180 		(void)printf("%-*.*s %-7s %-*s %-*s ",
181 			     fldsiz(acct, ac_comm),
182 			     fldsiz(acct, ac_comm), ab.ac_comm,
183 			     flagbits(ab.ac_flag),
184 			     UT_NAMESIZE, user_from_uid(ab.ac_uid, 0),
185 			     UT_LINESIZE, getdev(ab.ac_tty));
186 
187 
188 		/* user + system time */
189 		if (time & AC_CTIME) {
190 			(void)printf("%6.2f secs ",
191 				     (expand(ab.ac_utime) +
192 				      expand(ab.ac_stime))/AC_HZ);
193 		}
194 
195 		/* usr time */
196 		if (time & AC_UTIME) {
197 			(void)printf("%6.2f us ", expand(ab.ac_utime)/AC_HZ);
198 		}
199 
200 		/* system time */
201 		if (time & AC_STIME) {
202 			(void)printf("%6.2f sy ", expand(ab.ac_stime)/AC_HZ);
203 		}
204 
205 		/* elapsed time */
206 		if (time & AC_ETIME) {
207 			(void)printf("%8.2f es ", expand(ab.ac_etime)/AC_HZ);
208 		}
209 
210 		/* starting time */
211 		if (time & AC_BTIME) {
212 			(void)printf("%.16s ", ctime(&ab.ac_btime));
213 		}
214 
215 		/* exit time (starting time + elapsed time )*/
216 		if (time & AC_FTIME) {
217 			t = ab.ac_btime;
218 			t += (time_t)(expand(ab.ac_etime)/AC_HZ);
219 			(void)printf("%.16s ",
220 				     ctime(&t));
221 		}
222 		printf("\n");
223  	}
224  	exit(0);
225 }
226 
227 time_t
228 expand(t)
229 	u_int t;
230 {
231 	register time_t nt;
232 
233 	nt = t & 017777;
234 	t >>= 13;
235 	while (t) {
236 		t--;
237 		nt <<= 3;
238 	}
239 	return (nt);
240 }
241 
242 char *
243 flagbits(f)
244 	register int f;
245 {
246 	static char flags[20] = "-";
247 	char *p;
248 
249 #define	BIT(flag, ch)	if (f & flag) *p++ = ch
250 
251 	p = flags + 1;
252 	BIT(ASU, 'S');
253 	BIT(AFORK, 'F');
254 	BIT(ACOMPAT, 'C');
255 	BIT(ACORE, 'D');
256 	BIT(AXSIG, 'X');
257 	*p = '\0';
258 	return (flags);
259 }
260 
261 int
262 requested(argv, acp)
263 	register char *argv[];
264 	register struct acct *acp;
265 {
266 	register char *p;
267 
268 	do {
269 		p = user_from_uid(acp->ac_uid, 0);
270 		if (!strcmp(p, *argv))
271 			return (1);
272 		if ((p = getdev(acp->ac_tty)) && !strcmp(p, *argv))
273 			return (1);
274 		if (!strncmp(acp->ac_comm, *argv, fldsiz(acct, ac_comm)))
275 			return (1);
276 	} while (*++argv);
277 	return (0);
278 }
279 
280 char *
281 getdev(dev)
282 	dev_t dev;
283 {
284 	static dev_t lastdev = (dev_t)-1;
285 	static char *lastname;
286 
287 	if (dev == NODEV)			/* Special case. */
288 		return ("__");
289 	if (dev == lastdev)			/* One-element cache. */
290 		return (lastname);
291 	lastdev = dev;
292 	lastname = devname(dev, S_IFCHR);
293 	return (lastname);
294 }
295 
296 static void
297 usage()
298 {
299 	(void)fprintf(stderr,
300 "usage: lastcomm [-EScesu] [ -f file ] [command ...] [user ...] [tty ...]\n");
301 	exit(1);
302 }
303