summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Wrobel <wrobel@gentoo.org>2005-08-25 20:32:33 +0000
committerGunnar Wrobel <wrobel@gentoo.org>2005-08-25 20:32:33 +0000
commit6a06aa6fd47f05ce256880273b5edddf8141a070 (patch)
tree8036fcb9b5dc527e59095cc1cc49b9ff54f55410 /dev-util
parenttypo fix (diff)
downloadoverlay-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/README40
-rwxr-xr-xdev-util/svnreplicate/files/svnreplicate-59997
-rw-r--r--dev-util/svnreplicate/files/svnreplicate.conf76
-rw-r--r--dev-util/svnreplicate/svnreplicate-0.59.ebuild29
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/
+
+}