#!/usr/local/bin/perl # # DESC: # Program is used to sychronize write access to public motd file. # Program acquires advisory exclusive lock, then brings up editor # to edit file. On return from editor, motd file will be unlocked. # If program cannot acquire exclusive lock, it prints out the current # user holding exclusive lock. # # WRITTEN BY: # Andrew Choi # Fixed by: # Ben Scott # Arvin Hsu use strict; use Fcntl ':flock'; use Errno; use Getopt::Std; use File::Copy; my $motd = "/etc/motd.public"; my $motdbackup = "/tmp/motd.public.b.$>.$$"; # EUID + PID my $motdcurrent = "/tmp/motd.public.r.$>.$$"; # EUID + PID my $motdtemp = "/tmp/motd.public.t.$>.$$"; # EUID + PID my $motdmerged = "/tmp/motd.public.m.$>.$$"; # EUID + PID $motd = "/csua/tmp/motd.kinney" if (getpwuid($<))[0] eq 'kinney'; my $editor = $ENV{MOTDEDITOR}; $editor ||= $ENV{VISUAL}; $editor ||= $ENV{EDITOR}; $editor ||= "/usr/bin/ed"; my %opt; $opt{t} = 600; getopts("mnst:h?",\%opt); &Usage if $opt{h}; ## Set environment vars for vi users ## Thanks, jon. $ENV{NEXINIT} ||= $ENV{EXINIT}; unless ( $ENV{NEXINIT} ) { unless (open (EXRC,"$ENV{HOME}/.nexrc")) { unless (open (EXRC,"$ENV{HOME}/.exrc")) { $ENV{NEXINIT} = "set nolock"; } } unless ($ENV{NEXINIT}) { ($ENV{NEXINIT} = join ("",)) && close EXRC; } } $ENV{NEXINIT} .= "\nset nolock\n"; # Set alarm for being locked too damn long $SIG{ALRM} = \&Unlock; alarm $opt{t}; # Open file as append so that lock can be used on it open(M, "$motd") || die "open $motd: $!"; # Lock file. If success, execute edit command and exit flock(M, LOCK_EX|LOCK_NB); if ($!{EWOULDBLOCK}) { if ($opt{n}) { print "Motd currently locked. Editing anyways."; &Edit; } else { flock (M, LOCK_UN); print "Motd currently locked\nWait to acquire lock [y/N] "; chomp(my $ans = <>); if ($ans =~ /^y$/i) { flock(M, LOCK_EX); &Edit; } } } else { &Edit; } close M; system "/csua/bin/mtd"; exit 0; sub Unlock { flock(M, LOCK_UN); print "\r*** MOTD GOD PRONOUNCES: *** \r\n*** You have locked the motd for too long, you idle hoser... *** \r\n*** Your lock has been removed. *** \r\n"; } sub Usage { print STDERR "$0 Edit /etc/motd.public with locking and merge. Usage: $0 [-m] [-s] [-t n] -t n Set timeout for releasing lock to n seconds (default $opt{t}) -m Turns auto-merging off. -s If merge fails, will stomp over other changes. -n Does not wait for lock before allowing edit. (the other person may overwrite your post) "; exit 2; } sub Edit { alarm $opt{t}; print ""; #check if merge option is desired if (!$opt{m}) { copy($motd,$motdbackup); copy($motd,$motdcurrent); my $lastmodtime = (stat($motd))[9]; system("$editor $motdcurrent"); my $currmodtime = (stat($motd))[9]; if ($currmodtime > $lastmodtime) { &Merge; } else { copy($motdcurrent,$motd); } unlink ; } else { system("$editor $motd"); } } # Attempts to auto-merge using "merge, diff -c | patch, diff | patch" # If auto-merge fails, allows manual merge # Saves final version as $motd sub Merge { copy($motd,$motdmerged); system("merge -p -q -L \"Other Changes Below\" -L \"Original MOTD\" -L \"Your Changes Above\" $motd $motdbackup $motdcurrent > $motdtemp"); # Check to see if merge failed. if fail, try diff -c | patch if (($? >> 8) > 0) { system("diff -c $motdbackup $motdcurrent | patch -s -f $motdmerged 2> /dev/null"); # Check to see if diff -c | patch, if fail, try diff | patch if (($? >> 8) > 0) { system("diff $motdbackup $motdcurrent | patch -s -f $motdmerged 2> /dev/null"); # If everything has failed, put the merge notation in, and let the user edit. if (($? >> 8) > 0) { if ($opt{s}) { copy($motdcurrent,$motdmerged); } else { print "CONCURRENT CHANGES\r\nAll merge & patch attempts have failed.\r\nMerge Manually? [y/n]"; chomp(my $answ = <>); if ($answ =~ /^y$/i) { copy($motdtemp,$motdcurrent); copy($motd,$motdbackup); my $lastmodtime = (stat($motd))[9]; system("$editor $motdcurrent"); my $currmodtime = (stat($motd))[9]; if ($currmodtime > $lastmodtime) { &Merge; } else { copy($motdcurrent,$motdmerged); } } else { print "Stomp on other people's changes? [y/n]"; chomp(my $answ = <>); if ($answ =~ /^y$/i) { copy($motdcurrent,$motdmerged); } } } } } } else { # Merge was successful, copy it over. copy($motdtemp,$motdmerged); } copy($motdmerged,$motd); }