SysV versions of finger and last, with lastlog.
Greg A. Woods
woods at robohack.UUCP
Mon Jan 1 09:45:12 AEST 1990
Sorry, I didn't have the finger or last manual pages, and I haven't
written one for lastlog. The finger and last sources didn't have the
BSD copyright, but I assume they apply. Lastlog is PD.
----------- Cut Here ----------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: ReadMe.finger Makefile finger.c last.c lastlog.c
# Wrapped by woods at robohack on Sun Dec 31 17:36:05 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ReadMe.finger' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ReadMe.finger'\"
else
echo shar: Extracting \"'ReadMe.finger'\" \(3255 characters\)
sed "s/^X//" >'ReadMe.finger' <<'END_OF_FILE'
Xwoods at robohack -- Sun Dec 31 16:45:30 EST 1989
X
XI finally have managed to find time to get finger working nicely on my
XSysV machine (an AT&T 3B2/400 running SysVr3.1v2). This holiday
Xseason has been quite relaxing!
X
XWhile I tried to make as few changes as possible, I ended up making
Xquite a few, and as such the complete source is provided, not just the
Xdiffs. I have removed almost all of the special localizations and I
Xhave generalized the GECOS field format a bit. (See the comment at
Xthe top of the source for the GECOS field format description.)
X
X 1. There is now a macro, ISMODEM(), which should be defined to
X indicate which tty's are dialup.
X
X 2. The "office" field now has no special character flags, and
X now the first 14 chars are simply printed as the office.
X
X 3. There is a general info field which is always read and
X printed if it exists.
X
X 3. The TTY field in the -l and -w displays is now LMAX-1 chars
X wide (sizeof utmp.ut_line - 1), and special recognition of tty
X is gone.
X
X 4. The messages indicator in the -l and -w displays now has
X its own field (M), but is still displayed in the same manner.
X
X 5. Various changes to the code which formats and prints the
X reports. The long form is considerably more consistent for
X various forms of the GECOS field.
X
XI have written a companion programme, lastlog, to be called from
X/etc/profile, which updates the entry for the calling user in
X/usr/adm/lastlog. The records in this file are copies of the
X/etc/utmp record for the current login session, and finger has been
Xmodified to understand the new format. This programme is designed to
Xwork the way it does, such that it can't be fooled too easily.
XLastlog should be setgid to adm, and /usr/adm/lastlog should be
Xwriteable by the group adm for the best results!
X
XAlso included is a port of 'last'. This is handy, but when running
XSysV Accounting, not all that great.
X
XThere were a few more fixes and changes to finger as well, including
Xone courtesy of Steve Blasingame, who also did an initial port to SysV.
X
XI doubt this is derived from the most recent version of finger, as it
Xhas no support for remote fingering. For a large part of the current
XSysV community this won't be a big loss. However, as networking
Xbecomes more widespread, this feature will be required everywhere.
X
XAfter spending some time in the guts of finger, I would suggest that
Xit not be worked on too much more, but rather it should be
Xre-written. It is far better than some BSD utilities, but it has
Xquite a few hacks, most of which seem to have been done in order to
Xconform to some existing practice on the author's machine. Perhaps a
Xbetter thought out standard would help. In addition, the formatting
Xcode seems to have been hacked to make a pleasing output, not
Xdesigned. There are some pretty silly things, such as printf's for
Xblanks, and the worst of them I've removed. I almost ran the source
Xthrough cb or indent, and you'll notice slight variances in style
Xwhere my changes were thickest, but I did resist the temptation.
X
XAnyway, here it is. It does what I want, and works well for me!
XLet me know if you find any bugs. If there is a great demand for
Xfinger, perhaps I could re-post it to comp.sources.{misc|unix}.
X
X HAPPY NEW YEAR!
END_OF_FILE
if test 3255 -ne `wc -c <'ReadMe.finger'`; then
echo shar: \"'ReadMe.finger'\" unpacked with wrong size!
fi
# end of 'ReadMe.finger'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(895 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile - general makefile for normal one-file commands
X#
X# This file should not require editing when new commands are added.
X#
X# To install a new command:
X#
X# 'make CMD=<command_name> install'
X#
X# Created:
X#
X# woods at tslanpar: Tue May 19 22:33:14 EDT 1987
X#
X# BUGS:
X#
X# Unfortunately, this breaks the 'make cat cmp diff grep' idiom when
X# attempting to install. To do that would require a 'supermake'(?) and
X# may require a makefile for each command, and/or changes to built-in
X# rules in make.
X#
X
XSRCDIR = /usr/src/local/cmd
XBINDIR = /usr/local/bin
XMANDIR = /usr/local/man/man1
XMANDEST = man1
X
XCFLAGS = -O -DSYSV -DSYSVR3 $(DEBUG) $(PROF)
XLDFLAGS =
X
Xinstall: $(BINDIR)/$(CMD) $(MANDIR)/$(MANDEST)/$(CMD).1
X
X$(BINDIR)/$(CMD): $(CMD)
X install -c -m 755 $(CMD) $(BINDIR)
X
X$(MANDIR)/$(MANDEST)/$(CMD).1: $(CMD).1
X install -c -m 644 $(CMD).1 $(MANDIR)/$(MANDEST)
X
Xclean:
X rm -f a.out core *.o
END_OF_FILE
if test 895 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'finger.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'finger.c'\"
else
echo shar: Extracting \"'finger.c'\" \(32249 characters\)
sed "s/^X//" >'finger.c' <<'END_OF_FILE'
X#ifdef SYSVR3
X# ident "@(#)finger.c 4.1 (Berkeley) 10/1/80";
X# ident "@(#)finger.c 1.1.1.1 89/12/31 17:30:03 [LOCAL] (Greg A. Woods)"
X#else
X# ifndef lint
Xstatic char *sccsid = "@(#)finger.c 4.1 (Berkeley) 10/1/80";
Xstatic cahr *sccsloc= "@(#)finger.c 1.1.1.1\t89/12/31 17:30:03 [LOCAL] (Greg A. Woods)";
X# endif
X#endif
X
X/* This is a finger program. It prints out useful information about users
X * by digging it up from various system files. It is not very portable
X * because the most useful parts of the information (the full user name,
X * office, and phone numbers) are all stored in the VAX-unused gecos field
X * of /etc/passwd, which, unfortunately, other UNIXes use for other things.
X *
X * There are three output formats, all of which give login name, teletype
X * line number, and login time. The short output format is reminiscent
X * of finger on ITS, and gives one line of information per user containing
X * in addition to the minimum basic requirements (MBR), the full name of
X * the user, his idle time and office location and phone number. The
X * quick style output is UNIX who-like, giving only name, teletype and
X * login time. Finally, the long style output give the same information
X * as the short (in more legible format), the home directory and shell
X * of the user, and, if it exits, a copy of the file .plan in the users
X * home directory. Finger may be called with or without a list of people
X * to finger -- if no list is given, all the people currently logged in
X * are fingered.
X *
X * The program is validly called by one of the following:
X *
X * finger {short form list of users}
X * finger -l {long form list of users}
X * finger -b {briefer long form list of users}
X * finger -q {quick list of users}
X * finger -i {quick list of users with idle times}
X * finger namelist {long format list of specified users}
X * finger -s namelist {short format list of specified users}
X * finger -w namelist {narrow short format list of specified users}
X *
X * where 'namelist' is a list of users login names.
X * The other options can all be given after one '-', or each can have its
X * own '-'. The -f option disables the printing of headers for short and
X * quick outputs. The -b option briefens long format outputs. The -p
X * option turns off plans for long format outputs.
X */
X
X/*
X * The GECOS field should have the following format:
X *
X * :Real Name,location,office,home,extra:
X *
X * where:
X * location is text describing office location (14 chars)
X * office and home are phone numbers (7 or 10 digits only, no -'s)
X * extra is text for misc. data (1 line's worth)
X *
X * If the first field is not a phone #, then it will be the office. If there
X * is no office, it may be left blank (",,"). Any other, or all, field(s)
X * may be omitted. If there is only one phone number, no distinction will be
X * made between home and office, unless there is an office, in which case the
X * phone number defaults to home, unless a blank home field is given.s
X */
X
X/*
X * Fixes:
X * Steve Blasingame 09/01/86
X * fixed bug associated with reads from .plan and .project files
X * stored in a char. This prevented recognition of EOF.
X *
X * Greg Woods 12/31/89
X * Various changes to update for generic use and final porting
X * to SysV. Fixed arg2 in call to lseek, casting it to long.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <utmp.h>
X#include <signal.h>
X#include <pwd.h>
X#include <stdio.h>
X#ifndef SYSV
X# include <sgtty.h>
X# include <sccs.h>
X# include <lastlog.h>
X#else
X# include <string.h>
X# include <memory.h>
X# include <unistd.h>
X#endif
X#include <time.h>
X
X#ifndef SEEK_SET /* from unistd.h */
X/* Symbolic constants for the "lseek" routine: */
X# define SEEK_SET 0 /* Set file pointer to "offset" */
X# define SEEK_CUR 1 /* Set file pointer to current plus "offset" */
X# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
X#endif
X
X#define ASTERISK '*' /* ignore this in real name */
X#define BLANK ' ' /* blank character (i.e. space) */
X#define CAPITALIZE 0137& /* capitalize character macro */
X#define COMMA ',' /* separator in pw_gecos field */
X#define COMMAND '-' /* command line flag char */
X#define LINEBREAK 012 /* line feed */
X#define NULLSTR "" /* the null string, opposed to NULL */
X#define SAMENAME '&' /* repeat login name in real name */
X#define TALKABLE 0222 /* tty is writeable if 222 mode */
X#ifndef LASTLOG_FILE
X# define LASTLOG_FILE "/usr/adm/lastlog"
X#endif
X#ifndef ISMODEM
X# define ISMODEM(tty) (strncmp(tty, "tty13", 5) == 0)
X#endif
X
Xstruct utmp utmp; /* for sizeof */
X#define NMAX sizeof(utmp.ut_name)
X#define LMAX sizeof(utmp.ut_line)
X
Xstruct person { /* one for each person fingered */
X char name[NMAX+1]; /* login name */
X char tty[LMAX+1]; /* NULL terminated tty line */
X long loginat; /* time of login (possibly last) */
X long idletime; /* how long idle (if logged in) */
X short int loggedin; /* flag for being logged in */
X short int writeable; /* flag for tty being writeable */
X char *realname; /* pointer to full name */
X char *office; /* pointer to office name */
X char *officephone; /* pointer to office phone no. */
X char *homephone; /* pointer to home phone no. */
X char *random; /* for any random stuff in pw_gecos */
X struct passwd *pwd; /* structure of /etc/passwd stuff */
X struct person *link; /* link to next person */
X};
X
Xstruct passwd *NILPWD = 0;
Xstruct person *NILPERS = 0;
X
Xint persize = sizeof( struct person );
Xint pwdsize = sizeof( struct passwd );
X
Xchar LASTLOG[] = LASTLOG_FILE; /* last login info */
X#ifndef SYSV
Xchar USERLOG[] = "/etc/utmp"; /* who is logged in */
X#else
Xchar USERLOG[] = UTMP_FILE; /* who is logged in */
X#endif
Xchar outbuf[BUFSIZ]; /* output buffer */
Xchar *ctime();
X
Xint unbrief = 1; /* -b option default */
Xint header = 1; /* -f option default */
Xint hack = 1; /* -h option default */
Xint idle = 0; /* -i option default */
Xint large = 0; /* -l option default */
Xint match = 1; /* -m option default */
Xint plan = 1; /* -p option default */
Xint unquick = 1; /* -q option default */
Xint small = 0; /* -s option default */
Xint wide = 1; /* -w option default */
X
Xint lf;
Xint llopenerr;
X
Xlong tloc; /* current time */
X
X
X
Xmain( argc, argv )
X
Xint argc;
Xchar *argv[];
X
X{
X FILE *fp, *fopen(); /* for plans */
X struct passwd *getpwent(); /* read /etc/passwd */
X struct person *person1, *p, *pend; /* people */
X struct passwd *pw; /* temporary */
X struct utmp user; /* ditto */
X char *malloc();
X char *s, *pn, *ln;
X int c; /* storage for getc */
X char *PLAN = "/.plan"; /* what plan file is */
X char *PROJ = "/.project"; /* what project file */
X int PLANLEN = strlen( PLAN );
X int PROJLEN = strlen( PROJ );
X int numnames = 0;
X int orgnumnames;
X int uf;
X int usize = sizeof user;
X int unshort;
X int i, j;
X int fngrlogin;
X
X setbuf( stdout, outbuf ); /* buffer output */
X
X /* parse command line for (optional) arguments */
X
X i = 1;
X if( strcmp( *argv, "sh" ) ) {
X fngrlogin = 0;
X while( i++ < argc && (*++argv)[0] == COMMAND ) {
X for( s = argv[0] + 1; *s != NULL; s++ ) {
X switch (*s) {
X
X case 'b':
X unbrief = 0;
X break;
X
X case 'f':
X header = 0;
X break;
X
X case 'h':
X hack = 0;
X break;
X
X case 'i':
X idle = 1;
X unquick = 0;
X break;
X
X case 'l':
X large = 1;
X break;
X
X case 'm':
X match = 0;
X break;
X
X case 'p':
X plan = 0;
X break;
X
X case 'q':
X unquick = 0;
X break;
X
X case 's':
X small = 1;
X break;
X
X case 'w':
X wide = 0;
X break;
X
X default:
X fprintf( stderr, "finger: Usage -- 'finger [-bfhilmpqsw] [login1 [login2 ...] ]'\n" );
X exit( 1 );
X }
X }
X }
X }
X else {
X fngrlogin = 1;
X }
X if( unquick ) {
X time( &tloc );
X }
X else {
X if( idle ) {
X time( &tloc );
X }
X }
X
X /* i > argc means no login names given so get them by reading USERLOG */
X
X if( (i > argc) || fngrlogin ) {
X unshort = large;
X if( ( uf = open(USERLOG, 0) ) >= 0 ) {
X#ifndef SYSV
X user.ut_name[0] = NULL;
X while( user.ut_name[0] == NULL ) {
X if( read( uf, (char *) &user, usize ) != usize ) {
X printf( "\nNo one logged on\n" );
X exit( 0 );
X }
X }
X#else
X /* SYSV has lots of entry types */
X user.ut_type = EMPTY;
X while( user.ut_type != USER_PROCESS ) {
X if( read( uf, (char *) &user, usize ) != usize ) {
X printf( "\nNo one logged on\n" );
X exit( 0 );
X }
X }
X#endif
X person1 = (struct person *) malloc( persize );
X for( j = 0; j < NMAX; j++ ) {
X person1->tty[j] = user.ut_line[j];
X person1->name[j] = user.ut_name[j];
X }
X person1->name[NMAX] = NULL;
X person1->tty[NMAX] = NULL;
X person1->loginat = user.ut_time;
X person1->pwd = NILPWD;
X person1->loggedin = 1;
X numnames++;
X p = person1;
X while( read( uf, (char *) &user, usize ) == usize ) {
X#ifndef SYSV
X if( user.ut_name[0] == NULL ) continue;
X#else
X if( user.ut_type != USER_PROCESS ) continue;
X#endif
X p->link = (struct person *) malloc( persize );
X p = p->link;
X for( j = 0; j < NMAX; j++ ) {
X p->tty[j] = user.ut_line[j];
X p->name[j] = user.ut_name[j];
X }
X p->name[NMAX] = NULL;
X p->tty[NMAX] = NULL;
X p->loginat = user.ut_time;
X p->pwd = NILPWD;
X p->loggedin = 1;
X numnames++;
X }
X p->link = NILPERS;
X close( uf );
X }
X else {
X fprintf( stderr, "finger: error opening %s\n", USERLOG );
X exit( 2 );
X }
X
X /* if we are doing it, read /etc/passwd for the useful info */
X
X if( unquick ) {
X setpwent();
X fwopen();
X i = numnames;
X while( ( (pw = getpwent()) != NILPWD ) && ( i > 0 ) ) {
X p = person1;
X do {
X if( p->pwd == NILPWD ) {
X if( strcmp( p->name, pw->pw_name ) == 0 ) {
X p->pwd = (struct passwd *) malloc( pwdsize );
X pwdcopy( p->pwd, pw );
X decode( p );
X i--;
X }
X }
X p = p->link;
X }
X while( p != NILPERS );
X }
X fwclose();
X endpwent();
X }
X }
X
X /* get names from command line and check to see if they're logged in */
X
X else {
X unshort = ( small == 1 ? 0 : 1 );
X i++;
X person1 = (struct person *) malloc( persize );
X strcpy( person1->name, (argv++)[ 0 ] );
X person1->loggedin = 0;
X person1->pwd = NILPWD;
X numnames++;
X p = person1;
X while( i++ <= argc ) {
X p->link = (struct person *) malloc( persize );
X p = p->link;
X strcpy( p->name, (argv++)[ 0 ] );
X p->loggedin = 0;
X p->pwd = NILPWD;
X numnames++;
X }
X p->link = NILPERS;
X pend = p;
X
X /* if we are doing it, read /etc/passwd for the useful info */
X
X orgnumnames = numnames;
X if( unquick ) {
X setpwent();
X while( ( pw = getpwent() ) != NILPWD ) {
X p = person1;
X i = 0;
X do {
X if( strcmp( p->name, pw->pw_name ) == 0 ||
X matchcmp( pw->pw_gecos, pw->pw_name, p->name ) ) {
X if( p->pwd == NILPWD ) {
X p->pwd = (struct passwd *) malloc( pwdsize );
X pwdcopy( p->pwd, pw );
X }
X else { /* handle multiple logins -- append new
X "duplicate" entry to end of list */
X pend->link = (struct person *) malloc(persize);
X pend = pend->link;
X pend->link = NILPERS;
X strcpy( pend->name, p->name );
X pend->pwd = (struct passwd *) malloc(pwdsize);
X pwdcopy( pend->pwd, pw );
X numnames++;
X }
X }
X p = p->link;
X }
X while( ++i < orgnumnames );
X }
X endpwent();
X }
X
X /* Now get login information */
X
X if( ( uf = open(USERLOG, 0) ) >= 0 ) {
X while( read( uf, (char *) &user, usize ) == usize ) {
X#ifndef SYSV
X if( user.ut_name[0] == NULL ) continue;
X#else
X if( user.ut_type != USER_PROCESS ) continue;
X#endif
X p = person1;
X do {
X pw = p->pwd;
X if( pw == NILPWD ) {
X i = ( strcmp( p->name, user.ut_name ) ? 0 : NMAX );
X }
X else {
X i = 0;
X while( (i < NMAX) &&
X ( pw->pw_name[i] == user.ut_name[i]) ) {
X if( pw->pw_name[i] == NULL ) {
X i = NMAX;
X break;
X }
X i++;
X }
X }
X if( i == NMAX ) {
X if( p->loggedin == 1 ) {
X pend->link = (struct person *) malloc(persize);
X pend = pend->link;
X pend->link = NILPERS;
X strcpy( pend->name, p->name );
X for( j = 0; j < NMAX; j++ ) {
X pend->tty[j] = user.ut_line[j];
X }
X pend->tty[ NMAX ] = NULL;
X pend->loginat = user.ut_time;
X pend->loggedin = 2;
X if( pw == NILPWD ) {
X pend ->pwd = NILPWD;
X }
X else {
X pend->pwd = (struct passwd *) malloc(pwdsize);
X pwdcopy( pend->pwd, pw );
X }
X numnames++;
X }
X else {
X if( p->loggedin != 2 ) {
X for( j = 0; j < NMAX; j++ ) {
X p->tty[j] = user.ut_line[j];
X }
X p->tty[ NMAX ] = NULL;
X p->loginat = user.ut_time;
X p->loggedin = 1;
X }
X }
X }
X p = p->link;
X }
X while( p != NILPERS );
X }
X fwopen();
X p = person1;
X while( p != NILPERS ) {
X if( p->loggedin == 2 ) {
X p->loggedin = 1;
X }
X decode( p );
X p = p->link;
X }
X fwclose();
X close( uf );
X }
X else {
X fprintf( stderr, "finger: error opening %s\n", USERLOG );
X exit( 2 );
X }
X }
X
X /* print out what we got */
X
X if( header ) {
X if( unquick ) {
X if( !unshort ) {
X if( wide ) {
X printf(
X "Login Name M%*.*s Idle When Office\n", LMAX-1, LMAX-1, "TTY" );
X }
X else {
X printf(
X "Login M%*.*s Idle When Office\n", LMAX-1, LMAX-1, "TTY" );
X }
X }
X }
X else {
X printf( "Login TTY When" );
X if( idle ) {
X printf( " Idle" );
X }
X printf( "\n" );
X }
X }
X p = person1;
X do {
X if( unquick ) {
X if( unshort ) {
X personprint( p );
X if( p->pwd != NILPWD ) {
X if( hack ) {
X s = malloc(strlen((p->pwd)->pw_dir) + PROJLEN + 1 );
X strcpy( s, (p->pwd)->pw_dir );
X strcat( s, PROJ );
X if( ( fp = fopen( s, "r") ) != NULL ) {
X printf( "Project: " );
X while( ( c = getc(fp) ) != EOF ) {
X if( c == LINEBREAK ) {
X break;
X }
X putc( (char) c, stdout );
X }
X fclose( fp );
X printf( "\n" );
X }
X }
X if( plan ) {
X s = malloc( strlen( (p->pwd)->pw_dir ) + PLANLEN + 1 );
X strcpy( s, (p->pwd)->pw_dir );
X strcat( s, PLAN );
X if( ( fp = fopen( s, "r") ) == NULL ) {
X printf( "No Plan.\n" );
X }
X else {
X printf( "Plan:\n" );
X while( ( c = getc(fp) ) != EOF ) {
X putc((char) c, stdout );
X }
X fclose( fp );
X }
X }
X }
X if( p->link != NILPERS ) {
X printf( "\n" );
X }
X }
X else {
X shortprint( p );
X }
X }
X else {
X quickprint( p );
X }
X p = p->link;
X }
X while( p != NILPERS );
X exit(0);
X}
X
X
X/* given a pointer to a pwd (pfrom) copy it to another one, allocating
X * space for all the stuff in it. Note: Only the useful (what the
X * program currently uses) things are copied.
X */
X
Xpwdcopy( pto, pfrom ) /* copy relevant fields only */
X
Xstruct passwd *pto, *pfrom;
X{
X pto->pw_name = malloc( strlen( pfrom->pw_name ) + 1 );
X strcpy( pto->pw_name, pfrom->pw_name );
X pto->pw_uid = pfrom->pw_uid;
X pto->pw_gecos = malloc( strlen( pfrom->pw_gecos ) + 1 );
X strcpy( pto->pw_gecos, pfrom->pw_gecos );
X pto->pw_dir = malloc( strlen( pfrom->pw_dir ) + 1 );
X strcpy( pto->pw_dir, pfrom->pw_dir );
X pto->pw_shell = malloc( strlen( pfrom->pw_shell ) + 1 );
X strcpy( pto->pw_shell, pfrom->pw_shell );
X}
X
X
X/* print out information on quick format giving just name, tty, login time
X * and idle time if idle is set.
X */
X
Xquickprint( pers )
X
Xstruct person *pers;
X{
X int idleprinted;
X
X printf( "%-*.*s", NMAX, NMAX, pers->name );
X printf( " " );
X if( pers->loggedin ) {
X if( idle ) {
X findidle( pers );
X if( pers->writeable ) {
X printf( " %-*.*s %-16.16s", LMAX, LMAX,
X pers->tty, ctime( &pers->loginat ) );
X }
X else {
X printf( "*%-*.*s %-16.16s", LMAX, LMAX,
X pers->tty, ctime( &pers->loginat ) );
X }
X printf( " " );
X idleprinted = ltimeprint( &pers->idletime );
X }
X else {
X printf( " %-*.*s %-16.16s", LMAX, LMAX,
X pers->tty, ctime( &pers->loginat ) );
X }
X }
X else {
X printf( " Not Logged In" );
X }
X printf( "\n" );
X}
X
X
X/* print out information in short format, giving login name, full name,
X * tty, idle time, login time, office location and phone.
X */
X
Xshortprint( pers )
X
Xstruct person *pers;
X
X{
X struct passwd *pwdt = pers->pwd;
X char buf[ 26 ];
X int i, len, offset, dialup;
X
X if( pwdt == NILPWD ) {
X printf( "%-*.*s", NMAX, NMAX, pers->name );
X printf( " ???\n" );
X return;
X }
X printf( "%-*.*s", NMAX, NMAX, pwdt->pw_name );
X dialup = 0;
X if( wide ) {
X if( strlen( pers->realname ) > 0 ) {
X printf( " %-20.20s", pers->realname );
X }
X else {
X printf( " ??? " );
X }
X }
X if( pers->loggedin ) {
X if( pers->writeable ) {
X printf( " " );
X }
X else {
X printf( " *" );
X }
X }
X else {
X printf( " " );
X }
X if( strlen( pers->tty ) > 0 ) {
X if( ISMODEM(pers->tty) && pers->loggedin ) {
X dialup = 1;
X }
X printf( "%*.*s ", LMAX-1, LMAX-1, pers->tty );
X }
X else {
X printf( "%*.*s ", LMAX-1, LMAX-1, " " );
X }
X strcpy( buf, ctime( &pers->loginat ) );
X if( pers->loggedin ) {
X stimeprint( &pers->idletime );
X offset = 7;
X for( i = 4; i < 19; i++ ) {
X buf[i] = buf[i + offset];
X }
X printf( " %-9.9s ", buf );
X }
X else {
X printf( " " );
X offset = 4;
X for( i = 0; i <22; i++ ) {
X buf[i] = buf[i + offset];
X }
X printf( "<%-12.12s>", buf );
X }
X len = strlen( pers->homephone );
X if( dialup && (len > 0) ) {
X if( len == 8 ) {
X printf( " " );
X }
X else {
X if( len == 12 ) {
X printf( " " );
X }
X else {
X for( i = 1; i <= 21 - len; i++ ) {
X printf( " " );
X }
X }
X }
X printf( "%s", pers->homephone );
X }
X else {
X if( strlen( pers->office ) > 0 ) {
X printf( " %-11.11s", pers->office );
X if( strlen( pers->officephone ) > 0 ) {
X printf( " %8.8s", pers->officephone );
X }
X else {
X if( len == 8 ) {
X printf( " %8.8s", pers->homephone );
X }
X }
X }
X else {
X if( strlen( pers->officephone ) > 0 ) {
X printf( " %8.8s", pers->officephone );
X }
X else {
X if( len == 8 ) {
X printf( " %8.8s", pers->homephone );
X }
X else {
X if( len == 12 ) {
X printf( " %12.12s", pers->homephone );
X }
X }
X }
X }
X }
X printf( "\n" );
X}
X
X
X/* print out a person in long format giving all possible information.
X * directory and shell are inhibited if unbrief is clear.
X */
X
Xpersonprint( pers )
X
Xstruct person *pers;
X{
X struct passwd *pwdt = pers->pwd;
X int idleprinted;
X
X if( pwdt == NILPWD ) {
X printf( "Login name: %-10s\t\t\tIn real life: ???\n", pers->name );
X return;
X }
X printf( "Login name: %-10s", pwdt->pw_name );
X if( pers->loggedin && !pers->writeable ) {
X printf( " (messages off)\t" );
X }
X else {
X printf( "\t\t\t" );
X }
X if( strlen( pers->realname ) > 0 ) {
X printf( "In real life: %s", pers->realname );
X }
X if( strlen( pers->office ) > 0 ) {
X char pbuf[40];
X
X printf("\nOffice: ");
X if( strlen( pers->officephone ) > 0 ) {
X sprintf(pbuf, "%-.14s, %-.12s", pers->office, pers->officephone );
X printf( "%-26.26s", pbuf );
X if( strlen( pers->homephone ) > 0 ) {
X printf( "\tHome phone: %-.12s", pers->homephone );
X }
X }
X else {
X printf("%-14.14s", pers->office);
X if( strlen( pers->homephone ) > 0 ) {
X printf("\t\t\tHome phone: %-.12s", pers->homephone);
X }
X }
X }
X else {
X if( strlen( pers->officephone ) > 0 ) {
X printf( "\nOffice Phone: %-12.12s", pers->officephone );
X if( strlen( pers->homephone ) > 0 ) {
X printf( "\t\tHome Phone: %-.12s", pers->homephone );
X }
X }
X else {
X if( strlen( pers->homephone ) > 0 ) {
X printf( "\nPhone: %-.12s", pers->homephone );
X }
X }
X }
X if( strlen( pers->random ) > 0 ) {
X printf( "\n%s", pers->random );
X }
X if( unbrief ) {
X printf( "\nDirectory: %-25s", pwdt->pw_dir );
X if( strlen( pwdt->pw_shell ) > 0 ) {
X printf( "\tShell: %-s", pwdt->pw_shell );
X }
X }
X if( pers->loggedin ) {
X register char *ep = ctime( &pers->loginat );
X printf("\nOn since %15.15s on %-*.*s\t", &ep[4], LMAX, LMAX, pers->tty );
X idleprinted = ltimeprint( &pers->idletime );
X if( idleprinted ) {
X printf( " Idle Time" );
X }
X }
X else {
X register char *ep = ctime( &pers->loginat );
X printf("\nLast login %16.16s on %.*s", ep, LMAX, pers->tty );
X }
X printf( "\n" );
X}
X
X
X/*
X * very hacky section of code to format phone numbers. filled with
X * magic constants like 4, 7 and 10.
X */
X
Xchar *phone( s, len )
X
Xchar *s;
Xint len;
X{
X char *strsave();
X char fonebuf[ 15 ];
X int i;
X
X switch( len ) {
X
X case 7:
X for( i = 0; i <= 2; i++ ) {
X fonebuf[ i ] = *s++;
X }
X fonebuf[ 3 ] = '-';
X for( i = 0; i <= 3; i++ ) {
X fonebuf[ 4 + i ] = *s++;
X }
X fonebuf[ 8 ] = NULL;
X return( strsave( &fonebuf[0] ) );
X break;
X
X case 10:
X for( i = 0; i <= 2; i++ ) {
X fonebuf[ i ] = *s++;
X }
X fonebuf[ 3 ] = '-';
X for( i = 0; i <= 2; i++ ) {
X fonebuf[ 4 + i ] = *s++;
X }
X fonebuf[ 7 ] = '-';
X for( i = 0; i <= 3; i++ ) {
X fonebuf[ 8 + i ] = *s++;
X }
X fonebuf[ 12 ] = NULL;
X return( strsave( &fonebuf[0] ) );
X break;
X
X default:
X fprintf( stderr, "finger: error in phone numbering\n" );
X return( strsave(s) );
X break;
X }
X}
X
X
X/* decode the information in the gecos field of /etc/passwd
X * another hacky section of code, but given the format the stuff is in...
X */
X
Xdecode( pers )
X
Xstruct person *pers;
X
X{
X struct passwd *pwdt = pers->pwd;
X char buffer[ 40 ], *bp, *gp, *lp;
X char *phone();
X int alldigits;
X int len;
X int i;
X
X pers->realname = NULLSTR;
X pers->office = NULLSTR;
X pers->officephone = NULLSTR;
X pers->homephone = NULLSTR;
X pers->random = NULLSTR;
X if( pwdt != NILPWD ) {
X gp = pwdt->pw_gecos;
X bp = &buffer[ 0 ];
X if( *gp == ASTERISK )
X gp++;
X while( (*gp != NULL) && (*gp != COMMA) ) { /* name */
X if( *gp == SAMENAME ) {
X lp = pwdt->pw_name;
X *bp++ = CAPITALIZE(*lp++);
X while( *lp != NULL )
X *bp++ = *lp++;
X } else
X *bp++ = *gp;
X gp++;
X }
X *bp = NULL;
X len = strlen( &buffer[0] );
X if ( len ) {
X pers->realname = malloc( len + 1 );
X strcpy( pers->realname, &buffer[0] );
X }
X if( *gp++ == COMMA ) { /* office, supposedly */
X alldigits = 1;
X bp = &buffer[ 0 ];
X while( (*gp != NULL) && (*gp != COMMA) ) {
X *bp = *gp++;
X alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
X bp++;
X }
X *bp = NULL;
X len = strlen( &buffer[0] );
X if( alldigits ) { /* not the office, a phone number? */
X if( (len == 7) || (len == 10) ) {
X pers->homephone = phone( &buffer[0], len );
X } else if ( len ) { /* must be random stuff */
X pers->random = malloc( len + 1 );
X strcpy( pers->random, &buffer[0] );
X }
X } else if ( len ) { /* yes!, the office! */
X pers->office = malloc( len + 1 );
X strcpy( pers->office, &buffer[0] );
X }
X if( *gp++ == COMMA ) { /* office phone, if not already found */
X bp = &buffer[ 0 ];
X alldigits = 1;
X while( (*gp != NULL) && (*gp != COMMA) ) {
X *bp = *gp++;
X alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
X bp++;
X }
X *bp = NULL;
X len = strlen( &buffer[0] );
X if( alldigits && ( (len == 7) || (len == 10) ) ) {
X if( *pers->homephone != NULL )
X pers->officephone = pers->homephone;
X pers->homephone = phone( &buffer[0], len );
X } else if ( len ) { /* nope, random stuff */
X pers->random = malloc( len + 1 );
X strcpy( pers->random, &buffer[0] );
X }
X if( *gp++ == COMMA ) { /* home phone?? */
X bp = &buffer[ 0 ];
X alldigits = 1;
X while( (*gp != NULL) && (*gp != COMMA) ) {
X *bp = *gp++;
X alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
X bp++;
X }
X *bp = NULL;
X len = strlen( &buffer[0] );
X if( alldigits && ( (len == 7) || (len == 10) ) ) {
X /* lose first if 3 numbers */
X if( *pers->homephone != NULL )
X pers->officephone = pers->homephone;
X pers->homephone = phone( &buffer[0], len );
X } else if ( len ) { /* random stuff */
X pers->random = malloc( strlen( &buffer[0] ) + 1 );
X strcpy( pers->random, &buffer[0] );
X } else {
X if( *pers->homephone != NULL ) {
X pers->officephone = pers->homephone;
X pers->homephone = NULLSTR;
X }
X }
X if( *gp++ == COMMA ) { /* real random stuff?? */
X bp = &buffer[ 0 ];
X while( *gp != NULL ) {
X *bp = *gp++;
X bp++;
X }
X *bp = NULL;
X len = strlen( &buffer[0] );
X if ( len ) {
X pers->random = malloc( len + 1 );
X strcpy( pers->random, &buffer[0] );
X }
X }
X }
X }
X }
X if( pers->loggedin == 0 ) {
X findwhen( pers );
X }
X else {
X findidle( pers );
X }
X }
X}
X
X
X/* find the last log in of a user by checking the LASTLOG file.
X * the entry is indexed by the uid, so this can only be done if
X * the uid is known (which it isn't in quick mode)
X */
X
Xfwopen()
X{
X if( ( lf = open(LASTLOG, 0) ) >= 0 ) {
X llopenerr = 0;
X }
X else {
X fprintf( stderr, "finger: lastlog open error\n" );
X llopenerr = 1;
X }
X}
X
X
Xfindwhen( pers )
X
Xstruct person *pers;
X{
X struct passwd *pwdt = pers->pwd;
X#ifndef SYSV
X struct lastlog ll;
X#else
X struct utmp ll;
X#endif
X int llsize = sizeof ll;
X int i;
X
X if( !llopenerr ) {
X lseek( lf, (long) (pwdt->pw_uid*llsize), SEEK_SET );
X if( read( lf, (char *) &ll, llsize ) == llsize ) {
X#ifndef SYSV
X for( i = 0; i < LMAX; i++ ) {
X pers->tty[ i ] = ll.ll_line[ i ];
X }
X pers->tty[ LMAX ] = NULL;
X pers->loginat = ll.ll_time;
X#else
X for( i = 0; i < LMAX; i++ ) {
X pers->tty[ i ] = ll.ut_line[ i ];
X }
X pers->tty[ LMAX ] = NULL;
X pers->loginat = ll.ut_time;
X#endif
X }
X else {
X fprintf( stderr, "finger: lastlog read error\n" );
X pers->tty[ 0 ] = NULL;
X pers->loginat = 0L;
X }
X }
X else {
X pers->tty[ 0 ] = NULL;
X pers->loginat = 0L;
X }
X}
X
X
Xfwclose()
X{
X if( !llopenerr ) {
X close( lf );
X }
X}
X
X
X/* find the idle time of a user by doing a stat on /dev/histty,
X * where histty has been gotten from USERLOG, supposedly.
X */
X
Xfindidle( pers )
X
Xstruct person *pers;
X{
X struct stat ttystatus;
X struct passwd *pwdt = pers->pwd;
X char buffer[ 20 ];
X char *TTY = "/dev/";
X int TTYLEN = strlen( TTY );
X int i;
X
X strcpy( &buffer[0], TTY );
X i = 0;
X do {
X buffer[ TTYLEN + i ] = pers->tty[ i ];
X } while( ++i <= LMAX );
X if( stat( &buffer[0], &ttystatus ) >= 0 ) {
X time( &tloc );
X if( tloc < ttystatus.st_atime ) {
X pers->idletime = 0L;
X }
X else {
X pers->idletime = tloc - ttystatus.st_atime;
X }
X if( (ttystatus.st_mode & TALKABLE) == TALKABLE ) {
X pers->writeable = 1;
X }
X else {
X pers->writeable = 0;
X }
X }
X else {
X fprintf( stderr, "finger: error STATing %s\n", &buffer[0] );
X exit( 4 );
X }
X}
X
X
X/* print idle time in short format; this program always prints 4 characters;
X * if the idle time is zero, it prints 4 blanks.
X */
X
Xstimeprint( dt )
X
Xlong *dt;
X{
X struct tm *gmtime();
X struct tm *delta;
X
X delta = gmtime( dt );
X if( delta->tm_yday == 0 ) {
X if( delta->tm_hour == 0 ) {
X if( delta->tm_min >= 10 ) {
X printf( " %2.2d ", delta->tm_min );
X }
X else {
X if( delta->tm_min == 0 ) {
X printf( " " );
X }
X else {
X printf( " %1.1d ", delta->tm_min );
X }
X }
X }
X else {
X if( delta->tm_hour >= 10 ) {
X printf( "%3.3d:", delta->tm_hour );
X }
X else {
X printf( "%1.1d:%02.2d", delta->tm_hour, delta->tm_min );
X }
X }
X }
X else {
X printf( "%3dd", delta->tm_yday );
X }
X}
X
X
X/* print idle time in long format with care being taken not to pluralize
X * 1 minutes or 1 hours or 1 days.
X */
X
Xltimeprint( dt )
X
Xlong *dt;
X{
X struct tm *gmtime();
X struct tm *delta;
X int printed = 1;
X
X delta = gmtime( dt );
X if( delta->tm_yday == 0 ) {
X if( delta->tm_hour == 0 ) {
X if( delta->tm_min >= 10 ) {
X printf( "%2d minutes", delta->tm_min );
X }
X else {
X if( delta->tm_min == 0 ) {
X if( delta->tm_sec > 10 ) {
X printf( "%2d seconds", delta->tm_sec );
X }
X else {
X printed = 0;
X }
X }
X else {
X if( delta->tm_min == 1 ) {
X if( delta->tm_sec == 1 ) {
X printf( "%1d minute %1d second",
X delta->tm_min, delta->tm_sec );
X }
X else {
X printf( "%1d minute %d seconds",
X delta->tm_min, delta->tm_sec );
X }
X }
X else {
X if( delta->tm_sec == 1 ) {
X printf( "%1d minutes %1d second",
X delta->tm_min, delta->tm_sec );
X }
X else {
X printf( "%1d minutes %d seconds",
X delta->tm_min, delta->tm_sec );
X }
X }
X }
X }
X }
X else {
X if( delta->tm_hour >= 10 ) {
X printf( "%2d hours", delta->tm_hour );
X }
X else {
X if( delta->tm_hour == 1 ) {
X if( delta->tm_min == 1 ) {
X printf( "%1d hour %1d minute",
X delta->tm_hour, delta->tm_min );
X }
X else {
X printf( "%1d hour %2d minutes",
X delta->tm_hour, delta->tm_min );
X }
X }
X else {
X if( delta->tm_min == 1 ) {
X printf( "%1d hours %1d minute",
X delta->tm_hour, delta->tm_min );
X }
X else {
X printf( "%1d hours %2d minutes",
X delta->tm_hour, delta->tm_min );
X }
X }
X }
X }
X }
X else {
X if( delta->tm_yday >= 10 ) {
X printf( "%2d days", delta->tm_yday );
X }
X else {
X if( delta->tm_yday == 1 ) {
X if( delta->tm_hour == 1 ) {
X printf( "%1d day %1d hour",
X delta->tm_yday, delta->tm_hour );
X }
X else {
X printf( "%1d day %2d hours",
X delta->tm_yday, delta->tm_hour );
X }
X }
X else {
X if( delta->tm_hour == 1 ) {
X printf( "%1d days %1d hour",
X delta->tm_yday, delta->tm_hour );
X }
X else {
X printf( "%1d days %2d hours",
X delta->tm_yday, delta->tm_hour );
X }
X }
X }
X }
X return( printed );
X}
X
X
Xmatchcmp( gname, login, given )
X
Xchar *gname;
Xchar *login;
Xchar *given;
X{
X char buffer[ 20 ];
X char c;
X int flag, i, unfound;
X
X if( !match ) {
X return( 0 );
X }
X else {
X if( namecmp( login, given ) ) {
X return( 1 );
X }
X else {
X if( *gname == ASTERISK ) {
X gname++;
X }
X flag = 1;
X i = 0;
X unfound = 1;
X while( unfound ) {
X if( flag ) {
X c = *gname++;
X if( c == SAMENAME ) {
X flag = 0;
X c = *login++;
X }
X else {
X unfound = (*gname != COMMA) && (*gname != NULL);
X }
X }
X else {
X c = *login++;
X if( c == NULL ) {
X if( (*gname == COMMA) || (*gname == NULL) ) {
X break;
X }
X else {
X flag = 1;
X continue;
X }
X }
X }
X if( c == BLANK ) {
X buffer[i++] = NULL;
X if( namecmp( buffer, given ) ) {
X return( 1 );
X }
X i = 0;
X flag = 1;
X }
X else {
X buffer[ i++ ] = c;
X }
X }
X buffer[i++] = NULL;
X if( namecmp( buffer, given ) ) {
X return( 1 );
X }
X else {
X return( 0 );
X }
X }
X }
X}
X
X
Xnamecmp( name1, name2 )
X
Xchar *name1;
Xchar *name2;
X{
X char c1, c2;
X
X c1 = *name1;
X if( (('A' <= c1) && (c1 <= 'Z')) || (('a' <= c1) && (c1 <= 'z')) ) {
X c1 = CAPITALIZE( c1 );
X }
X c2 = *name2;
X if( (('A' <= c2) && (c2 <= 'Z')) || (('a' <= c2) && (c2 <= 'z')) ) {
X c2 = CAPITALIZE( c2 );
X }
X while( c1 == c2 ) {
X if( c1 == NULL ) {
X return( 1 );
X }
X c1 = *++name1;
X if( (('A'<=c1) && (c1<='Z')) || (('a'<=c1) && (c1<='z')) ) {
X c1 = CAPITALIZE( c1 );
X }
X c2 = *++name2;
X if( (('A'<=c2) && (c2<='Z')) || (('a'<=c2) && (c2<='z')) ) {
X c2 = CAPITALIZE( c2 );
X }
X }
X if( *name1 == NULL ) {
X while( ('0' <= *name2) && (*name2 <= '9') ) {
X name2++;
X }
X if( *name2 == NULL ) {
X return( 1 );
X }
X }
X else {
X if( *name2 == NULL ) {
X while( ('0' <= *name1) && (*name1 <= '9') ) {
X name1++;
X }
X if( *name1 == NULL ) {
X return( 1 );
X }
X }
X }
X return( 0 );
X}
X
X
Xchar *strsave( s )
X
Xchar *s;
X{
X char *malloc();
X char *p;
X
X p = malloc( strlen( s ) + 1 );
X strcpy( p, s );
X return( p );
X}
END_OF_FILE
if test 32249 -ne `wc -c <'finger.c'`; then
echo shar: \"'finger.c'\" unpacked with wrong size!
fi
# end of 'finger.c'
fi
if test -f 'last.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'last.c'\"
else
echo shar: Extracting \"'last.c'\" \(4274 characters\)
sed "s/^X//" >'last.c' <<'END_OF_FILE'
X#ifdef SYSVR3
X# ident "@(#)last.c 4.3 (Berkeley) 2/28/81";
X# ident "@(#)last.c 1.1.1.1 89/12/31 17:34:32 [LOCAL] (Greg A. Woods)"
X#else
X# ifndef lint
Xstatic char *sccsid = "@(#)last.c 4.3 (Berkeley) 2/28/81";
Xstatic cahr *sccsloc= "@(#)last.c 1.1.1.1\t89/12/31 17:34:32 [LOCAL] (Greg A. Woods)";
X# endif
X#endif
X/*
X * last
X */
X#include <sys/types.h>
X#include <stdio.h>
X#include <signal.h>
X#ifdef SYSV
X# include <sys/stat.h>
X#else
X# include <stat.h>
X#endif
X#include <utmp.h>
X
X#define NMAX sizeof(buf[0].ut_name)
X#define LMAX sizeof(buf[0].ut_line)
X#define SECDAY (24*60*60)
X
X#define lineq(a,b) (!strncmp(a,b,LMAX))
X#define nameq(a,b) (!strncmp(a,b,NMAX))
X
X#define MAXTTYS 256
X
X#ifndef WTMP_FILE
X# define WTMP_FILE "/usr/adm/wtmp"
X#endif
X
Xchar **argv;
Xint argc;
X
Xstruct utmp buf[128];
Xchar ttnames[MAXTTYS][LMAX+1];
Xlong logouts[MAXTTYS];
X
Xchar *ctime(), *strspl();
Xint onintr();
X
Xmain(ac, av)
X char **av;
X{
X register int i, k;
X int bl, wtmp;
X char *ct;
X register struct utmp *bp;
X long otime;
X struct stat stb;
X int print;
X char * crmsg = (char *)0;
X long crtime;
X
X time(&buf[0].ut_time);
X ac--, av++;
X argc = ac;
X argv = av;
X for (i = 0; i < argc; i++) {
X if (strlen(argv[i])>2)
X continue;
X if (!strcmp(argv[i], "~"))
X continue;
X if (getpwnam(argv[i]))
X continue;
X argv[i] = strspl("tty", argv[i]);
X }
X wtmp = open(WTMP_FILE, 0);
X if (wtmp < 0) {
X perror(WTMP_FILE);
X exit(1);
X }
X fstat(wtmp, &stb);
X bl = (stb.st_size + sizeof (buf)-1) / sizeof (buf);
X if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
X signal(SIGINT, onintr);
X signal(SIGQUIT, onintr);
X }
X for (bl--; bl >= 0; bl--) {
X lseek(wtmp, bl * sizeof (buf), 0);
X bp = &buf[read(wtmp, buf, sizeof (buf)) / sizeof(buf[0]) - 1];
X for ( ; bp >= buf; bp--) {
X print = want(bp);
X if (print) {
X ct = ctime(&bp->ut_time);
X printf("%-*.*s %-*.*s %10.10s %5.5s ",
X NMAX, NMAX, bp->ut_name,
X LMAX, LMAX, bp->ut_line, ct, 11+ct);
X }
X for (i = 0; i < MAXTTYS; i++) {
X if (ttnames[i][0] == 0) {
X strncpy(ttnames[i], bp->ut_line,
X sizeof(bp->ut_line));
X otime = logouts[i];
X logouts[i] = bp->ut_time;
X break;
X }
X if (lineq(ttnames[i], bp->ut_line)) {
X otime = logouts[i];
X logouts[i] = bp->ut_time;
X break;
X }
X }
X if (print) {
X if (otime == 0)
X printf(" still logged in\n");
X else {
X long delta;
X if (otime < 0) {
X otime = -otime;
X printf("- %s", crmsg);
X } else
X printf("- %5.5s",
X ctime(&otime)+11);
X delta = otime - bp->ut_time;
X if (delta < SECDAY)
X printf(" (%5.5s)\n",
X asctime(gmtime(&delta))+11);
X else
X printf(" (%ld+%5.5s)\n",
X delta / SECDAY,
X asctime(gmtime(&delta))+11);
X }
X fflush(stdout);
X }
X if (lineq(bp->ut_line, "~")) {
X for (i = 0; i < MAXTTYS; i++)
X logouts[i] = -bp->ut_time;
X if (nameq(bp->ut_name, "shutdown"))
X crmsg = "down ";
X else
X crmsg = "crash";
X }
X }
X }
X ct = ctime(&buf[0].ut_time);
X printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
X exit(0);
X}
X
Xonintr(signo)
X int signo;
X{
X char *ct;
X
X if (signo == SIGQUIT)
X signal(SIGQUIT, onintr);
X ct = ctime(&buf[0].ut_time);
X printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
X if (signo == SIGINT)
X exit(1);
X}
X
Xwant(bp)
X struct utmp *bp;
X{
X register char **av;
X register int ac;
X
X#ifdef SYSV
X switch (bp->ut_type) {
X case BOOT_TIME:
X /* sizeof(BOOT_MSG) > sizeof ut_name */
X strcpy(bp->ut_name, "reboot");
X break;
X case EMPTY:
X case RUN_LVL:
X case OLD_TIME:
X case NEW_TIME:
X case INIT_PROCESS:
X case LOGIN_PROCESS:
X case DEAD_PROCESS:
X case ACCOUNTING:
X bp->ut_name[0] = '\0'; /* ignore these ones */
X break;
X case USER_PROCESS:
X break; /* normal */
X default:
X strcpy(bp->ut_name, "unknown"); /* weird? */
X break;
X }
X#else
X if (bp->ut_line[0] == '~' && bp->ut_name[0] == '\0')
X strcpy(bp->ut_name, "reboot"); /* bandaid */
X#endif
X if (bp->ut_name[0] == 0)
X return (0);
X if (argc == 0)
X return (1);
X av = argv;
X for (ac = 0; ac < argc; ac++) {
X if (nameq(*av, bp->ut_name) || lineq(*av, bp->ut_line))
X return (1);
X av++;
X }
X return (0);
X}
X
Xchar *
Xstrspl(left, right)
X char *left, *right;
X{
X char *res = (char *)malloc(strlen(left)+strlen(right)+1);
X
X strcpy(res, left);
X strcat(res, right);
X return (res);
X}
END_OF_FILE
if test 4274 -ne `wc -c <'last.c'`; then
echo shar: \"'last.c'\" unpacked with wrong size!
fi
# end of 'last.c'
fi
if test -f 'lastlog.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'lastlog.c'\"
else
echo shar: Extracting \"'lastlog.c'\" \(3399 characters\)
sed "s/^X//" >'lastlog.c' <<'END_OF_FILE'
X#ifdef SYSVR3
X# ident "@(#)lastlog.c 1.1 89/12/30 17:49:02 [LOCAL] (Greg A. Woods)"
X#endif
X
X#ifndef SYSVR3
X# ifndef lint
X static char *SCCS_ID = "@(#)lastlog.c 1.1\t89/12/30 17:49:02 [LOCAL] (Greg A. Woods)";
X# endif
X#endif
X
X/*
X * lastlog - update the lastlog file
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <unistd.h>
X#include <string.h>
X#include <time.h>
X#include <fcntl.h>
X#include <utmp.h>
X
X#define LASTLOG "/usr/adm/lastlog"
X
Xchar *progname = NULL;
X
Xint
Xmain(argc, argv, envp)
X int argc;
X char *argv[];
X char *envp[];
X{
X unsigned short uid;
X int lfd;
X int ufd;
X char *ltm;
X struct utmp ut;
X int utent;
X int rec_sz = sizeof(ut);
X
X extern unsigned short getuid();
X
X if (!(progname = strrchr(argv[0], '/')))
X progname = argv[0];
X else
X progname++;
X if (argc > 1) {
X fprintf(stderr, "Usage: %s\n", progname);
X exit(2);
X }
X if ((lfd = open(LASTLOG, O_RDWR)) < 0 ) {
X fprintf(stderr, "%s: open(%s, O_RDWR): ", progname, LASTLOG);
X perror("");
X exit(1);
X }
X uid = getuid();
X if (uid > 0) {
X if (lseek(lfd, (off_t) (uid * rec_sz), SEEK_SET) == EOF) {
X fprintf(stderr, "%s: lseek(%d): ", progname, uid * rec_sz);
X perror(LASTLOG);
X exit(1);
X }
X }
X if (read(lfd, (char *) &ut, rec_sz) == rec_sz) {
X ltm = ctime(&ut.ut_time);
X ltm[24] = '\0';
X printf("Last login at %s on %s.\n", ltm, ut.ut_line);
X } else
X printf("No last login info found.\n");
X if ((ufd = open(UTMP_FILE, O_RDONLY)) < 0 ) {
X fprintf(stderr, "%s: open(%s, O_RDONLY): ", progname, UTMP_FILE);
X perror("");
X exit(1);
X }
X utent = ttyslot();
X if (utent > 0) {
X if (lseek(ufd, (off_t) (utent * rec_sz), SEEK_SET) == EOF) {
X fprintf(stderr, "%s: lseek(%d): ", progname, uid * rec_sz);
X perror(UTMP_FILE);
X exit(1);
X }
X } else {
X fprintf(stderr, "%s: I can't find your tty slot!\n", progname);
X exit(1);
X }
X if (read(ufd, (char *) &ut, rec_sz) < rec_sz) {
X fprintf(stderr, "%s: read(): ", progname);
X perror(UTMP_FILE);
X exit(1);
X }
X if (close(ufd) == EOF) {
X fprintf(stderr, "%s: close(): ", progname);
X perror(UTMP_FILE);
X exit(1);
X }
X#ifdef DEBUG
X printf("%d:%s:%s:%s:%d:", utent, ut.ut_user, ut.ut_id, ut.ut_line, ut.ut_pid);
X switch (ut.ut_type) {
X case BOOT_TIME:
X printf("BOOT_TIME");
X break;
X case EMPTY:
X printf("EMPTY");
X break;
X case RUN_LVL:
X printf("RUN_LVL");
X break;
X case OLD_TIME:
X printf("OLD_TIME");
X break;
X case NEW_TIME:
X printf("NEW_TIME");
X break;
X case INIT_PROCESS:
X printf("INIT_PROCESS");
X break;
X case LOGIN_PROCESS:
X printf("LOGIN_PROCESS");
X break;
X case DEAD_PROCESS:
X printf("DEAD_PROCESS");
X break;
X case ACCOUNTING:
X printf("ACCOUNTING");
X break;
X case USER_PROCESS:
X printf("USER_PROCESS");
X break;
X default:
X printf("unknown"); /* weird? */
X break;
X }
X if (ut.ut_type == DEAD_PROCESS)
X printf(":%d:%d", ut.ut_exit.e_termination, ut.ut_exit.e_exit);
X printf(":%s", ctime(&ut.ut_time));
X#endif
X if (ut.ut_type != USER_PROCESS) {
X fprintf(stderr, "%s: your utmp entry isn't right.\n", progname);
X exit(1);
X }
X if (lseek(lfd, (off_t) (uid * rec_sz), SEEK_SET) == EOF) {
X fprintf(stderr, "%s: lseek(%d): ", progname, uid * rec_sz);
X perror(LASTLOG);
X exit(1);
X }
X if (write(lfd, (char *) &ut, rec_sz) < rec_sz) {
X fprintf(stderr, "%s: write(): ", progname);
X perror(LASTLOG);
X exit(1);
X }
X if (close(lfd) == EOF) {
X fprintf(stderr, "%s: close(): ", progname);
X perror(LASTLOG);
X exit(1);
X }
X exit(0);
X /* NOTREACHED */
X}
END_OF_FILE
if test 3399 -ne `wc -c <'lastlog.c'`; then
echo shar: \"'lastlog.c'\" unpacked with wrong size!
fi
# end of 'lastlog.c'
fi
echo shar: End of shell archive.
exit 0
--
Greg A. Woods
woods@{robohack,gate,tmsoft,ontmoh,utgpu,gpu.utcs.Toronto.EDU,utorgpu.BITNET}
+1 416 443-1734 [h] +1 416 595-5425 [w] VE3-TCP Toronto, Ontario; CANADA
More information about the Alt.sources
mailing list