eval '(exit $?0)' && eval '[ -f /usr/local/bin/perl ] && exec /usr/local/bin/perl -S $0 ${1+"$@"}; exec perl -S $0 ${1+"$@"};'
& eval 'if ( -f /usr/local/bin/perl ) exec /usr/local/bin/perl -S $0 $argv:q ; exec perl -S $0 $argv:q'
     if 0;

# @(#)[Hyper-G] [HGS-C] hgbackup	1.00 [server backup] [Gerald Pani]

# 
#<copyright>
# 
# Copyright (c) 1996
# Institute for Information Processing and Computer Supported New Media (IICM),
# Graz University of Technology, Austria.
# 
#</copyright>
  
#<file>
#
# Name:       hgbackup.pl
#
# Purpose:    create a backup from the Hyper-G Server 
#
# Created:    12 Jan 96    Gerald Pani, Helmut Leitner
#
# Description:
# 
#</file>
# 
# $Id: hgbackup.pl,v 1.3 1996/01/25 15:50:44 gpani Exp $
#

$pname = &basename( $0);		# name of this program 

# homedir
$homedir = $ENV{'HOME'} || $ENV{'LOGDIR'} || (getpwuid($<))[7] 
    || &localDie( "$pname: Error: You're homeless!");

# restart if .hgrc is't included
&checkHgrc();

# variables and default values

# read resource file
$mail_to		= ''; 	# email to ...
$backup_log	= "$homedir/HG-backup.log";
$restore_log	= "$homedir/HG-restore.log";
$backup_dev	= '';
$backup_command	= '/bin/tar';
$backup_host	= '';
$rsh_command	= '';
$backup_user	= '';
$dd_command	= '/bin/dd';
$HG_backup_command_line	= '';
$HG_restore_command_line	= '';

# parse any switches
$testOnly = 0;
{
    local($arg);
    while ($arg = shift) {
	($arg =~ /^-h$/)    && (&localDie( &Usage())); # help
	($arg =~ /^-help$/) && (&localDie( &Usage())); # help
	($arg =~ /^-test/) && ($testOnly = 1, next); # help
	&localDie( &Usage());
    }
}

&readRC();

# testing variables

# backup_command executable?
&localDie( "Error: not executable $backup_command\n") unless (-f "$backup_command" && -x _);

if ($backup_host) {
    # rsh_command
    &localDie( "Error: not executable $rsh_command\n") unless (-f "$rsh_command" && -x _);
    local($testCommand) = $backup_user 
	? "$rsh_command $backup_host -l $backup_user /bin/echo test"
	    : "$rsh_command $backup_host /bin/echo test";
    print "testing remote shell command:\n\t$testCommand\n";
    local($testRSHcommand) = `$testCommand`;
    chop($testRSHcommand) if (defined($testRSHcommand));
    &localDie( "Error: can't execute $rsh_command on $backup_host\n") 
	unless (defined($testRSHcommand) && $testRSHcommand eq 'test');
    print "test ok\n\n";

    # dd_command
    $testCommand = $backup_user 
	? "$rsh_command $backup_host -l $backup_user /bin/ls $dd_command"
	    : "$rsh_command $backup_host /bin/ls $dd_command";
    print "testing dd command:\n\t$testCommand\n";
    $testRSHcommand = `$testCommand`;
    chop($testRSHcommand);
    &localDie( "Error: can't find $dd_command on $backup_host\n") unless ($testRSHcommand eq $dd_command);
    print "test ok\n\n";

    # backup_dev
    &localDie( "Error: in hg-backup.rc: backup_dev not specified\n") unless $backup_dev;
    $testCommand = $backup_user 
	? "$rsh_command $backup_host -l $backup_user /bin/ls $backup_dev"
	    : "$rsh_command $backup_host /bin/ls $backup_dev";
    print "testing backup device file:\n\t$testCommand\n";
    $testRSHcommand = `$testCommand`;
    chop($testRSHcommand);
    &localDie( "Error: can't find $backup_dev on $backup_host\n") unless ($testRSHcommand eq $backup_dev);
    print "test ok\n\n";
}
else {
    # backup_dev
    &localDie( "Error: in hg-backup.rc: backup_dev not specified\n") unless $backup_dev;
    &localDie( "Error: can't find $backup_dev on localhost\n") unless (-c "$backup_dev" && -w _);
}

