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