diff options
author | Gunnar Wrobel <wrobel@gentoo.org> | 2005-08-25 20:32:33 +0000 |
---|---|---|
committer | Gunnar Wrobel <wrobel@gentoo.org> | 2005-08-25 20:32:33 +0000 |
commit | 6a06aa6fd47f05ce256880273b5edddf8141a070 (patch) | |
tree | 8036fcb9b5dc527e59095cc1cc49b9ff54f55410 /dev-util | |
parent | typo fix (diff) | |
download | overlay-6a06aa6fd47f05ce256880273b5edddf8141a070.tar.gz overlay-6a06aa6fd47f05ce256880273b5edddf8141a070.tar.bz2 overlay-6a06aa6fd47f05ce256880273b5edddf8141a070.zip |
naming fix
svn path=/; revision=317
Diffstat (limited to 'dev-util')
-rw-r--r-- | dev-util/svnreplicate/files/README | 40 | ||||
-rwxr-xr-x | dev-util/svnreplicate/files/svnreplicate-59 | 997 | ||||
-rw-r--r-- | dev-util/svnreplicate/files/svnreplicate.conf | 76 | ||||
-rw-r--r-- | dev-util/svnreplicate/svnreplicate-0.59.ebuild | 29 |
4 files changed, 1142 insertions, 0 deletions
diff --git a/dev-util/svnreplicate/files/README b/dev-util/svnreplicate/files/README new file mode 100644 index 0000000..8c1d243 --- /dev/null +++ b/dev-util/svnreplicate/files/README @@ -0,0 +1,40 @@ +svn hook scripts: +================= + +This script gives you the possibillity to replicate subversion repositories. +This happens in a master/slave form, where every slave as well the master +can commit locally as long as the master is reachable by that paricular +slave repository. 1 master and N slaves are supported. + +The script can handle multiple replication sources and targets. It needs to +be able access its repositories locally and by calling a copy of itself +by secure shell. + +You have to plug in svnreplicate into the script hooks in you repository by +adding the follwing lines to the corresponding files: + +repository/hook/start-commit: +----------------------------- +/<path to script>/svnreplicate hook-start-commit "$REPOS" "$USER" || exit 1 + +repository/hook/pre-commit: +--------------------------- +/<path to script>/svnreplicate hook-pre-commit "$REPOS" "$TXN" || exit 1 + +repository/hook/post-commit: +---------------------------- +/<path to script>/svnreplicate hook-post-commit "$REPOS" "$REV" + +repository/hook/start-read: (does not exist at the moment) +---------------------------------------------------------- +Another hook is provided. its named 'hook-start'. + +Since svn does not support a read-hook, a starting slave has no way to +synchronize with the master. As soon as that's done, the slave will get +automatically updated on commit on the master. + +But to provide a startup synchronisation, so that a slave does not have to +wait for the first commit to get updated, the 'hook-start' hook exists. it +can be called in the (slave or master) svn server's startup scripts after +the svn server is running. + diff --git a/dev-util/svnreplicate/files/svnreplicate-59 b/dev-util/svnreplicate/files/svnreplicate-59 new file mode 100755 index 0000000..dd9b176 --- /dev/null +++ b/dev-util/svnreplicate/files/svnreplicate-59 @@ -0,0 +1,997 @@ +#!/usr/bin/perl + +use Sys::Syslog; +use Getopt::Long; +use IPC::Open2; + +Getopt::Long::Configure ("bundling"); + +push @INC, "/etc/svnreplicate"; +push @INC, "/etc"; +# push @INC, "/root/svntest/src/svnreplicate"; # remove after debug +# my $dot = pop @INC; +# push @INC, $dot; + +require "svnreplicate.conf"; + +my $local_rep = '?'; + +# +# debug levels +# + +$LOG_EMERG = 'emerg'; +$LOG_ALERT = 'alert'; +$LOG_CRIT = 'crit'; +$LOG_ERR = 'err'; +$LOG_WARN = 'warning'; +$LOG_NOTICE = 'notice'; +$LOG_INFO = 'info'; +$LOG_DEBUG = 'debug'; + +# +# helper functions +# + +sub help() +{ + print "\nThis command replicates subversion repository while allowing commits on\n"; + print "all nodes as long as the nodes can reach the master node.\n"; + print "\nUsage: svnreplicate [--help|cmd [args]]\n"; + print "\n cmd can be:\n\n"; + print "\thook-start\t\t<hook-rep>\n"; + print "\thook-start-commit\t<hook-rep> <commit-user>\n"; + print "\thook-pre-commit\t\t<hook-rep> <commit-txn>\n"; + print "\thook-post-commit\t<hook-rep> <commit-rev>\n"; + print "\tsync\t\t\t<caller-rep> <callee-rep> <caller-youngest-rev>\n"; + print "\tlock-sync\t\t<caller-rep> <callee-rep> <caller-youngest-rev>\n"; + print "\tcommit-unlock\t\t<caller-rep> <callee-rep> <y.-rev> <oldest-rev>\n"; + print "\n"; + print "The command and it's arguments can be supplied on the command line or\n"; + print "on the first line of standard input\n"; + print "\n"; + exit(1); +} + +sub logger +{ + my $level = shift; + my @args = @_; + my $text = join(//, @args); + + syslog($level, "[$local_rep] $text") if ($DO_LOG_SYSLOG); + if ($DO_LOG_FILE) + { + if (open(L, ">> $LOG_FILE")) + { + my $date = `date '+%Y-%m-%d %X'`; chomp($date); + print L $date . " [$local_rep:$level] " . $text . "\n"; + } + else + { + logger(LOG_ALERT, "failed to open log file $LOG_FILE!"); + $DO_LOG_FILE = 0; + } + } + return; +} + +sub exit_logger +{ + my $code = shift; + my $level = shift; + my @args = @_; + my $text = join(//, @args); + + logger($level, @args); + logger(LOG_DEBUG, "--- exiting pid $$"); + exit($code); +} + +sub lock_pid +{ + my $rep = shift; + my $PID_FILE = $PID_PATH . "/svnreplicate." . $rep . ".pid"; + + stat($PID_FILE); + if (-f _ && -r _) + { + my $p = ''; + + if (open(P, "< $PID_FILE")) + { + $p = <P>; + chomp($p); + close(P); + } + else + { + logger(LOG_ALERT, "failed to open pid file '$PID_FILE'!"); + return -2; + } + + `ps $p | grep -v PID`; + if (! /^$/) + { + logger(LOG_WARNING, "failed to lock pid file '$PID_FILE'!"); + logger(LOG_WARNING, "process with pid $p is already running!"); + return $pid; + } + } + elsif (-f _) + { + logger(LOG_ALERT, "failed to read pid file '$PID_FILE'!"); + return -2; + } + + if (open(F, "> $PID_FILE")) + { + print F "$$\n"; + close(F); + + my $p = ''; + if (open(P, "< $PID_FILE")) + { + $p = <P>; + chomp($p); + close(P); + } + else + { + logger(LOG_ALERT, "failed to open pid file '$PID_FILE'!"); + return -2; + } + + if ($p != $$) + { + logger(LOG_WARNING, "failed to lock pid file '$PID_FILE'!"); + logger(LOG_WARNING, "process with pid $p is already running!"); + return $p; + } + + logger(LOG_DEBUG, "successfully locked pid file for repository '" . $rep . "'"); + return 0; + } + else + { + logger(LOG_ALERT, "failed to write pid file '$PID_FILE'"); + return -1; + } + + return -3; +} + +sub unlock_pid +{ + my $rep = shift; + my $PID_FILE = $PID_PATH . "/svnreplicate." . $rep . ".pid"; + + unlink($PID_FILE); + logger(LOG_DEBUG, "successfully removed pid file for repository '" . $rep . "'"); +} + +sub lock_server +{ + my $rep = shift; + my $PID_FILE = $PID_PATH . "/svnreplicate.lock." . $rep . ".pid"; + + stat($PID_FILE); + if (-f _ && -r _) + { + my $p = ''; + + if (open(F, "< $PID_FILE")) + { + $p = <F>; + chomp($p); + close(F); + } + else + { + logger(LOG_ALERT, "failed to open server lock file '$PID_FILE'!"); + return -2; + } + + `ps $p | grep -v PID`; + if (! /^$/) + { + logger(LOG_WARNING, "failed to lock server lock file '$PID_FILE'!"); + logger(LOG_WARNING, "process with pid $p is already running!"); + return $p; + } + } + elsif (-f _) + { + logger(LOG_ALERT, "failed to read server lock file '$PID_FILE'!"); + return -2; + } + + if (open(F, "> $PID_FILE")) + { + print F "$$\n"; + close(F); + + my $p = ''; + if (open(F, "< $PID_FILE")) + { + $p = <F>; + chomp($p); + close(F); + } + else + { + logger(LOG_ALERT, "failed to open server lock file '$PID_FILE'!"); + return -2; + } + + if ($p != $$) + { + logger(LOG_WARNING, "failed to lock server lock file '$PID_FILE'!"); + logger(LOG_WARNING, "process with pid $p is already running!"); + return $p; + } + + logger(LOG_DEBUG, "successfully locked server lock file for repository '" . $rep . "'"); + return 0; + } + else + { + logger(LOG_ALERT, "failed to write server lock file '$PID_FILE'"); + return -1; + } + + return -3; +} + +sub unlock_server +{ + my $rep = shift; + my $PID_FILE = $PID_PATH . "/svnreplicate.lock." . $rep . ".pid"; + + unlink($PID_FILE); + logger(LOG_DEBUG, "successfully removed server lock file for repository '" . $rep . "'"); +} + +# +# command parsing +# + +sub cmd_parse() +{ + my %res = (); + + if ($#ARGV >= 0) + { + my $args = join(' ', @ARGV); + + logger(LOG_DEBUG, "cmd is '$args'"); + + if ($ARGV[0] =~ /^(hook-.+)$/) + { + # a hook-* command + $res{'cmd'} = $ARGV[0]; + $res{'hook'} = 1; + $res{'local-rep-name'} = $rep_path2rep{$ARGV[1]}; + $res{'local-rep'} = $ARGV[1]; + $res{'hook-rep'} = $ARGV[1]; + $res{'hook-txn'} = $ARGV[2] if ($ARGV[0] =~ /^hook-pre-commit$/); + $res{'hook-user'} = $ARGV[2] if ($ARGV[0] =~ /^hook-start-commit$/); + $res{'hook-rev'} = $ARGV[2] if ($ARGV[0] =~ /^hook-post-commit$/); + } + elsif ($ARGV[0] eq 'sync') + { + $res{'cmd'} = $ARGV[0]; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$ARGV[2]}; + $res{'local-rep'} = $ARGV[2]; + $res{'remote-rep'} = $ARGV[1]; + $res{'rev-youngest'} = $ARGV[3]; + } + elsif ($ARGV[0] eq 'lock-sync') + { + $res{'cmd'} = $ARGV[0]; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$ARGV[2]}; + $res{'local-rep'} = $ARGV[2]; + $res{'remote-rep'} = $ARGV[1]; + $res{'rev-youngest'} = $ARGV[3]; + } + elsif ($ARGV[0] eq 'commit-unlock') + { + $res{'cmd'} = $ARGV[0]; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$ARGV[2]}; + $res{'local-rep'} = $ARGV[2]; + $res{'remote-rep'} = $ARGV[1]; + $res{'rev-youngest'} = $ARGV[3]; + $res{'rev-oldest'} = $ARGV[4]; + } + elsif ($ARGV[0] eq 'slave-commit') + { + $res{'cmd'} = $ARGV[0]; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$ARGV[2]}; + $res{'local-rep'} = $ARGV[2]; + $res{'remote-rep'} = $ARGV[1]; + $res{'rev-youngest'} = $ARGV[3]; + $res{'rev-oldest'} = $ARGV[4]; + } + } + else + { + my $args = <STDIN>; chomp($args); + + logger(LOG_DEBUG, "stdin command found!"); + logger(LOG_DEBUG, "cmd is '$args'"); + + if ($args =~ /^([^\s]+) ([^\s]+)$/) + { + if ($1 eq "hook-start") + { + $res{'cmd'} = $1; + $res{'hook'} = 1; + $res{'local-rep-name'} = $rep_path2rep{$2}; + $res{'local-rep'} = $2; + $res{'hook-rep'} = $2; + } + } + elsif ($args =~ /^([^\s]+) ([^\s]+) ([^\s]+)$/) + { + if ($1 eq 'hook-pre-commit' || $1 eq 'hook-start-commit' || $1 eq 'hook-post-commit') + { + $res{'cmd'} = $1; + $res{'hook'} = 1; + $res{'local-rep-name'} = $rep_path2rep{$2}; + $res{'local-rep'} = $2; + $res{'hook-rep'} = $2; + $res{'hook-txn'} = $3 if ($1 =~ /^hook-pre-commit$/); + $res{'hook-user'} = $3 if ($1 =~ /^hook-start-commit$/); + $res{'hook-rev'} = $3 if ($1 =~ /^hook-post-commit$/); + } + } + elsif ($args =~ /^([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)$/) + { + if ($1 eq 'sync') + { + $res{'cmd'} = $1; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$2}; + $res{'local-rep'} = $2; + $res{'remote-rep'} = $3; + $res{'remote-rev-youngest'} = $4; + } + elsif ($1 eq 'lock-sync') + { + $res{'cmd'} = $1; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$2}; + $res{'local-rep'} = $2; + $res{'remote-rep'} = $3; + $res{'remote-rev-youngest'} = $4; + } + } + elsif ($args =~ /^([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)$/) + { + if ($1 eq 'commit-unlock') + { + $res{'cmd'} = $1; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$2}; + $res{'local-rep'} = $2; + $res{'remote-rep'} = $3; + $res{'rev-youngest'} = $4; + $res{'rev-oldest'} = $5; + } + elsif ($1 eq 'slave-commit') + { + $res{'cmd'} = $1; + $res{'hook'} = 0; + $res{'local-rep-name'} = $rep_path2rep{$2}; + $res{'local-rep'} = $2; + $res{'remote-rep'} = $3; + $res{'rev-youngest'} = $4; + $res{'rev-oldest'} = $5; + } + } + } + + $local_rep = $res{'local-rep-name'}; + + return \%res; +} + +sub cmd_dump +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my @keys = sort keys %cmd; + + if ($#keys < 0) + { + logger(LOG_ALERT, "could not parse command!"); + exit_logger(1, LOG_ALERT, "cmd = '" . join(' ', @ARGV) . "'"); + } + + foreach $k (@keys) + { + logger(LOG_INFO, "[" . $k . "] = " . $cmd{$k}); + } + + return; +} + +sub svn_dump +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $to = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($to); + my $from = $to; $from = $cmd{'remote-rev-youngest'} + 1 if ($cmd{'remote-rev-youngest'} =~ /^[0123456789]+$/); + my $res = ''; + + if ($from <= $to) + { + logger(LOG_INFO, "svn_dump() dump of repository '" . $cmd{'local-rep-name'} . "'"); + logger(LOG_INFO, "svn_dump() dumping revision " . $from . " to " . $to); + + # dump actual commit from this repository to %cmd + + my $dcmd = $cmd_svnadmin . " dump " . $rep_repl_path{$cmd{'local-rep-name'}} . " -r " . $from . ":" . $to . " --incremental 2>>" . $rep_repl_log{$cmd{'local-rep-name'}}; + + logger(LOG_DEBUG, "svn_dump() using '" . $dcmd . "'"); + + $res = "DUMP-DATA " . $from . " " . $to . "\n"; + if (open F, "$dcmd |") + { + while(<F>) + { + $res = $res . $_; + } + close(F); + + logger(LOG_INFO, "svn_dump() successfully dumped revisions $from to $to"); + } + else + { + exit_logger(0, LOG_ALERT, "svn_dump() failed to dump repository '" . $cmd{'local-rep'} . "'"); + } + } + else + { + logger(LOG_INFO, "svn_dump() no need to dump anything. both repository are in sync!"); + logger(LOG_INFO, "svn_dump() youngest revision is $to"); + $res = "DUMP-DATA " . $from . " " . $to . "\n"; + } + + $$cmdref{'dump'} = $res; + $$cmdref{'dump-rev-oldest'} = $from; + $$cmdref{'dump-rev-youngest'} = $to; + + return; +} + +sub svn_load +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $from = $cmd{'dump-rev-oldest'}; + my $to = $cmd{'dump-rev-youngest'}; + my $dump = $cmd{'dump'}; + + if ($from <= $to) + { + # load commit-dump from %cmd to this repository + + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + + logger(LOG_INFO, "svn_load() load of repository '" . $cmd{'local-rep-name'} . "'"); + logger(LOG_DEBUG, "svn_load() youngest revision before load is " . $rev); + logger(LOG_INFO, "svn_load() loading revisions " . $from . " to " . $to); + + my $lcmd = $cmd_svnadmin . " load " . $rep_repl_path{$cmd{'local-rep-name'}} . " >>" . $rep_repl_log{$cmd{'local-rep-name'}} . " 2>&1"; + + logger(LOG_DEBUG, "svn_load() using '" . $lcmd . "'"); + + if (open(F, "| $lcmd")) + { + print F $dump; + close(F); + } + + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + + logger(LOG_DEBUG, "svn_load() youngest revision after load is " . $rev); + + if ($rev ne $to) + { + exit_logger(1, LOG_ALERT, "svn_load() loading revisions " . $from . " to " . $to . " failed! youngest revision is " . $rev); + } + + logger(LOG_INFO, "svn_load() loading succeeded successfully!"); + } + else + { + logger(LOG_INFO, "svn_load() nothing to load. both repositories are in sync!"); + } +} + +sub dump_stdout +{ + my $cmdref = shift; + my %cmd = %$cmdref; + + print $cmd{'dump'} . "\n"; +} + +sub load_stdin +{ + my $cmdref = shift; + my %cmd = %$cmdref; + + my $dump = ''; + my $lines = 0; + my $res = <STDIN>; + + while(<STDIN>) + { + $dump = $dump . $_; + $lines++; + } + + if ($res =~ /^DUMP-DATA (.+) (.+)$/) + { + logger(LOG_INFO, "load_stdin() received repository dump [$lines lines] from repository '" . $rep_path2rep{$cmd{'remote-rep'}} . "'"); + $$cmdref{'dump'} = $dump; + $$cmdref{'dump-rev-oldest'} = $1; + $$cmdref{'dump-rev-youngest'} = $2; + } + else + { + exit_logger(1, LOG_ALERT, "load_stdin() failed to read dump from remote repository"); + } +} + +sub call_sync +{ + my $cmdref = shift; + my %cmd = %$cmdref; + + # call sync in master repository and store dump in %cmd + + logger(LOG_DEBUG, "call_sync() for master " . $rep_master . " called"); + + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + my $rcall = $rep_repl_comm{$cmd{'local-rep-name'}} . " " . $rep_repl_host{$rep_master} . " " . $rep_repl_script{$rep_master}; + my $rcmd = "sync " . $rep_repl_path{$rep_master} . " " . $cmd{'local-rep'} . " " . $rev; + + logger(LOG_INFO, "call_sync() found youngest revision is $rev"); + logger(LOG_INFO, "call_sync() sending command '" . $rcmd . "'"); + + if (open(F, "echo $rcmd | $rcall |")) + { + my $dump = ''; + my $lines = 0; + my $res = <F>; + + while(<F>) + { + $dump = $dump . $_; + $lines++; + } + close(F); + + if ($res =~ /^DUMP-DATA (.+) (.+)$/) + { + logger(LOG_INFO, "call_sync() received repository dump [$lines lines] from repository '" . $rep_master . "'"); + $$cmdref{'dump'} = $dump; + $$cmdref{'dump-rev-oldest'} = $1; + $$cmdref{'dump-rev-youngest'} = $2; + } + else + { + exit_logger(1, LOG_ALERT, "call_sync() failed to call 'sync' on remote repository"); + } + } + else + { + exit_logger(0, LOG_ALERT, "call_sync() failed to connect to master!"); + } +} + +sub call_lock_sync +{ + my $cmdref = shift; + my %cmd = %$cmdref; + + # call lock-sync in master repository and store dump in %cmd + + logger(LOG_DEBUG, "call_lock_sync() for master " . $rep_master . " called"); + + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + my $rcall = $rep_repl_comm{$cmd{'local-rep-name'}} . " " . $rep_repl_host{$rep_master} . " " . $rep_repl_script{$rep_master}; + my $rcmd = "lock-sync " . $rep_repl_path{$rep_master} . " " . $cmd{'local-rep'} . " " . $rev; + + logger(LOG_INFO, "call_lock_sync() found youngest revision is $rev"); + logger(LOG_INFO, "call_lock_sync() sending command '" . $rcmd . "'"); + + if (open(F, "echo $rcmd | $rcall |")) + { + my $dump = ''; + my $lines = 0; + my $res = <F>; + + while(<F>) + { + $dump = $dump . $_; + $lines++; + } + close(F); + + if ($res =~ /^DUMP-DATA (.+) (.+)$/) + { + logger(LOG_INFO, "call_lock_sync() received repository dump [$lines lines] from repository '" . $rep_master . "'"); + $$cmdref{'dump'} = $dump; + $$cmdref{'dump-rev-oldest'} = $1; + $$cmdref{'dump-rev-youngest'} = $2; + } + else + { + exit_logger(1, LOG_ALERT, "call_lock_sync() failed to call 'lock-sync' on remote repository"); + } + } + else + { + exit_logger(0, LOG_ALERT, "call_lock_sync() failed to connect to master!"); + } +} + +sub call_commit_unlock +{ + my $cmdref = shift; + my %cmd = %$cmdref; + + # call commit-unlock in master repository and send dump from %cmd + + logger(LOG_DEBUG, "call_commit_unlock() for master '" . $rep_master . "' called"); + + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); +# my $rcall = $rep_repl_comm{$cmd{'local-rep-name'}} . " " . $rep_repl_host{$rep_master} . " " . $rep_repl_script{$rep_master}; + my $rcall = $rep_repl_comm{$rep_master} . " " . $rep_repl_host{$rep_master} . " " . $rep_repl_script{$rep_master}; + my $rcmd = "commit-unlock " . $rep_repl_path{$rep_master} . " " . $cmd{'local-rep'} . " " . $rev . " " . $rev; + + logger(LOG_INFO, "call_commit_unlock() found youngest revision is $rev"); + logger(LOG_INFO, "call_commit_unlock() sending command '" . $rcmd . "'"); + + if (open2(R, W, "$rcall")) + { + my $dump = ''; + my $lines = 0; + + print W $rcmd . "\n"; + print W $cmd{'dump'}; + close(W); + + my $res = <R>; chomp($res); + close(R); + + if (($res =~ /^OK (.+)$/) && ($1 eq $cmd{'dump-rev-youngest'})) + { + logger(LOG_INFO, "call_commit_unlock() successfully sent local repository dump to repository '" . $rep_master . "'"); + } + else + { + exit_logger(1, LOG_ALERT, "call_commit_unlock() failed to call 'commit_unlock' on remote repository"); + } + } + else + { + exit_logger(1, LOG_ALERT, "call_commit_unlock() failed to connect to master!"); + } +} + +sub call_slave_commit +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $rep_slave = shift; + + # call slave-commit in master repository and send dump from %cmd + + logger(LOG_DEBUG, "call_slave_commit() on slave '" . $rep_slave . "' called"); + + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + my $rcall = $rep_repl_comm{$rep_slave} . " " . $rep_repl_host{$rep_slave} . " " . $rep_repl_script{$rep_slave}; + my $rcmd = "slave-commit " . $rep_repl_path{$rep_slave} . " " .$rep_repl_path{$rep_master} . " " . $rev . " " . $rev; + + logger(LOG_INFO, "call_slave_commit() found youngest revision is $rev"); + logger(LOG_INFO, "call_slave_commit() sending command '" . $rcmd . "'"); + + if (open2(R, W, "$rcall")) + { + my $dump = ''; + my $lines = 0; + + print W $rcmd . "\n"; + print W $cmd{'dump'}; + close(W); + + my $res = <R>; chomp($res); + close(R); + + if (($res =~ /^OK (.+)$/) && ($1 eq $cmd{'dump-rev-youngest'})) + { + logger(LOG_INFO, "call_slave_commit() successfully sent local repository dump to repository '" . $rep_master . "'"); + } + else + { + logger(LOG_WARNING, "call_slave_commit() failed to call 'slave-commit' on remote repository"); + } + } + else + { + logger(LOG_WARNING, "call_slave_commit() failed to connect to slave!"); + } +} + +sub exec_hook_start +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "hook_start() called for repository '$lrep'"); + + # check if this is the master + + if ($rep_type{$lrep} eq "MASTER") + { + logger(LOG_DEBUG, "hook_start() this is the master server, nothing to do"); + return 0; + } + + # this is a slave repository, we need to sync with the master + + call_sync($cmdref); + + # load the master's dump + + svn_load($cmdref); +} + +sub exec_hook_start_commit +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "hook_start_commit() called for repository '$lrep'"); + + # check if this is the master + + if ($rep_type{$lrep} eq "MASTER") + { + exit_logger(0, LOG_DEBUG, "hook_start_commit() this is the master server, nothing to do"); + } + + # this is a slave repository, we need to lock-sync with the master + + call_lock_sync($cmdref); + + # load the master's dump + + svn_load($cmdref); +} + +sub exec_hook_pre_commit +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "hook_pre_commit() called for repository '$lrep'"); + + # check if this is the master + + if ($rep_type{$lrep} eq "MASTER") + { + exit_logger(0, LOG_DEBUG, "hook_pre_commit() this is the master server, nothing to do"); + } + + # this is a slave repository + + exit_logger(0, LOG_DEBUG, "hook_pre_commit() this is the slave server, nothing to do"); +} + +sub exec_hook_post_commit +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "hook_post_commit() called for repository '$lrep'"); + + # check if this is the master + + if ($rep_type{$lrep} eq "MASTER") + { + logger(LOG_INFO, "updating slaves to newest master revision"); + + # this is a master repository, dump actual commit + + svn_dump($cmdref); + + # this is a master repository, we may call slave_commit on the slaves + + foreach $i (keys %rep_type) + { + next if ($i eq $rep_master); + call_slave_commit($cmdref, $i); + } + } + else + { + # this is a slave repository, dump actual commit + + svn_dump($cmdref); + + # this is a slave repository, we need to commit_unlock with the master + + call_commit_unlock($cmdref); + } +} + +sub exec_sync +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "sync() called for for repository '$lrep'"); + + # dump needed revisions + + svn_dump($cmdref); + + # return dump + + dump_stdout($cmdref); +} + +sub exec_lock_sync +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "lock_sync() called for repository '$lrep'"); + + # lock server + + while ((my $res = lock_server($cmd{'local-rep-name'})) != 0) + { + exit_logger(1, LOG_ALERT, "could not lock master repository '" . $cmd{'local-rep-name'} . "'!") if ($res < 0); + logger(LOG_WARNING, "master repository already locked!"); + exit_logger(1, LOG_WARNING, "detected instance with pid $res! could not lock master repository '" . $cmd{'local-rep-name'} . "'!"); + } + + # dump needed revisions and return them + + svn_dump($cmdref); + + # return dump + + dump_stdout($cmdref); +} + +sub exec_commit_unlock +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "commit_unlock() called for repository '$lrep'"); + + # receive dumped revision + + load_stdin($cmdref); + + # load received dump + + svn_load($cmdref); + + # this is a master repository, we may call slave_commit on the slaves + + logger(LOG_INFO, "updating slaves to newest master revision"); + + foreach $i (keys %rep_type) + { + next if ($i eq $rep_master); + next if ($i eq $rep_path2rep{$cmd{'remote-rep'}}); + call_slave_commit($cmdref, $i); + } + + # unlock server + + unlock_server($cmd{'local-rep-name'}); + + # return result + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + + print "OK " . $rev . "\n"; +} + +sub exec_slave_commit +{ + my $cmdref = shift; + my %cmd = %$cmdref; + my $lrep = $cmd{'local-rep-name'}; + + logger(LOG_DEBUG, "slave_commit() called for repository '$lrep'"); + + # receive dumped revision + + load_stdin($cmdref); + + # load received dump + + svn_load($cmdref); + + # return result + my $rev = `$cmd_svnlook youngest $rep_repl_path{$cmd{'local-rep-name'}}`; chomp($rev); + + print "OK " . $rev . "\n"; +} + +sub cmd_execute +{ + my $cmdref = shift; + my %cmd = %$cmdref; + + # hook calls + + exec_hook_start($cmdref) if ($cmd{'cmd'} eq 'hook-start'); + exec_hook_start_commit($cmdref) if ($cmd{'cmd'} eq 'hook-start-commit'); + exec_hook_pre_commit($cmdref) if ($cmd{'cmd'} eq 'hook-pre-commit'); + exec_hook_post_commit($cmdref) if ($cmd{'cmd'} eq 'hook-post-commit'); + + # protocol calls + + exec_sync($cmdref) if ($cmd{'cmd'} eq 'sync'); + exec_lock_sync($cmdref) if ($cmd{'cmd'} eq 'lock-sync'); + exec_commit_unlock($cmdref) if ($cmd{'cmd'} eq 'commit-unlock'); + exec_slave_commit($cmdref) if ($cmd{'cmd'} eq 'slave-commit'); +} + +# +# main +# + +MAIN: +{ + help() if ($ARGV[0] eq "--help"); + + openlog('svnreplicate', 'cons,pid', 'user'); + logger(LOG_DEBUG, "--- startup with pid $$"); + + my $cmdref = cmd_parse(); + cmd_dump($cmdref); + + my %cmd = %$cmdref; + if ($cmd{'local-rep-name'} =~ /^$/) + { + exit_logger(1, LOG_ALERT, "unknown repository $cmd{'local-rep'}!"); + } + + while ((my $res = lock_pid($cmd{'local-rep-name'})) != 0) + { + exit_logger(1, LOG_ALERT, "could not lock pid file!") if ($res < 0); + logger(LOG_WARNING, "detected instance with pid $res! waiting..."); + sleep(30); + } + + logger(LOG_DEBUG, "found master repository is '" . $rep_master . "'"); + + cmd_execute($cmdref); + unlock_pid($cmd{'local-rep-name'}); + + exit_logger(0, LOG_DEBUG, "run successfully finished"); +} diff --git a/dev-util/svnreplicate/files/svnreplicate.conf b/dev-util/svnreplicate/files/svnreplicate.conf new file mode 100644 index 0000000..8a6d61b --- /dev/null +++ b/dev-util/svnreplicate/files/svnreplicate.conf @@ -0,0 +1,76 @@ +#!/usr/bin/perl + +# +# file locations +# + +$LOG_FILE = '/var/log/svnreplicate.log'; +$PID_PATH = '/var/run'; + +# +# flags +# + +$DEBUG = '254'; # max is 255 + +$DO_LOG_SYSLOG = 1; +$DO_LOG_FILE = 1; + +# +# repository master +# + +$rep_name{'dc-rep'} = 'DataCore\'s Zebra Repository'; +$rep_url{'dc-rep'} = 'https://svn.zebra.datacore.ch/'; +$rep_type{'dc-rep'} = 'MASTER'; + +$rep_repl_comm{'dc-rep'} = 'ssh -i /root/.ssh/id_dsa.svnreplicate'; +$rep_repl_host{'dc-rep'} = 'root@god1.new.datacore.ch'; +$rep_repl_path{'dc-rep'} = '/root/svntest/rep-master'; +$rep_repl_script{'dc-rep'} = '/root/svntest/src/svnreplicate/svnreplicate'; +$rep_repl_log{'dc-rep'} = '/tmp/svnreplicate.dc-rep.log'; + +# +# repository slave 1 +# + +$rep_name{'pj-rep'} = 'Paul Jakma\'s Zebra Repository'; +$rep_url{'pj-rep'} = 'https://svn.zebra.dishone.st/'; +$rep_type{'pj-rep'} = 'SLAVE'; + +$rep_repl_comm{'pj-rep'} = 'ssh -i /root/.ssh/id_dsa.svnreplicate'; +$rep_repl_host{'pj-rep'} = 'root@god1.new.datacore.ch'; +$rep_repl_path{'pj-rep'} = '/root/svntest/rep-slave1'; +$rep_repl_script{'pj-rep'} = '/root/svntest/src/svnreplicate/svnreplicate'; +$rep_repl_log{'pj-rep'} = '/tmp/svnreplicate.pj-rep.log'; + +# +# command locations +# + +$cmd_svn = '/usr/bin/svn'; +$cmd_svnadmin = '/usr/bin/svnadmin'; +$cmd_svnlook = '/usr/bin/svnlook'; + +# +# repository list +# + +%rep_path2rep = (); +$rep_master = ""; + +foreach my $i (keys %rep_repl_path) +{ + $rep_path2rep{$rep_repl_path{$i}} = $i; + + if ($DEBUG > 254) + { + logger(LOG_DEBUG, "add $i: name '" . $i . "'"); + logger(LOG_DEBUG, "add $i: type '" . $rep_type{$i} . "'"); + logger(LOG_DEBUG, "add $i: url '" . $rep_url{$i} . "'"); + logger(LOG_DEBUG, "add $i: path '" . $rep_repl_path{$i} . "'"); + } + $rep_master = $i if ($rep_type{$i} =~ /^MASTER$/); +} + +1; diff --git a/dev-util/svnreplicate/svnreplicate-0.59.ebuild b/dev-util/svnreplicate/svnreplicate-0.59.ebuild new file mode 100644 index 0000000..249be25 --- /dev/null +++ b/dev-util/svnreplicate/svnreplicate-0.59.ebuild @@ -0,0 +1,29 @@ +# Copyright 1999-2004 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +IUSE="" + +DESCRIPTION="Provides replication for subversion repositories" +HOMEPAGE="https://open.datacore.ch/DCwiki.open/Wiki.jsp?page=SVNreplicate" +SRC_URI="" +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~x86" + +RDEPEND="dev-util/subversion + dev-lang/perl" + + +src_install() { + + cp ${FILESDIR}/svnreplicate-59 svnreplicate + dobin svnreplicate + + dodoc ${FILESDIR}/README + + mkdir -p ${D}/etc/svnreplicate + + cp ${FILESDIR}/svnreplicate.conf ${D}/etc/svnreplicate/ + +} |