#
# comments start with a # sign
#
# each item in the make file consists of
# three things:  a product, its ingredients and how to make it
#

CC = gcc -g -Wall

aac: aac.o aacbuffer.o myLib.o
	$(CC) aac.o aacbuffer.o myLib.o -o aac

aac.o: aac.c aac.h
	$(CC) -c aac.c

aacbuffer.o: aacbuffer.c aac.h
	$(CC) -c aacbuffer.c

run:
	./aac sample.wtmp


#
#
# This is a makefile.  A makefile contains rules that tell
# how to build a program, often from separate source files
#
# This sample makefile shows how to build dumputmp from
# the two source files dumputmp.c and utmplib.c
#
# You must modify this file so it contains rules to make
# your solution to the project
# (note: the indented lines MUST start with a single tab
#
#
#dumputmp: dumputmp.o utmplib.o
#	cc dumputmp.o utmplib.o -o dumputmp
#
#see: dumputmp sample.wtmp
#	./dumputmp sample.wtmp | more
#
#dumputmp.o: dumputmp.c
#	$(CC) -c dumputmp.c
#
#utmplib.o: utmplib.c
#	$(CC) -c utmplib.c
#
#clean:
#	rm -f *.o core dumputmp
#

_______


// aac.h

#define	YES	1
#define	NO	0

#include    <stdio.h>
#include    <stdlib.h>
#include    <sys/types.h>
#include    <utmp.h>
#include    <time.h>
#include    <fcntl.h>
#include    <string.h>
#include    <ctype.h>

//	linked list of structs
//
struct link {
	char        *id;                          // id       of log entry
	int          inTime;                      // in-time  of log entry
        int          outTime;                     // out-time of log entry
	struct link *next;                        // pointer to next link
};

void         fatal(char *errStr);
void         dumpfile( char *fileName, char *userName );
void         readUtpLine( struct utmp *rp, char *userName );
int          newEntry(char *id, int myInTime );
void         printLList();
void         emptyList();
float        tallyList();
struct link *lookup( char *str );
void         show_utrec( struct utmp *rp );
char        *typename( int typenum );
int          utmp_open( char *filename );
struct utmp *utmp_next();
int          utmp_reload();
void         utmp_close();


_____



// aac.c 
//
// - almost ac -
// mimick the ac command
// implimenting login names and alternate login info with -w

#include    "aac.h"
#define	MAXNAMES	512

//  globals
//
struct link gHead;                                // head of linked list

int main(int ac, char **av)
{
    int    i,
           nameCount = 0;
    float  totalTime = 0.0;                       // total log time
                                                  
    char  *fileName = WTMP_FILE,                  // default file name
          *userNames[MAXNAMES];                   // list of user names to tally
    
    // init linked list
    gHead.next = NULL;
    
    // loop through arguments
    for ( i = 1; i < ac; i++ ) {
        
            // mode change?
            if (av[i][0] == '-') {
                    switch (av[i][1]) {
                        case 'w':
                            if (i == ac - 1)
                                    fatal("missing arg for -w");
                            fileName = av[++i];
                            break;
                        default:
                            fatal("unknown option");
                    }
            }
            else {
                    userNames[nameCount] = av[i]; // append to userNames
                    nameCount++;
            }
    }

    if (! nameCount)                         
            fatal("ERROR: No user specified");            // no user name
    
    // loop through each name in userNames list
    for (i = 0; i < nameCount; i++) {
    
            // read file
            dumpfile( fileName, userNames[i] );
    
            // add up log times
            totalTime += tallyList();
     
            // empty list
            emptyList();
    }
    
    // output total time
    printf("\ttotal      %.2f\n", totalTime);
    
    return (0);		// return 0 means OK
}


//
//  Open file and dump records
//
void dumpfile( char *fileName, char *userName )
{
    struct utmp *utp,		                           // ptr to struct
    *utmp_next();                                          // declare its type

    if ( utmp_open( fileName ) == -1 ) {                   // open file
            perror( fileName );
            return ;
    }

    // loop, reading records
    while( ( utp = utmp_next() ) )
            readUtpLine( utp, userName );
    utmp_close();

}


//
//  Read a line of utp
//
void readUtpLine( struct utmp *rp, char *userName )
{
    struct link *linkp;
    
    
    // linkp = any listed entry with same id and no outTime (if any)
    linkp = lookup( rp->ut_id );
    
    
    switch (rp->ut_type) {
        case 1:
            // RUN_LVL
            
            break;
        case 2:
            // BOOT_TIME
                 // time at startup boot
            break;
        case 3:
            // OLD_TIME
                 //  original time
            break;
        case 4:
            // NEW_TIME
                 //  time change
            break;
        case 5:
            // INIT_PROCESS
            
            break;
        case 6:
            // LOGIN_PROCESS
            
            break;
        case 7:
            // USER_PROCESS
        
            // Has user logged in as another user without logging out?
            if ( linkp && strcmp(rp->ut_user, userName) != 0 )
                    linkp->outTime = linkp->inTime;   // yes, 'zero' broken id
            
            // is entry by user userName?
            if (strcmp(rp->ut_user, userName) == 0)
                    if ( ! linkp )                    // entry id not in list?
                            newEntry(rp->ut_id,rp->ut_time);
            break;
        case 8:
            // DEAD_PROCESS
            
            // check linkp (open entry in list with same id)
            if (linkp)
                    linkp->outTime = rp->ut_time;     // close using outTime
            break;
        case 9:
            // ACCOUNTING
            
            break;
        default:
            // EMPTY
            
            break;       
    }
}