# HG_backup_command_line
&localDie( "Error: in hg-backup.rc: HG_backup_command_line not specified\n") unless $HG_backup_command_line;
&localDie( "Error: in hg-backup.rc: backup_command not specified\n") 
    if (!$backup_command && $HG_backup_command_line =~ /\$backup_command/);
$HG_backup_command_line =~ s/\$backup_command/$backup_command/g;
&localDie( "Error: in hg-backup.rc: rsh_command not specified\n") 
    if (!$rsh_command && $HG_backup_command_line =~ /\$rsh_command/);
$HG_backup_command_line =~ s/\$rsh_command/$rsh_command/g;
&localDie( "Error: in hg-backup.rc: backup_host not specified\n") 
    if (!$backup_host && $HG_backup_command_line =~ /\$backup_host/);
$HG_backup_command_line =~ s/\$backup_host/$backup_host/g;
if ($backup_user) {
    $HG_backup_command_line =~ s/\$backup_user/$backup_user/g;
}
else {
    $HG_backup_command_line =~ s/-l\s+\$backup_user//g;
}
&localDie( "Error: in hg-backup.rc: dd_command not specified\n") 
    if (!$dd_command && $HG_backup_command_line =~ /\$dd_command/);
$HG_backup_command_line =~ s/\$dd_command/$dd_command/g;
&localDie( "Error: in hg-backup.rc: backup_dev not specified\n") 
    if (!$backup_dev && $HG_backup_command_line =~ /\$backup_dev/);
$HG_backup_command_line =~ s/\$backup_dev/$backup_dev/g;

# check sendmail
if (-f '/usr/lib/sendmail' && -x _) {
    $sendmail = '/usr/lib/sendmail';
}
elsif (-f '/etc/sendmail' && -x _) {
    $sendmail = '/etc/sendmail';
}

# check directories
$DIRdcs = $ENV{'DIRdcs'} || &localDie( "$pname: Error: Env.Var. DIRdcs not set !\n");
$DIRfts = $ENV{'DIRfts'} || &localDie( "$pname: Error: Env.Var. DIRfts not set !\n");
if (!($DIRdbs = $ENV{'DIRdbs'})) {
    if (!($DIRdbs = $ENV{'DIRserver'})) {
	&localDie( "$pname: Error: Env.Var. DIRdbs not set\n");
    }
    else {
	print( STDERR "$pname: Warning: Env.Var. DIRdbs not set. Using DIRserver\n");
    }
}

&checkSetDir($DIRdcs);
&checkSetDir($DIRfts);
&checkSetDir($DIRdbs);
chdir() || &localDie( "$pname: Error: chdir HOME: $!\n");

# set up HG_backup_error_command_line and HG_backup_command_line
$backup_files = "$DIRdbs/Object.db $DIRdbs/*.rel $DIRdbs/db.change.log $DIRdcs/local $DIRdcs/cgi $DIRfts/mirror $DIRfts/mirror.nc $homedir/.end_of_backup";
$HG_backup_command_line =~ s/[ \t]+/ /g;
$HG_backup_error_command_line = $HG_backup_command_line;
&localDie( "Error: HG_backup_command_line without <FILES_TO_BACKUP>\n") 
    if (!($HG_backup_command_line =~ /<FILES_TO_BACKUP/));
$HG_backup_command_line =~ s/<FILES_TO_BACKUP>/$backup_files/g;
$invalid_backup = "$homedir/.invalid_backup";
$HG_backup_error_command_line =~ s/<FILES_TO_BACKUP>/$invalid_backup/g;

