]> git.cameronkatri.com Git - apple_cmds.git/blob - adv_cmds/last/last.c
Create README.md
[apple_cmds.git] / adv_cmds / last / last.c
1 /*
2 * Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved
3 *
4 * Copyright (c) 1987, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * The NEXTSTEP Software License Agreement specifies the terms
8 * and conditions for redistribution.
9 *
10 * @(#)last.c 8.2 (Berkeley) 4/2/94
11 */
12
13
14 #ifndef lint
15 static const char copyright[] =
16 "@(#) Copyright (c) 1987, 1993, 1994\n\
17 The Regents of the University of California. All rights reserved.\n";
18 #endif /* not lint */
19
20 #include <sys/param.h>
21 #include <sys/stat.h>
22
23 #include <err.h>
24 #include <fcntl.h>
25 #include <paths.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <tzfile.h>
32 #include <unistd.h>
33 #include <utmpx.h>
34 #include <ctype.h>
35
36 #define NO 0 /* false/no */
37 #define YES 1 /* true/yes */
38
39 static const struct utmpx *prev; /* previous utmpx structure */
40
41 /* values from utmp.h, used for print formatting */
42 #define UT_NAMESIZE 8 /* old utmp.h value */
43 #define UT_LINESIZE 8
44 #define UT_HOSTSIZE 16
45
46 typedef struct arg {
47 const 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 id[_UTX_IDSIZE]; /* terminal id */
59 pid_t pid;
60 struct ttytab *next; /* linked list pointer */
61 } TTY;
62 TTY *ttylist; /* head of linked list */
63
64 static const char *crmsg; /* cause of last reboot */
65 static long currentout, /* current logout value */
66 maxrec; /* records to display */
67 time_t now;
68
69 void addarg __P((int, const char *));
70 TTY *addtty __P((const char *, pid_t));
71 void hostconv __P((const char *));
72 void onintr __P((int));
73 const char *ttyconv __P((const char *));
74 int want __P((struct utmpx *, int));
75 void wtmp __P((void));
76
77 int
78 main(argc, argv)
79 int argc;
80 char *argv[];
81 {
82 extern int optind;
83 extern char *optarg;
84 int ch;
85 char *p;
86 char *myname = *argv;
87
88 if ((p = strrchr(myname, '/')) != NULL)
89 myname = p + 1;
90 maxrec = -1;
91 while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != EOF)
92 switch (ch) {
93 case '0': case '1': case '2': case '3': case '4':
94 case '5': case '6': case '7': case '8': case '9':
95 /*
96 * kludge: last was originally designed to take
97 * a number after a dash.
98 */
99 if (maxrec == -1) {
100 p = argv[optind - 1];
101 if (p[0] == '-' && p[1] == ch && !p[2])
102 maxrec = atol(++p);
103 else
104 maxrec = atol(argv[optind] + 1);
105 if (!maxrec)
106 exit(0);
107 }
108 break;
109 case 'h':
110 hostconv(optarg);
111 addarg(HOST_TYPE, optarg);
112 break;
113 case 't':
114 addarg(TTY_TYPE, ttyconv(optarg));
115 break;
116 case 'f':
117 warnx("-f is unsupported");
118 break;
119 case '?':
120 default:
121 (void)fprintf(stderr,
122 "usage: last [-#] [-t tty] [-h hostname] [user ...]\n");
123 exit(1);
124 }
125
126 if (argc) {
127 setlinebuf(stdout);
128 for (argv += optind; *argv; ++argv) {
129 #define COMPATIBILITY
130 #ifdef COMPATIBILITY
131 /* code to allow "last p5" to work */
132 addarg(TTY_TYPE, ttyconv(*argv));
133 #endif
134 addarg(USER_TYPE, *argv);
135 }
136 }
137 wtmp();
138 exit(0);
139 }
140
141 /*
142 * wtmp --
143 * read through the wtmp file
144 */
145
146 void
147 wtmp()
148 {
149
150 struct utmpx *bp; /* current structure */
151 TTY *T; /* tty list entry */
152 long delta; /* time difference */
153 char *ct;
154
155 (void)time(&now);
156 (void)signal(SIGINT, onintr);
157 (void)signal(SIGQUIT, onintr);
158
159 setutxent_wtmp(0); /* zero means reverse chronological order */
160 while ((bp = getutxent_wtmp()) != NULL) {
161 prev = bp;
162 switch(bp->ut_type) {
163 case BOOT_TIME:
164 case SHUTDOWN_TIME:
165 /* everybody just logged out */
166 for (T = ttylist; T; T = T->next)
167 T->logout = -bp->ut_tv.tv_sec;
168 currentout = -bp->ut_tv.tv_sec;
169 crmsg = bp->ut_type == BOOT_TIME ? "crash" : "shutdown";
170 if (want(bp, NO)) {
171 ct = ctime(&bp->ut_tv.tv_sec);
172 printf("%-*s %-*s %-*.*s %10.10s %5.5s \n",
173 UT_NAMESIZE, bp->ut_type == BOOT_TIME ? "reboot" : "shutdown",
174 UT_LINESIZE, "~",
175 UT_HOSTSIZE, _UTX_HOSTSIZE,
176 bp->ut_host, ct, ct + 11);
177 if (maxrec != -1 && !--maxrec)
178 return;
179 }
180 continue;
181 case OLD_TIME:
182 case NEW_TIME:
183 if (want(bp, NO)) {
184 ct = ctime(&bp->ut_tv.tv_sec);
185 printf("%-*s %-*s %-*.*s %10.10s %5.5s \n",
186 UT_NAMESIZE, "date",
187 UT_LINESIZE, bp->ut_type == OLD_TIME ? "|" : "{",
188 UT_HOSTSIZE, _UTX_HOSTSIZE, bp->ut_host,
189 ct, ct + 11);
190 if (maxrec && !--maxrec)
191 return;
192 }
193 continue;
194 case USER_PROCESS:
195 case DEAD_PROCESS:
196 /* find associated tty */
197 for (T = ttylist;; T = T->next) {
198 if (!T) {
199 /* add new one */
200 T = addtty(bp->ut_id, bp->ut_pid);
201 break;
202 }
203 if (!memcmp(T->id, bp->ut_id, _UTX_IDSIZE) && T->pid == bp->ut_pid)
204 break;
205 }
206 if (bp->ut_type != DEAD_PROCESS && want(bp, YES)) {
207 ct = ctime(&bp->ut_tv.tv_sec);
208 printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ",
209 UT_NAMESIZE, _UTX_USERSIZE, bp->ut_user,
210 UT_LINESIZE, _UTX_LINESIZE, bp->ut_line,
211 UT_HOSTSIZE, _UTX_HOSTSIZE, bp->ut_host,
212 ct, ct + 11);
213 if (!T->logout)
214 puts(" still logged in");
215 else {
216 if (T->logout < 0) {
217 T->logout = -T->logout;
218 printf("- %s", crmsg);
219 }
220 else
221 printf("- %5.5s",
222 ctime(&T->logout)+11);
223 delta = T->logout - bp->ut_tv.tv_sec;
224 if (delta < SECSPERDAY)
225 printf(" (%5.5s)\n",
226 asctime(gmtime(&delta))+11);
227 else
228 printf(" (%ld+%5.5s)\n",
229 delta / SECSPERDAY,
230 asctime(gmtime(&delta))+11);
231 }
232 if (maxrec != -1 && !--maxrec)
233 return;
234 }
235 T->logout = bp->ut_tv.tv_sec;
236 continue;
237 }
238 }
239 endutxent_wtmp();
240 ct = ctime(prev ? &prev->ut_tv.tv_sec : &now);
241 printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
242 }
243
244 /*
245 * want --
246 * see if want this entry
247 */
248 int
249 want(bp, check)
250 struct utmpx *bp;
251 int check;
252 {
253 ARG *step;
254
255 if (!arglist)
256 return (YES);
257
258 for (step = arglist; step; step = step->next)
259 switch(step->type) {
260 case HOST_TYPE:
261 if (!strncasecmp(step->name, bp->ut_host, _UTX_HOSTSIZE))
262 return (YES);
263 break;
264 case TTY_TYPE:
265 {
266 char *line = bp->ut_line;
267 if (check) {
268 /*
269 * when uucp and ftp log in over a network, the entry in
270 * the utmpx file is the name plus their process id. See
271 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
272 */
273 if (!strncmp(line, "ftp", sizeof("ftp") - 1))
274 line = "ftp";
275 else if (!strncmp(line, "uucp", sizeof("uucp") - 1))
276 line = "uucp";
277 }
278 if (!strncmp(step->name, line, _UTX_LINESIZE))
279 return (YES);
280 break;
281 }
282 case USER_TYPE:
283 if (bp->ut_type == BOOT_TIME && !strncmp(step->name, "reboot", _UTX_USERSIZE))
284 return (YES);
285 if (bp->ut_type == SHUTDOWN_TIME && !strncmp(step->name, "shutdown", _UTX_USERSIZE))
286 return (YES);
287 if (!strncmp(step->name, bp->ut_user, _UTX_USERSIZE))
288 return (YES);
289 break;
290 }
291 return (NO);
292 }
293
294 /*
295 * addarg --
296 * add an entry to a linked list of arguments
297 */
298 void
299 addarg(type, arg)
300 int type;
301 const char *arg;
302 {
303 ARG *cur;
304
305 if (!(cur = (ARG *)malloc((u_int)sizeof(ARG))))
306 err(1, "malloc failure");
307 cur->next = arglist;
308 cur->type = type;
309 cur->name = arg;
310 arglist = cur;
311 }
312
313 /*
314 * addtty --
315 * add an entry to a linked list of ttys
316 */
317 TTY *
318 addtty(id, pid)
319 const char *id;
320 pid_t pid;
321 {
322 TTY *cur;
323
324 if (!(cur = (TTY *)malloc((u_int)sizeof(TTY))))
325 err(1, "malloc failure");
326 cur->next = ttylist;
327 cur->logout = currentout;
328 memmove(cur->id, id, _UTX_IDSIZE);
329 cur->pid = pid;
330 return (ttylist = cur);
331 }
332
333 /*
334 * hostconv --
335 * convert the hostname to search pattern; if the supplied host name
336 * has a domain attached that is the same as the current domain, rip
337 * off the domain suffix since that's what login(1) does.
338 */
339 void
340 hostconv(arg)
341 const char *arg;
342 {
343 static int first = 1;
344 static char *hostdot, name[MAXHOSTNAMELEN];
345 char *argdot;
346
347 if (!(argdot = strchr(arg, '.')))
348 return;
349 if (first) {
350 first = 0;
351 if (gethostname(name, sizeof(name)))
352 err(1, "gethostname");
353 hostdot = strchr(name, '.');
354 }
355 if (hostdot && !strcasecmp(hostdot, argdot))
356 *argdot = '\0';
357 }
358
359 /*
360 * ttyconv --
361 * convert tty to correct name.
362 */
363 const char *
364 ttyconv(arg)
365 const char *arg;
366 {
367 char *mval;
368 int kludge = 0;
369 int len = strlen(arg);
370
371 /*
372 * kludge -- we assume that most tty's end with
373 * a two character suffix.
374 */
375 if (len == 2)
376 kludge = 8; /* either 6 for "ttyxx" or 8 for "console"; use largest */
377 /*
378 * kludge -- we assume cloning ptys names are "ttys" followed by
379 * more than one digit
380 */
381 else if (len > 2 && *arg == 's') {
382 const char *cp = arg + 1;
383 while(*cp && isdigit(*cp))
384 cp++;
385 if (*cp == 0)
386 kludge = len + sizeof("tty");
387 }
388 if (kludge) {
389 if (!(mval = malloc(kludge)))
390 err(1, "malloc failure");
391 if (!strcmp(arg, "co"))
392 (void)strcpy(mval, "console");
393 else {
394 (void)strcpy(mval, "tty");
395 (void)strcpy(mval + sizeof("tty") - 1, arg);
396 }
397 return (mval);
398 }
399 if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
400 return (arg + sizeof(_PATH_DEV) - 1);
401 return (arg);
402 }
403
404 /*
405 * onintr --
406 * on interrupt, we inform the user how far we've gotten
407 */
408 void
409 onintr(signo)
410 int signo;
411 {
412 char *ct;
413
414 ct = ctime(prev ? &prev->ut_tv.tv_sec : &now);
415 printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
416 if (signo == SIGINT)
417 exit(1);
418 (void)fflush(stdout); /* fix required for rsh */
419 }