#!/usr/local/bin/perl5 ######################################################################### # ADMINISTRATION LOG - Log administrative changes to the system here. # # This software is public domain. The latest version of this software # is available from http://seriss.com/people/erco/unixtools # # -erco@3dsite.com ######################################################################### $PROGNAME = "admlog"; $DBASEDIR = "/usr/local/admlog.dir"; $DBASE = "${DBASEDIR}/admlog"; $DBASELOCK = "${DBASE}.lock"; $TMP = "/usr/tmp/$PROGNAME.$$"; $EDITOR = ( defined ( $ENV{EDITOR} ) ) ? $ENV{EDITOR} : "vi"; umask(022); # FILE LOCKING STUFF $LOCK_EX = 2; $LOCK_UN = 8; ################## ### FUNCTIONS ################## # LOCK THE FILE # $1 - original filename sub Lock { local ($filename) = @_; # CREATE LOCK if ( rename("${filename}","${filename}.lock" ) == 1 ) { # Lock created. # Save info about WHO has it locked and when. # local($user) = `logname`; $user =~ s/\n//; local($hostname) = `hostname`; $hostname =~ s/\n//; local($tty) = `tty`; $tty =~ s/\n//; local($date) = `date`; $date =~ s/\n//; open(WHO, ">$DBASEDIR/who"); print WHO "$user\@$hostname, pid $$ at $tty on $date\n"; close(WHO); return(0); } local($who) = `cat $DBASEDIR/who`; $who =~ s/\n//; print STDERR "$PROGNAME: ${DBASE}:\n" . "$PROGNAME: file locked by: $who\n"; exit(1); } # UNLOCK THE FILE # $1 - original filename sub Unlock { local ($filename) = @_; # CREATE LOCK if ( rename("${filename}.lock", "$filename" ) == 1 ) { unlink("$DBASEDIR/who"); return(0); } print STDERR "$PROGNAME: ${DBASE}:\n" . "$PROGNAME: HELP: your changes are stuck in ${filename}.lock!\n" . "$PROGNAME: HELP: You must 'mv ${filename}.lock ${filename}' by hand\n"; exit(1); } # TEMP COPY OF LOCKED OR UNLOCKED VERSION # $1 - original filename # $2 - temp copy filename # sub TempCopy { local ($orig, $copy) = @_; # IF NEITHER ORIGINAL NOR LOCKED EXISTS, FAIL if ( ! -e "$orig" && ! -e "${orig}.lock" ) { # CHECK AGAIN INCASE OF RACE CONDITION sleep(1); if ( ! -e ${orig} && ! -e "${orig}.lock" ) { print STDERR "$PROGNAME: FAIL: Can't find ${orig} or ${orig}.lock\n"; exit(1); } } while ( 1 ) { # FIRST, TRY UNLOCKED FILE if ( system("cp ${orig} ${copy} 2> /dev/null") == 0 ) { return(0); } # SECOND, TRY LOCKED FILE if ( system("cp ${orig}.lock ${copy} 2> /dev/null") == 0 ) { return(0); } # RACE CONDITION? TRY AGAIN sleep(1); } #NOTREACHED## } # JUST EDIT THE FILE (ASSUME LOCKING HANDLED) # $1 - filename sub Edit { local ($filename) = @_; return(system("$EDITOR ${filename}") >> 8) } # CHECK THE USER ID # $1 - uid to check sub EuidCheck { local ($uidcheck) = @_; if ( $> != $uidcheck ) { print STDERR "$PROGNAME: you must be root to edit the log\n"; exit(1); } return(0); } # MAKE A FOUR LEVEL BACKUP OF THE FILE (..just incase..) # $1 - filename to backup # $2 - actual name for backup copy sub Backup { local ($filename, $backupname) = @_; if ( -e "${backupname}.3" ) { rename("${backupname}.3", "${backupname}.4"); } if ( -e "${backupname}.2" ) { rename("${backupname}.2", "${backupname}.3"); } if ( -e "${backupname}.1" ) { rename("${backupname}.1", "${backupname}.2"); } return(system("cp $filename ${backupname}.1") >> 8 ); } # PREPEND A NEW ENTRY TO THE LOG # $1 - filename sub DateStamp { local ($filename) = @_; unless ( open(IN, "<$filename") ) { print STDERR "$PROGNAME: $filename: $!\n"; exit(1); } unless ( open(OUT, ">$filename.tmp") ) { print STDERR "$PROGNAME: $filename.tmp: $!\n"; exit(1); } local ($flag) = 0; local ($date) = `date +%D,%T`; $date =~ s/\n//; local ($logname) = `logname`; $logname =~ s/\n//; local ($msg) = <<"EOF"; --- ${date} - ${logname}: YOUR_ONE_LINE_DESCRIPTION Your_multi_line_comments_here EOF while ( ) { if ( $flag == 0 && ! /^#/ ) { $flag = 1; print OUT $msg; } print OUT $_; } # NOT ADDED YET? ADD AT END OF FILE if ( $flag == 0 ) { $flag = 1; print OUT $msg; } close(IN); close(OUT); if ( rename("$filename.tmp", "$filename") != 1 ) { print STDERR "$PROGNAME: rename('$filename.tmp', '$filename'): $!\n"; exit(1); } return(0); } # CREATE A NEW DBASE FROM SCRATCH # $1 - database sub CreateDbase { local ($filename) = @_; mkdir($DBASEDIR, 0755); # CREATE NEW FILE print "Creating $filename\n"; unless ( open(FD, ">>$filename") ) { print STDERR "$PROGNAME: $filename: $!\n"; exit(1); } $date = `date +%m/%d/%y,%H:%M`; $date =~ s/\n//; $you = `logname`; $you =~ s/\n//; print FD <<"EOF"; # A D M I N I S T R A T I O N L O G # # When modifying the system, log your changes with 'admlog -new'. # Put your new entries at the top, under the datestamp provided. # Please follow the format of other entries. # --- $date - $you: installed admlog admlog installed in $0, and the admlog database is in $DBASEDIR. EOF close(FD); return(0); } # PRINT HELP AND EXIT # No arguments sub HelpAndExit { print <<"EOF"; NAME: admlog - when changes are made to the system, log them here USAGE: admlog - Show the log in more(1) admlog -v - View the log in vi(1) admlog -n - New date stamped log entry admlog -e - Edit existing log entries admlog -s - Summary of log entries admlog -dbase_create - Create a new database from scratch admlog -help - help The -n and -e options put a lock on the log file to prevent more than one person from editing the file. EOF exit(1); } # INTERRUPT TRAP # Remove any junk # sub Trap { print STDERR "\n${PROGNAME}: killed (removing crap)\n"; unlink("${TMP}"); exit(1); } ################## ### MAIN ################## { $SIG{INTR} = 'Trap'; if ( $ARGV[0] eq "" ) { &TempCopy($DBASE, $TMP); system("more $TMP"); unlink($TMP); exit(0); } elsif ( $ARGV[0] =~ /^-v/ ) { &TempCopy($DBASE, $TMP); &Edit($TMP); unlink($TMP); exit(0); } elsif ( $ARGV[0] =~ /^-e/ ) { &EuidCheck(0); &Lock($DBASE); &Backup($DBASELOCK, $DBASE); &Edit($DBASELOCK); &Unlock($DBASE); exit(0); } elsif ( $ARGV[0] =~ /^-n/ ) { &EuidCheck(0); &Lock($DBASE); &Backup($DBASELOCK, $DBASE); &DateStamp($DBASELOCK); &Edit($DBASELOCK); &Unlock($DBASE); exit(0); } elsif ( $ARGV[0] eq "-dbase_create" ) { &EuidCheck(0); &CreateDbase($DBASE); exit(0); } elsif ( $ARGV[0] =~ /^-s/ ) { system("admlog | grep '^--- ' | more"); exit(0); } elsif ( $ARGV[0] =~ /^-/ ) { &HelpAndExit(); } else { print STDERR "${PROGNAME}: unknown option $ARGV[0]\n"; exit(1); } exit(0); }