# check host
$DBHost = $ENV{'DBHost'};	# get from environ;
defined($DBHost) 		|| &localDie( "$pname: Error: DBHost not set!\n");
chop($hostname = `hostname`);
$hostname eq $DBHost 		|| &localDie( "$pname: Error: DBHost ($DBHost) not equal to $hostname!\n");

select(STDOUT); $| = 1;		# unbuffered STDOUT

if ($testOnly) {
    print "\n";
    print "              SETTINGS:\n";
    print "              =========\n\n";
    print " mail_to = $mail_to\n";
    print " backup_log = $backup_log\n";
    print " backup_dev = $backup_dev\n";
    print " backup_command = $backup_command\n";
    print " backup_host = $backup_host\n";
    print " rsh_command = $rsh_command\n";
    print " backup_user = $backup_user\n";
    print " dd_command = $dd_command\n";
    print " HG_backup_command_line = $HG_backup_command_line\n\n";
    exit(0);
}

($ok, $running, $statDBS, $statDCS, $statFTS) = &serverStatus();

if (!$ok) {
    &localDie( "Error: can't retrieve server status\n");
}

if ($running) {
    # server running
    # set backup mark ftserver
    local( $ftRet, $ftbackuptime) = &ftsBackupBegin();
    if ($ftRet != 0) {
	&localDie( "Error: backup failed, try again later: can't set ftservers backup mark\n");
    }
    # freeze dcserver
    if (!&dcsThaw()) {
	# backup failed in initial dcsThaw
	&localDie( "Error: backup failed, try again later: can't thaw dcserver\n");
    }
    local( $ret, $dcbackuptime) = &dcsFreeze();
    if ($ret == 0) {
	# set backup mark
	local( $ret, $backuptime) = &dbsBackupBegin();
	if ($ret == 0) {
	    # create backup
	    unlink( "$homedir/.end_of_backup");
	    open(EOB, "> $homedir/.end_of_backup") 
		|| &localDie( "Error: can't create $homedir/.end_of_backup\n");
	    print EOB "State=running\n";
	    print EOB "DIRdbs=$DIRdbs\n";
	    print EOB "DIRdcs=$DIRdcs\n";
	    print EOB "DIRfts=$DIRfts\n";
	    print EOB "FTSmark=$ftbackuptime\n";
	    close(EOB);
	    local($backupOk) = &doBackup();
	    unlink( "$homedir/.end_of_backup");
	    if (!$backupOk) {
		# backup failed
		&backupFailed( "error in backup command\n");
		&dcsThaw();
		&localDie( "Error: backup failed: error in backup command\n");
	    }
	    if (!&dbsEndBackup( $backuptime)) {
		# backup failed
		&backupFailed( "wrong dbserver backuptime\n");
		&dcsThaw();
		&localDie( "Error: backup failed: wrong dbserver backuptime\n");
	    }
	    if (!&dcsThaw( $dcbackuptime)) {
		# backup failed
		&backupFailed( "wrong dcserver backuptime\n");
		&localDie( "Error: backup failed: wrong dcserver backuptime\n");
	    }
	    if (!&ftsEndBackup( $ftbackuptime)) {
		# backup failed
		&backupFailed( "wrong ftserver backuptime\n");
		&localDie( "Error: backup failed: wrong ftserver backuptime\n");
	    }
	    
	    # retrieve server status after backup
	    local($ok, $running, $statDBSafter, $statDCSafter, $statFTSafter) = &serverStatus();
	    if ($ok && $running) {
		if ($statDBS && $statDBS eq $statDBSafter 
		    && $statDCS && $statDCS eq $statDCSafter
		    && $statFTS && $statFTS eq $statFTSafter) {
		    # backup valid
		    &mailTo( "hgbackup", "everything seems to be ok");
		    exit(0);
		}
	    }
	    # backup failed
	    &backupFailed( "wrong server status\n");
	    &localDie( "Error: backup failed: wrong server status\n");
	}
	else {
	    # backup failed
	    &dcsThaw();
	    &localDie( "Error: backup failed, try again later: can't set backup begin mark\n");
	}
    }
    else {
	# backup failed in dcsFreeze
	&localDie( "Error: backup failed, try again later: can't freeze dcserver\n");
    }
}
else {
    # not running
    # create backup
    unlink( "$homedir/.end_of_backup");
    open(EOB, "> $homedir/.end_of_backup") 
	|| &localDie( "Error: can't create $homedir/.end_of_backup\n");
    print EOB "State=stopped\n";
    print EOB "DIRdbs=$DIRdbs\n";
    print EOB "DIRdcs=$DIRdcs\n";
    print EOB "DIRfts=$DIRfts\n";
    close(EOB);
    local($backupOk) = &doBackup();
    unlink( "$homedir/.end_of_backup");
    if (!$backupOk) {
	# backup failed
	&backupFailed( "error in backup command\n");
	&localDie( "Error: backup failed: error in backup command\n");
    }
	    
    # retrieve server status after backup
    local($ok, $statDBSAfter) = &staticStatus();
    if ($ok && $statDBS && $statDBS eq $statDBSAfter) {
	# backup valid
	&mailTo( "hgbackup", "everything seems to be ok");
	exit(0);
    }
    else {
	# backup failed
	&backupFailed( "wrong server state\n");
	&localDie( "Error: backup failed: wrong server state\n");
    }
}

