#!/usr/bin/perl ################################# Constants ################################# $PBUDDY = "/usr/libexec/PlistBuddy"; $SQLITE = "/usr/bin/sqlite3"; $SERVER_ADMIN="/usr/sbin/serveradmin"; $TAR = "/usr/bin/gnutar"; $SBS_CONF = "/private/etc/server_backup"; $MKTEMP_PATH = "/usr/bin/mktemp"; $JABBER_USER = "_jabber"; $ServiceConf = "75-iChatServer.plist"; $servermgrJabberConfigPath = "/Library/Preferences/com.apple.ichatserver.plist"; $ServiceName = "iChatServer"; $tmpPath = "/private/var/jabberd/tmp"; %$BigList=""; @arrayServerAdminPrefs=""; %dbinfo = {}; # Enum for deciding what to do with output ($OUTPUT_NONE, $OUTPUT_DEBUG, $OUTPUT_VERBOSE, $OUTPUT_FUNCLOG, $OUTPUT_PRINT) = (0..4); $LOG_PATH = &_get_log_path(@ARGV); # global, for service-specific log path if($ENV{VERBOSE} eq 1) {$VERBOSE = '1';} if($ENV{DEBUG} eq 1) {$DEBUG = '1';} if($ENV{FUNCLOG} eq 1) {$FUNCLOG = '1';} &_log($OUTPUT_VERBOSE, "iChatServer_restore was called.\n"); ParseOptions(); if ($DEBUG) { dumpAssociativeArray(@ARGV); } validateOptionsAndDispatch(@ARGV); exit(); ################################################################################ sub validateOptionsAndDispatch() { %BigList=@_; my $nothing = 0; SWITCH: { if (uc($BigList{"-cmd"}) eq uc("actions")) { if($DEBUG) {print("actions\n");} Actions(); last SWITCH; } if ((uc($BigList{"-cmd"}) eq uc("browse")) && (-e ($BigList{"-path"}))) { if ($DEBUG) {print("browse\n");} Browse(); last SWITCH; } if (uc($BigList{"-cmd"}) eq uc("help")) { if($DEBUG) {print("help\n");} Usage(); last SWITCH; } if ((uc($BigList{"-cmd"}) eq uc("restore")) && (-e ($BigList{"-path"}))) { print("restore\n"); Restore(); last SWITCH; } if (uc($BigList{"-cmd"}) eq uc("version")) { if($DEBUG) {print("version\n");} Version(); last SWITCH; } $nothing = 1; } if($nothing eq 1) {print("Legal options were not supplied!\n");Usage();} } ################################################################################ sub Actions() { &_log($OUTPUT_FUNCLOG, "Start Actions-------------------------------------------------------+\n"); &_log($OUTPUT_VERBOSE, (qq(${PBUDDY} -c \"Print :RestoreActions\" $SBS_CONF/$ServiceConf) . "\n")); $Version = qx(${PBUDDY} -c \"Print :RestoreActions\" $SBS_CONF/$ServiceConf); &_log($OUTPUT_PRINT, $Version); &_log($OUTPUT_FUNCLOG, "End Actions-------------------------------------------------------+\n"); } ################################################################################ sub Browse() { my $SBS_PATH = $BigList{"-path"}; &_log($OUTPUT_FUNCLOG, "Start Browse-------------------------------------------------------+\n"); if ($SBS_PATH eq "") { &_log($OUTPUT_PRINT, "Legal options were not supplied!\n"); Usage(); } my $browseFilePath=$SBS_PATH . "/$ServiceName.browse.plist"; if(-e $browseFilePath) { open(FILEFD, $browseFilePath); @LINES = ; while($Line =shift(@LINES)) { chomp($Line); printf("%s\n", $Line); } } else { opendir(SBSDIR, $SBS_PATH); my @storedFiles = readdir(SBSDIR); closedir(SBSDIR); foreach my $storedFile (@storedFiles) { if ($storedFile =~ /iChatServer.conf.tar.gz/ || $storedFile =~ /iChatServer.data.tar.gz/) { print "$SBS_PATH/$storedFile\n"; } } } &_log($OUTPUT_FUNCLOG, "End Browse-------------------------------------------------------+\n"); } ################################################################################ sub Restore() { my $jabberdDataPath = "/var/jabberd"; my $jabberdDBPath = ""; $jabberdDBPath = "$jabberdDataPath/sqlite"; my $sqliteDefaultDbPath = "$jabberdDBPath/jabberd2.db"; my $jabberdSqliteDataFile = ""; my $jabberDataTmpDir = ""; my $C2SPID = "/var/run/jabberd/c2s.pid"; my $SBS_PATH = $BigList{"-path"}; my $OPT = $BigList{"-opt"}; my $initialJabberdState = 0; my $cmd; my $origCWD; # find sqlite database path my $pbuddyDbPath = qx(${PBUDDY} -c \"Print :jabberdDatabasePath:\" $servermgrJabberConfigPath); chomp($pbuddyDbPath); if (-e $pbuddyDbPath) { $jabberdSqliteDataFile = $pbuddyDbPath; } else { $jabberdSqliteDataFile = $sqliteDefaultDbPath; } umask(077); &_log($OUTPUT_FUNCLOG, "Start Restore-------------------------------------------------------+\n"); if ($OPT ne "configuration" && $OPT ne "data" && $OPT ne "all") { &_log($OUTPUT_PRINT, "No valid -opt selected for restore operation.\n"); return 1; } if (! -e $SBS_PATH) { &_log($OUTPUT_PRINT, "Supplied -path does not exist.\n"); return 1; } # Shutdown jabber if its enabled my $res = `ps -xu \"$JABBER_USER\" |grep c2s|grep -v grep`; if ($res ne "") { &_log($OUTPUT_VERBOSE, "Stopping jabber service\n"); qx($SERVER_ADMIN stop jabber); $initialJabberdState = 1; for (my $x = 0; $x < 5; $x++) { $res = `ps -xu \"$JABBER_USER\" |grep c2s|grep -v grep`; if ($res eq "") { last; } sleep 3; } } if ($res ne "") { &_log($OUTPUT_PRINT, "Error: Cannot stop jabber service\n"); return 1; } if ($OPT eq "all" || $OPT eq "configuration") { # Backup old config my @configurationFiles; my @configurationFilesPbuddy = qx(${PBUDDY} -c \"Print :ConfigurationFilesToTar:\" $SBS_CONF/$ServiceConf); if ($#configurationFilesPbuddy > 1) { for (my $i = 1; $i < $#configurationFilesPbuddy; $i++) { my $configTmp = $configurationFilesPbuddy[$i]; chomp($configTmp); $configTmp =~ s/^\s*//; push(@configurationFiles, $configTmp); } } if ($VERBOSE) { $cmd = "$TAR --ignore-failed-read -cvzf /etc/jabberd/iChatServer_settings_before_restore.tar.gz"; } else { $cmd = "$TAR --ignore-failed-read -czf /etc/jabberd/iChatServer_settings_before_restore.tar.gz"; } foreach my $configFile (@configurationFiles) { # Allow for wildcards in the plist strings. Expand them and push them onto the array for later processing. if ($configFile =~ /\*/) { &_log($OUTPUT_DEBUG, "Found a wildcard in the plist ($configFile), expanding for later processing...\n"); my @expFiles = `ls -1 $configFile`; chomp(@expFiles); foreach my $expFile (@expFiles) { &_log($OUTPUT_DEBUG, "Adding wildcard expansion to configurationFiles array: $expFile\n"); push(@configurationFiles, $expFile); } next; } $cmd .= " \"$configFile\""; } &_log($OUTPUT_DEBUG, "Executing command: $cmd\n"); system($cmd); # Restore config opendir(SBSDIR, "$SBS_PATH") || die ("Couldn't open the directory $SBS_PATH for reading.\n");; my @confTarFiles = readdir(SBSDIR); closedir(SBSDIR); # should only be one conf file in path. stop at first file. foreach my $confTarFile (@confTarFiles) { if ($confTarFile =~ /iChatServer.conf.tar.gz$/) { $origCWD = $ENV{PWD}; chdir("/"); if ($VERBOSE) { $cmd = "$TAR -xzvf \"$SBS_PATH/$confTarFile\""; } else { $cmd = "$TAR -xzf \"$SBS_PATH/$confTarFile\""; } system($cmd); chdir($origCWD); last; } } } if ($OPT eq "all" || $OPT eq "data") { # Backup old data so it can be manually restored if ($VERBOSE) { $cmd = "$TAR -cvzf \"$jabberdDataPath/jabber_data_before_restore.tar.gz\" \"$jabberdDBPath\""; } else { $cmd = "$TAR -czf \"$jabberdDataPath/jabber_data_before_restore.tar.gz\" \"$jabberdDBPath\""; } &_log($OUTPUT_VERBOSE, "Executing command: $cmd\n"); system($cmd); # Restore data opendir(SBSDIR, "$SBS_PATH") || die ("Couldn't open the directory $SBS_PATH for reading.\n");; my @sbsFiles = readdir(SBSDIR); closedir(SBSDIR); # should only be one data file in the path. stop at first file. my @sqlFiles; SBSFILESCAN: foreach my $sbsFile (@sbsFiles) { if ($sbsFile =~ /iChatServer.data.tar.gz$/) { for (my $i = 0; $i < 5; $i++) { $jabberDataTmpDir = `$MKTEMP_PATH -d $tmpPath/jabber_restore.XXXXXXXXXXXXXXXXXXXXXXXX`; chomp($jabberDataTmpDir); if (-e $jabberDataTmpDir) { last; } if ($i == 4) { die "Error: Cannot create temporary file:\n$jabberDataTmpDir"; } } $origCWD = $ENV{PWD}; chdir("$jabberDataTmpDir") || die ("Could not chdir to $jabberDataTmpDir"); if ($VERBOSE) { $cmd = "$TAR -xvzf \"$SBS_PATH/$sbsFile\""; print "Executing command: $cmd\n"; } else { $cmd = "$TAR -xzf \"$SBS_PATH/$sbsFile\""; } system($cmd); chdir($origCWD); opendir(SQL_TMP_DIR, "$jabberDataTmpDir"); @sqlFiles = readdir(SQL_TMP_DIR); closedir(SQL_TMP_DIR); # should only be one SQL file in the data file. stop at first file. foreach my $sqlFile (@sqlFiles) { if ($sqlFile =~ /\.sql$/) { $cmd = "rm -f \"$jabberdSqliteDataFile\"; $SQLITE \"$jabberdSqliteDataFile\" < \"$jabberDataTmpDir/$sqlFile\""; &_log($OUTPUT_VERBOSE, "Executing command: $cmd\n"); system($cmd); system("chmod 640 \"$jabberdSqliteDataFile\""); system("chown jabber:jabber \"$jabberdSqliteDataFile\""); unlink("$jabberDataTmpDir/$sqlFile"); qx(rm -Rf "$jabberDataTmpDir"); last SBSFILESCAN; # should only be one SQL file } } } } } # Restart jabberd if necessary if ($initialJabberdState) { &_log($OUTPUT_VERBOSE, "Restarting iChatServer service\n"); qx($SERVER_ADMIN start jabber); } &_log($OUTPUT_FUNCLOG, "End Restore-------------------------------------------------------+\n"); } ################################################################################ sub Version() { &_log($OUTPUT_FUNCLOG, "Start Version-------------------------------------------------------+\n"); $Version = qx(${PBUDDY} -c \"Print :Version\" $SBS_CONF/$ServiceConf); &_log($OUTPUT_PRINT, $Version); &_log($OUTPUT_FUNCLOG, "End Version-------------------------------------------------------+\n"); } ################################################################################ # ParseOptions takes a list of possible options and a boolean indicating # whether the option has a value following, and sets up an associative array # %opt of the values of the options given on the command line. It removes all # the arguments it uses from @ARGV and returns them in @optArgs. # sub ParseOptions { local (@optval) = @_; local ($opt, @opts, %valFollows, @newargs); while (@optval) { $opt = shift(@optval); push(@opts,$opt); $valFollows{$opt} = shift(@optval); } @optArgs = (); %opt = (); arg: while (defined($arg = shift(@ARGV))) { foreach $opt (@opts) { if ($arg eq $opt) { push(@optArgs, $arg); if ($valFollows{$opt}) { if (@ARGV == 0) { Usage(); } $opt{$opt} = shift(@ARGV); push(@optArgs, $opt{$opt}); } else { $opt{$opt} = 1; } next arg; } } push(@newargs,$arg); } @ARGV = @newargs; } ################################################################################ sub dumpAssociativeArray() { %BigList=@_; while(($theKey, $theVal) = each (%BigList)) { print "$theKey is the key for value $theVal\n"; } if($BigList{"-cmd"} eq "backup") { print "cmd := ", $BigList{"-cmd"}, "\n"; } } ################################################################################ sub Usage() { print "Usage:\n"; print "iChatServer_restore supports the following options:\n"; print " -cmd actions : Prints out the dictionary of BackupActions from the conf file := $SBS_CONF$ServiceConf\n"; print " -cmd browse -path path : \n"; print " where path is the path to the mounted image for browsing.\n"; print " -cmd restore -path path -opt option\n"; print " where path is the path to the mounted image where the data was backed-up.\n"; print " where option is one of: configuration, data, all\n"; print " -cmd help : Displays this usage.\n"; print " -cmd version : Prints out the version value from the property list := $SBS_CONF$ServiceConf\n"; exit(0); } ################################################################################ sub timestamp() { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); $year += 1900; $mon += 1; if ( $hour =~ /^\d$/ ) { $hour = "0" . $hour; } if ( $min =~ /^\d$/ ) { $min = "0" . $min; } if ( $sec =~ /^\d$/ ) { $sec = "0" . $sec; } my $ret = $year."-".$mon."-".$mday."-$hour:$min:$sec"; #my $ret = $year."-".$mon."-".$mday; return $ret; } ################################################################################ # Handle the various output modes, log to our file sub _log { my $type = shift; my $msg = shift; # log the message to our service-specific log file if ($LOG_PATH ne "") { open(OUT, ">>$LOG_PATH") || die "ERROR: Cannot open log file at path: $LOG_PATH : $!"; print OUT ×tamp."\t".$msg; close(OUT); } # print to STDOUT if appropriate if (($type == $OUTPUT_PRINT) || ($FUNCLOG && ($type == $OUTPUT_FUNCLOG)) || ($DEBUG && ($type == $OUTPUT_DEBUG)) || ($VERBOSE && ($type == $OUTPUT_VERBOSE))) { print $msg; } } # Pull the -log arg from argv sub _get_log_path { my ($theKey, $theVal); my %theArgList = @ARGV; while(($theKey, $theVal) = each (%theArgList)) { if ($theKey eq "-log") { return $theVal; } } return ""; }