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 |
141766c0 | 14 | static char sccsid[] = "@(#)last.c 5.6 (Berkeley) %G%"; |
22e155fc | 15 | #endif not lint |
37c640e2 | 16 | |
c9af8668 BJ |
17 | /* |
18 | * last | |
19 | */ | |
20 | #include <sys/types.h> | |
40d69e19 | 21 | #include <sys/stat.h> |
0e3f8c72 KB |
22 | #include <sys/file.h> |
23 | #include <signal.h> | |
24 | #include <time.h> | |
25 | #include <pwd.h> | |
c9af8668 | 26 | #include <utmp.h> |
0e3f8c72 KB |
27 | #include <strings.h> |
28 | #include <stdio.h> | |
29 | #include <ctype.h> | |
c9af8668 | 30 | |
0e3f8c72 KB |
31 | #define MAXTTYS 200 /* max ttys last can handle */ |
32 | #define SECDAY (24*60*60) /* seconds in a day */ | |
33 | #define NO 0 /* false/no */ | |
34 | #define YES 1 /* true/yes */ | |
c9af8668 | 35 | |
0e3f8c72 KB |
36 | static struct utmp buf[500]; /* utmp read buffer */ |
37 | #define HMAX sizeof(buf[0].ut_host) /* size of utmp host field */ | |
38 | #define LMAX sizeof(buf[0].ut_line) /* size of utmp tty field */ | |
39 | #define NMAX sizeof(buf[0].ut_name) /* size of utmp name field */ | |
c9af8668 | 40 | |
0e3f8c72 KB |
41 | #define lineq(a,b) (!strncmp(a,b,LMAX)) |
42 | #define nameq(a,b) (!strncmp(a,b,NMAX)) | |
43 | #define hosteq(a,b) (!strncmp(a,b,HMAX)) | |
c9af8668 | 44 | |
0e3f8c72 KB |
45 | typedef struct ttytab { |
46 | long logout; /* log out time */ | |
47 | char tty[LMAX + 1]; /* terminal name */ | |
48 | } TTYS; | |
c9af8668 | 49 | |
0e3f8c72 KB |
50 | static TTYS tab[MAXTTYS + 1]; /* tty table */ |
51 | static char **sargs; /* start of selections args */ | |
c9af8668 | 52 | |
0e3f8c72 KB |
53 | main(argc,argv) |
54 | int argc; | |
55 | char **argv; | |
c9af8668 | 56 | { |
0e3f8c72 KB |
57 | register struct utmp *bp; /* current structure */ |
58 | register TTYS *T; /* table entry */ | |
59 | register long maxrec = -1; /* records to display */ | |
60 | register int indx; /* array offsets */ | |
61 | struct stat stb; /* stat of file for size */ | |
62 | long delta, /* time difference */ | |
63 | atol(), lseek(), time(); | |
64 | int bl, /* reads to do */ | |
65 | bytes, /* bytes read */ | |
66 | wtmp, /* wtmp file descriptor */ | |
67 | onintr(); | |
68 | char *ct, /* ctime return */ | |
69 | *crmsg, /* crash message */ | |
70 | *file, /* user specified file */ | |
71 | *asctime(), *ctime(), *strspl(); | |
72 | ||
73 | file = "/usr/adm/wtmp"; | |
74 | for (--argc,sargs = argv = ++argv,indx = 0;indx < argc;++indx) { | |
75 | if (argv[indx][0] == '-' && isdigit(argv[indx][1])) { | |
76 | if ((maxrec = atol(argv[indx] + 1)) <= 0) { | |
77 | fputs("last: bad line count value.\n",stderr); | |
78 | exit(1); | |
79 | } | |
80 | ++sargs; | |
b50e7db3 DW |
81 | continue; |
82 | } | |
0e3f8c72 KB |
83 | if (!strncmp(argv[indx],"-f",2)) { |
84 | if (argv[indx][2]) { | |
85 | file = argv[indx] + 2; | |
86 | ++sargs; | |
87 | } | |
88 | else if (++indx == argc) { | |
89 | fputs("last: option requires an argument -- f\n",stderr); | |
90 | exit(1); | |
91 | } | |
92 | else { | |
93 | file = argv[indx]; | |
94 | sargs += 2; | |
95 | } | |
c9af8668 | 96 | continue; |
0e3f8c72 KB |
97 | } |
98 | if (strlen(argv[indx]) > 2) | |
ac7e4ccf | 99 | continue; |
0e3f8c72 | 100 | if (!strcmp(argv[indx],"~")) |
400ed241 | 101 | continue; |
0e3f8c72 | 102 | if (getpwnam(argv[indx])) |
c9af8668 | 103 | continue; |
0e3f8c72 | 104 | argv[indx] = strspl(argv[indx]); |
c9af8668 | 105 | } |
0e3f8c72 KB |
106 | |
107 | if ((wtmp = open(file,O_RDONLY,0)) < 0 || fstat(wtmp,&stb) == -1) { | |
108 | perror(file); | |
c9af8668 BJ |
109 | exit(1); |
110 | } | |
0e3f8c72 KB |
111 | bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); |
112 | ||
113 | time(&buf[0].ut_time); | |
114 | signal(SIGINT,onintr); | |
115 | signal(SIGQUIT,onintr); | |
116 | ||
117 | tab[MAXTTYS].logout = -1; /* end flag value */ | |
118 | while (--bl >= 0) { | |
119 | if (lseek(wtmp,(long)(bl * sizeof(buf)),L_SET) == -1 || (bytes = read(wtmp,(char *)buf,sizeof(buf))) == -1) { | |
120 | perror(file); | |
121 | exit(1); | |
122 | } | |
123 | for (bp = &buf[bytes / sizeof(buf[0]) - 1];bp >= buf;--bp) { | |
124 | if (lineq(bp->ut_line,"~")) { | |
125 | /* | |
126 | * if the name is empty and the terminal | |
127 | * line is '~', it's a shutdown of some | |
128 | * sort; see utmp(5) for more info. | |
129 | */ | |
130 | for (T = tab;T->logout != -1;++T) | |
131 | T->logout = -bp->ut_time; | |
132 | crmsg = nameq(bp->ut_name,"shutdown") ? "down " : "crash"; | |
133 | if (!bp->ut_name[0]) | |
134 | strcpy(bp->ut_name,"reboot"); | |
135 | if (want(bp,NO)) { | |
136 | ct = ctime(&bp->ut_time); | |
137 | 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); | |
138 | if (maxrec != -1 && !--maxrec) | |
139 | exit(0); | |
140 | } | |
141 | continue; | |
c9af8668 | 142 | } |
0e3f8c72 KB |
143 | for (T = tab;;) { |
144 | if (T->logout <= 0) { | |
145 | bcopy(bp->ut_line,T->tty,LMAX); | |
c9af8668 BJ |
146 | break; |
147 | } | |
0e3f8c72 | 148 | if (lineq(T->tty,bp->ut_line)) |
c9af8668 | 149 | break; |
0e3f8c72 KB |
150 | if ((++T)->logout == -1) { |
151 | fputs("last: too many terminals.\n",stderr); | |
152 | exit(1); | |
c9af8668 BJ |
153 | } |
154 | } | |
0e3f8c72 KB |
155 | if (bp->ut_name[0] && want(bp,YES)) { |
156 | ct = ctime(&bp->ut_time); | |
157 | 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); | |
158 | if (!T->logout) | |
159 | puts(" still logged in"); | |
c9af8668 | 160 | else { |
0e3f8c72 KB |
161 | if (T->logout < 0) { |
162 | T->logout = -T->logout; | |
163 | printf("- %s",crmsg); | |
164 | } | |
165 | else | |
166 | printf("- %5.5s",ctime(&T->logout)+11); | |
167 | delta = T->logout - bp->ut_time; | |
c9af8668 | 168 | if (delta < SECDAY) |
0e3f8c72 | 169 | printf(" (%5.5s)\n",asctime(gmtime(&delta))+11); |
c9af8668 | 170 | else |
0e3f8c72 | 171 | printf(" (%ld+%5.5s)\n",delta / SECDAY,asctime(gmtime(&delta))+11); |
c9af8668 | 172 | } |
0e3f8c72 | 173 | if (maxrec != -1 && !--maxrec) |
b50e7db3 | 174 | exit(0); |
c9af8668 | 175 | } |
0e3f8c72 | 176 | T->logout = bp->ut_time; |
c9af8668 BJ |
177 | } |
178 | } | |
179 | ct = ctime(&buf[0].ut_time); | |
0e3f8c72 | 180 | printf("\nwtmp begins %10.10s %5.5s \n",ct,ct + 11); |
c9af8668 BJ |
181 | exit(0); |
182 | } | |
183 | ||
184 | onintr(signo) | |
0e3f8c72 | 185 | int signo; |
c9af8668 | 186 | { |
0e3f8c72 KB |
187 | char *ct, |
188 | *ctime(); | |
c9af8668 | 189 | |
c9af8668 | 190 | ct = ctime(&buf[0].ut_time); |
0e3f8c72 KB |
191 | printf("\ninterrupted %10.10s %5.5s \n",ct,ct + 11); |
192 | fflush(stdout); /* fix required for rsh */ | |
c9af8668 BJ |
193 | if (signo == SIGINT) |
194 | exit(1); | |
195 | } | |
196 | ||
0e3f8c72 KB |
197 | want(bp,check) |
198 | register struct utmp *bp; | |
199 | int check; | |
c9af8668 | 200 | { |
ed99b1a7 KB |
201 | register char **indx, |
202 | *C; | |
203 | register int cnt; | |
0e3f8c72 | 204 | |
ed99b1a7 | 205 | if (check) |
0e3f8c72 KB |
206 | /* |
207 | * when uucp and ftp log in over a network, the entry in the | |
208 | * utmp file is the name plus their process id. See etc/ftpd.c | |
209 | * and usr.bin/uucp/uucpd.c for more information. | |
210 | */ | |
211 | if (!strncmp(bp->ut_line,"ftp",3)) | |
212 | bp->ut_line[3] = '\0'; | |
213 | else if (!strncmp(bp->ut_line,"uucp",4)) | |
214 | bp->ut_line[4] = '\0'; | |
0e3f8c72 KB |
215 | if (!*sargs) |
216 | return(YES); | |
ed99b1a7 KB |
217 | /* |
218 | * match hostname only, case independent; | |
219 | * leave internet numbers alone | |
220 | */ | |
221 | if (!isdigit(*bp->ut_host)) { | |
222 | for (C = bp->ut_host,cnt = HMAX;*C != '.' && cnt--;++C) | |
223 | if (isupper(*C)) | |
224 | *C = tolower(*C); | |
225 | if (*C == '.') | |
226 | *C = '\0'; | |
227 | else | |
228 | C = NULL; | |
229 | } | |
141766c0 KB |
230 | else |
231 | C = NULL; | |
0e3f8c72 | 232 | for (indx = sargs;*indx;++indx) |
ed99b1a7 KB |
233 | if (nameq(*indx,bp->ut_name) || lineq(*indx,bp->ut_line) || hosteq(*indx,bp->ut_host)) { |
234 | if (C) | |
235 | *C = '.'; | |
0e3f8c72 | 236 | return(YES); |
ed99b1a7 | 237 | } |
0e3f8c72 | 238 | return(NO); |
c9af8668 BJ |
239 | } |
240 | ||
241 | char * | |
0e3f8c72 KB |
242 | strspl(str) |
243 | char *str; | |
c9af8668 | 244 | { |
0e3f8c72 KB |
245 | register char *res; |
246 | char *malloc(); | |
c9af8668 | 247 | |
0e3f8c72 KB |
248 | if (!(res = malloc((u_int)(4 + strlen(str))))) { |
249 | fputs("last: malloc failure.\n",stderr); | |
250 | exit(1); | |
251 | } | |
252 | strcpy(res,"tty"); | |
253 | strcpy(res + 3,str); | |
254 | return(res); | |
c9af8668 | 255 | } |