sub dcsFreeze {
    # freeze dcserver
    # Parameters: empty
    # Return values:
    #   on success:
    #     (0, timeOfBackup)
    if (open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print CONTR "U\nzDCSfreeze\nq\n";
	close( CONTR);
	if ($?) {
	    local($CmdStatus) = $? >> 8;
	    $CmdStatus = unpack('c',pack('c',$CmdStatus));
	    print STDERR "dcsFreeze: no connection to server $CmdStatus\n";
	    unlink( "hgbackup$$.tmp");
	    return (1, $CmdStatus);
	}
	local($system) = 0;
	local($ok) = '';
	if (open(CONTR, "< hgbackup$$.tmp")) {
	    while (<CONTR>) {
		/^User: \S+\(system\)$/ && ($system = 1);
		/^dcserver frozen 0x([0-9a-f]+)$/ && ($ok = $1);
	    }
	    close(CONTR);
	    unlink( "hgbackup$$.tmp");
	    if ($system) {
		if ($ok) {
		    return (0, $ok);
		}
		print STDERR "dcsFreeze: no dcserver backup time stamp\n";
		return (5, '');
	    }
	    else {
		print STDERR "dcsFreeze: not system user\n";
		return (2,'');
	    }
	}
	else {
	    print STDERR "dcsFreeze: can't open tmp file\n";
	    return (3,'');
	}
    }
    else {
	print STDERR "dcsFreeze: can't open pipe: $!\n";
	return (4,'');
    }
}

sub dcsThaw {
    # thaw dcserver
    # Parameters: dcbackuptime
    # Return values:
    #   on success:
    #     1
    local($dcbackuptime) = @_;
    if (!open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print STDERR "dcsThaw: can't open pipe: $!\n";
	return 0;
    }
    print CONTR "U\nzDCSthaw\nq\n";
    close( CONTR);
    if ($?) {
	local($CmdStatus) = $? >> 8;
	$CmdStatus = unpack('c',pack('c',$CmdStatus));
	print STDERR "dcsThaw: no connection to server $CmdStatus\n";
	unlink( "hgbackup$$.tmp");
	return 0;
    }
    local($system) = 0;
    local($ok) = '';
    if (!open(CONTR, "< hgbackup$$.tmp")) {
	print STDERR "dcsThaw: can't open tmp file\n";
	return 0;
    }
    while (<CONTR>) {
	/^User: \S+\(system\)$/ && ($system = 1);
	/^dcserver thawed 0x([0-9a-f]+)$/ && ($ok = $1);
    }
    close(CONTR);
    unlink( "hgbackup$$.tmp");
    if (!$system) {
	print STDERR "dcsThaw: not system user\n";
	return 0;
    }
    $dcbackuptime = '00000000' unless defined($dcbackuptime);
#     if ($ok && !defined($dcbackuptime)) {
# 	# without parameter
# 	return 1;
#     }
    if ($ok && $ok eq $dcbackuptime) {
	return 1;
    }
    print STDERR "dcsThaw: wrong dcserver backup time stamp\n";
    return 0;
}

