📄 last.c
字号:
/* * last.c Re-implementation of the 'last' command, this time * for Linux. Yes I know there is BSD last, but I * just felt like writing this. No thanks :-). * Also, this version gives lots more info (especially with -x) * * Author: Miquel van Smoorenburg, miquels@cistron.nl * * Version: @(#)last 2.85 30-Jul-2004 miquels@cistron.nl * * This file is part of the sysvinit suite, * Copyright 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <sys/types.h>#include <sys/stat.h>#include <sys/fcntl.h>#include <time.h>#include <stdio.h>#include <ctype.h>#include <utmp.h>#include <errno.h>#include <malloc.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <getopt.h>#include <netinet/in.h>#include <netdb.h>#include <arpa/inet.h>#include "oldutmp.h"#ifndef SHUTDOWN_TIME# define SHUTDOWN_TIME 254#endifchar *Version = "@(#) last 2.85 31-Apr-2004 miquels";#define CHOP_DOMAIN 0 /* Define to chop off local domainname. */#define NEW_UTMP 1 /* Fancy & fast utmp read code. */#define UCHUNKSIZE 16384 /* How much we read at once. *//* Double linked list of struct utmp's */struct utmplist { struct utmp ut; struct utmplist *next; struct utmplist *prev;};struct utmplist *utmplist = NULL;/* Types of listing */#define R_CRASH 1 /* No logout record, system boot in between */#define R_DOWN 2 /* System brought down in decent way */#define R_NORMAL 3 /* Normal */#define R_NOW 4 /* Still logged in */#define R_REBOOT 5 /* Reboot record. */#define R_PHANTOM 6 /* No logout record but session is stale. */#define R_TIMECHANGE 7 /* NEW_TIME or OLD_TIME *//* Global variables */int maxrecs = 0; /* Maximum number of records to list. */int recsdone = 0; /* Number of records listed */int showhost = 1; /* Show hostname too? */int altlist = 0; /* Show hostname at the end. */int usedns = 0; /* Use DNS to lookup the hostname. */int useip = 0; /* Print IP address in number format */int oldfmt = 0; /* Use old libc5 format? */char **show = NULL; /* What do they want us to show */char *ufile; /* Filename of this file */time_t lastdate; /* Last date we've seen */char *progname; /* Name of this program */#if CHOP_DOMAINchar hostname[256]; /* For gethostbyname() */char *domainname; /* Our domainname. */#endif/* * Convert old utmp format to new. */void uconv(struct oldutmp *oldut, struct utmp *utn){ memset(utn, 0, sizeof(struct utmp)); utn->ut_type = oldut->ut_type; utn->ut_pid = oldut->ut_pid; utn->ut_time = oldut->ut_oldtime; utn->ut_addr = oldut->ut_oldaddr; strncpy(utn->ut_line, oldut->ut_line, OLD_LINESIZE); strncpy(utn->ut_user, oldut->ut_user, OLD_NAMESIZE); strncpy(utn->ut_host, oldut->ut_host, OLD_HOSTSIZE);}#if NEW_UTMP/* * Read one utmp entry, return in new format. * Automatically reposition file pointer. */int uread(FILE *fp, struct utmp *u, int *quit){ static int utsize; static char buf[UCHUNKSIZE]; char tmp[1024]; static off_t fpos; static int bpos; struct oldutmp uto; int r; off_t o; if (quit == NULL && u != NULL) { /* * Normal read. */ if (oldfmt) { r = fread(&uto, sizeof(uto), 1, fp); uconv(&uto, u); } else r = fread(u, sizeof(struct utmp), 1, fp); return r; } if (u == NULL) { /* * Initialize and position. */ utsize = oldfmt ? sizeof(uto) : sizeof(struct utmp); fseeko(fp, 0, SEEK_END); fpos = ftello(fp); if (fpos == 0) return 0; o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE; if (fseeko(fp, o, SEEK_SET) < 0) { fprintf(stderr, "%s: seek failed!\n", progname); return 0; } bpos = (int)(fpos - o); if (fread(buf, bpos, 1, fp) != 1) { fprintf(stderr, "%s: read failed!\n", progname); return 0; } fpos = o; return 1; } /* * Read one struct. From the buffer if possible. */ bpos -= utsize; if (bpos >= 0) { if (oldfmt) uconv((struct oldutmp *)(buf + bpos), u); else memcpy(u, buf + bpos, sizeof(struct utmp)); return 1; } /* * Oops we went "below" the buffer. We should be able to * seek back UCHUNKSIZE bytes. */ fpos -= UCHUNKSIZE; if (fpos < 0) return 0; /* * Copy whatever is left in the buffer. */ memcpy(tmp + (-bpos), buf, utsize + bpos); if (fseeko(fp, fpos, SEEK_SET) < 0) { perror("fseek"); return 0; } /* * Read another UCHUNKSIZE bytes. */ if (fread(buf, UCHUNKSIZE, 1, fp) != 1) { perror("fread"); return 0; } /* * The end of the UCHUNKSIZE byte buffer should be the first * few bytes of the current struct utmp. */ memcpy(tmp, buf + UCHUNKSIZE + bpos, -bpos); bpos += UCHUNKSIZE; if (oldfmt) uconv((struct oldutmp *)tmp, u); else memcpy(u, tmp, sizeof(struct utmp)); return 1;}#else /* NEW_UTMP *//* * Read one utmp entry, return in new format. * Automatically reposition file pointer. */int uread(FILE *fp, struct utmp *u, int *quit){ struct oldutmp uto; off_t r; if (u == NULL) { r = oldfmt ? sizeof(struct oldutmp) : sizeof(struct utmp); fseek(fp, -1 * r, SEEK_END); return 1; } if (!oldfmt) { r = fread(u, sizeof(struct utmp), 1, fp); if (r == 1) { if (fseeko(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0) if (quit) *quit = 1; } return r; } r = fread(&uto, sizeof(struct oldutmp), 1, fp); if (r == 1) { if (fseeko(fp, -2 * sizeof(struct oldutmp), SEEK_CUR) < 0) if (quit) *quit = 1; uconv(&uto, u); } return r;}#endif/* * Try to be smart about the location of the BTMP file */#ifndef BTMP_FILE#define BTMP_FILE getbtmp()char *getbtmp(){ static char btmp[128]; char *p; strcpy(btmp, WTMP_FILE); if ((p = strrchr(btmp, '/')) == NULL) p = btmp; else p++; *p = 0; strcat(btmp, "btmp"); return btmp;}#endif/* * Print a short date. */char *showdate(){ char *s = ctime(&lastdate); s[16] = 0; return s;}/* * SIGINT handler */void int_handler(){ printf("Interrupted %s\n", showdate()); exit(1);}/* * SIGQUIT handler */void quit_handler(){ printf("Interrupted %s\n", showdate()); signal(SIGQUIT, quit_handler);}/* * Get the basename of a filename */char *mybasename(char *s){ char *p; if ((p = strrchr(s, '/')) != NULL) p++; else p = s; return p;}/* * Lookup a host with DNS. */int dns_lookup(char *result, int size, int useip, int32_t *a){ struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr *sa; int salen, flags; unsigned int topnibble; int mapped = 0; flags = useip ? NI_NUMERICHOST : 0; /* * IPv4 or IPv6 ? We use 2 heuristics: * 1. Current IPv6 range uses 2000-3fff. Outside of * that is illegal and must be IPv4. * 2. If last 3 bytes are 0, must be IPv4 * 3. If IPv6 in IPv4, handle as IPv4 * * Ugly. */ if (a[0] == 0 && a[1] == 0 && a[2] == htonl (0xffff)) mapped = 1; topnibble = ntohl((unsigned int)a[0]) >> 28; if (topnibble < 2 || topnibble > 3 || mapped || (a[1] == 0 && a[2] == 0 && a[3] == 0)) { /* IPv4 */ sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr.s_addr = mapped ? a[3] : a[0]; sa = (struct sockaddr *)&sin; salen = sizeof(sin); } else { /* IPv6 */ memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = 0; memcpy(sin6.sin6_addr.s6_addr, a, 16); sa = (struct sockaddr *)&sin6; salen = sizeof(sin6); } return getnameinfo(sa, salen, result, size, NULL, 0, flags);}/* * Show one line of information on screen */int list(struct utmp *p, time_t t, int what){ time_t secs, tmp; char logintime[32]; char logouttime[32]; char length[32]; char final[128]; char utline[UT_LINESIZE+1]; char domain[256]; char *s, **walk; int mins, hours, days; int r, len; /* * uucp and ftp have special-type entries */ utline[0] = 0; strncat(utline, p->ut_line, UT_LINESIZE); if (strncmp(utline, "ftp", 3) == 0 && isdigit(utline[3])) utline[3] = 0; if (strncmp(utline, "uucp", 4) == 0 && isdigit(utline[4])) utline[4] = 0; /* * Is this something we wanna show? */ if (show) { for (walk = show; *walk; walk++) { if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 || strcmp(utline, *walk) == 0 || (strncmp(utline, "tty", 3) == 0 && strcmp(utline + 3, *walk) == 0)) break; } if (*walk == NULL) return 0; } /* * Calculate times */ tmp = (time_t)p->ut_time; strcpy(logintime, ctime(&tmp)); logintime[16] = 0; sprintf(logouttime, "- %s", ctime(&t) + 11); logouttime[7] = 0; secs = t - p->ut_time; mins = (secs / 60) % 60; hours = (secs / 3600) % 24; days = secs / 86400; if (days) sprintf(length, "(%d+%02d:%02d)", days, hours, mins); else sprintf(length, " (%02d:%02d)", hours, mins); switch(what) { case R_CRASH: sprintf(logouttime, "- crash"); break; case R_DOWN: sprintf(logouttime, "- down "); break; case R_NOW: length[0] = 0; sprintf(logouttime, " still"); sprintf(length, "logged in"); break; case R_PHANTOM: length[0] = 0; sprintf(logouttime, " gone"); sprintf(length, "- no logout"); break; case R_REBOOT: logouttime[0] = 0; /* Print machine uptime */ break; case R_TIMECHANGE: logouttime[0] = 0; length[0] = 0; break; case R_NORMAL: break; } /* * Look up host with DNS if needed. */ r = -1; if (usedns || useip) r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6); if (r < 0) { len = UT_HOSTSIZE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -