Tizen 2.1 base
[platform/upstream/sysvinit.git] / src / last.c
1 /*
2  * last.c       Re-implementation of the 'last' command, this time
3  *              for Linux. Yes I know there is BSD last, but I
4  *              just felt like writing this. No thanks :-).
5  *              Also, this version gives lots more info (especially with -x)
6  *
7  * Author:      Miquel van Smoorenburg, miquels@cistron.nl
8  *
9  * Version:     @(#)last  2.85  30-Jul-2004  miquels@cistron.nl
10  *
11  *              This file is part of the sysvinit suite,
12  *              Copyright 1991-2004 Miquel van Smoorenburg.
13  *
14  *              This program is free software; you can redistribute it and/or
15  *              modify it under the terms of the GNU General Public License
16  *              as published by the Free Software Foundation; either version
17  *              2 of the License, or (at your option) any later version.
18  */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/fcntl.h>
23 #include <time.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <utmp.h>
27 #include <errno.h>
28 #include <malloc.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <getopt.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <arpa/inet.h>
37 #include "oldutmp.h"
38
39 #ifndef SHUTDOWN_TIME
40 #  define SHUTDOWN_TIME 254
41 #endif
42
43 char *Version = "@(#) last 2.85 31-Apr-2004 miquels";
44
45 #define CHOP_DOMAIN     0       /* Define to chop off local domainname. */
46 #define NEW_UTMP        1       /* Fancy & fast utmp read code. */
47 #define UCHUNKSIZE      16384   /* How much we read at once. */
48
49 /* Double linked list of struct utmp's */
50 struct utmplist {
51   struct utmp ut;
52   struct utmplist *next;
53   struct utmplist *prev;
54 };
55 struct utmplist *utmplist = NULL;
56
57 /* Types of listing */
58 #define R_CRASH         1 /* No logout record, system boot in between */
59 #define R_DOWN          2 /* System brought down in decent way */
60 #define R_NORMAL        3 /* Normal */
61 #define R_NOW           4 /* Still logged in */
62 #define R_REBOOT        5 /* Reboot record. */
63 #define R_PHANTOM       6 /* No logout record but session is stale. */
64 #define R_TIMECHANGE    7 /* NEW_TIME or OLD_TIME */
65
66 /* Global variables */
67 int maxrecs = 0;        /* Maximum number of records to list. */
68 int recsdone = 0;       /* Number of records listed */
69 int showhost = 1;       /* Show hostname too? */
70 int altlist = 0;        /* Show hostname at the end. */
71 int usedns = 0;         /* Use DNS to lookup the hostname. */
72 int useip = 0;          /* Print IP address in number format */
73 int fulltime = 0;       /* Print full dates and times */
74 int oldfmt = 0;         /* Use old libc5 format? */
75 char **show = NULL;     /* What do they want us to show */
76 char *ufile;            /* Filename of this file */
77 time_t lastdate;        /* Last date we've seen */
78 char *progname;         /* Name of this program */
79 #if CHOP_DOMAIN
80 char hostname[256];     /* For gethostbyname() */
81 char *domainname;       /* Our domainname. */
82 #endif
83
84 /*
85  *      Convert old utmp format to new.
86  */
87 void uconv(struct oldutmp *oldut, struct utmp *utn)
88 {
89         memset(utn, 0, sizeof(struct utmp));
90         utn->ut_type = oldut->ut_type;
91         utn->ut_pid  = oldut->ut_pid;
92         utn->ut_time = oldut->ut_oldtime;
93         utn->ut_addr = oldut->ut_oldaddr;
94         strncpy(utn->ut_line, oldut->ut_line, OLD_LINESIZE);
95         strncpy(utn->ut_user, oldut->ut_user, OLD_NAMESIZE);
96         strncpy(utn->ut_host, oldut->ut_host, OLD_HOSTSIZE);
97 }
98
99 #if NEW_UTMP
100 /*
101  *      Read one utmp entry, return in new format.
102  *      Automatically reposition file pointer.
103  */
104 int uread(FILE *fp, struct utmp *u, int *quit)
105 {
106         static int utsize;
107         static char buf[UCHUNKSIZE];
108         char tmp[1024];
109         static off_t fpos;
110         static int bpos;
111         struct oldutmp uto;
112         int r;
113         off_t o;
114
115         if (quit == NULL && u != NULL) {
116                 /*
117                  *      Normal read.
118                  */
119                 if (oldfmt) {
120                         r = fread(&uto, sizeof(uto), 1, fp);
121                         uconv(&uto, u);
122                 } else
123                         r = fread(u, sizeof(struct utmp), 1, fp);
124                 return r;
125         }
126
127         if (u == NULL) {
128                 /*
129                  *      Initialize and position.
130                  */
131                 utsize = oldfmt ? sizeof(uto) : sizeof(struct utmp);
132                 fseeko(fp, 0, SEEK_END);
133                 fpos = ftello(fp);
134                 if (fpos == 0)
135                         return 0;
136                 o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE;
137                 if (fseeko(fp, o, SEEK_SET) < 0) {
138                         fprintf(stderr, "%s: seek failed!\n", progname);
139                         return 0;
140                 }
141                 bpos = (int)(fpos - o);
142                 if (fread(buf, bpos, 1, fp) != 1) {
143                         fprintf(stderr, "%s: read failed!\n", progname);
144                         return 0;
145                 }
146                 fpos = o;
147                 return 1;
148         }
149
150         /*
151          *      Read one struct. From the buffer if possible.
152          */
153         bpos -= utsize;
154         if (bpos >= 0) {
155                 if (oldfmt)
156                         uconv((struct oldutmp *)(buf + bpos), u);
157                 else
158                         memcpy(u, buf + bpos, sizeof(struct utmp));
159                 return 1;
160         }
161
162         /*
163          *      Oops we went "below" the buffer. We should be able to
164          *      seek back UCHUNKSIZE bytes.
165          */
166         fpos -= UCHUNKSIZE;
167         if (fpos < 0)
168                 return 0;
169
170         /*
171          *      Copy whatever is left in the buffer.
172          */
173         memcpy(tmp + (-bpos), buf, utsize + bpos);
174         if (fseeko(fp, fpos, SEEK_SET) < 0) {
175                 perror("fseek");
176                 return 0;
177         }
178
179         /*
180          *      Read another UCHUNKSIZE bytes.
181          */
182         if (fread(buf, UCHUNKSIZE, 1, fp) != 1) {
183                 perror("fread");
184                 return 0;
185         }
186
187         /*
188          *      The end of the UCHUNKSIZE byte buffer should be the first
189          *      few bytes of the current struct utmp.
190          */
191         memcpy(tmp, buf + UCHUNKSIZE + bpos, -bpos);
192         bpos += UCHUNKSIZE;
193
194         if (oldfmt)
195                 uconv((struct oldutmp *)tmp, u);
196         else
197                 memcpy(u, tmp, sizeof(struct utmp));
198
199         return 1;
200 }
201
202 #else /* NEW_UTMP */
203
204 /*
205  *      Read one utmp entry, return in new format.
206  *      Automatically reposition file pointer.
207  */
208 int uread(FILE *fp, struct utmp *u, int *quit)
209 {
210         struct oldutmp uto;
211         off_t r;
212
213         if (u == NULL) {
214                 r = oldfmt ? sizeof(struct oldutmp) : sizeof(struct utmp);
215                 fseek(fp, -1 * r, SEEK_END);
216                 return 1;
217         }
218
219         if (!oldfmt) {
220                 r = fread(u, sizeof(struct utmp), 1, fp);
221                 if (r == 1) {
222                         if (fseeko(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0)
223                                 if (quit) *quit = 1;
224                 }
225                 return r;
226         }
227         r = fread(&uto, sizeof(struct oldutmp), 1, fp);
228         if (r == 1) {
229                 if (fseeko(fp, -2 * sizeof(struct oldutmp), SEEK_CUR) < 0)
230                         if (quit) *quit = 1;
231                 uconv(&uto, u);
232         }
233
234         return r;
235 }
236 #endif
237
238 /*
239  *      Try to be smart about the location of the BTMP file
240  */
241 #ifndef BTMP_FILE
242 #define BTMP_FILE getbtmp()
243 char *getbtmp()
244 {
245         static char btmp[128];
246         char *p;
247
248         strcpy(btmp, WTMP_FILE);
249         if ((p = strrchr(btmp, '/')) == NULL)
250                 p = btmp;
251         else
252                 p++;
253         *p = 0;
254         strcat(btmp, "btmp");
255         return btmp;
256 }
257 #endif
258
259 /*
260  *      Print a short date.
261  */
262 char *showdate()
263 {
264         char *s = ctime(&lastdate);
265         s[16] = 0;
266         return s;
267 }
268
269 /*
270  *      SIGINT handler
271  */
272 void int_handler()
273 {
274         printf("Interrupted %s\n", showdate());
275         exit(1);
276 }
277
278 /*
279  *      SIGQUIT handler
280  */
281 void quit_handler()
282 {
283         printf("Interrupted %s\n", showdate());
284         signal(SIGQUIT, quit_handler);
285 }
286
287 /*
288  *      Get the basename of a filename
289  */
290 char *mybasename(char *s)
291 {
292         char *p;
293
294         if ((p = strrchr(s, '/')) != NULL)
295                 p++;
296         else
297                 p = s;
298         return p;
299 }
300
301 /*
302  *      Lookup a host with DNS.
303  */
304 int dns_lookup(char *result, int size, int useip, int32_t *a)
305 {
306         struct sockaddr_in      sin;
307         struct sockaddr_in6     sin6;
308         struct sockaddr         *sa;
309         int                     salen, flags;
310         unsigned int            topnibble;
311         unsigned int            azero = 0, sitelocal = 0;
312         int                     mapped = 0;
313
314         flags = useip ? NI_NUMERICHOST : 0;
315
316         /*
317          *      IPv4 or IPv6 ? We use 2 heuristics:
318          *      1. Current IPv6 range uses 2000-3fff or fec0-feff.
319          *         Outside of that is illegal and must be IPv4.
320          *      2. If last 3 bytes are 0, must be IPv4
321          *      3. If IPv6 in IPv4, handle as IPv4
322          *
323          *      Ugly.
324          */
325         if (a[0] == 0 && a[1] == 0 && a[2] == htonl (0xffff))
326                 mapped = 1;
327         topnibble = ntohl((unsigned int)a[0]) >> 28;
328
329         azero = ntohl((unsigned int)a[0]) >> 16;
330         sitelocal = (azero >= 0xfec0 && azero <= 0xfeff) ? 1 : 0;
331         
332         if (((topnibble < 2 || topnibble > 3) && (!sitelocal)) || mapped ||
333             (a[1] == 0 && a[2] == 0 && a[3] == 0)) {
334                 /* IPv4 */
335                 sin.sin_family = AF_INET;
336                 sin.sin_port = 0;
337                 sin.sin_addr.s_addr = mapped ? a[3] : a[0];
338                 sa = (struct sockaddr *)&sin;
339                 salen = sizeof(sin);
340         } else {
341                 /* IPv6 */
342                 memset(&sin6, 0, sizeof(sin6));
343                 sin6.sin6_family = AF_INET6;
344                 sin6.sin6_port = 0;
345                 memcpy(sin6.sin6_addr.s6_addr, a, 16);
346                 sa = (struct sockaddr *)&sin6;
347                 salen = sizeof(sin6);
348         }
349
350         return getnameinfo(sa, salen, result, size, NULL, 0, flags);
351 }
352
353 /*
354  *      Show one line of information on screen
355  */
356 int list(struct utmp *p, time_t t, int what)
357 {
358         time_t          secs, tmp;
359         char            logintime[32];
360         char            logouttime[32];
361         char            length[32];
362         char            final[128];
363         char            utline[UT_LINESIZE+1];
364         char            domain[256];
365         char            *s, **walk;
366         int             mins, hours, days;
367         int             r, len;
368
369         /*
370          *      uucp and ftp have special-type entries
371          */
372         utline[0] = 0;
373         strncat(utline, p->ut_line, UT_LINESIZE);
374         if (strncmp(utline, "ftp", 3) == 0 && isdigit(utline[3]))
375                 utline[3] = 0;
376         if (strncmp(utline, "uucp", 4) == 0 && isdigit(utline[4]))
377                 utline[4] = 0;
378
379         /*
380          *      Is this something we wanna show?
381          */
382         if (show) {
383                 for (walk = show; *walk; walk++) {
384                         if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 ||
385                             strcmp(utline, *walk) == 0 ||
386                             (strncmp(utline, "tty", 3) == 0 &&
387                              strcmp(utline + 3, *walk) == 0)) break;
388                 }
389                 if (*walk == NULL) return 0;
390         }
391
392         /*
393          *      Calculate times
394          */
395         tmp = (time_t)p->ut_time;
396         strcpy(logintime, ctime(&tmp));
397         if (fulltime)
398                 sprintf(logouttime, "- %s", ctime(&t));
399         else {
400                 logintime[16] = 0;
401                 sprintf(logouttime, "- %s", ctime(&t) + 11);
402                 logouttime[7] = 0;
403         }
404         secs = t - p->ut_time;
405         mins  = (secs / 60) % 60;
406         hours = (secs / 3600) % 24;
407         days  = secs / 86400;
408         if (days)
409                 sprintf(length, "(%d+%02d:%02d)", days, hours, mins);
410         else
411                 sprintf(length, " (%02d:%02d)", hours, mins);
412
413         switch(what) {
414                 case R_CRASH:
415                         sprintf(logouttime, "- crash");
416                         break;
417                 case R_DOWN:
418                         sprintf(logouttime, "- down ");
419                         break;
420                 case R_NOW:
421                         length[0] = 0;
422                         if (fulltime)
423                                 sprintf(logouttime, "  still logged in");
424                         else {
425                                 sprintf(logouttime, "  still");
426                                 sprintf(length, "logged in");
427                         }
428                         break;
429                 case R_PHANTOM:
430                         length[0] = 0;
431                         if (fulltime)
432                                 sprintf(logouttime, "  gone - no logout");
433                         else {
434                                 sprintf(logouttime, "   gone");
435                                 sprintf(length, "- no logout");
436                         }
437                         break;
438                 case R_REBOOT:
439                         break;
440                 case R_TIMECHANGE:
441                         logouttime[0] = 0;
442                         length[0] = 0;
443                         break;
444                 case R_NORMAL:
445                         break;
446         }
447
448         /*
449          *      Look up host with DNS if needed.
450          */
451         r = -1;
452         if (usedns || useip)
453                 r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6);
454         if (r < 0) {
455                 len = UT_HOSTSIZE;
456                 if (len >= sizeof(domain)) len = sizeof(domain) - 1;
457                 domain[0] = 0;
458                 strncat(domain, p->ut_host, len);
459         }
460
461         if (showhost) {
462 #if CHOP_DOMAIN
463                 /*
464                  *      See if this is in our domain.
465                  */
466                 if (!usedns && (s = strchr(p->ut_host, '.')) != NULL &&
467                      strcmp(s + 1, domainname) == 0) *s = 0;
468 #endif
469                 if (!altlist) {
470                         snprintf(final, sizeof(final),
471                                 fulltime ?
472                                 "%-8.8s %-12.12s %-16.16s %-24.24s %-26.26s %-12.12s\n" :
473                                 "%-8.8s %-12.12s %-16.16s %-16.16s %-7.7s %-12.12s\n",
474                                 p->ut_name, utline,
475                                 domain, logintime, logouttime, length);
476                 } else {
477                         snprintf(final, sizeof(final), 
478                                 fulltime ?
479                                 "%-8.8s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" :
480                                 "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s %s\n",
481                                 p->ut_name, utline,
482                                 logintime, logouttime, length, domain);
483                 }
484         } else
485                 snprintf(final, sizeof(final),
486                         fulltime ?
487                         "%-8.8s %-12.12s %-24.24s %-26.26s %-12.12s\n" :
488                         "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s\n",
489                         p->ut_name, utline,
490                         logintime, logouttime, length);
491
492         /*
493          *      Print out "final" string safely.
494          */
495         for (s = final; *s; s++) {
496                 if (*s == '\n' || (*s >= 32 && (unsigned char)*s <= 126))
497                         putchar(*s);
498                 else
499                         putchar('*');
500         }
501
502         recsdone++;
503         if (maxrecs && recsdone >= maxrecs)
504                 return 1;
505
506         return 0;
507 }
508
509
510 /*
511  *      show usage
512  */
513 void usage(char *s)
514 {
515         fprintf(stderr, "Usage: %s [-num | -n num] [-f file] "
516                         "[-t YYYYMMDDHHMMSS] "
517                         "[-R] [-adioxF] [username..] [tty..]\n", s);
518         exit(1);
519 }
520
521 time_t parsetm(char *ts)
522 {
523         struct tm       u, origu;
524         time_t          tm;
525
526         memset(&tm, 0, sizeof(tm));
527
528         if (sscanf(ts, "%4d%2d%2d%2d%2d%2d", &u.tm_year,
529             &u.tm_mon, &u.tm_mday, &u.tm_hour, &u.tm_min,
530             &u.tm_sec) != 6)
531                 return (time_t)-1;
532
533         u.tm_year -= 1900;
534         u.tm_mon -= 1;
535         u.tm_isdst = -1;
536
537         origu = u;
538
539         if ((tm = mktime(&u)) == (time_t)-1)
540                 return tm;
541
542         /*
543          *      Unfortunately mktime() is much more forgiving than
544          *      it should be.  For example, it'll gladly accept
545          *      "30" as a valid month number.  This behavior is by
546          *      design, but we don't like it, so we want to detect
547          *      it and complain.
548          */
549         if (u.tm_year != origu.tm_year ||
550             u.tm_mon != origu.tm_mon ||
551             u.tm_mday != origu.tm_mday ||
552             u.tm_hour != origu.tm_hour ||
553             u.tm_min != origu.tm_min ||
554             u.tm_sec != origu.tm_sec)
555                 return (time_t)-1;
556
557         return tm;
558 }
559
560 int main(int argc, char **argv)
561 {
562   FILE *fp;             /* Filepointer of wtmp file */
563
564   struct utmp ut;       /* Current utmp entry */
565   struct utmp oldut;    /* Old utmp entry to check for duplicates */
566   struct utmplist *p;   /* Pointer into utmplist */
567   struct utmplist *next;/* Pointer into utmplist */
568
569   time_t lastboot = 0;  /* Last boottime */
570   time_t lastrch = 0;   /* Last run level change */
571   time_t lastdown;      /* Last downtime */
572   time_t begintime;     /* When wtmp begins */
573   int whydown = 0;      /* Why we went down: crash or shutdown */
574
575   int c, x;             /* Scratch */
576   struct stat st;       /* To stat the [uw]tmp file */
577   int quit = 0;         /* Flag */
578   int down = 0;         /* Down flag */
579   int lastb = 0;        /* Is this 'lastb' ? */
580   int extended = 0;     /* Lots of info. */
581   char *altufile = NULL;/* Alternate wtmp */
582
583   time_t until = 0;     /* at what time to stop parsing the file */
584
585   progname = mybasename(argv[0]);
586
587   /* Process the arguments. */
588   while((c = getopt(argc, argv, "f:n:RxadFiot:0123456789")) != EOF)
589     switch(c) {
590         case 'R':
591                 showhost = 0;
592                 break;
593         case 'x':
594                 extended = 1;
595                 break;
596         case 'n':
597                 maxrecs = atoi(optarg);
598                 break;
599         case 'o':
600                 oldfmt = 1;
601                 break;
602         case 'f':
603                 if((altufile = malloc(strlen(optarg)+1)) == NULL) {
604                         fprintf(stderr, "%s: out of memory\n",
605                                 progname);
606                         exit(1);
607                 }
608                 strcpy(altufile, optarg);
609                 break;
610         case 'd':
611                 usedns++;
612                 break;
613         case 'i':
614                 useip++;
615                 break;
616         case 'a':
617                 altlist++;
618                 break;
619         case 'F':
620                 fulltime++;
621                 break;
622         case 't':
623                 if ((until = parsetm(optarg)) == (time_t)-1) {
624                         fprintf(stderr, "%s: Invalid time value \"%s\"\n",
625                                 progname, optarg);
626                         usage(progname);
627                 }
628                 break;
629         case '0': case '1': case '2': case '3': case '4':
630         case '5': case '6': case '7': case '8': case '9':
631                 maxrecs = 10*maxrecs + c - '0';
632                 break;
633         default:
634                 usage(progname);
635                 break;
636     }
637   if (optind < argc) show = argv + optind;
638
639   /*
640    *    Which file do we want to read?
641    */
642   if (strcmp(progname, "lastb") == 0) {
643         ufile = BTMP_FILE;
644         lastb = 1;
645   } else
646         ufile = WTMP_FILE;
647   if (altufile)
648         ufile = altufile;
649   time(&lastdown);
650   lastrch = lastdown;
651
652   /*
653    *    Fill in 'lastdate'
654    */
655   lastdate = lastdown;
656
657 #if CHOP_DOMAIN
658   /*
659    *    Find out domainname.
660    *
661    *    This doesn't work on modern systems, where only a DNS
662    *    lookup of the result from hostname() will get you the domainname.
663    *    Remember that domainname() is the NIS domainname, not DNS.
664    *    So basically this whole piece of code is bullshit.
665    */
666   hostname[0] = 0;
667   (void) gethostname(hostname, sizeof(hostname));
668   if ((domainname = strchr(hostname, '.')) != NULL) domainname++;
669   if (domainname == NULL || domainname[0] == 0) {
670         hostname[0] = 0;
671         (void) getdomainname(hostname, sizeof(hostname));
672         hostname[sizeof(hostname) - 1] = 0;
673         domainname = hostname;
674         if (strcmp(domainname, "(none)") == 0 || domainname[0] == 0)
675                 domainname = NULL;
676   }
677 #endif
678
679   /*
680    *    Install signal handlers
681    */
682   signal(SIGINT, int_handler);
683   signal(SIGQUIT, quit_handler);
684
685   /*
686    *    Open the utmp file
687    */
688   if ((fp = fopen(ufile, "r")) == NULL) {
689         x = errno;
690         fprintf(stderr, "%s: %s: %s\n", progname, ufile, strerror(errno));
691         if (altufile == NULL && x == ENOENT)
692                 fprintf(stderr, "Perhaps this file was removed by the "
693                         "operator to prevent logging %s info.\n", progname);
694         exit(1);
695   }
696
697   /*
698    *    Optimize the buffer size.
699    */
700   setvbuf(fp, NULL, _IOFBF, UCHUNKSIZE);
701
702   /*
703    *    Read first structure to capture the time field
704    */
705   if (uread(fp, &ut, NULL) == 1)
706         begintime = ut.ut_time;
707   else {
708         fstat(fileno(fp), &st);
709         begintime = st.st_ctime;
710         quit = 1;
711   }
712
713   /*
714    *    Go to end of file minus one structure
715    *    and/or initialize utmp reading code.
716    */
717   uread(fp, NULL, NULL);
718
719   /*
720    *    Read struct after struct backwards from the file.
721    */
722   while(!quit) {
723
724         if (uread(fp, &ut, &quit) != 1)
725                 break;
726
727         if (until && until < ut.ut_time)
728                 continue;
729
730         if (memcmp(&ut, &oldut, sizeof(struct utmp)) == 0) continue;
731         memcpy(&oldut, &ut, sizeof(struct utmp));
732         lastdate = ut.ut_time;
733
734         if (lastb) {
735                 quit = list(&ut, ut.ut_time, R_NORMAL);
736                 continue;
737         }
738
739         /*
740          *      Set ut_type to the correct type.
741          */
742         if (strncmp(ut.ut_line, "~", 1) == 0) {
743                 if (strncmp(ut.ut_user, "shutdown", 8) == 0)
744                         ut.ut_type = SHUTDOWN_TIME;
745                 else if (strncmp(ut.ut_user, "reboot", 6) == 0)
746                         ut.ut_type = BOOT_TIME;
747                 else if (strncmp(ut.ut_user, "runlevel", 8) == 0)
748                         ut.ut_type = RUN_LVL;
749         }
750 #if 1 /*def COMPAT*/
751         /*
752          *      For stupid old applications that don't fill in
753          *      ut_type correctly.
754          */
755         else {
756                 if (ut.ut_type != DEAD_PROCESS &&
757                     ut.ut_name[0] && ut.ut_line[0] &&
758                     strcmp(ut.ut_name, "LOGIN") != 0)
759                         ut.ut_type = USER_PROCESS;
760                 /*
761                  *      Even worse, applications that write ghost
762                  *      entries: ut_type set to USER_PROCESS but
763                  *      empty ut_name...
764                  */
765                 if (ut.ut_name[0] == 0)
766                         ut.ut_type = DEAD_PROCESS;
767
768                 /*
769                  *      Clock changes.
770                  */
771                 if (strcmp(ut.ut_name, "date") == 0) {
772                         if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
773                         if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
774                 }
775         }
776 #endif
777
778         switch (ut.ut_type) {
779                 case SHUTDOWN_TIME:
780                         if (extended) {
781                                 strcpy(ut.ut_line, "system down");
782                                 quit = list(&ut, lastboot, R_NORMAL);
783                         }
784                         lastdown = lastrch = ut.ut_time;
785                         down = 1;
786                         break;
787                 case OLD_TIME:
788                 case NEW_TIME:
789                         if (extended) {
790                                 strcpy(ut.ut_line,
791                                 ut.ut_type == NEW_TIME ? "new time" :
792                                         "old time");
793                                 quit = list(&ut, lastdown, R_TIMECHANGE);
794                         }
795                         break;
796                 case BOOT_TIME:
797                         strcpy(ut.ut_line, "system boot");
798                         quit = list(&ut, lastdown, R_REBOOT);
799                         lastboot = ut.ut_time;
800                         down = 1;
801                         break;
802                 case RUN_LVL:
803                         x = ut.ut_pid & 255;
804                         if (extended) {
805                                 sprintf(ut.ut_line, "(to lvl %c)", x);
806                                 quit = list(&ut, lastrch, R_NORMAL);
807                         }
808                         if (x == '0' || x == '6') {
809                                 lastdown = ut.ut_time;
810                                 down = 1;
811                                 ut.ut_type = SHUTDOWN_TIME;
812                         }
813                         lastrch = ut.ut_time;
814                         break;
815
816                 case USER_PROCESS:
817                         /*
818                          *      This was a login - show the first matching
819                          *      logout record and delete all records with
820                          *      the same ut_line.
821                          */
822                         c = 0;
823                         for (p = utmplist; p; p = next) {
824                                 next = p->next;
825                                 if (strncmp(p->ut.ut_line, ut.ut_line,
826                                     UT_LINESIZE) == 0) {
827                                         /* Show it */
828                                         if (c == 0) {
829                                                 quit = list(&ut, p->ut.ut_time,
830                                                         R_NORMAL);
831                                                 c = 1;
832                                         }
833                                         if (p->next) p->next->prev = p->prev;
834                                         if (p->prev)
835                                                 p->prev->next = p->next;
836                                         else
837                                                 utmplist = p->next;
838                                         free(p);
839                                 }
840                         }
841                         /*
842                          *      Not found? Then crashed, down, still
843                          *      logged in, or missing logout record.
844                          */
845                         if (c == 0) {
846                                 if (lastboot == 0) {
847                                         c = R_NOW;
848                                         /* Is process still alive? */
849                                         if (ut.ut_pid > 0 &&
850                                             kill(ut.ut_pid, 0) != 0 &&
851                                             errno == ESRCH)
852                                                 c = R_PHANTOM;
853                                 } else
854                                         c = whydown;
855                                 quit = list(&ut, lastboot, c);
856                         }
857                         /* FALLTHRU */
858
859                 case DEAD_PROCESS:
860                         /*
861                          *      Just store the data if it is
862                          *      interesting enough.
863                          */
864                         if (ut.ut_line[0] == 0)
865                                 break;
866                         if ((p = malloc(sizeof(struct utmplist))) == NULL) {
867                                 fprintf(stderr, "%s: out of memory\n",
868                                         progname);
869                                 exit(1);
870                         }
871                         memcpy(&p->ut, &ut, sizeof(struct utmp));
872                         p->next  = utmplist;
873                         p->prev  = NULL;
874                         if (utmplist) utmplist->prev = p;
875                         utmplist = p;
876                         break;
877
878         }
879         /*
880          *      If we saw a shutdown/reboot record we can remove
881          *      the entire current utmplist.
882          */
883         if (down) {
884                 lastboot = ut.ut_time;
885                 whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH;
886                 for (p = utmplist; p; p = next) {
887                         next = p->next;
888                         free(p);
889                 }
890                 utmplist = NULL;
891                 down = 0;
892         }
893   }
894   printf("\n%s begins %s", mybasename(ufile), ctime(&begintime));
895
896   fclose(fp);
897
898   /*
899    *    Should we free memory here? Nah. This is not NT :)
900    */
901   return 0;
902 }