sub backupFailed {
    local($message) = @_;
    unlink( "$invalid_backup");
    if (!open(EOB, "> $invalid_backup")) {
	print STDERR "Error: can't create $invalid_backup\n";
	return;
    }
    print EOB "BACKUP FAILED ", &localTime( time), "\n";
    print EOB "$message\n" if $message;
    close(EOB);
    local($ret) = system( "$HG_backup_error_command_line");
    unlink( "$invalid_backup");
    return;
}

# creates a string representing the actual time
sub localTime {
  local( $time) = @_;
  local( $sec, $min, $hour, $mday, $mon, $year) = localtime( $time);
  sprintf("%02d/%02d/%02d %02d:%02d:%02d", $year, $mon + 1, $mday, $hour, $min, $sec);
}

sub dbsBackupBegin {
    # set backup begin mark
    # Parameters: empty
    # Return values:
    #   on success:
    #     (0, timeOfBackup)
    if (open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print CONTR "U\nzbeginBackup\nq\n";
	close( CONTR);
	if ($?) {
	    local($CmdStatus) = $? >> 8;
	    $CmdStatus = unpack('c',pack('c',$CmdStatus));
	    print STDERR "dbsBackupBegin: no connection to server $CmdStatus\n";
	    unlink( "hgbackup$$.tmp");
	    return (1, $CmdStatus);
	}
	local($system) = 0;
	local($ok) = '';
	if (open(CONTR, "< hgbackup$$.tmp")) {
	    while (<CONTR>) {
		/^User: \S+\(system\)$/ && ($system = 1);
		/^backup point marked 0x([0-9a-f]+)$/ && ($ok = $1);
	    }
	    close(CONTR);
	    unlink( "hgbackup$$.tmp");
	    if ($system) {
		if ($ok) {
		    return (0, $ok);
		}
		print STDERR "dbsBackupBegin: no dbserver backup time stamp\n";
		return (5, '');
	    }
	    else {
		print STDERR "dbsBackupBegin: not system user\n";
		return (2,'');
	    }
	}
	else {
	    print STDERR "dbsBackupBegin: can't open tmp file\n";
	    return (3,'');
	}
    }
    else {
	print STDERR "dbsBackupBegin: can't open pipe: $!\n";
	return (4,'');
    }
}

sub ftsBackupBegin {
    # set backup begin mark
    # Parameters: empty
    # Return values:
    #   on success:
    #     (0, timeOfBackup)
    if (open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print CONTR "U\nzFTSbeginBackup\nq\n";
	close( CONTR);
	if ($?) {
	    local($CmdStatus) = $? >> 8;
	    $CmdStatus = unpack('c',pack('c',$CmdStatus));
	    print STDERR "ftsBackupBegin: no connection to server $CmdStatus\n";
	    unlink( "hgbackup$$.tmp");
	    return (1, $CmdStatus);
	}
	local($system) = 0;
	local($ok) = '';
	if (open(CONTR, "< hgbackup$$.tmp")) {
	    while (<CONTR>) {
		/^User: \S+\(system\)$/ && ($system = 1);
		/^ftserver backup point marked 0x([0-9a-f]+)$/ && ($ok = $1);
	    }
	    close(CONTR);
	    unlink( "hgbackup$$.tmp");
	    if ($system) {
		if ($ok) {
		    return (0, $ok);
		}
		print STDERR "ftsBackupBegin: no ftserver backup time stamp\n";
		return (5, '');
	    }
	    else {
		print STDERR "ftsBackupBegin: not system user\n";
		return (2,'');
	    }
	}
	else {
	    print STDERR "ftsBackupBegin: can't open tmp file\n";
	    return (3,'');
	}
    }
    else {
	print STDERR "ftsBackupBegin: can't open pipe: $!\n";
	return (4,'');
    }
}

