xref: /original-bsd/usr.bin/last/last.c (revision a76afa45)
1 /*
2  * Copyright (c) 1987 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) 1987 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)last.c	5.15 (Berkeley) 05/10/89";
26 #endif /* not lint */
27 
28 /*
29  * last
30  */
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/file.h>
34 #include <signal.h>
35 #include <time.h>
36 #include <utmp.h>
37 #include <stdio.h>
38 #include <paths.h>
39 
40 #define	SECDAY	(24*60*60)			/* seconds in a day */
41 #define	NO	0				/* false/no */
42 #define	YES	1				/* true/yes */
43 
44 static struct utmp	buf[1024];		/* utmp read buffer */
45 
46 typedef struct arg {
47 	char	*name;				/* argument */
48 #define	HOST_TYPE	-2
49 #define	TTY_TYPE	-3
50 #define	USER_TYPE	-4
51 	int	type;				/* type of arg */
52 	struct arg	*next;			/* linked list pointer */
53 } ARG;
54 ARG	*arglist;				/* head of linked list */
55 
56 typedef struct ttytab {
57 	long	logout;				/* log out time */
58 	char	tty[UT_LINESIZE + 1];		/* terminal name */
59 	struct ttytab	*next;			/* linked list pointer */
60 } TTY;
61 TTY	*ttylist;				/* head of linked list */
62 
63 static long	currentout,			/* current logout value */
64 		maxrec;				/* records to display */
65 static char	*file = _PATH_WTMP;		/* wtmp file */
66 
67 main(argc, argv)
68 	int argc;
69 	char **argv;
70 {
71 	extern int optind;
72 	extern char *optarg;
73 	int ch;
74 	long atol();
75 	char *p, *ttyconv();
76 
77 	maxrec = -1;
78 	while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != EOF)
79 		switch((char)ch) {
80 		case '0': case '1': case '2': case '3': case '4':
81 		case '5': case '6': case '7': case '8': case '9':
82 			/*
83 			 * kludge: last was originally designed to take
84 			 * a number after a dash.
85 			 */
86 			if (maxrec == -1) {
87 				p = argv[optind - 1];
88 				if (p[0] == '-' && p[1] == ch && !p[2])
89 					maxrec = atol(++p);
90 				else
91 					maxrec = atol(argv[optind] + 1);
92 				if (!maxrec)
93 					exit(0);
94 			}
95 			break;
96 		case 'f':
97 			file = optarg;
98 			break;
99 		case 'h':
100 			hostconv(optarg);
101 			addarg(HOST_TYPE, optarg);
102 			break;
103 		case 't':
104 			addarg(TTY_TYPE, ttyconv(optarg));
105 			break;
106 		case '?':
107 		default:
108 			fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr);
109 			exit(1);
110 		}
111 	for (argv += optind; *argv; ++argv) {
112 #define	COMPATIBILITY
113 #ifdef	COMPATIBILITY
114 		/* code to allow "last p5" to work */
115 		addarg(TTY_TYPE, ttyconv(*argv));
116 #endif
117 		addarg(USER_TYPE, *argv);
118 	}
119 	wtmp();
120 	exit(0);
121 }
122 
123 /*
124  * wtmp --
125  *	read through the wtmp file
126  */
127 static
128 wtmp()
129 {
130 	register struct utmp	*bp;		/* current structure */
131 	register TTY	*T;			/* tty list entry */
132 	struct stat	stb;			/* stat of file for size */
133 	long	bl, delta,			/* time difference */
134 		lseek(), time();
135 	int	bytes, wfd,
136 		onintr();
137 	char	*ct, *crmsg,
138 		*asctime(), *ctime(), *strcpy();
139 	TTY	*addtty();
140 
141 	if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) {
142 		perror(file);
143 		exit(1);
144 	}
145 	bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
146 
147 	(void)time(&buf[0].ut_time);
148 	(void)signal(SIGINT, onintr);
149 	(void)signal(SIGQUIT, onintr);
150 
151 	while (--bl >= 0) {
152 		if (lseek(wfd, (long)(bl * sizeof(buf)), L_SET) == -1 ||
153 		    (bytes = read(wfd, (char *)buf, sizeof(buf))) == -1) {
154 			fprintf(stderr, "last: %s: ", file);
155 			perror((char *)NULL);
156 			exit(1);
157 		}
158 		for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
159 			/*
160 			 * if the terminal line is '~', the machine stopped.
161 			 * see utmp(5) for more info.
162 			 */
163 			if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
164 				/* everybody just logged out */
165 				for (T = ttylist; T; T = T->next)
166 					T->logout = -bp->ut_time;
167 				currentout = -bp->ut_time;
168 				crmsg = strncmp(bp->ut_name, "shutdown",
169 				    UT_NAMESIZE) ? "crash" : "shutdown";
170 				if (want(bp, NO)) {
171 					ct = ctime(&bp->ut_time);
172 					printf("%-*.*s  %-*.*s %-*.*s %10.10s %5.5s \n", UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, UT_LINESIZE, UT_LINESIZE, bp->ut_line, UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, ct, ct + 11);
173 					if (maxrec != -1 && !--maxrec)
174 						return;
175 				}
176 				continue;
177 			}
178 			/*
179 			 * if the line is '{' or '|', date got set; see
180 			 * utmp(5) for more info.
181 			 */
182 			if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|')
183 			    && !bp->ut_line[1]) {
184 				if (want(bp, NO)) {
185 					ct = ctime(&bp->ut_time);
186 					printf("%-*.*s  %-*.*s %-*.*s %10.10s %5.5s \n", UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, UT_LINESIZE, UT_LINESIZE, bp->ut_line, UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, ct, ct + 11);
187 					if (maxrec && !--maxrec)
188 						return;
189 				}
190 				continue;
191 			}
192 			/* find associated tty */
193 			for (T = ttylist;; T = T->next) {
194 				if (!T) {
195 					/* add new one */
196 					T = addtty(bp->ut_line);
197 					break;
198 				}
199 				if (!strncmp(T->tty, bp->ut_line, UT_LINESIZE))
200 					break;
201 			}
202 			if (bp->ut_name[0] && want(bp, YES)) {
203 				ct = ctime(&bp->ut_time);
204 				printf("%-*.*s  %-*.*s %-*.*s %10.10s %5.5s ", UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, UT_LINESIZE, UT_LINESIZE, bp->ut_line, UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, ct, ct + 11);
205 				if (!T->logout)
206 					puts("  still logged in");
207 				else {
208 					if (T->logout < 0) {
209 						T->logout = -T->logout;
210 						printf("- %s", crmsg);
211 					}
212 					else
213 						printf("- %5.5s", ctime(&T->logout)+11);
214 					delta = T->logout - bp->ut_time;
215 					if (delta < SECDAY)
216 						printf("  (%5.5s)\n", asctime(gmtime(&delta))+11);
217 					else
218 						printf(" (%ld+%5.5s)\n", delta / SECDAY, asctime(gmtime(&delta))+11);
219 				}
220 				if (maxrec != -1 && !--maxrec)
221 					return;
222 			}
223 			T->logout = bp->ut_time;
224 		}
225 	}
226 	ct = ctime(&buf[0].ut_time);
227 	printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
228 }
229 
230 /*
231  * want --
232  *	see if want this entry
233  */
234 static
235 want(bp, check)
236 	register struct utmp *bp;
237 	int check;
238 {
239 	register ARG *step;
240 
241 	if (check)
242 		/*
243 		 * when uucp and ftp log in over a network, the entry in
244 		 * the utmp file is the name plus their process id.  See
245 		 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
246 		 */
247 		if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
248 			bp->ut_line[3] = '\0';
249 		else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
250 			bp->ut_line[4] = '\0';
251 	if (!arglist)
252 		return(YES);
253 
254 	for (step = arglist; step; step = step->next)
255 		switch(step->type) {
256 		case HOST_TYPE:
257 			if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
258 				return(YES);
259 			break;
260 		case TTY_TYPE:
261 			if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
262 				return(YES);
263 			break;
264 		case USER_TYPE:
265 			if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
266 				return(YES);
267 			break;
268 	}
269 	return(NO);
270 }
271 
272 /*
273  * addarg --
274  *	add an entry to a linked list of arguments
275  */
276 static
277 addarg(type, arg)
278 	int type;
279 	char *arg;
280 {
281 	register ARG *cur;
282 	char *malloc();
283 
284 	if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) {
285 		fputs("last: malloc failure.\n", stderr);
286 		exit(1);
287 	}
288 	cur->next = arglist;
289 	cur->type = type;
290 	cur->name = arg;
291 	arglist = cur;
292 }
293 
294 /*
295  * addtty --
296  *	add an entry to a linked list of ttys
297  */
298 static TTY *
299 addtty(ttyname)
300 	char *ttyname;
301 {
302 	register TTY *cur;
303 	char *malloc();
304 
305 	if (!(cur = (TTY *)malloc((u_int)sizeof(TTY)))) {
306 		fputs("last: malloc failure.\n", stderr);
307 		exit(1);
308 	}
309 	cur->next = ttylist;
310 	cur->logout = currentout;
311 	bcopy(ttyname, cur->tty, UT_LINESIZE);
312 	return(ttylist = cur);
313 }
314 
315 /*
316  * hostconv --
317  *	convert the hostname to search pattern; if the supplied host name
318  *	has a domain attached that is the same as the current domain, rip
319  *	off the domain suffix since that's what login(1) does.
320  */
321 static
322 hostconv(arg)
323 	char *arg;
324 {
325 	static int first = 1;
326 	static char *hostdot, name[MAXHOSTNAMELEN];
327 	char *argdot, *index();
328 
329 	if (!(argdot = index(arg, '.')))
330 		return;
331 	if (first) {
332 		first = 0;
333 		if (gethostname(name, sizeof(name))) {
334 			perror("last: gethostname");
335 			exit(1);
336 		}
337 		hostdot = index(name, '.');
338 	}
339 	if (hostdot && !strcasecmp(hostdot, argdot))
340 		*argdot = '\0';
341 }
342 
343 /*
344  * ttyconv --
345  *	convert tty to correct name.
346  */
347 static char *
348 ttyconv(arg)
349 	char *arg;
350 {
351 	char *mval, *malloc(), *strcpy();
352 
353 	/*
354 	 * kludge -- we assume that all tty's end with
355 	 * a two character suffix.
356 	 */
357 	if (strlen(arg) == 2) {
358 		/* either 6 for "ttyxx" or 8 for "console" */
359 		if (!(mval = malloc((u_int)8))) {
360 			fputs("last: malloc failure.\n", stderr);
361 			exit(1);
362 		}
363 		if (!strcmp(arg, "co"))
364 			(void)strcpy(mval, "console");
365 		else {
366 			(void)strcpy(mval, "tty");
367 			(void)strcpy(mval + 3, arg);
368 		}
369 		return(mval);
370 	}
371 	if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
372 		return(arg + 5);
373 	return(arg);
374 }
375 
376 /*
377  * onintr --
378  *	on interrupt, we inform the user how far we've gotten
379  */
380 static
381 onintr(signo)
382 	int signo;
383 {
384 	char *ct, *ctime();
385 
386 	ct = ctime(&buf[0].ut_time);
387 	printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
388 	if (signo == SIGINT)
389 		exit(1);
390 	(void)fflush(stdout);			/* fix required for rsh */
391 }
392