last.c

Go to the documentation of this file.
00001 /* last - display login history                 Author: Terrence W. Holm */
00002 
00003 /* last-        Display the user log-in history.
00004  *              Last(1) searches backwards through the file of log-in
00005  *              records (/usr/adm/wtmp), displaying the length of
00006  *              log-in sessions as requested by the options:
00007  *
00008  * Usage:       last [-r] [-count] [-f file] [name] [tty] ...
00009  *
00010  *              -r      Search backwards only until the last reboot
00011  *                      record.
00012  *
00013  *              -count  Only print out <count> records. Last(1) stops
00014  *                      when either -r or -count is satisfied, or at
00015  *                      the end of the file if neither is given.
00016  *
00017  *              -f file Use "file" instead of "/usr/adm/wtmp".
00018  *
00019  *              name    Print records for the user "name".
00020  *
00021  *              tty     Print records for the terminal "tty". Actually,
00022  *                      a list of names may be given and all records
00023  *                      that match either the user or tty name are
00024  *                      printed. If no names are given then all records
00025  *                      are displayed.
00026  *
00027  *              A sigquit (^\) causes last(1) to display how far it
00028  *              has gone back in the log-in record file, it then
00029  *              continues. This is used to check on the progress of
00030  *              long running searches. A sigint will stop last(1).
00031  *
00032  * Author:      Terrence W. Holm        May 1988
00033  *
00034  * Revision:
00035  *              Fred van Kempen, October 1989
00036  *               -Adapted to MSS.
00037  *               -Adapted to new utmp database.
00038  *
00039  *              Fred van Kempen, December 1989
00040  *               -Adapted to POSIX (MINIX 1.5)
00041  *
00042  *              Fred van Kempen, January 1990
00043  *               -Final edit for 1.5
00044  *
00045  *              Philip Homburg, March 1992
00046  *               -Include host in output
00047  *
00048  *              Kees J. Bot, July 1997
00049  *               -Approximate system uptime from last reboot record
00050  */
00051 #include <sys/types.h>
00052 #include <signal.h>
00053 #include <string.h>
00054 #include <utmp.h>
00055 #include <time.h>
00056 #include <stdlib.h>
00057 #include <stdio.h>
00058 #include <errno.h>
00059 
00060 #include <minix/paths.h>
00061 
00062 #define  FALSE  0
00063 #define  TRUE   1
00064 #define  RLOGIN 1
00065 
00066 #define  BUFFER_SIZE     4096   /* Room for wtmp records */
00067 #define  MAX_WTMP_COUNT  ( BUFFER_SIZE / sizeof(struct utmp) )
00068 
00069 #define  min( a, b )     ( (a < b) ? a : b )
00070 #define  max( a, b )     ( (a > b) ? a : b )
00071 
00072 
00073 typedef struct logout {         /* A logout time record */
00074   char line[12];                /* The terminal name */
00075   long time;                    /* The logout time */
00076   struct logout *next;          /* Next in linked list */
00077 } logout;
00078 
00079 
00080 static char *Version = "@(#) LAST 1.7 (10/24/92)";
00081 
00082 
00083 /* command-line option flags */
00084 char boot_limit = FALSE;        /* stop on latest reboot */
00085 char count_limit = FALSE;       /* stop after print_count */
00086 char tell_uptime = FALSE;       /* tell uptime since last reboot */
00087 int print_count;
00088 char *prog;                     /* name of this program */
00089 int arg_count;                  /* used to select specific */
00090 char **args;                    /* users and ttys */
00091 
00092 /* global variables */
00093 long boot_time = 0;             /* Zero means no reboot yet */
00094 char *boot_down;                /* "crash" or "down " flag */
00095 logout *first_link = NULL;      /* List of logout times */
00096 int interrupt = FALSE;          /* If sigint or sigquit occurs */
00097 
00098 _PROTOTYPE(int main, (int argc, char **argv));
00099 _PROTOTYPE(void Sigint, (int sig));
00100 _PROTOTYPE(void Sigquit, (int sig));
00101 _PROTOTYPE(void usage, (void));
00102 _PROTOTYPE(void Process, (struct utmp *wtmp));
00103 _PROTOTYPE(int Print_Record, (struct utmp *wtmp));
00104 _PROTOTYPE(void Print_Duration, (long from, long to));
00105 _PROTOTYPE(void Print_Uptime, (void));
00106 _PROTOTYPE(void Record_Logout_Time, (struct utmp *wtmp));
00107 
00108 /* Sigint() and Sigquit() Flag occurrence of an interrupt. */
00109 void Sigint(sig)
00110 int sig;
00111 {
00112   interrupt = SIGINT;
00113 }
00114 
00115 
00116 void Sigquit(sig)
00117 int sig;
00118 {
00119   interrupt = SIGQUIT;
00120 }
00121 
00122 
00123 void usage()
00124 {
00125   fprintf(stderr,
00126         "Usage: last [-r] [-u] [-count] [-f file] [name] [tty] ...\n");
00127   exit(-1);
00128 }
00129 
00130 
00131 /* A log-in record format file contains four types of records.
00132  *
00133  *  [1] generated on a system reboot:
00134  *
00135  *      line="~", name="reboot", host="", time=date()
00136  *
00137  *
00138  *  [2] generated after a shutdown:
00139  *
00140  *      line="~", name="shutdown", host="", time=date()
00141  *
00142  *
00143  *  [3] generated on a successful login(1)
00144  *
00145  *      line=ttyname(), name=cuserid(), host=, time=date()
00146  *
00147  *
00148  *  [4] generated by init(8) on a logout
00149  *
00150  *      line=ttyname(), name="", host="", time=date()
00151  *
00152  *
00153  * Note: This version of last(1) does not recognize the '|' and '}' time
00154  *       change records. Last(1) pairs up line login's and logout's to
00155  *       generate four types of output lines:
00156  *
00157  *        [1] a system reboot or shutdown
00158  *
00159  *         reboot    ~       Mon May 16 14:16
00160  *         shutdown  ~       Mon May 16 14:15
00161  *
00162  *        [2] a login with a matching logout
00163  *
00164  *         edwin     tty1    Thu May 26 20:05 - 20:32  (00:27)
00165  *
00166  *        [3] a login followed by a reboot or shutdown
00167  *
00168  *         root      tty0    Mon May 16 13:57 - crash  (00:19)
00169  *         root      tty1    Mon May 16 13:45 - down   (00:30)
00170  *
00171  *        [4] a login not followed by a logout or reboot
00172  *
00173  *         terry     tty0    Thu May 26 21:19   still logged in
00174  */
00175 void Process(wtmp)
00176 struct utmp *wtmp;
00177 {
00178   logout *link;
00179   logout *next_link;
00180   char is_reboot;
00181 
00182   /* suppress the job number on an "ftp" line */
00183   if (!strncmp(wtmp->ut_line, "ftp", (size_t)3)) strncpy(wtmp->ut_line, "ftp", (size_t)8);
00184 
00185   if (!strcmp(wtmp->ut_line, "~")) {
00186         /* A reboot or shutdown record  */
00187         if (boot_limit) exit(0);
00188 
00189         if (Print_Record(wtmp)) putchar('\n');
00190         boot_time = wtmp->ut_time;
00191 
00192         is_reboot = !strcmp(wtmp->ut_name, "reboot");
00193         if (is_reboot)
00194                 boot_down = "crash";
00195         else
00196                 boot_down = "down ";
00197 
00198         if (tell_uptime) {
00199                 if (!is_reboot) {
00200                         fprintf(stderr,
00201                 "%s: no reboot record added to wtmp file on system boot!\n",
00202                                 prog);
00203                         exit(1);
00204                 }
00205                 Print_Uptime();
00206                 exit(0);
00207         }
00208 
00209         /* remove any logout records */
00210         for (link = first_link; link != NULL; link = next_link) {
00211                 next_link = link->next;
00212                 free(link);
00213         }
00214         first_link = NULL;
00215   } else if (wtmp->ut_name[0] == '\0') {
00216         /* A logout record */
00217         Record_Logout_Time(wtmp);
00218   } else {
00219         /* A login record */
00220         for (link = first_link; link != NULL; link = link->next)
00221                 if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
00222                         /* found corresponding logout record */
00223                         if (Print_Record(wtmp)) {
00224                                 printf("- %.5s ", ctime(&link->time) + 11);
00225                                 Print_Duration(wtmp->ut_time, link->time);
00226                         }
00227                         /* record login time */
00228                         link->time = wtmp->ut_time;
00229                         return;
00230                 }
00231         /* could not find a logout record for this login tty */
00232         if (Print_Record(wtmp))
00233                 if (boot_time == 0)     /* still on */
00234                         printf("  still logged in\n");
00235                 else {          /* system crashed while on */
00236                         printf("- %s ", boot_down);
00237                         Print_Duration(wtmp->ut_time, boot_time);
00238                 }
00239         Record_Logout_Time(wtmp);       /* Needed in case of 2
00240                                          * consecutive logins  */
00241   }
00242 }
00243 
00244 
00245 /* Print_Record(wtmp) If the record was requested, then print out
00246  * the user name, terminal, host and time.
00247  */
00248 int Print_Record(wtmp)
00249 struct utmp *wtmp;
00250 {
00251   int i;
00252   char print_flag = FALSE;
00253 
00254   /* just interested in the uptime? */
00255   if (tell_uptime) return(FALSE);
00256 
00257   /* check if we have already printed the requested number of records */
00258   if (count_limit && print_count == 0) exit(0);
00259 
00260   for (i = 0; i < arg_count; ++i)
00261         if (!strncmp(args[i], wtmp->ut_name, sizeof(wtmp->ut_name)) ||
00262             !strncmp(args[i], wtmp->ut_line, sizeof(wtmp->ut_line)))
00263                 print_flag = TRUE;
00264 
00265   if (arg_count == 0 || print_flag) {
00266 #ifdef RLOGIN
00267         printf("%-8.8s  %-8.8s %-16.16s %.16s ",
00268                wtmp->ut_name, wtmp->ut_line, wtmp->ut_host,
00269                ctime(&wtmp->ut_time));
00270 #else
00271         printf("%-8.8s  %-8.8s  %.16s ",
00272                wtmp->ut_name, wtmp->ut_line, ctime(&wtmp->ut_time));
00273 #endif
00274         --print_count;
00275         return(TRUE);
00276   }
00277   return(FALSE);
00278 }
00279 
00280 
00281 /* Print_Duration(from, to) Calculate and print the days and hh:mm between
00282  * the log-in and the log-out.
00283  */
00284 void Print_Duration(from, to)
00285 long from;
00286 long to;
00287 {
00288   long delta, days, hours, minutes;
00289 
00290   delta = max(to - from, 0);
00291   days = delta / (24L * 60L * 60L);
00292   delta = delta % (24L * 60L * 60L);
00293   hours = delta / (60L * 60L);
00294   delta = delta % (60L * 60L);
00295   minutes = delta / 60L;
00296 
00297   if (days > 0)
00298         printf("(%ld+", days);
00299   else
00300         printf(" (");
00301 
00302   printf("%02ld:%02ld)\n", hours, minutes);
00303 }
00304 
00305 
00306 /* Print_Uptime() Calculate and print the "uptime" between the last recorded
00307  * boot and the current time.
00308  */
00309 void Print_Uptime()
00310 {
00311 #define NLOADS 3
00312   int nloads;
00313   double loads[NLOADS];
00314   char *utmp_file = _PATH_UTMP;
00315   unsigned nusers;
00316   struct utmp ut;
00317   FILE *uf;
00318   time_t now;
00319   struct tm *tm;
00320   unsigned long up;
00321 
00322   /* Count the number of active users in the utmp file. */
00323   if ((uf = fopen(utmp_file, "r")) == NULL) {
00324         fprintf(stderr, "%s: %s: %s\n", prog, utmp_file, strerror(errno));
00325         exit(1);
00326   }
00327 
00328   nusers = 0;
00329   while (fread(&ut, sizeof(ut), 1, uf) == 1) {
00330 #ifdef USER_PROCESS
00331         if (ut.ut_type == USER_PROCESS) nusers++;
00332 #else
00333         if (ut.ut_name[0] != 0 && ut.ut_line[0] != 0) nusers++;
00334 #endif
00335   }
00336   fclose(uf);
00337 
00338   /* Current time. */
00339   now = time((time_t *) NULL);
00340   tm = localtime(&now);
00341 
00342   /* Uptime. */
00343   up = now - boot_time;
00344 
00345   printf(" %d:%02d  up", tm->tm_hour, tm->tm_min);
00346   if (up >= 24 * 3600L) {
00347         unsigned long days = up / (24 * 3600L);
00348         printf(" %lu day%s,", days, days == 1 ? "" : "s");
00349   }
00350   printf(" %lu:%02lu,", (up % (24 * 3600L)) / 3600, (up % 3600) / 60);
00351   printf("  %u user%s", nusers, nusers == 1 ? "" : "s");
00352   if((nloads = getloadavg(loads, NLOADS)) > 0) {
00353         int i;
00354         printf(", load averages:");
00355         for(i = 0; i < nloads; i++)
00356                 printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
00357   }
00358   printf("\n");
00359 }
00360 
00361 
00362 /* Record_Logout_Time(wtmp) A linked list of "last logout time" is kept.
00363  * Each element of the list is for one terminal.
00364  */
00365 void Record_Logout_Time(wtmp)
00366 struct utmp *wtmp;
00367 {
00368   logout *link;
00369 
00370   /* see if the terminal is already in the list */
00371   for (link = first_link; link != NULL; link = link->next)
00372         if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
00373                 link->time = wtmp->ut_time;
00374                 return;
00375         }
00376   /* allocate a new logout record, for a tty not previously encountered */
00377   link = (logout *) malloc(sizeof(logout));
00378   if (link == NULL) {
00379         fprintf(stderr, "%s: malloc failure\n", prog);
00380         exit(1);
00381   }
00382   strncpy(link->line, wtmp->ut_line, (size_t)8);
00383   link->time = wtmp->ut_time;
00384   link->next = first_link;
00385 
00386   first_link = link;
00387 }
00388 
00389 
00390 int main(argc, argv)
00391 int argc;
00392 char *argv[];
00393 {
00394   char *wtmp_file = _PATH_WTMP;
00395   FILE *f;
00396   long size;                    /* Number of wtmp records in the file    */
00397   int wtmp_count;               /* How many to read into wtmp_buffer     */
00398   struct utmp wtmp_buffer[MAX_WTMP_COUNT];
00399 
00400   if ((prog = strrchr(argv[0], '/')) == NULL) prog = argv[0]; else prog++;
00401 
00402   --argc;
00403   ++argv;
00404 
00405   while (argc > 0 && *argv[0] == '-') {
00406         if (!strcmp(argv[0], "-r"))
00407                 boot_limit = TRUE;
00408         else
00409         if (!strcmp(argv[0], "-u"))
00410                 tell_uptime = TRUE;
00411         else if (argc > 1 && !strcmp(argv[0], "-f")) {
00412                 wtmp_file = argv[1];
00413                 --argc;
00414                 ++argv;
00415         } else if ((print_count = atoi(argv[0] + 1)) > 0)
00416                 count_limit = TRUE;
00417         else
00418                 usage();
00419 
00420         --argc;
00421         ++argv;
00422   }
00423 
00424   arg_count = argc;
00425   args = argv;
00426 
00427   if (!strcmp(prog, "uptime")) tell_uptime = TRUE;
00428 
00429   if ((f = fopen(wtmp_file, "r")) == NULL) {
00430         perror(wtmp_file);
00431         exit(1);
00432   }
00433   if (fseek(f, 0L, 2) != 0 || (size = ftell(f)) % sizeof(struct utmp) != 0) {
00434         fprintf(stderr, "%s: invalid wtmp file\n", prog);
00435         exit(1);
00436   }
00437   if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
00438         signal(SIGINT, Sigint);
00439         signal(SIGQUIT, Sigquit);
00440   }
00441   size /= sizeof(struct utmp);  /* Number of records in wtmp     */
00442 
00443   if (size == 0) wtmp_buffer[0].ut_time = time((time_t *)0);
00444 
00445   while (size > 0) {
00446         wtmp_count = (int) min(size, MAX_WTMP_COUNT);
00447         size -= (long) wtmp_count;
00448 
00449         fseek(f, size * sizeof(struct utmp), 0);
00450 
00451 
00452         if (fread(&wtmp_buffer[0], sizeof(struct utmp), (size_t)wtmp_count, f)
00453             != wtmp_count) {
00454                 fprintf(stderr, "%s: read error on wtmp file\n", prog);
00455                 exit(1);
00456         }
00457         while (--wtmp_count >= 0) {
00458                 Process(&wtmp_buffer[wtmp_count]);
00459                 if (interrupt) {
00460                         printf("\ninterrupted %.16s \n",
00461                            ctime(&wtmp_buffer[wtmp_count].ut_time));
00462 
00463                         if (interrupt == SIGINT) exit(2);
00464 
00465                         interrupt = FALSE;
00466                         signal(SIGQUIT, Sigquit);
00467                 }
00468         }
00469 
00470   }                             /* end while(size > 0) */
00471 
00472   if (tell_uptime) {
00473         fprintf(stderr,
00474                 "%s: no reboot record in wtmp file to compute uptime from\n",
00475                 prog);
00476         return(1);
00477   }
00478 
00479   printf("\nwtmp begins %.16s \n", ctime(&wtmp_buffer[0].ut_time));
00480   return(0);
00481 }

Generated on Fri Apr 14 22:57:08 2006 for minix by  doxygen 1.4.6