sub dbsEndBackup {
    # end of backup
    # Parameters: backuptime
    # Return values:
    #   on success:
    #     1
    local( $backuptime) = @_;
    if (open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print CONTR "U\nzendBackup\nq\n";
	close( CONTR);
	if ($?) {
	    local($CmdStatus) = $? >> 8;
	    $CmdStatus = unpack('c',pack('c',$CmdStatus));
	    print STDERR "Error: in dbcontrol $CmdStatus\n";
	    unlink( "hgbackup$$.tmp");
	    return 0;
	}
	local($system) = 0;
	local($ok) = '';
	if (open(CONTR, "< hgbackup$$.tmp")) {
	    while (<CONTR>) {
		/^User: \S+\(system\)$/ && ($system = 1);
		/^end of backup 0x([0-9a-f]+)$/ && ($ok = $1);
	    }
	    close(CONTR);
	    unlink( "hgbackup$$.tmp");
	    if ($system) {
		if ($ok && $ok eq $backuptime) {
		    return 1;
		}
		print STDERR "Error: wrong backup time stamp\n";
	    }
	    else {
		print STDERR "Error: not system user\n";
	    }
	    return 0;
	}
    }
    return 0;
}

sub ftsEndBackup {
    # end of backup
    # Parameters: backuptime
    # Return values:
    #   on success:
    #     1
    local( $backuptime) = @_;
    if (open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print CONTR "U\nzFTSendBackup\nq\n";
	close( CONTR);
	if ($?) {
	    local($CmdStatus) = $? >> 8;
	    $CmdStatus = unpack('c',pack('c',$CmdStatus));
	    print STDERR "Error: in dbcontrol $CmdStatus\n";
	    unlink( "hgbackup$$.tmp");
	    return 0;
	}
	local($system) = 0;
	local($ok) = '';
	if (open(CONTR, "< hgbackup$$.tmp")) {
	    while (<CONTR>) {
		/^User: \S+\(system\)$/ && ($system = 1);
		/^ftserver end of backup 0x([0-9a-f]+)$/ && ($ok = $1);
	    }
	    close(CONTR);
	    unlink( "hgbackup$$.tmp");
	    if ($system) {
		if ($ok && $ok eq $backuptime) {
		    return 1;
		}
		print STDERR "Error: wrong ftserver backup time stamp\n";
	    }
	    else {
		print STDERR "Error: not system user\n";
	    }
	    return 0;
	}
    }
    return 0;
}

sub doBackup {
    # $HG_backup_command_line
    print "\nexecuting command:\n   $HG_backup_command_line\n\n";

    local($command) = "($HG_backup_command_line) 2>&1";
    if (!open(LOG, "> $backup_log")) {
	print( STDERR "Error: $!: opening $backup_log\n");
	return 0;
    }
    select(LOG); $| = 1;		# unbuffered 
    select(STDOUT);
    if (!open(BCKP, "$command |")) {
	print( STDERR "Error: $!: executing $command\n");
	print LOG "Error: $!: executing $command\n";
	close(LOG);
	return 0;
    }
    while (<BCKP>) {
	print $_;
	print LOG $_;
    }
    close(BCKP);
    close(LOG);
    return 1;
}

sub staticStatus {
    local($time, $size) = &filetime( "$DIRdbs/db.change.log");
    if (!$time) {
	return (0, '');
    }
    local($sDBS) = "$time $size";
    ($time, $size) = &filetime( "$DIRdbs/.");
    if (!$time) {
	return (0, '');
    }
    $sDBS .= " $time $size";
    return (1, $sDBS);
}

sub filetime {
  local($file) = @_;
  local(@arr) = stat("$file");	# ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
			      	# $size, $atime, $mtime, $ctime, $blksize, $blocks)
  local(@result) = ($arr[9], $arr[7]);	# ($mtime, $size, $mode)
  @result;
}

