#!/usr/bin/perl # $Id: distit,v 1.33 1997/01/03 00:22:40 drz Exp drz $ # Copyright (c) 1996 Tim Newsome # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You can find a copy of the GNU General Public License at # ftp://prep.ai.mit.edu/pub/gnu/COPYING or by writing to the Free # Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # I wrote this cute little perl script to keep my .files the same on my # miscellaneous shell accounts. The program gets all the files you specify # from all your accounts. Then it determines which one is the newest, and # copies that to all your accounts. It uses ssh, but it should work with # rsh too. # Put a list of all your accounts in ~/.di_accounts, and a list of files # you want distributed in ~/.di_files. You can put a comment in by starting # the line with a #. Here are mine: # ------ .di_accounts ------ # drz@localhost # drz@raven.cybercomm.net # tim@regal.megasoft.com # tim@reddevil.megasoft.com # ------ .di_files ------ # .addressbook # .di_* # .fvwmrc # .indent.pro # .ircrc # .lice.save # .netscape/bookmarks.html # .signature # .vimrc # .zshrc # distit now also takes command line parameters. # To include an account not in your .di_accounts or a file not in your # .di_files just add it on the command line. # If you don't want distit to read your .di_files or .di_accounts use # -nf and -na on the commandline respectively. That way you could for # instance use # distit -nf -na .forward me@old.account me@different.old.account # To only distribute certain files to certain accounts. # Enjoy! I'd appreciate any feedback you may have. sub user_configurable { # Where to put temporary files. This directory will be "rm -rf"ed so # pick a directory that will only be used for distit. $tempdir = "$mydir/".".distit"; # Verbosity level. # 0 == quiet, 5 == max $chatter = 2; # You should be able to change those to rsh without any big problems. $ssh = "ssh"; $scp = "scp"; # If all your accounts have it installed, you can use md5sum which should # be more reliable (and a bit slower) in detecting changes. $sum = "sum"; # Are there any other programs that are flag-compatible with tar? $tar = "tar"; # The config files. $di_accounts = "$mydir/.di_accounts"; $di_files = "$mydir/.di_files"; } sub init { $mydir = $ENV{'HOME'}; if ( $mydir eq "" ) { $me = `whoami`; chomp($me); $mydir = "/home/$me"; } user_configurable; $use_accounts = 1; $use_files = 1; for $arg (@ARGV) { if ($arg eq "-na") { $use_accounts = 0; } elsif ( $arg eq "-nf" ) { $use_files = 0; } elsif ($arg =~ /^\w*\@/) { # It's an account name. push @accounts, $arg; } else { # It's a file-name. push @files, $arg; } } if ($use_accounts) { open(ACCOUNTS, $di_accounts) || die "Can't open $di_accounts: $!"; while () { unless (/^#/ || /^\s*$/) { chomp; push @accounts, $_; } } close ACCOUNTS; } if ($use_files) { open(FILES, $di_files) || die "Can't open $di_files: $!"; while () { unless (/^#/ || /^\s*$/) { chomp; push @files, $_; } } close FILES; } %monthnum = ("Jan", "01", "Feb", "02", "Mar", "03", "Apr", "04", "May", "05", "Jun", "06", "Jul", "07", "Aug", "08", "Sep", "09", "Oct", "10", "Nov", "11", "Dec", "12"); $time = localtime; ($curmonth, $curyear) = ($time =~ /\w*\s*(\w*).*(\d{4})/); # This works on all shells, 2>&1 is bash-clone specific. #$tostdout = "|& cat"; } sub is_up { local $host = $_[0]; open(PING, "$ping $host |"); while() { } } sub main { # Expand any shell globbing. $tmp = `echo @files`; chomp($tmp); @files = split(/ /, $tmp); # Zeroth, set all files to not existing. for $account (@accounts) { for $file (@files) { $sum{$file}{$account} = "!exists"; } } # First, get sum and date info about the files. for $account (@accounts) { chatter(1, "Getting info from $account...\n"); ($user, $host) = ($account =~ /(.*)\@(.*)/); $flist = ""; for $file (@files) { $flist .= " $file"; } # Add /dev/null so sum will always print a filename, even if $flist # only contains one file. $cmd = "$ssh -l $user $host \"$sum $flist /dev/null; ls -ld $flist\""; chatter(3, "$cmd\n"); open(INFO, "$cmd |") || die "Couldn't open \"$cmd\": $!"; while () { chomp; if (/^\s*(\d+)\s+(\d+)\s+(\S+)$/) { # Result of sum $sum{$3}{$account} = "$1-$2"; } elsif (/^(\w+)\s+(\S+)$/) { # Result of md5sum $sum{$2}{$account} = $1; } elsif (/(\d+)\s+(\w+)\s+(\d+)\s+(\S+)\s+(\S+)$/) { # result of ls # $yot == year or time # ls prints either the year, or the modification time depending on # how long ago the file was last modified (cutoff is 6 months) ($size, $month, $date, $yot, $fname) = ($1, $2, $3, $4, $5); if ($yot =~ /(\d*):(\d*)/) { # File has been modified in the last 6 months, so the year # wasn't given. if ($monthnum{$curmonth} >= $monthnum{$month}) { $year = $curyear; } else { $year = $curyear - 1; } $hour = $1; $minute = $2; } else { $year = $yot; $hour = "00"; $minute = "00"; } $date{$fname}{$account} = sprintf( "$year$monthnum{$month}%02d$hour$minute", $date); } chatter(4, "$_\n"); } close INFO; } for $file (@files) { chatter(2, "Info for $file:\n"); for $account (@accounts) { chatter(2, sprintf("\t%30s\t%12s\t$date{$file}{$account}\n", $account, $sum{$file}{$account})); } } # Now, let's see which files need to be redisted. for $file (@files) { $sum = ""; for $account (@accounts) { if ($sum{$file}{$account} ne "") { if ($sum ne "" && $sum ne $sum{$file}{$account}) { push @distlist, $file; last; } $sum = $sum{$file}{$account}; } } } if ($#distlist == -1) { chatter(1, "All files are up-to-date everywhere.\n"); return; } chatter(1, "Redistributing @distlist...\n"); # Next, get the latest edition of each file. $cmd = "rm -rf $tempdir ; mkdir $tempdir"; chatter(3, "$cmd\n"); system($cmd); chdir $tempdir; for $file (@distlist) { $lastdate = 0; $distacct{$file} = ""; for $account (@accounts) { if ($date{$file}{$account} > $lastdate) { $lastdate = $date{$file}{$account}; $distacct{$file} = $account; } } chatter(1, "Leeching $file from $distacct{$file}...\n"); #$cmd = "$scp -l $distacct{$file}:~/$file $tempdir/"; ($user, $host) = ($distacct{$file} =~ /(.*)\@(.*)/); $cmd = "$ssh -l $user $host $tar cfp - $file 2>/dev/null | tar xfp -"; chatter(3, "$cmd\n"); system($cmd); } # And send it over. $tarfile = "$tempdir/updates.tar"; for $account(@accounts) { $flist = ""; for $file (@distlist) { if ($sum{$file}{$distacct{$file}} ne $sum{$file}{$account}) { # This account has a different file from the one we're # disting. $flist .= " $file"; } } if ($flist ne "") { chatter(1, "Sharing the wealth with $account...\n"); $cmd = "$tar cfp $tarfile $flist"; chatter(3, "$cmd\n"); system($cmd);# || die "\"$cmd\" failed: $!"; ($user, $host) = ($account =~ /(.*)\@(.*)/); $cmd = "$ssh -l $user $host $tar xfp - < $tarfile"; chatter(3, "$cmd\n"); system($cmd); } } # Finally, clear up our directory. system("rm -rf $tempdir"); return; } sub chatter { local ($level, $text) = ($_[0], $_[1]); if ($level <= $chatter) { print $text; } } init; main;