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