sub basename {
  local($path) = @_;
  
  return '' unless $path =~ m,(^|/)([^/]+)$,;
  return $2;
}

# environment variables from ~/.hgrc should be set. One of them should
 # be 'HGRC'. If 'HGRC' isn't set, then include ~/.hgrc and restart
 # this program.
sub checkHgrc {
  return if defined( $ENV{'HGRC'});

  $ENV{'HGRC'} = '';
  &localDie( "$pname: Error: Couldn't read $homedir/.hgrc") if (! -r "$homedir/.hgrc");
  exec( "/bin/csh -c \'(source $homedir/.hgrc ; $0 @ARGV)\'") ||
      &localDie( "$pname: Error: exec: $!");
}

sub readRC { 
    if (open(RC, "< $homedir/.hg-backup.rc")) {
	local($linenum) = 0;
	local($line) = '';
	local($append) = 0;
	while (<RC>) {
	    $linenum++;
	    if ($append) {
		if (/^(.*)\\$/) {
		    $line .=  " " . $1;
		    next;
		}
		elsif (/^\s*$/) {
		    # empty line
		    $append = 0;
		}
		else {
		    (/^(.*)$/) && ($line .= " " . $1);
		    $append = 0;
		}
	    }
	    else {
		if (/^\s*#/) {
		    # comment line
		    next;
		}
		if (/^\s*$/) {
		    # empty line
		    next;
		}
		if (/^(.*)\\$/) {
		    $line = $1;
		    $append = 1;
		    next;
		}
		else {
		    (/^(.*)$/) && ($line = $1);
		}
	    }

	    ($line =~ /^\s*mail_to\s*=\s*(\S+)\s*$/) && ($mail_to = $1) && next;
	    ($line =~ /^\s*backup_log\s*=\s*(\S+)\s*$/) && ($backup_log = $1) && next;
	    ($line =~ /^\s*restore_log\s*=\s*(\S+)\s*$/) && ($restore_log = $1) && next;
	    ($line =~ /^\s*backup_dev\s*=\s*(\S+)\s*$/) && ($backup_dev = $1) && next;
	    ($line =~ /^\s*backup_command\s*=\s*(\S+)\s*$/) && ($backup_command = $1) && next;
	    ($line =~ /^\s*backup_host\s*=\s*(\S+)\s*$/) && ($backup_host = $1) && next;
	    ($line =~ /^\s*rsh_command\s*=\s*(\S+)\s*$/) && ($rsh_command = $1) && next;
	    ($line =~ /^\s*backup_user\s*=\s*(\S+)\s*$/) && ($backup_user = $1) && next;
	    ($line =~ /^\s*dd_command\s*=\s*(\S+)\s*$/) && ($dd_command = $1) && next;
	    ($line =~ /^\s*HG_backup_command_line\s*=\s*(.+)\s*$/) && ($HG_backup_command_line = $1) && next;
	    ($line =~ /^\s*HG_restore_command_line\s*=\s*(.+)\s*$/) && ($HG_restore_command_line = $1) && next;
	    if ($line =~ /^\s*(\S+)\s*=\s*(.+)\s*$/) {
		&localDie( "$pname: Error: hg-backup.rc ($linenum): unknown variable $1\n");
	    }
	    &localDie( "$pname: Error: hg-backup.rc ($linenum): unexpected line\n\t$line\n");
	}
	close (RC);
    }
    else {
	&localDie( "$pname: Error: can't open $homedir/.hg-backup.rc: $!\n");
    }
    return 0;
}

sub checkSetDir {
  local($dir) = @_;
  -d $dir 		|| &localDie( "$pname: Error: $dir is not a directory!\n");
  chdir($dir) 		|| &localDie( "$pname: Error: can't change into $dir!\n");
  (-o ".") 		|| &localDie( "$pname: Error: current user not owner of $dir!\n");
  return 1;
}
  