//
//  Add new entry to linked list
//
int newEntry(char *id, int myInTime)
{
	char		 *myId ;
	struct link	 *newLink;

	// get memory and allocate values for struct
	myId = malloc( 1 + strlen(id));
	if (myId == NULL)
                fatal("Out of memory");
	strcpy( myId, id );
        
	newLink = malloc(sizeof(struct link));
	if (newLink == NULL)
                fatal("Out of memory");
	
	// store values into newLink
	newLink->id        = myId;
	newLink->inTime    = myInTime;
        newLink->outTime   = 0;
        newLink->next      = gHead.next;       // attach list to link
        
	gHead.next         = newLink;          // make head pt to link
        
	return YES;
}

//
//  Print Linked List,  prints id, inTime and outTimes of items in list
//
void printLList()
{
    struct link *linkp;
    
    for ( linkp = gHead.next ; linkp ; linkp = linkp->next )
            printf("id = %s   logIn = %d   logOut = %d\n", 
                    linkp->id, linkp->inTime, linkp->outTime);
}

//
//  Return pointer to any items with an id the same as str and 
//  are 'open'.  Open meaning they have a zero outTime.
//
struct link *lookup( char *str )
{
    struct link *linkp;

    for( linkp = gHead.next ; linkp ; linkp = linkp->next )
            if ( strcmp( linkp->id, str) == 0 && ! linkp->outTime) // if found
                    return linkp;		                   // ret value
    return NULL;					           // not found
}


//
//  Tally total time for all entries in linked list
//
float tallyList() {
    
    struct link *linkp;
    
    int    myEnd,
           myStart,
           myDiff;
    float  listTime = 0.0;
            
    for( linkp = gHead.next ; linkp ; linkp = linkp->next ) {
            myEnd   = linkp->outTime;
            myStart = linkp->inTime;
            
            // if myEnd is 0 then use current time (still logged on)
            if (! myEnd)
                    myEnd = time(NULL);

            myDiff = (myEnd - myStart);
            
            // convert difference to hours and add to total
            listTime += (myDiff / 3600.0);
    }
    
    // ensure no value > 0 is less than 0.01
    if (listTime > 0.0 && listTime < 0.01)
            listTime = 0.01;
    
    return (listTime);
}

//
//  Clear all entries in linked list
//
void emptyList()
{
    struct link * linkp,				      // link
                * prevLink;                                   // previous link

    // free all links except end
    prevLink = NULL;
    for( linkp = gHead.next ; linkp ; prevLink = linkp, linkp = linkp->next ) {
            if (prevLink)
                    free(prevLink);                           // free memory
    }
    
    // cleanup end and head
    free(linkp);
    gHead.next = NULL;
}


//
//  Display entire record
//
void show_utrec( struct utmp *rp )
{
    char        *typename();

    printf("%-8.8s ", rp->ut_user );
    printf("%-8.8s ", rp->ut_id   );
    printf("%-8.8s ", rp->ut_line );
    printf("%6d ", rp->ut_pid );
    printf("%4d %-14.14s ", rp->ut_type , typename(rp->ut_type) );
    printf("%12d ", rp->ut_time );
    printf("%s", rp->ut_host );
    putchar('\n');
}


//
//  Convert typenum int to string
//
char * typename( int typenum )
{
    char *uttypes[] = { "EMPTY",
                        "RUN_LVL",
                        "BOOT_TIME",
                        "OLD_TIME",
                        "NEW_TIME",
                        "INIT_PROCESS",
                        "LOGIN_PROCESS", 
                        "USER_PROCESS",
                        "DEAD_PROCESS",
                        "ACCOUNTING" };
                        
    return uttypes[typenum];
}



________



// utmplib.c  - functions to buffer reads from utmp file 
//
//      functions are
//              utmp_open( filename )   - open file
//                      returns -1 on error
//              utmp_next( )            - return pointer to next struct
//                      returns NULL on eof
//              utmp_close()            - close file
//
//      reads NRECS per read and then doles them out from the buffer
//

#include    "aac.h"

#define NRECS   16
#define NULLUT  ((struct utmp *)NULL)
#define UTSIZE  (sizeof(struct utmp))

static  char    utmpbuf[NRECS * UTSIZE];            // storage
static  int     num_recs;                           // num stored
static  int     cur_rec;                            // next to go
static  int     fd_utmp = -1;                       // read from

int utmp_open( char *filename )
{
    fd_utmp = open( filename, O_RDONLY );           // open it
    cur_rec = num_recs = 0;                         // no recs yet
    return fd_utmp;                                 // report
}

struct utmp *utmp_next()
{
    struct utmp *recp;

    if ( fd_utmp == -1 )                            // error?
            return NULLUT;
    if ( cur_rec==num_recs && utmp_reload()==0 )    // any more?
            return NULLUT;
        
    // get address of next record
    recp = ( struct utmp *) &utmpbuf[cur_rec * UTSIZE];
    cur_rec++;
    return recp;
}

int utmp_reload()
//
//      read next bunch of records into buffer
//
{
    int     amt_read;
    
    // read them in   
    amt_read = read( fd_utmp , utmpbuf, NRECS * UTSIZE );   
    
    // how many did we get?                   
    num_recs = amt_read/UTSIZE;                     
    
    // reset pointer                                          
    cur_rec  = 0;
    return num_recs;
}

void utmp_close()
{
    if ( fd_utmp != -1 )                            // don't close if not
            close( fd_utmp );                       // open
}
