483 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* last - display login history			Author: Terrence W. Holm */
 | 
						|
 | 
						|
/* last-	Display the user log-in history.
 | 
						|
 *		Last(1) searches backwards through the file of log-in
 | 
						|
 *		records (/usr/adm/wtmp), displaying the length of
 | 
						|
 *		log-in sessions as requested by the options:
 | 
						|
 *
 | 
						|
 * Usage:	last [-r] [-count] [-f file] [name] [tty] ...
 | 
						|
 *
 | 
						|
 *		-r	Search backwards only until the last reboot
 | 
						|
 *			record.
 | 
						|
 *
 | 
						|
 *		-count	Only print out <count> records. Last(1) stops
 | 
						|
 *			when either -r or -count is satisfied, or at
 | 
						|
 *			the end of the file if neither is given.
 | 
						|
 *
 | 
						|
 *		-f file	Use "file" instead of "/usr/adm/wtmp".
 | 
						|
 *
 | 
						|
 *		name	Print records for the user "name".
 | 
						|
 *
 | 
						|
 *		tty	Print records for the terminal "tty". Actually,
 | 
						|
 *			a list of names may be given and all records
 | 
						|
 *			that match either the user or tty name are
 | 
						|
 *			printed. If no names are given then all records
 | 
						|
 *			are displayed.
 | 
						|
 *
 | 
						|
 *		A sigquit (^\) causes last(1) to display how far it
 | 
						|
 *		has gone back in the log-in record file, it then
 | 
						|
 *		continues. This is used to check on the progress of
 | 
						|
 *		long running searches. A sigint will stop last(1).
 | 
						|
 *
 | 
						|
 * Author:	Terrence W. Holm	May 1988
 | 
						|
 *
 | 
						|
 * Revision:
 | 
						|
 *		Fred van Kempen, October 1989
 | 
						|
 *		 -Adapted to MSS.
 | 
						|
 *		 -Adapted to new utmp database.
 | 
						|
 *
 | 
						|
 *		Fred van Kempen, December 1989
 | 
						|
 *		 -Adapted to POSIX (MINIX 1.5)
 | 
						|
 *
 | 
						|
 *		Fred van Kempen, January 1990
 | 
						|
 *		 -Final edit for 1.5
 | 
						|
 *
 | 
						|
 *		Philip Homburg, March 1992
 | 
						|
 *		 -Include host in output
 | 
						|
 *
 | 
						|
 *		Kees J. Bot, July 1997
 | 
						|
 *		 -Approximate system uptime from last reboot record
 | 
						|
 */
 | 
						|
#include <sys/types.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <string.h>
 | 
						|
#include <utmp.h>
 | 
						|
#include <time.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
#include <minix/paths.h>
 | 
						|
 | 
						|
#define  FALSE	0
 | 
						|
#define  TRUE	1
 | 
						|
#define  RLOGIN	1
 | 
						|
 | 
						|
#define  BUFFER_SIZE     4096	/* Room for wtmp records */
 | 
						|
#define  MAX_WTMP_COUNT  ( BUFFER_SIZE / sizeof(struct utmp) )
 | 
						|
 | 
						|
#define  min( a, b )     ( (a < b) ? a : b )
 | 
						|
#define  max( a, b )	 ( (a > b) ? a : b )
 | 
						|
 | 
						|
 | 
						|
typedef struct logout {		/* A logout time record */
 | 
						|
  char line[12];		/* The terminal name */
 | 
						|
  long time;			/* The logout time */
 | 
						|
  struct logout *next;		/* Next in linked list */
 | 
						|
} logout;
 | 
						|
 | 
						|
 | 
						|
static char *Version = "@(#) LAST 1.7 (10/24/92)";
 | 
						|
 | 
						|
 | 
						|
/* command-line option flags */
 | 
						|
char boot_limit = FALSE;	/* stop on latest reboot */
 | 
						|
char count_limit = FALSE;	/* stop after print_count */
 | 
						|
char tell_uptime = FALSE;	/* tell uptime since last reboot */
 | 
						|
int print_count;
 | 
						|
char *prog;			/* name of this program */
 | 
						|
int arg_count;			/* used to select specific */
 | 
						|
char **args;			/* users and ttys */
 | 
						|
 | 
						|
/* global variables */
 | 
						|
long boot_time = 0;		/* Zero means no reboot yet */
 | 
						|