sub serverRunning {
    # connect server and retrieve status
    # Parameters: empty
    # Return values:
    #   on success:
    #      (ok = 0, dbUpSince, dcUpSince, ftUpSince)
    #   on failure:
    #      (error = 1, CmdStatus, '', '')
    #      (error = {2,3,4}, '', '', '')
    if (open(CONTR, "| dbcontrol > hgbackup$$.tmp")) {
	print CONTR "U\nzstat\nzdcstat\nzftstat\nq\n";
	close( CONTR);
	if ($?) {
	    # can't connect server or timeout
	    local($CmdStatus) = $? >> 8;
	    $CmdStatus = unpack('c',pack('c',$CmdStatus));
	    unlink( "hgbackup$$.tmp");
	    return (1, $CmdStatus, '', '');
	}
	local($system) = 0;
	local($dbUpSince) = '';
	local($dcUpSince) = '';
	local($ftUpSince) = '';
	if (open(CONTR, "< hgbackup$$.tmp")) {
	    while (<CONTR>) {
		/^User: \S+\(system\)$/ && ($system = 1);
		if (/^UpSince=([0-9\/ :]+) 0x[0-9a-f]+$/) {
		    if (!$dbUpSince) {
			$dbUpSince = $1;
		    }
		    elsif (!$dcUpSince) {
			$dcUpSince = $1;
		    }
		    else {
			$ftUpSince = $1;
		    }
		}
	    }
	    close(CONTR);
	    unlink( "hgbackup$$.tmp");
	    if ($system) {
		# identified as system, status retrieved
		return (0, $dbUpSince, $dcUpSince, $ftUpSince);
	    }
	    else {
		# not identified as system
		return (2,'','','');
	    }
	}
	else {
	    # can't open tmp-file
	    return (3,'','','');
	}
    }
    else {
	# can't open control connection
	return (4,'','','');
    }
}

sub serverStatus {
    # retrieve status of server
    # Parameters: empty
    # Return values:
    #   on success:
    #      (ok = 1, 1, dbUpSince, dcUpSince, ftUpSince)  server running
    #      (ok = 1, 0, dbStatus, '', '')  server stopped
    local($ret, $dbUpSince, $dcUpSince, $ftUpSince) = &serverRunning();
    if ($ret == 0) {
	# server running
	return (1, 1, $dbUpSince, $dcUpSince, $ftUpSince);
    }
    elsif ($ret == 1) {
	# server stopped
	if ($dbUpSince == 1) {
	    # unclear state 
	    print STDERR "Error: maybe dbcontrol not found\n";
	    return (0, 0, '', '', '');
	}
	else {
	    print STDERR "serverStatus: no connection to server $dbUpSince\n";
	    # retrieve status of stopped server
	    local( $ok, $sDBS) = &staticStatus();
	    if ($ok) {
		return (1, 0, $sDBS, '', '');
	    }
	    else {
		return (0, 0, '', '', '');
	    }
	}
    }
    elsif($ret == 2) {
	print STDERR "Error: not system user\n";
	return (0, 0, '', '', '');
    }
    elsif($ret == 3) {
	print STDERR "Error: can't open temporary file\n";
	return (0, 0, '', '', '');
    }
    elsif($ret == 4) {
	print STDERR "Error: open failed\n";
    }
    return (0, 0, '', '', '');
}

sub mailTo {
    local($subject, $message) = @_;
    return unless $mail_to;
    return unless $sendmail;
    local($cid);
    return if ($cid = fork());	# parent
    return unless defined($cid); # parent return if no fork
    open(MAIL, "| $sendmail -t -oi");
    print MAIL "To: $mail_to\nSubject: $subject\n\n$message";
    close(MAIL);
    exit(0);
}
  
sub localDie {
    local($message) = @_;
    if ($message) {
	&mailTo( "hgbackup error", $message);
	die "$message";
    }
    die;
}

sub Usage {
  return "
Usage: $0 [-h|-help] [-test]

        -h -help        this message
        -test           reads .hg-backup.rc
                        and prints variable settings

   For customizations copy 
     $homedir/samples/hg-backup.rc
   tox
     $homedir/.hg-backup.rc
   and change the variables there.
";
}

