]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/last/last.c
This commit was generated by cvs2svn to compensate for changes in r68349,
[FreeBSD/FreeBSD.git] / usr.bin / last / last.c
1 /*
2  * Copyright (c) 1987, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD$
34  */
35
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1987, 1993, 1994\n\
39         The Regents of the University of California.  All rights reserved.\n";
40 #endif /* not lint */
41
42 #ifndef lint
43 static char sccsid[] = "@(#)last.c      8.2 (Berkeley) 4/2/94";
44 #endif /* not lint */
45
46 #include <sys/param.h>
47 #include <sys/stat.h>
48
49 #include <err.h>
50 #include <fcntl.h>
51 #include <locale.h>
52 #include <paths.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <time.h>
58 #include <unistd.h>
59 #include <utmp.h>
60 #include <sys/queue.h>
61
62 #define NO      0                               /* false/no */
63 #define YES     1                               /* true/yes */
64
65 static struct utmp      buf[1024];              /* utmp read buffer */
66
67 typedef struct arg {
68         char    *name;                          /* argument */
69 #define HOST_TYPE       -2
70 #define TTY_TYPE        -3
71 #define USER_TYPE       -4
72         int     type;                           /* type of arg */
73         struct arg      *next;                  /* linked list pointer */
74 } ARG;
75 ARG     *arglist;                               /* head of linked list */
76
77 LIST_HEAD(ttylisthead, ttytab) ttylist;
78
79 struct ttytab {
80         time_t  logout;                         /* log out time */
81         char    tty[UT_LINESIZE + 1];           /* terminal name */
82         LIST_ENTRY(ttytab) list;
83 };
84
85 static long     currentout,                     /* current logout value */
86                 maxrec;                         /* records to display */
87 static char     *file = _PATH_WTMP;             /* wtmp file */
88 static int      sflag = 0;                      /* show delta in seconds */
89 static int      width = 5;                      /* show seconds in delta */
90
91 void     addarg __P((int, char *));
92 void     hostconv __P((char *));
93 void     onintr __P((int));
94 char    *ttyconv __P((char *));
95 int      want __P((struct utmp *));
96 void     wtmp __P((void));
97
98 void
99 usage(void)
100 {
101         (void)fprintf(stderr,
102         "usage: last [-#] [-f file] [-h hostname] [-t tty] [-s|w] [user ...]\n");
103         exit(1);
104 }
105
106 int
107 main(argc, argv)
108         int argc;
109         char *argv[];
110 {
111         int ch;
112         char *p;
113
114         (void) setlocale(LC_TIME, "");
115
116         maxrec = -1;
117         while ((ch = getopt(argc, argv, "0123456789f:h:st:w")) != -1)
118                 switch (ch) {
119                 case '0': case '1': case '2': case '3': case '4':
120                 case '5': case '6': case '7': case '8': case '9':
121                         /*
122                          * kludge: last was originally designed to take
123                          * a number after a dash.
124                          */
125                         if (maxrec == -1) {
126                                 p = argv[optind - 1];
127                                 if (p[0] == '-' && p[1] == ch && !p[2])
128                                         maxrec = atol(++p);
129                                 else
130                                         maxrec = atol(argv[optind] + 1);
131                                 if (!maxrec)
132                                         exit(0);
133                         }
134                         break;
135                 case 'f':
136                         file = optarg;
137                         break;
138                 case 'h':
139                         hostconv(optarg);
140                         addarg(HOST_TYPE, optarg);
141                         break;
142                 case 's':
143                         sflag++;        /* Show delta as seconds */
144                         break;
145                 case 't':
146                         addarg(TTY_TYPE, ttyconv(optarg));
147                         break;
148                 case 'w':
149                         width = 8;
150                         break;
151                 case '?':
152                 default:
153                         usage();
154                 }
155
156         if (sflag && width == 8) usage();
157
158         if (argc) {
159                 setlinebuf(stdout);
160                 for (argv += optind; *argv; ++argv) {
161 #define COMPATIBILITY
162 #ifdef  COMPATIBILITY
163                         /* code to allow "last p5" to work */
164                         addarg(TTY_TYPE, ttyconv(*argv));
165 #endif
166                         addarg(USER_TYPE, *argv);
167                 }
168         }
169         wtmp();
170         exit(0);
171 }
172
173 /*
174  * wtmp --
175  *      read through the wtmp file
176  */
177 void
178 wtmp()
179 {
180         struct utmp     *bp;                    /* current structure */
181         struct ttytab   *tt, *ttx;              /* ttylist entry */
182         struct stat     stb;                    /* stat of file for size */
183         long    bl;
184         time_t  delta;                          /* time difference */
185         int     bytes, wfd;
186         char    *crmsg;
187         char ct[80];
188         struct tm *tm;
189
190         LIST_INIT(&ttylist);
191
192         if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1)
193                 err(1, "%s", file);
194         bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
195
196         (void)time(&buf[0].ut_time);
197         (void)signal(SIGINT, onintr);
198         (void)signal(SIGQUIT, onintr);
199
200         while (--bl >= 0) {
201                 if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 ||
202                     (bytes = read(wfd, buf, sizeof(buf))) == -1)
203                         err(1, "%s", file);
204                 for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
205                         /*
206                          * if the terminal line is '~', the machine stopped.
207                          * see utmp(5) for more info.
208                          */
209                         if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
210                                 /* everybody just logged out */
211                                 for (tt = ttylist.lh_first; tt;) {
212                                         LIST_REMOVE(tt, list);
213                                         ttx = tt;
214                                         tt = tt->list.le_next;
215                                         free(ttx);
216                                 }
217                                 currentout = -bp->ut_time;
218                                 crmsg = strncmp(bp->ut_name, "shutdown",
219                                     UT_NAMESIZE) ? "crash" : "shutdown";
220                                 if (want(bp)) {
221                                         tm = localtime(&bp->ut_time);
222                                         (void) strftime(ct, sizeof(ct), "%c", tm);
223                                         printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n",
224                                             UT_NAMESIZE, UT_NAMESIZE,
225                                             bp->ut_name, UT_LINESIZE,
226                                             UT_LINESIZE, bp->ut_line,
227                                             UT_HOSTSIZE, UT_HOSTSIZE,
228                                             bp->ut_host, ct, ct + 11);
229                                         if (maxrec != -1 && !--maxrec)
230                                                 return;
231                                 }
232                                 continue;
233                         }
234                         /*
235                          * if the line is '{' or '|', date got set; see
236                          * utmp(5) for more info.
237                          */
238                         if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|')
239                             && !bp->ut_line[1]) {
240                                 if (want(bp)) {
241                                         tm = localtime(&bp->ut_time);
242                                         (void) strftime(ct, sizeof(ct), "%c", tm);
243                                         printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n",
244                                             UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
245                                             UT_LINESIZE, UT_LINESIZE, bp->ut_line,
246                                             UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
247                                             ct, ct + 11);
248                                         if (maxrec && !--maxrec)
249                                                 return;
250                                 }
251                                 continue;
252                         }
253                         if (bp->ut_name[0] == '\0' || want(bp)) {
254                                 /* find associated tty */
255                                 for (tt = ttylist.lh_first; ; tt = tt->list.le_next) {
256                                         if (tt == NULL) {
257                                                 /* add new one */
258                                                 tt = malloc(sizeof(struct ttytab));
259                                                 if (tt == NULL)
260                                                         err(1, "malloc failure");
261                                                 tt->logout = currentout;
262                                                 strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
263                                                 LIST_INSERT_HEAD(&ttylist, tt, list);
264                                                 break;
265                                         }
266                                         if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
267                                                 break;
268                                 }
269                                 if (bp->ut_name[0]) {
270                                         /*
271                                          * when uucp and ftp log in over a network, the entry in
272                                          * the utmp file is the name plus their process id.  See
273                                          * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
274                                          */
275                                         if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
276                                                 bp->ut_line[3] = '\0';
277                                         else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
278                                                 bp->ut_line[4] = '\0';
279                                         tm = localtime(&bp->ut_time);
280                                         (void) strftime(ct, sizeof(ct), "%c", tm);
281                                         printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ",
282                                             UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
283                                             UT_LINESIZE, UT_LINESIZE, bp->ut_line,
284                                             UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
285                                             ct, ct + 11);
286                                         if (!tt->logout)
287                                                 puts("  still logged in");
288                                         else {
289                                                 if (tt->logout < 0) {
290                                                         tt->logout = -tt->logout;
291                                                         printf("- %s", crmsg);
292                                                 }
293                                                 else {
294                                                         tm = localtime(&tt->logout);
295                                                         (void) strftime(ct, sizeof(ct), "%c", tm);
296                                                         printf("- %5.5s", ct + 11);
297                                                 }
298                                                 delta = tt->logout - bp->ut_time;
299                                                 if ( sflag ) {
300                                                         printf("  (%8lu)\n", 
301                                                                 delta);
302                                                 } else {
303                                                     tm = gmtime(&delta);
304                                                     (void) strftime(ct, sizeof(ct), "%c", tm);
305                                                     if (delta < 86400)
306                                                         printf("  (%*.*s)\n", width, width, ct + 11);
307                                                     else
308                                                         printf(" (%ld+%*.*s)\n",
309                                                             delta / 86400, width, width, ct + 11);
310                                                 }
311                                         }
312                                         LIST_REMOVE(tt, list);
313                                         free(tt);
314                                         if (maxrec != -1 && !--maxrec)
315                                                 return;
316                                 } else {
317                                         tt->logout = bp->ut_time;
318                                 }
319                         }
320                 }
321         }
322         tm = localtime(&buf[0].ut_time);
323         (void) strftime(ct, sizeof(ct), "\nwtmp begins %c\n", tm);
324         printf("%s", ct);
325 }
326
327 /*
328  * want --
329  *      see if want this entry
330  */
331 int
332 want(bp)
333         struct utmp *bp;
334 {
335         ARG *step;
336
337         if (!arglist)
338                 return (YES);
339
340         for (step = arglist; step; step = step->next)
341                 switch(step->type) {
342                 case HOST_TYPE:
343                         if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
344                                 return (YES);
345                         break;
346                 case TTY_TYPE:
347                         if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
348                                 return (YES);
349                         break;
350                 case USER_TYPE:
351                         if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
352                                 return (YES);
353                         break;
354         }
355         return (NO);
356 }
357
358 /*
359  * addarg --
360  *      add an entry to a linked list of arguments
361  */
362 void
363 addarg(type, arg)
364         int type;
365         char *arg;
366 {
367         ARG *cur;
368
369         if (!(cur = (ARG *)malloc((u_int)sizeof(ARG))))
370                 err(1, "malloc failure");
371         cur->next = arglist;
372         cur->type = type;
373         cur->name = arg;
374         arglist = cur;
375 }
376
377 /*
378  * hostconv --
379  *      convert the hostname to search pattern; if the supplied host name
380  *      has a domain attached that is the same as the current domain, rip
381  *      off the domain suffix since that's what login(1) does.
382  */
383 void
384 hostconv(arg)
385         char *arg;
386 {
387         static int first = 1;
388         static char *hostdot, name[MAXHOSTNAMELEN];
389         char *argdot;
390
391         if (!(argdot = strchr(arg, '.')))
392                 return;
393         if (first) {
394                 first = 0;
395                 if (gethostname(name, sizeof(name)))
396                         err(1, "gethostname");
397                 hostdot = strchr(name, '.');
398         }
399         if (hostdot && !strcasecmp(hostdot, argdot))
400                 *argdot = '\0';
401 }
402
403 /*
404  * ttyconv --
405  *      convert tty to correct name.
406  */
407 char *
408 ttyconv(arg)
409         char *arg;
410 {
411         char *mval;
412
413         /*
414          * kludge -- we assume that all tty's end with
415          * a two character suffix.
416          */
417         if (strlen(arg) == 2) {
418                 /* either 6 for "ttyxx" or 8 for "console" */
419                 if (!(mval = malloc((u_int)8)))
420                         err(1, "malloc failure");
421                 if (!strcmp(arg, "co"))
422                         (void)strcpy(mval, "console");
423                 else {
424                         (void)strcpy(mval, "tty");
425                         (void)strcpy(mval + 3, arg);
426                 }
427                 return (mval);
428         }
429         if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
430                 return (arg + 5);
431         return (arg);
432 }
433
434 /*
435  * onintr --
436  *      on interrupt, we inform the user how far we've gotten
437  */
438 void
439 onintr(signo)
440         int signo;
441 {
442         char ct[80];
443         struct tm *tm;
444
445         tm = localtime(&buf[0].ut_time);
446         (void) strftime(ct, sizeof(ct), "%c", tm);
447         printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
448         if (signo == SIGINT)
449                 exit(1);
450         (void)fflush(stdout);                   /* fix required for rsh */
451 }