]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/last/last.c
This commit was generated by cvs2svn to compensate for changes in r76726,
[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 <langinfo.h>
52 #include <locale.h>
53 #include <paths.h>
54 #include <signal.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <time.h>
59 #include <unistd.h>
60 #include <utmp.h>
61 #include <sys/queue.h>
62
63 #define NO      0                               /* false/no */
64 #define YES     1                               /* true/yes */
65
66 static struct utmp      buf[1024];              /* utmp read buffer */
67
68 typedef struct arg {
69         char    *name;                          /* argument */
70 #define HOST_TYPE       -2
71 #define TTY_TYPE        -3
72 #define USER_TYPE       -4
73         int     type;                           /* type of arg */
74         struct arg      *next;                  /* linked list pointer */
75 } ARG;
76 ARG     *arglist;                               /* head of linked list */
77
78 LIST_HEAD(ttylisthead, ttytab) ttylist;
79
80 struct ttytab {
81         time_t  logout;                         /* log out time */
82         char    tty[UT_LINESIZE + 1];           /* terminal name */
83         LIST_ENTRY(ttytab) list;
84 };
85
86 static long     currentout,                     /* current logout value */
87                 maxrec;                         /* records to display */
88 static char     *file = _PATH_WTMP;             /* wtmp file */
89 static int      sflag = 0;                      /* show delta in seconds */
90 static int      width = 5;                      /* show seconds in delta */
91 static int      d_first;
92
93 void     addarg __P((int, char *));
94 void     hostconv __P((char *));
95 void     onintr __P((int));
96 char    *ttyconv __P((char *));
97 int      want __P((struct utmp *));
98 void     wtmp __P((void));
99
100 void
101 usage(void)
102 {
103         (void)fprintf(stderr,
104         "usage: last [-#] [-f file] [-h hostname] [-t tty] [-s|w] [user ...]\n");
105         exit(1);
106 }
107
108 int
109 main(argc, argv)
110         int argc;
111         char *argv[];
112 {
113         int ch;
114         char *p;
115
116         (void) setlocale(LC_TIME, "");
117         d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
118
119         maxrec = -1;
120         while ((ch = getopt(argc, argv, "0123456789f:h:st:w")) != -1)
121                 switch (ch) {
122                 case '0': case '1': case '2': case '3': case '4':
123                 case '5': case '6': case '7': case '8': case '9':
124                         /*
125                          * kludge: last was originally designed to take
126                          * a number after a dash.
127                          */
128                         if (maxrec == -1) {
129                                 p = argv[optind - 1];
130                                 if (p[0] == '-' && p[1] == ch && !p[2])
131                                         maxrec = atol(++p);
132                                 else
133                                         maxrec = atol(argv[optind] + 1);
134                                 if (!maxrec)
135                                         exit(0);
136                         }
137                         break;
138                 case 'f':
139                         file = optarg;
140                         break;
141                 case 'h':
142                         hostconv(optarg);
143                         addarg(HOST_TYPE, optarg);
144                         break;
145                 case 's':
146                         sflag++;        /* Show delta as seconds */
147                         break;
148                 case 't':
149                         addarg(TTY_TYPE, ttyconv(optarg));
150                         break;
151                 case 'w':
152                         width = 8;
153                         break;
154                 case '?':
155                 default:
156                         usage();
157                 }
158
159         if (sflag && width == 8) usage();
160
161         if (argc) {
162                 setlinebuf(stdout);
163                 for (argv += optind; *argv; ++argv) {
164 #define COMPATIBILITY
165 #ifdef  COMPATIBILITY
166                         /* code to allow "last p5" to work */
167                         addarg(TTY_TYPE, ttyconv(*argv));
168 #endif
169                         addarg(USER_TYPE, *argv);
170                 }
171         }
172         wtmp();
173         exit(0);
174 }
175
176 /*
177  * wtmp --
178  *      read through the wtmp file
179  */
180 void
181 wtmp()
182 {
183         struct utmp     *bp;                    /* current structure */
184         struct ttytab   *tt, *ttx;              /* ttylist entry */
185         struct stat     stb;                    /* stat of file for size */
186         long    bl;
187         time_t  delta;                          /* time difference */
188         int     bytes, wfd;
189         char    *crmsg;
190         char ct[80];
191         struct tm *tm;
192
193         LIST_INIT(&ttylist);
194
195         if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1)
196                 err(1, "%s", file);
197         bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
198
199         (void)time(&buf[0].ut_time);
200         (void)signal(SIGINT, onintr);
201         (void)signal(SIGQUIT, onintr);
202
203         while (--bl >= 0) {
204                 if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 ||
205                     (bytes = read(wfd, buf, sizeof(buf))) == -1)
206                         err(1, "%s", file);
207                 for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
208                         /*
209                          * if the terminal line is '~', the machine stopped.
210                          * see utmp(5) for more info.
211                          */
212                         if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
213                                 /* everybody just logged out */
214                                 for (tt = LIST_FIRST(&ttylist); tt;) {
215                                         LIST_REMOVE(tt, list);
216                                         ttx = tt;
217                                         tt = LIST_NEXT(tt, list);
218                                         free(ttx);
219                                 }
220                                 currentout = -bp->ut_time;
221                                 crmsg = strncmp(bp->ut_name, "shutdown",
222                                     UT_NAMESIZE) ? "crash" : "shutdown";
223                                 if (want(bp)) {
224                                         tm = localtime(&bp->ut_time);
225                                         (void) strftime(ct, sizeof(ct),
226                                                      d_first ? "%a %e %b %R" :
227                                                                "%a %b %e %R",
228                                                      tm);
229                                         printf("%-*.*s %-*.*s %-*.*s %s\n",
230                                             UT_NAMESIZE, UT_NAMESIZE,
231                                             bp->ut_name, UT_LINESIZE,
232                                             UT_LINESIZE, bp->ut_line,
233                                             UT_HOSTSIZE, UT_HOSTSIZE,
234                                             bp->ut_host, ct);
235                                         if (maxrec != -1 && !--maxrec)
236                                                 return;
237                                 }
238                                 continue;
239                         }
240                         /*
241                          * if the line is '{' or '|', date got set; see
242                          * utmp(5) for more info.
243                          */
244                         if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|')
245                             && !bp->ut_line[1]) {
246                                 if (want(bp)) {
247                                         tm = localtime(&bp->ut_time);
248                                         (void) strftime(ct, sizeof(ct),
249                                                      d_first ? "%a %e %b %R" :
250                                                                "%a %b %e %R",
251                                                      tm);
252                                         printf("%-*.*s %-*.*s %-*.*s %s\n",
253                                             UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
254                                             UT_LINESIZE, UT_LINESIZE, bp->ut_line,
255                                             UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
256                                             ct);
257                                         if (maxrec && !--maxrec)
258                                                 return;
259                                 }
260                                 continue;
261                         }
262                         if (bp->ut_name[0] == '\0' || want(bp)) {
263                                 /* find associated tty */
264                                 LIST_FOREACH(tt, &ttylist, list)
265                                         if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
266                                                 break;
267
268                                 if (tt == NULL) {
269                                         /* add new one */
270                                         tt = malloc(sizeof(struct ttytab));
271                                         if (tt == NULL)
272                                                 err(1, "malloc failure");
273                                         tt->logout = currentout;
274                                         strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
275                                         LIST_INSERT_HEAD(&ttylist, tt, list);
276                                 }
277
278                                 if (bp->ut_name[0]) {
279                                         /*
280                                          * when uucp and ftp log in over a network, the entry in
281                                          * the utmp file is the name plus their process id.  See
282                                          * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
283                                          */
284                                         if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
285                                                 bp->ut_line[3] = '\0';
286                                         else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
287                                                 bp->ut_line[4] = '\0';
288                                         tm = localtime(&bp->ut_time);
289                                         (void) strftime(ct, sizeof(ct),
290                                                      d_first ? "%a %e %b %R" :
291                                                                "%a %b %e %R",
292                                                      tm);
293                                         printf("%-*.*s %-*.*s %-*.*s %s ",
294                                             UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
295                                             UT_LINESIZE, UT_LINESIZE, bp->ut_line,
296                                             UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
297                                             ct);
298                                         if (!tt->logout)
299                                                 puts("  still logged in");
300                                         else {
301                                                 if (tt->logout < 0) {
302                                                         tt->logout = -tt->logout;
303                                                         printf("- %s", crmsg);
304                                                 }
305                                                 else {
306                                                         tm = localtime(&tt->logout);
307                                                         (void) strftime(ct, sizeof(ct), "%R", tm);
308                                                         printf("- %s", ct);
309                                                 }
310                                                 delta = tt->logout - bp->ut_time;
311                                                 if ( sflag ) {
312                                                         printf("  (%8lu)\n", 
313                                                                 delta);
314                                                 } else {
315                                                     tm = gmtime(&delta);
316                                                     (void) strftime(ct, sizeof(ct),
317                                                      width >= 8 ? "%T" : "%R",
318                                                      tm);
319                                                     if (delta < 86400)
320                                                         printf("  (%s)\n", ct);
321                                                     else
322                                                         printf(" (%ld+%s)\n",
323                                                             delta / 86400,  ct);
324                                                 }
325                                         }
326                                         LIST_REMOVE(tt, list);
327                                         free(tt);
328                                         if (maxrec != -1 && !--maxrec)
329                                                 return;
330                                 } else {
331                                         tt->logout = bp->ut_time;
332                                 }
333                         }
334                 }
335         }
336         tm = localtime(&buf[0].ut_time);
337         (void) strftime(ct, sizeof(ct), "\nwtmp begins %c\n", tm);
338         printf("%s", ct);
339 }
340
341 /*
342  * want --
343  *      see if want this entry
344  */
345 int
346 want(bp)
347         struct utmp *bp;
348 {
349         ARG *step;
350
351         if (!arglist)
352                 return (YES);
353
354         for (step = arglist; step; step = step->next)
355                 switch(step->type) {
356                 case HOST_TYPE:
357                         if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
358                                 return (YES);
359                         break;
360                 case TTY_TYPE:
361                         if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
362                                 return (YES);
363                         break;
364                 case USER_TYPE:
365                         if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
366                                 return (YES);
367                         break;
368         }
369         return (NO);
370 }
371
372 /*
373  * addarg --
374  *      add an entry to a linked list of arguments
375  */
376 void
377 addarg(type, arg)
378         int type;
379         char *arg;
380 {
381         ARG *cur;
382
383         if (!(cur = (ARG *)malloc((u_int)sizeof(ARG))))
384                 err(1, "malloc failure");
385         cur->next = arglist;
386         cur->type = type;
387         cur->name = arg;
388         arglist = cur;
389 }
390
391 /*
392  * hostconv --
393  *      convert the hostname to search pattern; if the supplied host name
394  *      has a domain attached that is the same as the current domain, rip
395  *      off the domain suffix since that's what login(1) does.
396  */
397 void
398 hostconv(arg)
399         char *arg;
400 {
401         static int first = 1;
402         static char *hostdot, name[MAXHOSTNAMELEN];
403         char *argdot;
404
405         if (!(argdot = strchr(arg, '.')))
406                 return;
407         if (first) {
408                 first = 0;
409                 if (gethostname(name, sizeof(name)))
410                         err(1, "gethostname");
411                 hostdot = strchr(name, '.');
412         }
413         if (hostdot && !strcasecmp(hostdot, argdot))
414                 *argdot = '\0';
415 }
416
417 /*
418  * ttyconv --
419  *      convert tty to correct name.
420  */
421 char *
422 ttyconv(arg)
423         char *arg;
424 {
425         char *mval;
426
427         /*
428          * kludge -- we assume that all tty's end with
429          * a two character suffix.
430          */
431         if (strlen(arg) == 2) {
432                 /* either 6 for "ttyxx" or 8 for "console" */
433                 if (!(mval = malloc((u_int)8)))
434                         err(1, "malloc failure");
435                 if (!strcmp(arg, "co"))
436                         (void)strcpy(mval, "console");
437                 else {
438                         (void)strcpy(mval, "tty");
439                         (void)strcpy(mval + 3, arg);
440                 }
441                 return (mval);
442         }
443         if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
444                 return (arg + 5);
445         return (arg);
446 }
447
448 /*
449  * onintr --
450  *      on interrupt, we inform the user how far we've gotten
451  */
452 void
453 onintr(signo)
454         int signo;
455 {
456         char ct[80];
457         struct tm *tm;
458
459         tm = localtime(&buf[0].ut_time);
460         (void) strftime(ct, sizeof(ct),
461                         d_first ? "%a %e %b %R" : "%a %b %e %R",
462                         tm);
463         printf("\ninterrupted %s\n", ct);
464         if (signo == SIGINT)
465                 exit(1);
466         (void)fflush(stdout);                   /* fix required for rsh */
467 }