Commit | Line | Data |
---|---|---|
22e155fc DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
8 | char copyright[] = | |
9 | "@(#) Copyright (c) 1980 Regents of the University of California.\n\ | |
10 | All rights reserved.\n"; | |
11 | #endif not lint | |
12 | ||
37c640e2 | 13 | #ifndef lint |
53f69172 | 14 | static char sccsid[] = "@(#)last.c 5.7 (Berkeley) %G%"; |
22e155fc | 15 | #endif not lint |
37c640e2 | 16 | |
c9af8668 BJ |
17 | /* |
18 | * last | |
19 | */ | |
53f69172 | 20 | #include <sys/param.h> |
40d69e19 | 21 | #include <sys/stat.h> |
0e3f8c72 KB |
22 | #include <sys/file.h> |
23 | #include <signal.h> | |
24 | #include <time.h> | |
c9af8668 | 25 | #include <utmp.h> |
0e3f8c72 | 26 | #include <stdio.h> |
c9af8668 | 27 | |
53f69172 KB |
28 | #define MAXTTYS 500 /* max ttys last can handle */ |
29 | #define SECDAY (24*60*60) /* seconds in a day */ | |
30 | #define NO 0 /* false/no */ | |
31 | #define YES 1 /* true/yes */ | |
c9af8668 | 32 | |
53f69172 | 33 | static struct utmp buf[1024]; /* utmp read buffer */ |
c9af8668 | 34 | |
53f69172 KB |
35 | #define HMAX sizeof(buf[0].ut_host) /* size of utmp host field */ |
36 | #define LMAX sizeof(buf[0].ut_line) /* size of utmp tty field */ | |
37 | #define NMAX sizeof(buf[0].ut_name) /* size of utmp name field */ | |
38 | ||
39 | typedef struct arg { | |
40 | char *name; /* argument */ | |
41 | #define HOST_TYPE -2 | |
42 | #define TTY_TYPE -3 | |
43 | #define USER_TYPE -4 | |
44 | int type; /* type of arg */ | |
45 | struct arg *next; /* linked list pointer */ | |
46 | } ARG; | |
47 | ARG *head; /* head of linked list */ | |
c9af8668 | 48 | |
0e3f8c72 KB |
49 | typedef struct ttytab { |
50 | long logout; /* log out time */ | |
51 | char tty[LMAX + 1]; /* terminal name */ | |
52 | } TTYS; | |
c9af8668 | 53 | |
0e3f8c72 | 54 | static TTYS tab[MAXTTYS + 1]; /* tty table */ |
53f69172 KB |
55 | static long maxrec; /* records to display */ |
56 | static char *file = "/usr/adm/wtmp"; /* wtmp file */ | |
57 | ||
58 | main(argc, argv) | |
59 | int argc; | |
60 | char **argv; | |
61 | { | |
62 | extern int optind; | |
63 | extern char *optarg; | |
64 | int ch; | |
65 | char *malloc(); | |
66 | long atol(); | |
67 | ||
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) | |
77 | maxrec = atol(argv[optind - 1] + 1); | |
78 | break; | |
79 | case 'f': | |
80 | file = optarg; | |
81 | break; | |
82 | case 'h': | |
83 | hostconv(optarg); | |
84 | addarg(HOST_TYPE, optarg); | |
85 | break; | |
86 | case 't': | |
87 | { | |
88 | char *mval, *strcpy(); | |
89 | ||
90 | /* | |
91 | * kludge -- we assume that all tty's end with | |
92 | * a two character suffix. | |
93 | */ | |
94 | if (strlen(optarg) == 2) { | |
95 | /* either 6 for "ttyxx" or 8 for "console" */ | |
96 | if (!(mval = malloc((u_int)8))) { | |
97 | fputs("last: malloc failure.\n", stderr); | |
98 | exit(1); | |
99 | } | |
100 | if (!strcmp(optarg, "co")) | |
101 | (void)strcpy(mval, "console"); | |
102 | else { | |
103 | (void)strcpy(mval, "tty"); | |
104 | (void)strcpy(mval + 3, optarg); | |
105 | } | |
106 | addarg(TTY_TYPE, mval); | |
107 | } | |
108 | else | |
109 | addarg(TTY_TYPE, optarg); | |
110 | break; | |
111 | } | |
112 | case '?': | |
113 | default: | |
114 | fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr); | |
115 | exit(1); | |
116 | } | |
117 | for (argv += optind; *argv; ++argv) | |
118 | addarg(USER_TYPE, *argv); | |
119 | wtmp(); | |
120 | exit(0); | |
121 | } | |
c9af8668 | 122 | |
53f69172 KB |
123 | /* |
124 | * wtmp -- | |
125 | * read through the wtmp file | |
126 | */ | |
127 | static | |
128 | wtmp() | |
c9af8668 | 129 | { |
0e3f8c72 KB |
130 | register struct utmp *bp; /* current structure */ |
131 | register TTYS *T; /* table entry */ | |
0e3f8c72 | 132 | struct stat stb; /* stat of file for size */ |
53f69172 KB |
133 | long bl, delta, /* time difference */ |
134 | lseek(), time(); | |
135 | int bytes, wfd, | |
0e3f8c72 | 136 | onintr(); |
53f69172 KB |
137 | char *ct, *crmsg, |
138 | *asctime(), *ctime(), *strcpy(); | |
0e3f8c72 | 139 | |
53f69172 | 140 | if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) { |
0e3f8c72 | 141 | perror(file); |
c9af8668 BJ |
142 | exit(1); |
143 | } | |
0e3f8c72 KB |
144 | bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); |
145 | ||
53f69172 KB |
146 | (void)time(&buf[0].ut_time); |
147 | (void)signal(SIGINT, onintr); | |
148 | (void)signal(SIGQUIT, onintr); | |
0e3f8c72 KB |
149 | |
150 | tab[MAXTTYS].logout = -1; /* end flag value */ | |
151 | while (--bl >= 0) { | |
53f69172 KB |
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); | |
0e3f8c72 KB |
156 | exit(1); |
157 | } | |
53f69172 KB |
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 (!strncmp(bp->ut_line, "~", LMAX)) { | |
164 | for (T = tab; T->logout != -1; ++T) | |
0e3f8c72 | 165 | T->logout = -bp->ut_time; |
53f69172 KB |
166 | crmsg = strncmp(bp->ut_name, "shutdown", NMAX) |
167 | ? "crash" : "down "; | |
0e3f8c72 | 168 | if (!bp->ut_name[0]) |
53f69172 KB |
169 | (void)strcpy(bp->ut_name, "reboot"); |
170 | if (want(bp, NO)) { | |
0e3f8c72 | 171 | ct = ctime(&bp->ut_time); |
53f69172 KB |
172 | printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n", NMAX, NMAX, bp->ut_name, LMAX, LMAX, bp->ut_line, HMAX, HMAX, bp->ut_host, ct, ct + 11); |
173 | if (maxrec && !--maxrec) | |
174 | return; | |
0e3f8c72 KB |
175 | } |
176 | continue; | |
c9af8668 | 177 | } |
53f69172 KB |
178 | for (T = tab;;) { /* find assoc. tty */ |
179 | if (T->logout <= 0) { /* unused entry */ | |
180 | bcopy(bp->ut_line, T->tty, LMAX); | |
c9af8668 BJ |
181 | break; |
182 | } | |
53f69172 | 183 | if (!strncmp(T->tty, bp->ut_line, LMAX)) |
c9af8668 | 184 | break; |
0e3f8c72 | 185 | if ((++T)->logout == -1) { |
53f69172 | 186 | fputs("last: too many terminals.\n", stderr); |
0e3f8c72 | 187 | exit(1); |
c9af8668 BJ |
188 | } |
189 | } | |
53f69172 | 190 | if (bp->ut_name[0] && want(bp, YES)) { |
0e3f8c72 | 191 | ct = ctime(&bp->ut_time); |
53f69172 | 192 | printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ", NMAX, NMAX, bp->ut_name, LMAX, LMAX, bp->ut_line, HMAX, HMAX, bp->ut_host, ct, ct + 11); |
0e3f8c72 KB |
193 | if (!T->logout) |
194 | puts(" still logged in"); | |
c9af8668 | 195 | else { |
0e3f8c72 KB |
196 | if (T->logout < 0) { |
197 | T->logout = -T->logout; | |
53f69172 | 198 | printf("- %s", crmsg); |
0e3f8c72 KB |
199 | } |
200 | else | |
53f69172 | 201 | printf("- %5.5s", ctime(&T->logout)+11); |
0e3f8c72 | 202 | delta = T->logout - bp->ut_time; |
c9af8668 | 203 | if (delta < SECDAY) |
53f69172 | 204 | printf(" (%5.5s)\n", asctime(gmtime(&delta))+11); |
c9af8668 | 205 | else |
53f69172 | 206 | printf(" (%ld+%5.5s)\n", delta / SECDAY, asctime(gmtime(&delta))+11); |
c9af8668 | 207 | } |
0e3f8c72 | 208 | if (maxrec != -1 && !--maxrec) |
53f69172 | 209 | return; |
c9af8668 | 210 | } |
0e3f8c72 | 211 | T->logout = bp->ut_time; |
c9af8668 BJ |
212 | } |
213 | } | |
214 | ct = ctime(&buf[0].ut_time); | |
53f69172 | 215 | printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11); |
c9af8668 BJ |
216 | } |
217 | ||
53f69172 KB |
218 | /* |
219 | * want -- | |
220 | * see if want this entry | |
221 | */ | |
222 | static | |
223 | want(bp, check) | |
224 | register struct utmp *bp; | |
225 | int check; | |
c9af8668 | 226 | { |
53f69172 | 227 | register ARG *step; |
0e3f8c72 | 228 | |
ed99b1a7 | 229 | if (check) |
0e3f8c72 KB |
230 | /* |
231 | * when uucp and ftp log in over a network, the entry in the | |
232 | * utmp file is the name plus their process id. See etc/ftpd.c | |
233 | * and usr.bin/uucp/uucpd.c for more information. | |
234 | */ | |
53f69172 | 235 | if (!strncmp(bp->ut_line, "ftp", 3)) |
0e3f8c72 | 236 | bp->ut_line[3] = '\0'; |
53f69172 | 237 | else if (!strncmp(bp->ut_line, "uucp", 4)) |
0e3f8c72 | 238 | bp->ut_line[4] = '\0'; |
53f69172 | 239 | if (!head) |
0e3f8c72 | 240 | return(YES); |
53f69172 KB |
241 | |
242 | for (step = head; step; step = step->next) | |
243 | switch(step->type) { | |
244 | case HOST_TYPE: | |
245 | if (!strncasecmp(step->name, bp->ut_host, HMAX)) | |
246 | return(YES); | |
247 | break; | |
248 | case TTY_TYPE: | |
249 | if (!strncmp(step->name, bp->ut_line, LMAX)) | |
250 | return(YES); | |
251 | break; | |
252 | case USER_TYPE: | |
253 | if (!strncmp(step->name, bp->ut_name, NMAX)) | |
254 | return(YES); | |
255 | break; | |
ed99b1a7 | 256 | } |
0e3f8c72 | 257 | return(NO); |
c9af8668 BJ |
258 | } |
259 | ||
53f69172 KB |
260 | /* |
261 | * addarg -- | |
262 | * add an entry to a linked list of arguments | |
263 | */ | |
264 | static | |
265 | addarg(type, arg) | |
266 | int type; | |
267 | char *arg; | |
c9af8668 | 268 | { |
53f69172 | 269 | register ARG *cur; |
0e3f8c72 | 270 | char *malloc(); |
c9af8668 | 271 | |
53f69172 KB |
272 | if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) { |
273 | fputs("last: malloc failure.\n", stderr); | |
0e3f8c72 KB |
274 | exit(1); |
275 | } | |
53f69172 KB |
276 | cur->next = head; |
277 | cur->type = type; | |
278 | cur->name = arg; | |
279 | head = cur; | |
280 | } | |
281 | ||
282 | /* | |
283 | * hostconv -- | |
284 | * convert the hostname to search pattern; if the supplied host name | |
285 | * has a domain attached that is the same as the current domain, rip | |
286 | * off the domain suffix since that's what login(1) does. | |
287 | */ | |
288 | static | |
289 | hostconv(arg) | |
290 | char *arg; | |
291 | { | |
292 | static int first = 1; | |
293 | static char *hostdot, | |
294 | name[MAXHOSTNAMELEN]; | |
295 | char *argdot, *index(); | |
296 | ||
297 | if (!(argdot = index(arg, '.'))) | |
298 | return; | |
299 | if (first) { | |
300 | first = 0; | |
301 | if (gethostname(name, sizeof(name))) { | |
302 | perror("last: gethostname"); | |
303 | exit(1); | |
304 | } | |
305 | hostdot = index(name, '.'); | |
306 | } | |
307 | if (hostdot && !strcasecmp(hostdot, argdot)) | |
308 | *argdot = '\0'; | |
309 | } | |
310 | ||
311 | /* | |
312 | * onintr -- | |
313 | * on interrupt, we inform the user how far we've gotten | |
314 | */ | |
315 | static | |
316 | onintr(signo) | |
317 | int signo; | |
318 | { | |
319 | char *ct, *ctime(); | |
320 | ||
321 | ct = ctime(&buf[0].ut_time); | |
322 | printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11); | |
323 | if (signo == SIGINT) | |
324 | exit(1); | |
325 | (void)fflush(stdout); /* fix required for rsh */ | |
c9af8668 | 326 | } |