moran@tron.UUCP (11/15/88)
Posting-number: Volume 5, Issue 49 Submitted-by: "A. Nonymous" <moran@tron.UUCP> Archive-name: s5last # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # last.1 # Makefile # last.c # This archive created: Thu Nov 10 00:50:45 1988 echo shar: extracting last.1 sed 's/^X //' << \SHAR_EOF > last.1 X .\" Copyright (c) 1988 Harvey R. Moran Jr. X .\" 1936 Altavue Rd. X .\" Catonsville, Md., 21228 X .\" X .\" moran%tron.UUCP@umbc3.UMD.EDU Internet X .\" {wb3ffv,netsys}!hrmhpc!harvey UUCP X .\" X .\" This software may be freely distributed provided that this X .\" copyright notice is left intact and provided that: X .\" a) The source code in machine readable format X .\" is included with any binary distribution. X .\" b) If a binary version of this program or a derived work is sold, X .\" the source code must be provided in machine readable X .\" format for no additional charge. X .\" c) No derived works may impose restrictions limiting free X .\" distribution of the source code. X .\" Source code in this context includes the C language source code X .\" provided here as file "last.c" and the manual page provide here as X .\" file "last.1". It also includes any works derived from either of these. X .TH last 1L LOCAL X .SH NAME X last \- indicate last logins of users X .SH SYNTAX X .B last X [ X .I -N X ] [ X .I name X ] X .SH DESCRIPTION X The X .I last X command X looks back in the X .I wtmp X file which records all logins and logouts for information about X a user, or all users in reverse time order. X The optional argument X .I -N X specifies the maximum number of lines to print. X The optional X .I name X specifies the name of a user of interest. X If the session is still continuing \fBlast\fR X so indicates. X .PP X The X .I last X command X with no arguments prints a record of all logins and logouts. X .SH ORIGIN X This command is modeled on the X .I last X command which comes with the X Berkeley Software Distribution (BSD) of UNIX(tm). The Berkeley version X also permits specifying a terminal name. It also includes the X field specifying the remote machine in the case of a network login. X No such field is present in the System V.2 version of /etc/wtmp. X .SH FILES X /etc/wtmp login data base X .SH SEE\ ALSO X utmp(4) login data base format description SHAR_EOF if test 2013 -ne "`wc -c last.1`" then echo shar: error transmitting last.1 '(should have been 2013 characters)' fi echo shar: extracting Makefile sed 's/^X //' << \SHAR_EOF > Makefile X DEFS = -DMAXENTRIES=1024 # maximum number of entries in /etc/wtmp handled X CFLAGS = -O -s $(DEFS) # -O(ptomize) -s(trip symbols) X X last: last.c X cc $(CFLAGS) -o last last.c SHAR_EOF if test 172 -ne "`wc -c Makefile`" then echo shar: error transmitting Makefile '(should have been 172 characters)' fi echo shar: extracting last.c sed 's/^X //' << \SHAR_EOF > last.c X /* X * last.c -- examine the wtmp file for last login information X * X * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= X * Copyright (c) 1988 Harvey R. Moran Jr. X * 1936 Altavue Rd. X * Catonsville, Md., 21228 X * X * moran%tron.UUCP@umbc3.UMD.EDU Internet X * {wb3ffv,netsys}!hrmhpc!harvey UUCP X * X * This software may be freely distributed provided that this X * copyright notice is left intact and provided that: X * a) The source code in machine readable format X * is included with any binary distribution. X * b) If a binary version of this program or a derived work is sold, X * the source code must be provided in machine readable X * format for no additional charge. X * c) No derived works may impose restrictions limiting free X * distribution of the source code. X * Source code in this context includes the C language source code X * provided here as file "last.c" and the manual page provide here as X * file "last.1". It also includes any works derived from either of these. X * X * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= X * Developed using Microport System V/AT 2.3U X * X * usage: X * last [ -N ] [ username ] X * X * This will print the usage of the system as X * recorded in WTMP_FILE (defined in /usr/include/utmp.h) for X * all users except the ones listed in exclude[] (below). X * The option -N specifies that a maximum of N lines of X * output will be generated. X * The optional username form will restrict information printed X * to be logins by the specified user. X * X * This command is modeled on the "last" command which is distributed X * with the Berkeley Software Distribution (BSD) of UNIX(tm). X * The print formats are slightly different. The field which X * specifies a network login identification are not provided with this X * version because the information is not available in the System V.2 X * UNIX(tm) WTMP_FILE file. X * X * It will also quit (with error message) if there are more than X * MAXENTRIES entries in your WTMP_FILE (/etc/wtmp on my system). X * X * Harvey Moran 11/10/88 X */ X #include <stdio.h> X #include <fcntl.h> X #include <sys/types.h> X #include <utmp.h> X #include <time.h> X X #ifndef MAXENTRIES X # define MAXENTRIES 1024 /* maximum entries in WTMP_FILE handled */ X #endif X X #define STILL_IN -1L X #define AVOID -1L X X typedef int BOOL; X #define YES 1 X #define NO 0 X X typedef struct utmp WTMP_t; X WTMP_t proc_tab; X X typedef struct { X char user_name[9]; X short pid; X time_t x_time; X time_t y_time; X } ENTRY; X X ENTRY entries[MAXENTRIES]; X X /* X * user process user names to be excluded from display X * change per local installation. Perhaps these should be X * in an environment variable instead, but there are already X * so many of them. X */ X char *exclude[] = { "rc2", "crtsaver", "LOGIN", NULL }; X X char *keep_list = (char *) NULL; X X /* X * forward declared functions X */ X X char *delta_time(); X void qsort(); X int cmp_ENTRY(), cmp_ENTRY_1(); X X main(ac, av) X int ac; X char *av[]; X { X int i, j, fd; X unsigned count = (unsigned) -1; /* BIG unsigned int. */ X char intime[27], outtime[27], elapsed[27]; X long time_tmp; X X if ( ac > 1 && (i = atoi(av[1])) < 0 ) { X count = (unsigned) -i; X for ( i = 2; i < ac; ++i ) X av[i-1] = av[i]; X av[i] = NULL; X --ac; X } X if ( (fd=open(WTMP_FILE, (O_RDONLY|O_NDELAY))) == -1 ) { X fprintf(stderr, "%s:Can't open %s\n", av[0], WTMP_FILE); X exit(1); X } X i = 0; X while ( read(fd, &proc_tab, sizeof(WTMP_t)) == sizeof(WTMP_t) ) { X switch ( proc_tab.ut_type ) { X case EMPTY: /* Empty slot in the WTMP_FILE */ X case INIT_PROCESS: /* Process started by "init" */ X case LOGIN_PROCESS: /* A "getty" process waiting for login */ X case ACCOUNTING: X break; /* IGNORE all these forever */ X X case RUN_LVL: /* Change of Run Level */ X case BOOT_TIME: /* Boot time */ X case OLD_TIME: /* Old time of Day was */ X case NEW_TIME: /* New Time of Day set */ X break; /* IGNORE all these for now */ X X case USER_PROCESS: /* A user process was started */ X case DEAD_PROCESS: /* A user (I think) process died */ X strncpy(entries[i].user_name, proc_tab.ut_user, sizeof(proc_tab.ut_user)); X if ( omit(entries[i].user_name, ac, av) ) X break; X if ( ac == 2 && strcmp(av[1], entries[i].user_name) ) X break; /* not the user of interest */ X entries[i].pid = proc_tab.ut_pid; X entries[i].x_time = proc_tab.ut_time; X if ( ++i > (sizeof(entries)/sizeof(ENTRY)) ) { X fprintf(stderr, "Too many entries in %s\n", WTMP_FILE); X fprintf(stderr, "Fix program %s\n", av[0]); X exit(2); X } X break; X X default: X fprintf(stderr, "Ignoring GARBAGE in file %s. (ut_type = 0x%02x)\n", WTMP_FILE, proc_tab.ut_type); X break; X } X } X /* X * sort by pid, x_time X */ X qsort((char *) entries, i, sizeof(ENTRY), cmp_ENTRY); X j = 0; X #define E entries /* minimize line wrapping of source code */ X do { X if ( j == (i-1) ) { X /* X * final login has no corresponding logout X * avoid referencing array element with is empty (or does not exist) X */ X E[j].y_time = STILL_IN; X break; X } X else if ( E[j].pid != E[j+1].pid ) { /* login has no corresponding logout */ X E[j].y_time = STILL_IN; X ++j; X } X else { /* E[j].pid == E[j+1].pid , i.e. login/logout pair */ X E[j].y_time = E[j+1].x_time; X E[j+1].x_time = AVOID; X j += 2; X } X } while ( j < i ); X /* X * sort by descending x_time X */ X qsort((char *) entries, i, sizeof(ENTRY), cmp_ENTRY_1); X for ( j = 0; j < i && count; ++j ) { X if ( E[j].x_time == AVOID ) X continue; X else if ( E[j].y_time == STILL_IN ) { X time_tmp = (long) E[j].x_time; X (void) strcpy(intime, asctime(localtime(&time_tmp))); X intime[20] = '\0'; /* strip year */ X printf("%-8s %s %s still-logged-in\n", E[j].user_name, intime, " "); X --count; X } X else { X time_tmp = (long) E[j].x_time; X (void) strcpy(intime, asctime(localtime(&time_tmp))); X time_tmp = (long) E[j].y_time; X (void) strcpy(outtime, asctime(localtime(&time_tmp))); X intime[20] = outtime[20] = '\0'; /* strip year */ X time_tmp = (long) (E[j].y_time - E[j].x_time); X (void) strcpy(elapsed, delta_time(time_tmp)); X printf("%-8s %s-%s %s\n", E[j].user_name, intime, outtime+10, elapsed); X --count; X } X } X #undef E X } X X /* X * return comparison of 2 ENTRY type records X * suitable for sorting by: pid, x_time X */ X X cmp_ENTRY(a, b) X ENTRY *a, *b; X { X if ( a->pid < b->pid ) X return (-1); X if ( a->pid > b->pid ) X return (1); X if ( a->x_time < b->x_time ) X return (-1); X if ( a->x_time > b->x_time ) X return (1); X return (0); X } X X /* X * return comparison of 2 ENTRY type records X * suitable for a descending sort by: x_time X */ X cmp_ENTRY_1(a,b) X ENTRY *a, *b; X { X if ( a->x_time < b->x_time ) X return (1); X if ( a->x_time > b->x_time ) X return (-1); X return (0); X } X X X /* X * return whether to omit this name X * It is omitted if: X * a) the name matches something in the exclude list X * b) the name does not match something in the invocation argument list X * and the argument list is not null X */ X BOOL X omit(name, ac, av) X char *name; X int ac; X char *av[]; X { X char **p; X int i; X X for ( p = &exclude[0]; *p != (char *) NULL; ++p ) { X if ( strcmp(*p, name) == 0 ) X return (YES); X } X if ( ac == 1 ) /* empty argument list, do not omit any */ X return (NO); X for ( i = 1; i < ac; ++i ) { X if ( strcmp(name, av[i]) == 0 ) X return (NO); X } X return (YES); X } X X X char * X delta_time(t) X long t; X { X static char AscTime[11]; X int sec, min, hr, da; X long x, y; X X sec = t % 60L; X x = (t - (long) sec)/60L; X min = x % 60L; X x = (x - (long) min)/60L; X hr = x % 24; X x = (x - (long) hr)/24L; X da = x; X if ( da ) X (void) sprintf(AscTime, "%3d+%02d:%02d:%02d", da, hr, min, sec); X else X (void) sprintf(AscTime, " %02d:%02d:%02d", hr, min, sec); X return (AscTime); X } SHAR_EOF if test 7908 -ne "`wc -c last.c`" then echo shar: error transmitting last.c '(should have been 7908 characters)' fi # End of shell archive exit 0