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