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