char *boot_down;		/* "crash" or "down " flag */
 | 
						|
logout *first_link = NULL;	/* List of logout times */
 | 
						|
int interrupt = FALSE;		/* If sigint or sigquit occurs */
 | 
						|
 | 
						|
_PROTOTYPE(int main, (int argc, char **argv));
 | 
						|
_PROTOTYPE(void Sigint, (int sig));
 | 
						|
_PROTOTYPE(void Sigquit, (int sig));
 | 
						|
_PROTOTYPE(void usage, (void));
 | 
						|
_PROTOTYPE(void Process, (struct utmp *wtmp));
 | 
						|
_PROTOTYPE(int Print_Record, (struct utmp *wtmp));
 | 
						|
_PROTOTYPE(void Print_Duration, (long from, long to));
 | 
						|
_PROTOTYPE(void Print_Uptime, (void));
 | 
						|
_PROTOTYPE(void Record_Logout_Time, (struct utmp *wtmp));
 | 
						|
 | 
						|
/* Sigint() and Sigquit() Flag occurrence of an interrupt. */
 | 
						|
void Sigint(sig)
 | 
						|
int sig;
 | 
						|
{
 | 
						|
  interrupt = SIGINT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Sigquit(sig)
 | 
						|
int sig;
 | 
						|
{
 | 
						|
  interrupt = SIGQUIT;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void usage()
 | 
						|
{
 | 
						|
  fprintf(stderr,
 | 
						|
	"Usage: last [-r] [-u] [-count] [-f file] [name] [tty] ...\n");
 | 
						|
  exit(-1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* A log-in record format file contains four types of records.
 | 
						|
 *
 | 
						|
 *  [1] generated on a system reboot:
 | 
						|
 *
 | 
						|
 *	line="~", name="reboot", host="", time=date()
 | 
						|
 *
 | 
						|
 *
 | 
						|
 *  [2] generated after a shutdown:
 | 
						|
 *
 | 
						|
 *	line="~", name="shutdown", host="", time=date()
 | 
						|
 *
 | 
						|
 *
 | 
						|
 *  [3] generated on a successful login(1)
 | 
						|
 *
 | 
						|
 *	line=ttyname(), name=cuserid(), host=, time=date()
 | 
						|
 *
 | 
						|
 *
 | 
						|
 *  [4] generated by init(8) on a logout
 | 
						|
 *
 | 
						|
 *	line=ttyname(), name="", host="", time=date()
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * Note: This version of last(1) does not recognize the '|' and '}' time
 | 
						|
 *	 change records. Last(1) pairs up line login's and logout's to
 | 
						|
 *	 generate four types of output lines:
 | 
						|
 *
 | 
						|
 *	  [1] a system reboot or shutdown
 | 
						|
 *
 | 
						|
 *	   reboot    ~       Mon May 16 14:16
 | 
						|
 *	   shutdown  ~       Mon May 16 14:15
 | 
						|
 *
 | 
						|
 *	  [2] a login with a matching logout
 | 
						|
 *
 | 
						|
 *	   edwin     tty1    Thu May 26 20:05 - 20:32  (00:27)
 | 
						|
 *
 | 
						|
 *	  [3] a login followed by a reboot or shutdown
 | 
						|
 *
 | 
						|
 *	   root      tty0    Mon May 16 13:57 - crash  (00:19)
 | 
						|
 *	   root      tty1    Mon May 16 13:45 - down   (00:30)
 | 
						|
 *
 | 
						|
 *	  [4] a login not followed by a logout or reboot
 | 
						|
 *
 | 
						|
 *	   terry     tty0    Thu May 26 21:19   still logged in
 | 
						|
 */
 | 
						|
void Process(wtmp)
 | 
						|
struct utmp *wtmp;
 | 
						|
{
 | 
						|
  logout *link;
 | 
						|
  logout *next_link;
 | 
						|
  char is_reboot;
 | 
						|
 | 
						|
  /* suppress the job number on an "ftp" line */
 | 
						|
  if (!strncmp(wtmp->ut_line, "ftp", (size_t)3)) strncpy(wtmp->ut_line, "ftp", (size_t)8);
 | 
						|
 | 
						|
  if (!strcmp(wtmp->ut_line, "~")) {
 | 
						|
	/* A reboot or shutdown record  */
 | 
						|
	if (boot_limit) exit(0);
 | 
						|
 | 
						|
	if (Print_Record(wtmp)) putchar('\n');
 | 
						|
	boot_time = wtmp->ut_time;
 | 
						|
 | 
						|
	is_reboot = !strcmp(wtmp->ut_name, "reboot");
 | 
						|
	if (is_reboot)
 | 
						|
		boot_down = "crash";
 | 
						|
	else
 | 
						|
		boot_down = "down ";
 | 
						|
 | 
						|
	if (tell_uptime) {
 | 
						|
		if (!is_reboot) {
 | 
						|
			fprintf(stderr,
 | 
						|
		"%s: no reboot record added to wtmp file on system boot!\n",
 | 
						|
				prog);
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
		Print_Uptime();
 | 
						|
		exit(0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* remove any logout records */
 | 
						|
	for (link = first_link; link != NULL; link = next_link) {
 | 
						|
		next_link = link->next;
 | 
						|
		free(link);
 | 
						|
	}
 | 
						|
	first_link = NULL;
 | 
						|
  } else if (wtmp->ut_name[0] == '\0') {
 | 
						|
	/* A logout record */
 | 
						|
	Record_Logout_Time(wtmp);
 | 
						|
  } else {
 | 
						|
	/* A login record */
 | 
						|
	for (link = first_link; link != NULL; link = link->next)
 | 
						|
		if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
 | 
						|
			/* found corresponding logout record */
 | 
						|
			if (Print_Record(wtmp)) {
 | 
						|
				printf("- %.5s ", ctime(&link->time) + 11);
 | 
						|
				Print_Duration(wtmp->ut_time, link->time);
 | 
						|
			}
 | 
						|
			/* record login time */
 | 
						|
			link->time = wtmp->ut_time;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	/* could not find a logout record for this login tty */
 | 
						|
	if (Print_Record(wtmp))
 | 
						|
		if (boot_time == 0)	/* still on */
 | 
						|
			printf("  still logged in\n");
 | 
						|
		else {		/* system crashed while on */
 | 
						|
			printf("- %s ", boot_down);
 | 
						|
			Print_Duration(wtmp->ut_time, boot_time);
 | 
						|
		}
 | 
						|
	Record_Logout_Time(wtmp);	/* Needed in case of 2
 | 
						|
					 * consecutive logins  */
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Print_Record(wtmp) If the record was requested, then print out
 | 
						|
 * the user name, terminal, host and time.
 | 
						|
 */
 | 
						|
int Print_Record(wtmp)
 | 
						|
struct utmp *wtmp;
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  char print_flag = FALSE;
 | 
						|
 | 
						|
  /* just interested in the uptime? */
 | 
						|
  if (tell_uptime) return(FALSE);
 | 
						|
 | 
						|
  /* check if we have already printed the requested number of records */
 | 
						|
  if (count_limit && print_count == 0) exit(0);
 | 
						|
 | 
						|
  for (i = 0; i < arg_count; ++i)
 | 
						|
	if (!strncmp(args[i], wtmp->ut_name, sizeof(wtmp->ut_name)) ||
 | 
						|
	    !strncmp(args[i], wtmp->ut_line, sizeof(wtmp->ut_line)))
 | 
						|
		print_flag = TRUE;
 | 
						|
 | 
						|
  if (arg_count == 0 || print_flag) {
 | 
						|
#ifdef RLOGIN
 | 
						|
	printf("%-8.8s  %-8.8s %-16.16s %.16s ",
 | 
						|
	       wtmp->ut_name, wtmp->ut_line, wtmp->ut_host,
 | 
						|
	       ctime(&wtmp->ut_time));
 | 
						|
#else
 | 
						|
	printf("%-8.8s  %-8.8s  %.16s ",
 | 
						|
	       wtmp->ut_name, wtmp->ut_line, ctime(&wtmp->ut_time));
 | 
						|
#endif
 | 
						|
	--print_count;
 | 
						|
	return(TRUE);
 | 
						|
  }
 | 
						|
  return(FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Print_Duration(from, to) Calculate and print the days and hh:mm between
 | 
						|
 * the log-in and the log-out.
 | 
						|
 */
 | 
						|
void Print_Duration(from, to)
 | 
						|
long from;
 | 
						|
long to;
 | 
						|
{
 | 
						|
  long delta, days, hours, minutes;
 | 
						|
 | 
						|
  delta = max(to - from, 0);
 | 
						|
  days = delta / (24L * 60L * 60L);
 | 
						|
  delta = delta % (24L * 60L * 60L);
 | 
						|
  hours = delta / (60L * 60L);
 | 
						|
  delta = delta % (60L * 60L);
 | 
						|
  minutes = delta / 60L;
 | 
						|
 | 
						|
  if (days > 0)
 | 
						|
	printf("(%ld+", days);
 | 
						|
  else
 | 
						|
	printf(" (");
 | 
						|
 | 
						|
  printf("%02ld:%02ld)\n", hours, minutes);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Print_Uptime() Calculate and print the "uptime" between the last recorded
 | 
						|
 * boot and the current time.
 | 
						|
 */
 | 
						|
void Print_Uptime()
 | 
						|
{
 | 
						|
#define NLOADS 3
 | 
						|
  int nloads;
 | 
						|
  double loads[NLOADS];
 | 
						|
  char *utmp_file = _PATH_UTMP;
 | 
						|
  unsigned nusers;
 | 
						|
  struct utmp ut;
 | 
						|
  FILE *uf;
 | 
						|
  time_t now;
 | 
						|
  struct tm *tm;
 | 
						|
  unsigned long up;
 | 
						|
 | 
						|
  /* Count the number of active users in the utmp file. */
 | 
						|
  if ((uf = fopen(utmp_file, "r")) == NULL) {
 | 
						|
	fprintf(stderr, "%s: %s: %s\n", prog, utmp_file, strerror(errno));
 | 
						|
	exit(1);
 | 
						|
  }
 | 
						|
 | 
						|
  nusers = 0;
 | 
						|
  while (fread(&ut, sizeof(ut), 1, uf) == 1) {
 | 
						|
#ifdef USER_PROCESS
 | 
						|
	if (ut.ut_type == USER_PROCESS) nusers++;
 | 
						|
#else
 | 
						|
	if (ut.ut_name[0] != 0 && ut.ut_line[0] != 0) nusers++;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  fclose(uf);
 | 
						|
 | 
						|
  /* Current time. */
 | 
						|
  now = time((time_t *) NULL);
 | 
						|
  tm = localtime(&now);
 | 
						|
 | 
						|
  /* Uptime. */
 | 
						|
  up = now - boot_time;
 | 
						|
 | 
						|
  printf(" %d:%02d  up", tm->tm_hour, tm->tm_min);
 | 
						|
  if (up >= 24 * 3600L) {
 | 
						|
	unsigned long days = up / (24 * 3600L);
 | 
						|
	printf(" %lu day%s,", days, days == 1 ? "" : "s");
 | 
						|
  }
 | 
						|
  printf(" %lu:%02lu,", (up % (24 * 3600L)) / 3600, (up % 3600) / 60);
 | 
						|
  printf("  %u user%s", nusers, nusers == 1 ? "" : "s");
 | 
						|
  if((nloads = getloadavg(loads, NLOADS)) > 0) {
 | 
						|
	int i;
 | 
						|
	printf(", load averages:");
 | 
						|
	for(i = 0; i < nloads; i++)
 | 
						|
		printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
 | 
						|
  }
 | 
						|
  printf("\n");
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Record_Logout_Time(wtmp) A linked list of "last logout time" is kept.
 | 
						|
 * Each element of the list is for one terminal.
 | 
						|
 */
 | 
						|
void Record_Logout_Time(wtmp)
 | 
						|
struct utmp *wtmp;
 | 
						|
{
 | 
						|
  logout *link;
 | 
						|
 | 
						|
  /* see if the terminal is already in the list */
 | 
						|
  for (link = first_link; link != NULL; link = link->next)
 | 
						|
	if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
 | 
						|
		link->time = wtmp->ut_time;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
  /* allocate a new logout record, for a tty not previously encountered */
 | 
						|
  link = (logout *) malloc(sizeof(logout));
 | 
						|
  if (link == NULL) {
 | 
						|
	fprintf(stderr, "%s: malloc failure\n", prog);
 | 
						|
	exit(1);
 | 
						|
  }
 | 
						|
  strncpy(link->line, wtmp->ut_line, (size_t)8);
 | 
						|
  link->time = wtmp->ut_time;
 | 
						|
  link->next = first_link;
 | 
						|
 | 
						|
  first_link = link;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int main(argc, argv)
 | 
						|
int argc;
 | 
						|
char *argv[];
 | 
						|
{
 | 
						|
  char *wtmp_file = _PATH_WTMP;
 | 
						|
  FILE *f;
 | 
						|
  long size;			/* Number of wtmp records in the file	 */
 | 
						|
  int wtmp_count;		/* How many to read into wtmp_buffer	 */
 | 
						|
  struct utmp wtmp_buffer[MAX_WTMP_COUNT];
 | 
						|
 | 
						|
  if ((prog = strrchr(argv[0], '/')) == NULL) prog = argv[0]; else prog++;
 | 
						|
 | 
						|
  --argc;
 | 
						|
  ++argv;
 | 
						|
 | 
						|
  while (argc > 0 && *argv[0] == '-') {
 | 
						|
	if (!strcmp(argv[0], "-r"))
 | 
						|
		boot_limit = TRUE;
 | 
						|
	else
 | 
						|
	if (!strcmp(argv[0], "-u"))
 | 
						|
		tell_uptime = TRUE;
 | 
						|
	else if (argc > 1 && !strcmp(argv[0], "-f")) {
 | 
						|
		wtmp_file = argv[1];
 | 
						|
		--argc;
 | 
						|
		++argv;
 | 
						|
	} else if ((print_count = atoi(argv[0] + 1)) > 0)
 | 
						|
		count_limit = TRUE;
 | 
						|
	else
 | 
						|
		usage();
 | 
						|
 | 
						|
	--argc;
 | 
						|
	++argv;
 | 
						|
  }
 | 
						|
 | 
						|
  arg_count = argc;
 | 
						|
  args = argv;
 | 
						|
 | 
						|
  if (!strcmp(prog, "uptime")) tell_uptime = TRUE;
 | 
						|
 | 
						|
  if ((f = fopen(wtmp_file, "r")) == NULL) {
 | 
						|
	perror(wtmp_file);
 | 
						|
	exit(1);
 | 
						|
  }
 | 
						|
  if (fseek(f, 0L, 2) != 0 || (size = ftell(f)) % sizeof(struct utmp) != 0) {
 | 
						|
	fprintf(stderr, "%s: invalid wtmp file\n", prog);
 | 
						|
	exit(1);
 | 
						|
  }
 | 
						|
  if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
 | 
						|
	signal(SIGINT, Sigint);
 | 
						|
	signal(SIGQUIT, Sigquit);
 | 
						|
  }
 | 
						|
  size /= sizeof(struct utmp);	/* Number of records in wtmp	 */
 | 
						|
 | 
						|
  if (size == 0) wtmp_buffer[0].ut_time = time((time_t *)0);
 | 
						|
 | 
						|
  while (size > 0) {
 | 
						|
	wtmp_count = (int) min(size, MAX_WTMP_COUNT);
 | 
						|
	size -= (long) wtmp_count;
 | 
						|
 | 
						|
	fseek(f, size * sizeof(struct utmp), 0);
 | 
						|
 | 
						|
 | 
						|
	if (fread(&wtmp_buffer[0], sizeof(struct utmp), (size_t)wtmp_count, f)
 | 
						|
	    != wtmp_count) {
 | 
						|
		fprintf(stderr, "%s: read error on wtmp file\n", prog);
 | 
						|
		exit(1);
 | 
						|
	}
 | 
						|
	while (--wtmp_count >= 0) {
 | 
						|
		Process(&wtmp_buffer[wtmp_count]);
 | 
						|
		if (interrupt) {
 | 
						|
			printf("\ninterrupted %.16s \n",
 | 
						|
			   ctime(&wtmp_buffer[wtmp_count].ut_time));
 | 
						|
 | 
						|
			if (interrupt == SIGINT) exit(2);
 | 
						|
 | 
						|
			interrupt = FALSE;
 | 
						|
			signal(SIGQUIT, Sigquit);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
  }				/* end while(size > 0) */
 | 
						|
 | 
						|
  if (tell_uptime) {
 | 
						|
	fprintf(stderr,
 | 
						|
		"%s: no reboot record in wtmp file to compute uptime from\n",
 | 
						|
		prog);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
 | 
						|
  printf("\nwtmp begins %.16s \n", ctime(&wtmp_buffer[0].ut_time));
 | 
						|
  return(0);
 | 
						|
}
 | 
						|
 |