#!/usr/bin/perl use strict; use warnings; =pod =head1 SYNOPSIS su - public sync-web [www|offline] =head1 DESCRIPTION Synchronizes the web server from local files. Use .sync-web-control.pl to control. See /usr/local/libexec/.sync-web-control.pl for details. =cut # #use lib '/usr/local/lib'; use File::Basename (); use File::Copy (); use File::Find (); no warnings 'File::Find'; # about deleted files during back-sync (finddepth would prevent that, but is prob. slower) use File::stat (); use Zelea::Sync (); sub _back_sync_file; # # File::Find "wanted" sub that deletes the file unless it exists in $_to_dir. # our $_from_dir; sub _sync_dir( $ ); # ( dir ) # # A kind of File::Find::find that only descends into subdirectories # if the "wanted" function (always _synch_file) returns true. # sub _sync_file( $$ ); # ( base name, file ) ret$ # # _sync_dir "wanted" sub that copies the file to $_to_dir, # if out-of-date or non-existent there. # $_from_dir must contain the root directory exactly as passed to _sync_dir. # # So, given: # # $_from_dir = /var/from/somewhere # $_from_dir = /var/to/elsewhere # $file = /var/from/somewhere/specific/file # # the $file will be synchronized with: # # $to_file = /var/to/elsewhere/specific/file # # File copying is subject to .sync-web-control.pl, # and any forbidden files will be deleted from the server, if already there. # sub to_sync_default( $$ ); # ( base name, file ) ret$ # # What is this? It does not appear to be called. # our $_to_dir; our $_to_sync_SUB; my $server = shift; defined $server or $server = 'www'; { my $user_name = `whoami`; chomp $user_name; # guard against root running it my $expected_name = 'public'; # so that only public files sent $user_name eq $expected_name or die "must run as user '$expected_name'"; } my $command; my $mountpoint = "/mnt/lan/$server"; my $was_mounted_here = 0; # till proven otherwise { open MTAB, " ) { $command = "/bin/mount $mountpoint"; system $command and die 'unable to execute: ' . $command; $was_mounted_here = 1; } close MTAB; } } { my $local_dir = '/home/mike'; my $server_dir = $mountpoint . '/var/www/localhost/htdocs'; # Back-synchronize the server, deleting any files that are no longer on home. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` $_from_dir = $server_dir; $_to_dir = $local_dir; File::Find::find( {no_chdir=>1, wanted=>\&_back_sync_file}, $_from_dir ); # Synchronize the server from home. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` $_from_dir = $local_dir; $_to_dir = $server_dir; # File::Find::find( {follow_fast=>1, wanted=>\&_sync_file}, $_from_dir ); ## not the right approach, because it descends to full depth # instead I need a trimmed traversal (else it will pass me children even after I've rejected the directory) # and the follow options are hell when there are multiple links to the same file, as I have _sync_dir( $_from_dir ); } { my $local_dir = '/usr/local/share/icons'; my $server_dir = $mountpoint . '/var/www/localhost/icons/local'; # Back-synchronize the server, deleting any files that are no longer on home. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` $_from_dir = $server_dir; $_to_dir = $local_dir; File::Find::find( {no_chdir=>1, wanted=>\&_back_sync_file}, $_from_dir ); # Synchronize the server from home. # ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` $_from_dir = $local_dir; $_to_dir = $server_dir; # File::Find::find( {follow_fast=>1, wanted=>\&_sync_file}, $_from_dir ); ## not the right approach, because it descends to full depth # instead I need a trimmed traversal (else it will pass me children even after I've rejected the directory) # and the follow options are hell when there are multiple links to the same file, as I have _sync_dir( $_from_dir ); } # - - - if( $was_mounted_here ) { $command = "/bin/umount $mountpoint"; system $command and die 'unable to execute: ' . $command; } exit; sub _back_sync_file { my $from_file = $File::Find::name; # /var/to/elsewhere/com/z/p/X.class $from_file eq $_from_dir and return; # do not sync root dir my $rel_file = substr( $from_file, length($_from_dir) ); # /com/z/p/X.class my $to_file = $_to_dir . $rel_file; -e $to_file or Zelea::Sync::delete( $from_file, $rel_file ); } #my $trace; sub _sync_dir( $ ) { my $dir = shift; my $DIR; opendir( $DIR, $dir ) or die $dir . ': ' . $!; my $name; my $file; while( $name = readdir $DIR ) { $name eq '.' || $name eq '..' and next; #$trace = $name eq 'conf'; $file = $dir . '/' . $name; my $wanted = _sync_file( $name, $file ); #$trace and print "\$file=$file\n"; #if( $trace ) #{ # my $target = readlink $file; # my $result = -d $target; # print "\$result=$result\n"; #} $wanted && -d $file and _sync_dir( $file ); # recurse } closedir( $DIR ); } sub _sync_file( $$ ) { my $name = shift; # X.class my $from_file = shift; # /var/from/somewhere/com/z/p/X.class my $wanted = 0; # till proven otherwise # $from_file eq $_from_dir and return $wanted; # do not sync root dir (but only File::Find passes that in, not my _sync_dir) my $rel_file = substr( $from_file, length($_from_dir) ); # /com/z/p/X.class my $to_file = $_to_dir . $rel_file; # Read control file. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { my $from_subdir = File::Basename::dirname( $from_file ); my $control_file = $from_subdir . '/.sync-web-control.pl'; if( !-f $control_file ) { $control_file = '/usr/local/libexec/.sync-web-control.pl'; } our $_sync_file_last_control; if( !defined $_sync_file_last_control || $control_file ne $_sync_file_last_control ) { # print " loading $control_file\n"; do $control_file or die $!; # without death, compile errors are silent $_sync_file_last_control = $control_file; } } # Skip files that are not supposed to be synched. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if( !&$_to_sync_SUB( $name, $from_file )) { -e $to_file and Zelea::Sync::delete( $to_file, $rel_file ); return $wanted; } $wanted = 1; # Sync. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if( -d $from_file ) { -d $to_file and return $wanted; Zelea::Sync::echo( $Zelea::Sync::prefix_add . $Zelea::Sync::prefix_dir, $rel_file ); mkdir( $to_file ) or die; } else # file { my $mtime_from = File::stat::stat($from_file)->mtime; if( -e $to_file ) { my $mtime_to = File::stat::stat($to_file)->mtime; $mtime_from > $mtime_to or return $wanted; } Zelea::Sync::echo( $Zelea::Sync::prefix_add . $Zelea::Sync::prefix_file, $rel_file ); my $to_subdir = File::Basename::dirname( $to_file ); File::Copy::copy( $from_file, $to_subdir .'/' ) or die $! . ": $from_file\n"; utime( time, $mtime_from, $to_file ) or warn; } return $wanted; } sub to_sync_default( $$ ) { my $name = shift; my $file = shift; #if( $file =~ m'personal/resume-detail/scheduler/screen-shot.css' ) #{ # my $pass = -r $file && $name !~ m'^\.'; ##if( $pass && -l $file ) ##{ ## my $target = readlink $file; ## my $base_dir = File::Basename::dirname( $file ); ## print " $base_dir\n"; ## $pass = -r $target; ##} # $pass or $pass = 0; # print "$pass $file\n"; #} # return -r $file && $name !~ m'^\.' && (!-l $file || -r readlink $file); ## -r $file dereferences links, so no need -r $file or return 0; $name eq '.sync-web-control.pl' and return 0; $file =~ m'^/home/mike/project/jaxe/repo/(?:.+/)?\.#' and return 0; $file =~ m'^/home/mike/project/jaxe/repo/CVS' and return 0; $file =~ m'^/home/mike/project/jaxe/repo/jaxe/(?:.+/)?CVS' and return 0; return 1; } __END__ =pod =head1 AUTHOR Michael Allan =cut