📄 last.c
字号:
if (len >= sizeof(domain)) len = sizeof(domain) - 1; domain[0] = 0; strncat(domain, p->ut_host, len); } if (showhost) {#if CHOP_DOMAIN /* * See if this is in our domain. */ if (!usedns && (s = strchr(p->ut_host, '.')) != NULL && strcmp(s + 1, domainname) == 0) *s = 0;#endif if (!altlist) { snprintf(final, sizeof(final), "%-8.8s %-12.12s %-16.16s " "%-16.16s %-7.7s %-12.12s\n", p->ut_name, utline, domain, logintime, logouttime, length); } else { snprintf(final, sizeof(final), "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s %s\n", p->ut_name, utline, logintime, logouttime, length, domain); } } else snprintf(final, sizeof(final), "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s\n", p->ut_name, utline, logintime, logouttime, length); /* * Print out "final" string safely. */ for (s = final; *s; s++) { if (*s == '\n' || (*s >= 32 && (unsigned char)*s <= 126)) putchar(*s); else putchar('*'); } recsdone++; if (maxrecs && recsdone >= maxrecs) return 1; return 0;}/* * show usage */void usage(char *s){ fprintf(stderr, "Usage: %s [-num | -n num] [-f file] " "[-t YYYYMMDDHHMMSS] " "[-R] [-x] [-o] [username..] [tty..]\n", s); exit(1);}time_t parsetm(char *ts){ struct tm u = {0}, origu; time_t tm; if (sscanf(ts, "%4d%2d%2d%2d%2d%2d", &u.tm_year, &u.tm_mon, &u.tm_mday, &u.tm_hour, &u.tm_min, &u.tm_sec) != 6) return (time_t)-1; u.tm_year -= 1900; u.tm_mon -= 1; u.tm_isdst = -1; origu = u; if ((tm = mktime(&u)) == (time_t)-1) return tm; /* * Unfortunately mktime() is much more forgiving than * it should be. For example, it'll gladly accept * "30" as a valid month number. This behavior is by * design, but we don't like it, so we want to detect * it and complain. */ if (u.tm_year != origu.tm_year || u.tm_mon != origu.tm_mon || u.tm_mday != origu.tm_mday || u.tm_hour != origu.tm_hour || u.tm_min != origu.tm_min || u.tm_sec != origu.tm_sec) return (time_t)-1; return tm;}int main(int argc, char **argv){ FILE *fp; /* Filepointer of wtmp file */ struct utmp ut; /* Current utmp entry */ struct utmp oldut; /* Old utmp entry to check for duplicates */ struct utmplist *p; /* Pointer into utmplist */ struct utmplist *next;/* Pointer into utmplist */ time_t lastboot = 0; /* Last boottime */ time_t lastrch = 0; /* Last run level change */ time_t lastdown; /* Last downtime */ time_t begintime; /* When wtmp begins */ int whydown = 0; /* Why we went down: crash or shutdown */ int c, x; /* Scratch */ struct stat st; /* To stat the [uw]tmp file */ int quit = 0; /* Flag */ int down = 0; /* Down flag */ int lastb = 0; /* Is this 'lastb' ? */ int extended = 0; /* Lots of info. */ char *altufile = NULL;/* Alternate wtmp */ time_t until = 0; /* at what time to stop parsing the file */ progname = mybasename(argv[0]); /* Process the arguments. */ while((c = getopt(argc, argv, "f:n:Rxadiot:0123456789")) != EOF) switch(c) { case 'R': showhost = 0; break; case 'x': extended = 1; break; case 'n': maxrecs = atoi(optarg); break; case 'o': oldfmt = 1; break; case 'f': if((altufile = malloc(strlen(optarg)+1)) == NULL) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } strcpy(altufile, optarg); break; case 'd': usedns++; break; case 'i': useip++; break; case 'a': altlist++; break; case 't': if ((until = parsetm(optarg)) == (time_t)-1) { fprintf(stderr, "%s: Invalid time value \"%s\"\n", progname, optarg); usage(progname); } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': maxrecs = 10*maxrecs + c - '0'; break; default: usage(progname); break; } if (optind < argc) show = argv + optind; /* * Which file do we want to read? */ if (strcmp(progname, "lastb") == 0) { ufile = BTMP_FILE; lastb = 1; } else ufile = WTMP_FILE; if (altufile) ufile = altufile; time(&lastdown); lastrch = lastdown; /* * Fill in 'lastdate' */ lastdate = lastdown;#if CHOP_DOMAIN /* * Find out domainname. * * This doesn't work on modern systems, where only a DNS * lookup of the result from hostname() will get you the domainname. * Remember that domainname() is the NIS domainname, not DNS. * So basically this whole piece of code is bullshit. */ hostname[0] = 0; (void) gethostname(hostname, sizeof(hostname)); if ((domainname = strchr(hostname, '.')) != NULL) domainname++; if (domainname == NULL || domainname[0] == 0) { hostname[0] = 0; (void) getdomainname(hostname, sizeof(hostname)); hostname[sizeof(hostname) - 1] = 0; domainname = hostname; if (strcmp(domainname, "(none)") == 0 || domainname[0] == 0) domainname = NULL; }#endif /* * Install signal handlers */ signal(SIGINT, int_handler); signal(SIGQUIT, quit_handler); /* * Open the utmp file */ if ((fp = fopen(ufile, "r")) == NULL) { x = errno; fprintf(stderr, "%s: %s: %s\n", progname, ufile, strerror(errno)); if (altufile == NULL && x == ENOENT) fprintf(stderr, "Perhaps this file was removed by the " "operator to prevent logging %s info.\n", progname); exit(1); } /* * Optimize the buffer size. */ setvbuf(fp, NULL, _IOFBF, UCHUNKSIZE); /* * Read first structure to capture the time field */ if (uread(fp, &ut, NULL) == 1) begintime = ut.ut_time; else { fstat(fileno(fp), &st); begintime = st.st_ctime; quit = 1; } /* * Go to end of file minus one structure * and/or initialize utmp reading code. */ uread(fp, NULL, NULL); /* * Read struct after struct backwards from the file. */ while(!quit) { if (uread(fp, &ut, &quit) != 1) break; if (until && until < ut.ut_time) continue; if (memcmp(&ut, &oldut, sizeof(struct utmp)) == 0) continue; memcpy(&oldut, &ut, sizeof(struct utmp)); lastdate = ut.ut_time; if (lastb) { quit = list(&ut, ut.ut_time, R_NORMAL); continue; } /* * Set ut_type to the correct type. */ if (strncmp(ut.ut_line, "~", 1) == 0) { if (strncmp(ut.ut_user, "shutdown", 8) == 0) ut.ut_type = SHUTDOWN_TIME; else if (strncmp(ut.ut_user, "reboot", 6) == 0) ut.ut_type = BOOT_TIME; else if (strncmp(ut.ut_user, "runlevel", 7) == 0) ut.ut_type = RUN_LVL; }#if 1 /*def COMPAT*/ /* * For stupid old applications that don't fill in * ut_type correctly. */ else { if (ut.ut_type != DEAD_PROCESS && ut.ut_name[0] && ut.ut_line[0] && strcmp(ut.ut_name, "LOGIN") != 0) ut.ut_type = USER_PROCESS; /* * Even worse, applications that write ghost * entries: ut_type set to USER_PROCESS but * empty ut_name... */ if (ut.ut_name[0] == 0) ut.ut_type = DEAD_PROCESS; /* * Clock changes. */ if (strcmp(ut.ut_name, "date") == 0) { if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME; if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME; } }#endif switch (ut.ut_type) { case SHUTDOWN_TIME: if (extended) { strcpy(ut.ut_line, "system down"); quit = list(&ut, lastdown, R_NORMAL); } lastdown = lastrch = ut.ut_time; down = 1; break; case OLD_TIME: case NEW_TIME: if (extended) { strcpy(ut.ut_line, ut.ut_type == NEW_TIME ? "new time" : "old time"); quit = list(&ut, lastdown, R_TIMECHANGE); } break; case BOOT_TIME: strcpy(ut.ut_line, "system boot"); quit = list(&ut, lastdown, R_REBOOT); down = 1; break; case RUN_LVL: x = ut.ut_pid & 255; if (extended) { sprintf(ut.ut_line, "(to lvl %c)", x); quit = list(&ut, lastrch, R_NORMAL); } if (x == '0' || x == '6') { lastdown = ut.ut_time; down = 1; ut.ut_type = SHUTDOWN_TIME; } lastrch = ut.ut_time; break; case USER_PROCESS: /* * This was a login - show the first matching * logout record and delete all records with * the same ut_line. */ c = 0; for (p = utmplist; p; p = next) { next = p->next; if (strncmp(p->ut.ut_line, ut.ut_line, UT_LINESIZE) == 0) { /* Show it */ if (c == 0) { quit = list(&ut, p->ut.ut_time, R_NORMAL); c = 1; } if (p->next) p->next->prev = p->prev; if (p->prev) p->prev->next = p->next; else utmplist = p->next; free(p); } } /* * Not found? Then crashed, down, still * logged in, or missing logout record. */ if (c == 0) { if (lastboot == 0) { c = R_NOW; /* Is process still alive? */ if (ut.ut_pid > 0 && kill(ut.ut_pid, 0) != 0 && errno == ESRCH) c = R_PHANTOM; } else c = whydown; quit = list(&ut, lastboot, c); } /* FALLTHRU */ case DEAD_PROCESS: /* * Just store the data if it is * interesting enough. */ if (ut.ut_line[0] == 0) break; if ((p = malloc(sizeof(struct utmplist))) == NULL) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } memcpy(&p->ut, &ut, sizeof(struct utmp)); p->next = utmplist; p->prev = NULL; if (utmplist) utmplist->prev = p; utmplist = p; break; } /* * If we saw a shutdown/reboot record we can remove * the entire current utmplist. */ if (down) { lastboot = ut.ut_time; whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH; for (p = utmplist; p; p = next) { next = p->next; free(p); } utmplist = NULL; down = 0; } } printf("\n%s begins %s", mybasename(ufile), ctime(&begintime)); fclose(fp); /* * Should we free memory here? Nah. This is not NT :) */ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -