xref: /original-bsd/usr.bin/lastcomm/lastcomm.c (revision 4d072710)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)lastcomm.c	5.6 (Berkeley) 06/29/88";
26 #endif /* not lint */
27 
28 /*
29  * last command
30  */
31 #include <sys/param.h>
32 #include <sys/acct.h>
33 #include <sys/file.h>
34 #include <sys/stat.h>
35 #include <utmp.h>
36 #include <struct.h>
37 #include <ctype.h>
38 #include <stdio.h>
39 
40 struct	acct buf[DEV_BSIZE / sizeof (struct acct)];
41 
42 time_t	expand();
43 char	*flagbits();
44 char	*getname();
45 char	*getdev();
46 
47 main(argc, argv)
48 	int argc;
49 	char *argv[];
50 {
51 	extern int optind;
52 	extern char *optarg;
53 	register struct acct *acp;
54 	register int bn, cc;
55 	struct stat sb;
56 	int ch, fd;
57 	char *acctfile, *strcpy(), *ctime();
58 	long lseek();
59 
60 	acctfile = NULL;
61 	while ((ch = getopt(argc, argv, "f:")) != EOF)
62 		switch((char)ch) {
63 		case 'f':
64 			acctfile = optarg;
65 			break;
66 		case '?':
67 		default:
68 			fputs("lastcomm [ -f file ]\n", stderr);
69 			exit(1);
70 		}
71 	argv += optind;
72 	if (!acctfile)
73 		acctfile = "/usr/adm/acct";
74 	fd = open(acctfile, O_RDONLY);
75 	if (fd < 0) {
76 		perror(acctfile);
77 		exit(1);
78 	}
79 	(void)fstat(fd, &sb);
80 	for (bn = btodb(sb.st_size); bn >= 0; bn--) {
81 		(void)lseek(fd, (off_t)dbtob(bn), L_SET);
82 		cc = read(fd, buf, DEV_BSIZE);
83 		if (cc < 0) {
84 			perror("read");
85 			break;
86 		}
87 		acp = buf + (cc / sizeof (buf[0])) - 1;
88 		for (; acp >= buf; acp--) {
89 			register char *cp;
90 			time_t x;
91 
92 			if (acp->ac_comm[0] == '\0')
93 				(void)strcpy(acp->ac_comm, "?");
94 			for (cp = &acp->ac_comm[0];
95 			     cp < &acp->ac_comm[fldsiz(acct, ac_comm)] && *cp;
96 			     cp++)
97 				if (!isascii(*cp) || iscntrl(*cp))
98 					*cp = '?';
99 			if (*argv && !ok(argv, acp))
100 				continue;
101 			x = expand(acp->ac_utime) + expand(acp->ac_stime);
102 			printf("%-*.*s %s %-*s %-*s %6.2f secs %.16s\n",
103 				fldsiz(acct, ac_comm),
104 				fldsiz(acct, ac_comm),
105 				acp->ac_comm, flagbits(acp->ac_flag),
106 				fldsiz(utmp, ut_name),
107 				(cp = getname(acp->ac_uid)) ? cp : "",
108 				fldsiz(utmp, ut_line), getdev(acp->ac_tty),
109 				x / (double)AHZ, ctime(&acp->ac_btime));
110 		}
111 	}
112 }
113 
114 time_t
115 expand (t)
116 	unsigned t;
117 {
118 	register time_t nt;
119 
120 	nt = t & 017777;
121 	t >>= 13;
122 	while (t) {
123 		t--;
124 		nt <<= 3;
125 	}
126 	return (nt);
127 }
128 
129 char *
130 flagbits(f)
131 	register int f;
132 {
133 	static char flags[20];
134 	char *p, *strcpy();
135 
136 #define	BIT(flag, ch)	if (f & flag) *p++ = ch;
137 	p = strcpy(flags, "-    ");
138 	BIT(ASU, 'S');
139 	BIT(AFORK, 'F');
140 	BIT(ACOMPAT, 'C');
141 	BIT(ACORE, 'D');
142 	BIT(AXSIG, 'X');
143 	return (flags);
144 }
145 
146 ok(argv, acp)
147 	register char *argv[];
148 	register struct acct *acp;
149 {
150 	register char *cp;
151 
152 	do {
153 		if ((cp = getname(acp->ac_uid)) && !strcmp(cp, *argv) ||
154 		    (cp = getdev(acp->ac_tty)) && !strcmp(cp, *argv) ||
155 		    !strncmp(acp->ac_comm, *argv, fldsiz(acct, ac_comm)))
156 			return(1);
157 	} while (*++argv);
158 	return(0);
159 }
160 
161 /* should be done with nameserver or database */
162 
163 #include <pwd.h>
164 
165 struct	utmp utmp;
166 #define	NMAX	(sizeof (utmp.ut_name))
167 #define SCPYN(a, b)	strncpy(a, b, NMAX)
168 
169 #define NCACHE	64		/* power of 2 */
170 #define CAMASK	NCACHE - 1
171 
172 char *
173 getname(uid)
174 	uid_t uid;
175 {
176 	extern int _pw_stayopen;
177 	static struct ncache {
178 		uid_t	uid;
179 		char	name[NMAX+1];
180 	} c_uid[NCACHE];
181 	register struct passwd *pw;
182 	register struct ncache *cp;
183 
184 	_pw_stayopen = 1;
185 	cp = c_uid + (uid & CAMASK);
186 	if (cp->uid == uid && *cp->name)
187 		return(cp->name);
188 	if (!(pw = getpwuid(uid)))
189 		return((char *)0);
190 	cp->uid = uid;
191 	SCPYN(cp->name, pw->pw_name);
192 	return(cp->name);
193 }
194 
195 #include <sys/dir.h>
196 
197 #define N_DEVS		43		/* hash value for device names */
198 #define NDEVS		500		/* max number of file names in /dev */
199 
200 struct	devhash {
201 	dev_t	dev_dev;
202 	char	dev_name [fldsiz(utmp, ut_line) + 1];
203 	struct	devhash * dev_nxt;
204 };
205 struct	devhash *dev_hash[N_DEVS];
206 struct	devhash *dev_chain;
207 #define HASH(d)	(((int) d) % N_DEVS)
208 
209 setupdevs()
210 {
211 	register DIR * fd;
212 	register struct devhash * hashtab;
213 	register ndevs = NDEVS;
214 	struct direct * dp;
215 	char *malloc();
216 
217 	/*NOSTRICT*/
218 	hashtab = (struct devhash *)malloc(NDEVS * sizeof(struct devhash));
219 	if (hashtab == (struct devhash *)0) {
220 		fputs("No mem for dev table\n", stderr);
221 		return;
222 	}
223 	if ((fd = opendir("/dev")) == NULL) {
224 		perror("/dev");
225 		return;
226 	}
227 	while (dp = readdir(fd)) {
228 		if (dp->d_ino == 0)
229 			continue;
230 		if (dp->d_name[0] != 't' && strcmp(dp->d_name, "console"))
231 			continue;
232 		strncpy(hashtab->dev_name, dp->d_name, fldsiz(utmp, ut_line));
233 		hashtab->dev_name[fldsiz(utmp, ut_line)] = 0;
234 		hashtab->dev_nxt = dev_chain;
235 		dev_chain = hashtab;
236 		hashtab++;
237 		if (--ndevs <= 0)
238 			break;
239 	}
240 	closedir(fd);
241 }
242 
243 char *
244 getdev(dev)
245 	dev_t dev;
246 {
247 	register struct devhash *hp, *nhp;
248 	struct stat statb;
249 	char name[fldsiz(devhash, dev_name) + 6];
250 	static dev_t lastdev = (dev_t) -1;
251 	static char *lastname;
252 	static int init = 0;
253 	char *strcpy(), *strcat();
254 
255 	if (dev == NODEV)
256 		return ("__");
257 	if (dev == lastdev)
258 		return (lastname);
259 	if (!init) {
260 		setupdevs();
261 		init++;
262 	}
263 	for (hp = dev_hash[HASH(dev)]; hp; hp = hp->dev_nxt)
264 		if (hp->dev_dev == dev) {
265 			lastdev = dev;
266 			return (lastname = hp->dev_name);
267 		}
268 	for (hp = dev_chain; hp; hp = nhp) {
269 		nhp = hp->dev_nxt;
270 		strcpy(name, "/dev/");
271 		strcat(name, hp->dev_name);
272 		if (stat(name, &statb) < 0)	/* name truncated usually */
273 			continue;
274 		if ((statb.st_mode & S_IFMT) != S_IFCHR)
275 			continue;
276 		hp->dev_dev = statb.st_rdev;
277 		hp->dev_nxt = dev_hash[HASH(hp->dev_dev)];
278 		dev_hash[HASH(hp->dev_dev)] = hp;
279 		if (hp->dev_dev == dev) {
280 			dev_chain = nhp;
281 			lastdev = dev;
282 			return (lastname = hp->dev_name);
283 		}
284 	}
285 	dev_chain = (struct devhash *) 0;
286 	return ("??");
287 }
288