lastcomm.c revision 92920
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3578859Sddstatic const char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1980, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381590Srgrimes#endif /* not lint */
391590Srgrimes
401590Srgrimes#ifndef lint
4127571Scharnier#if 0
421590Srgrimesstatic char sccsid[] = "@(#)lastcomm.c	8.1 (Berkeley) 6/6/93";
4327571Scharnier#endif
4427571Scharnierstatic const char rcsid[] =
4550477Speter  "$FreeBSD: head/usr.bin/lastcomm/lastcomm.c 92920 2002-03-22 01:22:50Z imp $";
461590Srgrimes#endif /* not lint */
471590Srgrimes
481590Srgrimes#include <sys/param.h>
491590Srgrimes#include <sys/stat.h>
501590Srgrimes#include <sys/acct.h>
511590Srgrimes
521590Srgrimes#include <ctype.h>
531590Srgrimes#include <err.h>
541590Srgrimes#include <fcntl.h>
5590878Simp#include <pwd.h>
561590Srgrimes#include <stdio.h>
571590Srgrimes#include <stdlib.h>
581590Srgrimes#include <string.h>
591590Srgrimes#include <unistd.h>
601590Srgrimes#include <utmp.h>
611590Srgrimes#include "pathnames.h"
621590Srgrimes
6392920Simptime_t	 expand(u_int);
6492920Simpchar	*flagbits(int);
6592920Simpconst	 char *getdev(dev_t);
6692920Simpint	 requested(char *[], struct acct *);
6792920Simpstatic	 void usage(void);
681590Srgrimes
6916849Swosch#define AC_UTIME 1 /* user */
7016849Swosch#define AC_STIME 2 /* system */
7116849Swosch#define AC_ETIME 4 /* elapsed */
7216849Swosch#define AC_CTIME 8 /* user + system time, default */
7316849Swosch
7416849Swosch#define AC_BTIME 16 /* starting time */
7516849Swosch#define AC_FTIME 32 /* exit time (starting time + elapsed time )*/
7616849Swosch
7716849Swosch#define AC_HZ ((double)AHZ)
7816849Swosch
791590Srgrimesint
801590Srgrimesmain(argc, argv)
811590Srgrimes	int argc;
821590Srgrimes	char *argv[];
831590Srgrimes{
8478859Sdd	char *p;
851590Srgrimes	struct acct ab;
861590Srgrimes	struct stat sb;
871590Srgrimes	FILE *fp;
881590Srgrimes	off_t size;
891590Srgrimes	time_t t;
901590Srgrimes	int ch;
9178859Sdd	const char *acctfile;
9278859Sdd	int flags = 0;
931590Srgrimes
941590Srgrimes	acctfile = _PATH_ACCT;
9524360Simp	while ((ch = getopt(argc, argv, "f:usecSE")) != -1)
961590Srgrimes		switch((char)ch) {
971590Srgrimes		case 'f':
981590Srgrimes			acctfile = optarg;
991590Srgrimes			break;
10016849Swosch
10116849Swosch		case 'u':
10278859Sdd			flags |= AC_UTIME; /* user time */
10316849Swosch			break;
10416849Swosch		case 's':
10578859Sdd			flags |= AC_STIME; /* system time */
10616849Swosch			break;
10716849Swosch		case 'e':
10878859Sdd			flags |= AC_ETIME; /* elapsed time */
10916849Swosch			break;
11016849Swosch        	case 'c':
11178859Sdd                        flags |= AC_CTIME; /* user + system time */
11216849Swosch			break;
11316849Swosch
11416849Swosch        	case 'S':
11578859Sdd                        flags |= AC_BTIME; /* starting time */
11616849Swosch			break;
11716849Swosch        	case 'E':
11816849Swosch			/* exit time (starting time + elapsed time )*/
11978859Sdd                        flags |= AC_FTIME;
12016849Swosch			break;
12116849Swosch
1221590Srgrimes		case '?':
1231590Srgrimes		default:
1241590Srgrimes			usage();
1251590Srgrimes		}
12616849Swosch
12716849Swosch	/* default user + system time and starting time */
12878859Sdd	if (!flags) {
12978859Sdd	    flags = AC_CTIME | AC_BTIME;
13016849Swosch	}
13116849Swosch
1321590Srgrimes	argc -= optind;
1331590Srgrimes	argv += optind;
1341590Srgrimes
1351590Srgrimes	/* Open the file. */
1361590Srgrimes	if ((fp = fopen(acctfile, "r")) == NULL || fstat(fileno(fp), &sb))
1371590Srgrimes		err(1, "%s", acctfile);
1381590Srgrimes
1391590Srgrimes	/*
1401590Srgrimes	 * Round off to integral number of accounting records, probably
1411590Srgrimes	 * not necessary, but it doesn't hurt.
1421590Srgrimes	 */
1431590Srgrimes	size = sb.st_size - sb.st_size % sizeof(struct acct);
1441590Srgrimes
1451590Srgrimes	/* Check if any records to display. */
14678859Sdd	if ((unsigned)size < sizeof(struct acct))
1471590Srgrimes		exit(0);
1481590Srgrimes
1491590Srgrimes	/*
1501590Srgrimes	 * Seek to before the last entry in the file; use lseek(2) in case
1511590Srgrimes	 * the file is bigger than a "long".
1521590Srgrimes	 */
1531590Srgrimes	size -= sizeof(struct acct);
1541590Srgrimes	if (lseek(fileno(fp), size, SEEK_SET) == -1)
1551590Srgrimes		err(1, "%s", acctfile);
1561590Srgrimes
1571590Srgrimes	for (;;) {
1581590Srgrimes		if (fread(&ab, sizeof(struct acct), 1, fp) != 1)
1591590Srgrimes			err(1, "%s", acctfile);
1601590Srgrimes
1611590Srgrimes		if (fseek(fp, 2 * -(long)sizeof(struct acct), SEEK_CUR) == -1)
1621590Srgrimes			err(1, "%s", acctfile);
1631590Srgrimes
1641590Srgrimes		if (size == 0)
1651590Srgrimes			break;
1661590Srgrimes		size -= sizeof(struct acct);
1671590Srgrimes
1681590Srgrimes		if (ab.ac_comm[0] == '\0') {
1691590Srgrimes			ab.ac_comm[0] = '?';
1701590Srgrimes			ab.ac_comm[1] = '\0';
1711590Srgrimes		} else
1721590Srgrimes			for (p = &ab.ac_comm[0];
17367443Sphk			    p < &ab.ac_comm[AC_COMM_LEN] && *p; ++p)
1741590Srgrimes				if (!isprint(*p))
1751590Srgrimes					*p = '?';
1761590Srgrimes		if (*argv && !requested(argv, &ab))
1771590Srgrimes			continue;
1781590Srgrimes
17978341Smikeh		(void)printf("%-*.*s %-7s %-*s %-*s",
18067443Sphk			     AC_COMM_LEN, AC_COMM_LEN, ab.ac_comm,
18116849Swosch			     flagbits(ab.ac_flag),
18216849Swosch			     UT_NAMESIZE, user_from_uid(ab.ac_uid, 0),
18316849Swosch			     UT_LINESIZE, getdev(ab.ac_tty));
18416849Swosch
18516849Swosch
18616849Swosch		/* user + system time */
18778859Sdd		if (flags & AC_CTIME) {
18878341Smikeh			(void)printf(" %6.2f secs",
18916849Swosch				     (expand(ab.ac_utime) +
19016849Swosch				      expand(ab.ac_stime))/AC_HZ);
19116849Swosch		}
19216849Swosch
19316849Swosch		/* usr time */
19478859Sdd		if (flags & AC_UTIME) {
19578341Smikeh			(void)printf(" %6.2f us", expand(ab.ac_utime)/AC_HZ);
19616849Swosch		}
19716849Swosch
19816849Swosch		/* system time */
19978859Sdd		if (flags & AC_STIME) {
20078341Smikeh			(void)printf(" %6.2f sy", expand(ab.ac_stime)/AC_HZ);
20116849Swosch		}
20216849Swosch
20316849Swosch		/* elapsed time */
20478859Sdd		if (flags & AC_ETIME) {
20578341Smikeh			(void)printf(" %8.2f es", expand(ab.ac_etime)/AC_HZ);
20616849Swosch		}
20716849Swosch
20816849Swosch		/* starting time */
20978859Sdd		if (flags & AC_BTIME) {
21078341Smikeh			(void)printf(" %.16s", ctime(&ab.ac_btime));
21116849Swosch		}
21216849Swosch
21316849Swosch		/* exit time (starting time + elapsed time )*/
21478859Sdd		if (flags & AC_FTIME) {
21516849Swosch			t = ab.ac_btime;
21616849Swosch			t += (time_t)(expand(ab.ac_etime)/AC_HZ);
21778341Smikeh			(void)printf(" %.16s", ctime(&t));
21816849Swosch		}
21916849Swosch		printf("\n");
22016849Swosch 	}
22116849Swosch 	exit(0);
2221590Srgrimes}
2231590Srgrimes
2241590Srgrimestime_t
2251590Srgrimesexpand(t)
2261590Srgrimes	u_int t;
2271590Srgrimes{
22878859Sdd	time_t nt;
2291590Srgrimes
2301590Srgrimes	nt = t & 017777;
2311590Srgrimes	t >>= 13;
2321590Srgrimes	while (t) {
2331590Srgrimes		t--;
2341590Srgrimes		nt <<= 3;
2351590Srgrimes	}
2361590Srgrimes	return (nt);
2371590Srgrimes}
2381590Srgrimes
2391590Srgrimeschar *
2401590Srgrimesflagbits(f)
24178859Sdd	int f;
2421590Srgrimes{
2431590Srgrimes	static char flags[20] = "-";
2441590Srgrimes	char *p;
2451590Srgrimes
2461590Srgrimes#define	BIT(flag, ch)	if (f & flag) *p++ = ch
2471590Srgrimes
2481590Srgrimes	p = flags + 1;
2491590Srgrimes	BIT(ASU, 'S');
2501590Srgrimes	BIT(AFORK, 'F');
2511590Srgrimes	BIT(ACOMPAT, 'C');
2521590Srgrimes	BIT(ACORE, 'D');
2531590Srgrimes	BIT(AXSIG, 'X');
2541590Srgrimes	*p = '\0';
2551590Srgrimes	return (flags);
2561590Srgrimes}
2571590Srgrimes
2581590Srgrimesint
2591590Srgrimesrequested(argv, acp)
26078859Sdd	char *argv[];
26178859Sdd	struct acct *acp;
2621590Srgrimes{
26378859Sdd	const char *p;
2641590Srgrimes
2651590Srgrimes	do {
2661590Srgrimes		p = user_from_uid(acp->ac_uid, 0);
2678874Srgrimes		if (!strcmp(p, *argv))
2681590Srgrimes			return (1);
2691590Srgrimes		if ((p = getdev(acp->ac_tty)) && !strcmp(p, *argv))
2701590Srgrimes			return (1);
27167443Sphk		if (!strncmp(acp->ac_comm, *argv, AC_COMM_LEN))
2721590Srgrimes			return (1);
2731590Srgrimes	} while (*++argv);
2741590Srgrimes	return (0);
2751590Srgrimes}
2761590Srgrimes
27778859Sddconst char *
2781590Srgrimesgetdev(dev)
2791590Srgrimes	dev_t dev;
2801590Srgrimes{
2811590Srgrimes	static dev_t lastdev = (dev_t)-1;
28278859Sdd	static const char *lastname;
2831590Srgrimes
2841590Srgrimes	if (dev == NODEV)			/* Special case. */
2851590Srgrimes		return ("__");
2861590Srgrimes	if (dev == lastdev)			/* One-element cache. */
2871590Srgrimes		return (lastname);
2881590Srgrimes	lastdev = dev;
2891590Srgrimes	lastname = devname(dev, S_IFCHR);
2901590Srgrimes	return (lastname);
2911590Srgrimes}
2921590Srgrimes
29327571Scharnierstatic void
2941590Srgrimesusage()
2951590Srgrimes{
2961590Srgrimes	(void)fprintf(stderr,
29727571Scharnier"usage: lastcomm [-EScesu] [ -f file ] [command ...] [user ...] [tty ...]\n");
2981590Srgrimes	exit(1);
2991590Srgrimes}
300