#!/usr/bin/perl # # Copyright (C) 2001-2004 Mandrakesoft by Sebastien DUPONT # Updated 2002-2004 by Stew Benedict # Redistribution of this file is permitted under the terms of the GNU # Public License (GPL) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. use lib qw(/usr/lib/libDrakX); use standalone; #- warning, standalone must be loaded very first, for 'explanations' use strict; use interactive; use common; use detect_devices; # Backend Options. # make this global for status screen my ($window1, $my_win); my $central_widget; my $previous_widget; my $current_widget; my $interactive; my $up_box; my $advanced_box; my $box2; my $cfg_file_exist = 0; my @user_list_all; my $DEBUG = 0; my $restore_sys = 1; my $restore_user = 1; my $restore_other = 1; my $restore_step_sys_date = ""; my $restore_step_other_date = ""; my @user_backuped; my @sys_backuped; my @other_backuped; my @user_list_to_restore; my @sys_list_to_restore; my @other_list_to_restore; my $button_box; my $button_box_tmp; my $next_widget; my $system_state; my $restore_state; my $save_path_entry; my $restore_find_path_entry; my $new_path_entry; my $pbar; my $pbar1; my $pbar2; my $pbar3; my $plabel; my $plabel1; my $plabel2; my $plabel3; my $stext; my $list_model; my $the_time; my @user_list_to_restore2; my $restore_path = "/"; my $restore_other_path = 0; my $restore_other_src; my $path_to_find_restore; my $other_media_hd; my $backup_bef_restore = 0; my $table; my @user_list_backuped; my @files_corrupted; my %check_user_to_restore; my $remove_user_before_restore = 0; my @file_list_to_send_by_ftp; my $results; my @net_methods = ("ftp", "rsync", "ssh", "webdav"); my @media_types = ("hd", "cd", "tape"); my %cd_devices; my $std_device; my @tape_devices; my $in; # config. FILES -> Default PATH & Global variables. my @user_list; my $cfg_dir = "/etc/drakxtools/drakbackup/"; my $cfg_file = $cfg_dir . "drakbackup.conf"; my $log_file = "/var/log/drakbackup"; my $log_buff; my $manual_user = 0; my $backup_daemon = 0; my $daemon = 0; my $use_hd = 1; my $custom_cron = 0; my $session_offset = ''; my $scp_port = 22; my $user_home = $ENV{HOME}; my $nonroot_user = 0; my $media_problem = 0; my $vol_name = 'Drakbackup'; my $good_restore_path = 1; my @no_devices = translate(N_("No device found")); my %help; my %conf; my $time_string = "* * * * *"; my $exec_string = "export USER=$ENV{USER}; /usr/sbin/drakbackup --daemon > /dev/null 2>&1"; my $ignore_files_list; my @list_of_rpm_to_install; my @other_files; my @sys_files = "/etc"; my @files_for_direct_tape; my $host_passwd; my $untar_prefix = "tar -C $restore_path -x"; # allow not-root user with own config if ($ENV{USER} ne 'root' && $ENV{HOME} ne '/root') { standalone::explanations("Running as $ENV{USER}..."); #- doesn't get defined when run from cron $user_home = "/home/$ENV{USER}" if $user_home eq ''; $cfg_dir = "$user_home/.drakbackup/"; $conf{PATH_TO_SAVE} = $cfg_dir . "backups"; $log_file = $cfg_dir . "drakbackup.log"; $nonroot_user = 1; $conf{NO_SYS_FILES} = 1; @user_list = $ENV{USER}; } else { $user_home = "/root"; $conf{PATH_TO_SAVE} = "/var/lib/drakbackup"; } $cfg_file = $cfg_dir . "drakbackup.conf"; my $backup_key = $user_home . "/.ssh/identity-drakbackup"; foreach (@ARGV) { /--default/ and backend_mode(); /--daemon/ and daemon_mode(); /--show-conf/ and show_conf(); /--cd-info/ and get_cd_info(), exit(0); /--debug/ and $DEBUG = 1, next; } sub setup_tooltips() { %help = ( 'use_expect' => N("Expect is an extension to the TCL scripting language that allows interactive sessions without user intervention."), 'remember_pass' => N("Store the password for this system in drakbackup configuration."), 'erase_cdrw' => N("For a multisession CD, only the first session will erase the cdrw. Otherwise the cdrw is erased before each backup."), 'use_incr_decr' => N("This option will save files that have changed. Exact behavior depends on whether incremental or differential mode is used."), 'use_incremental' => N("Incremental backups only save files that have changed or are new since the last backup."), 'use_differential' => N("Differential backups only save files that have changed or are new since the original 'base' backup."), 'send_mail_to' => N("This should be a local user or email address that you want the backup results sent to. You will need to define a functioning mail server."), 'backupignore' => N("Files or wildcards listed in a .backupignore file at the top of a directory tree will not be backed up."), 'delete_files' => N("For backups to other media, files are still created on the hard drive, then moved to the other media. Enabling this option will remove the hard drive tar files after the backup."), 'dir_or_module' => N("Some protocols, like rsync, may be configured at the server end. Rather than using a directory path, you would use the 'module' name for the service path."), 'when_space' => N("Custom allows you to specify your own day and time. The other options use run-parts in /etc/crontab."), ); } sub show_conf() { print "DrakBackup configuration:\n\n"; read_conf_file(); system_state(); print "$system_state\n"; exit(0); } sub backend_mode() { build_backup_files(); exit(0); } sub daemon_mode() { $daemon = 1; build_backup_files(); exit(0); } if (check_for_xserver()) { eval { require ugtk2 }; die "Can't load ugtk2...\n" if $@; ugtk2->import(qw(:create :dialogs :helpers :wrappers)); interactive_mode(); } else { die "Can't run in console mode..."; } sub set_help_tip { my ($entry, $key) = @_; gtkset_tip(Gtk2::Tooltips->new, $entry, formatAlaTeX($help{$key})); } sub all_user_list() { if ($nonroot_user) { @user_list_all = $ENV{USER}; return; } my $user; my $uid; @user_list_all = (); my @passwd = cat_("/etc/passwd"); foreach (@passwd) { ($user, $uid) = (split(/:/, $_))[0, 2]; if ($uid >= 500 && $uid < 65000 || $uid == 0) { push @user_list_all, $user; } } } sub the_time() { my ($sec, $min, $hour, $day, $month, $year) = localtime(time()); sprintf("_%d%02d%02d_%02d%02d%02d", $year + 1900, $month + 1, $day, $hour, $min, $sec); } sub get_tape_info() { my @line_data; my $info = "$ENV{HOME}/tmp/dmesg"; @tape_devices = (); system("dmesg -s 100000 | grep 'st[0-9] at' > $info"); my @info = cat_($info); foreach (@info) { @line_data = split(/[ \t,]+/, $_); push @tape_devices, "/dev/" . $line_data[3]; } unlink($info); } sub get_free_space { my ($dir) = @_; my $free = `df -P $dir | tail -1`; my @line_data = split(/[ \t,]+/, $free); my $free_space = int($line_data[3] / 1024); return $free_space; } sub check_storage_quota { my ($dir) = @_; my $used = `du -b $dir`; my $used_space = $used / 1024 / 1024; if ($used_space > $conf{MAX_SPACE}) { return $used_space; } else { return 0; } } sub get_cd_info() { my @line_data; my @drive_names; my $i; require_rpm("cdrecord") if $nonroot_user; # just trying load ide-cd, since it doesn't seem to be loaded by default $nonroot_user ? `cdrecord -scanbus -dev=ATA > /dev/null 2>&1` : `modprobe ide-cd` unless -f "/proc/sys/dev/cdrom"; my @cd_info = cat_("/proc/sys/dev/cdrom/info"); my %data = ( "drive speed" => 'speed', "Can change speed" => 'chg_speed', "Can read multisession" => 'multisession', "Can write CD-R" => 'cdr', "Can write CD-RW" => 'cdrw', "Can write DVD-R" => 'dvdr', "Can write DVD-RAM" => 'dvdram' ); my $cd_drives; foreach (@cd_info) { @line_data = split(/[:\t]+/, $_); if ($line_data[0] =~ /drive name/) { $cd_drives = @line_data-1; chop($line_data[$cd_drives]); @drive_names = @line_data; print "drives: $cd_drives\n" unless $interactive; } chop($line_data[$cd_drives]) if $cd_drives; foreach my $key (keys %data) { if ($line_data[0] eq $key) { for ($i = 1; $i <= $cd_drives; $i++) { $cd_devices{$drive_names[$i]}{$data{$key}} = $line_data[$i]; } } } } #- now just report the data if we called --cd-info from the command line foreach my $key (keys %cd_devices) { my $rec_dev = $key; my $prefix; $rec_dev =~ s/sr/sg/; $prefix = "ATAPI:" if $rec_dev =~ /hd/; my $can_record = $cd_devices{$key}{cdr} || $cd_devices{$key}{cdrw} || $cd_devices{$key}{dvdr}; $cd_devices{$key}{rec_dev} = $prefix . "/dev/" . $rec_dev if $can_record; if (!$interactive) { print "\n{$key}->{rec_dev} = $cd_devices{$key}{rec_dev}\n"; print "{$key}->{speed} = $cd_devices{$key}{speed}\n"; print "{$key}->{chg_speed} = $cd_devices{$key}{chg_speed}\n"; print "{$key}->{multisession} = $cd_devices{$key}{multisession}\n"; print "{$key}->{cdr} = $cd_devices{$key}{cdr}\n"; print "{$key}->{cdrw} = $cd_devices{$key}{cdrw}\n"; print "{$key}->{dvdr} = $cd_devices{$key}{dvdr}\n"; print "{$key}->{dvdram} = $cd_devices{$key}{dvdram}\n"; } else { delete $cd_devices{$key} if $cd_devices{$key}{rec_dev} eq ''; } } } sub save_conf_file() { write_sitecopyrc() if $conf{NET_PROTO} eq 'webdav'; write_password_file() if $conf{NET_PROTO} eq 'rsync' && $conf{PASSWD}; return 1 if $conf{SEND_MAIL} && verify_mail_setup(); #- don't save this, but retain it for this session if ($conf{REMEMBER_PASS} != 1) { $host_passwd = $conf{PASSWD}; $conf{PASSWD} = undef; } if ($backup_daemon && $conf{DAEMON_MEDIA} eq '') { show_warning("f", N("No media selected for cron operation.")); return 1; } if ($backup_daemon && $conf{DAEMON_TIME_SPACE} eq '') { show_warning("f", N("No interval selected for cron operation.")); return 1; } if (!$backup_daemon) { $conf{DAEMON_TIME_SPACE} = ""; $conf{DAEMON_MEDIA} = ""; } $conf{NO_USER_FILES} = '' if @user_list == (); $conf{OTHER_FILES} = list_to_conf(@other_files); $conf{HOME_FILES} = list_to_conf(@user_list); $conf{SYS_FILES} = list_to_conf(@sys_files); mkdir_p($cfg_dir) if !-d $cfg_dir; setVarsInSh($cfg_file, \%conf); $conf{PASSWD} = $host_passwd if $conf{REMEMBER_PASS} != 1; chmod(0600, $cfg_file); save_cron_files(); 0; } sub read_cron_files() { my $daemon_found = 0; foreach (qw(hourly daily weekly monthly)) { if (-f "/etc/cron.$_/drakbackup" && !$nonroot_user) { $conf{DAEMON_TIME_SPACE} = $_; $daemon_found = 1; last; } } if ($conf{DAEMON_TIME_SPACE} ne "custom") { !$daemon_found and $backup_daemon = 0; } else { $custom_cron = 1; my $tmpcron = "$ENV{HOME}/tmp/crontab.tmp"; $tmpcron = `crontab -l | tail +4`; my @cronline = grep { /drakbackup/ } $tmpcron; if (@cronline) { @cronline = split(" ", $cronline[0]); my @crondetail = splice(@cronline, 0, 5); $time_string = join(" ", @crondetail); } } } sub save_cron_files() { my $tmpcron = "$ENV{HOME}/tmp/crontab.tmp"; if ($nonroot_user && $conf{DAEMON_TIME_SPACE} ne "custom" && $conf{DAEMON_TIME_SPACE} ne '' && $backup_daemon) { show_warning("w", N("Interval cron not available as non-root")); $conf{DAEMON_TIME_SPACE} = 'custom'; return 1; } else { foreach (qw(hourly daily weekly monthly)) { -f "/etc/cron.$_/drakbackup" and rm_rf("/etc/cron.$_/drakbackup") if !$nonroot_user; } } my @cron_file = ("#!/bin/sh\n", "export USER=root\n", "/usr/sbin/drakbackup --daemon > /dev/null 2>&1\n"); if ($conf{DAEMON_TIME_SPACE} ne "custom" && $conf{DAEMON_TIME_SPACE} ne '' && $backup_daemon) { output_p("/etc/cron.$conf{DAEMON_TIME_SPACE}/drakbackup", @cron_file); system("chmod +x /etc/cron.$conf{DAEMON_TIME_SPACE}/drakbackup"); } if ($conf{DAEMON_TIME_SPACE} eq "custom" || !$backup_daemon) { my $newdetail = $backup_daemon && join(" ", $time_string, $exec_string, "\n"); system("crontab -l | tail +4 > $tmpcron"); my @cronlines = cat_($tmpcron); my $index = 0; foreach (@cronlines) { if (/$exec_string/) { splice(@cronlines, $index, 1); } $index++; } push(@cronlines, $newdetail) if $backup_daemon; output($tmpcron, @cronlines); system("crontab $tmpcron"); unlink($tmpcron); } } sub upgrade_conf_file() { my @new_conf; $DEBUG and print "Old syntax...upgrading...\n"; my @conf_data = cat_($cfg_file); chop @conf_data; foreach (@conf_data) { push @new_conf, $_ . "=1\n" if !/=/; if (/^OTHER_FILES/) { my (@new_data) = split /=/; my @new_args = split(" ", $new_data[1]); push @new_conf, $new_data[0] . "=" . join(",", @new_args) . "\n"; } elsif (/=/ && !/TAR.GZ/) { my $has_arg = split /=/; push @new_conf, "$_\n" if $has_arg > 1; } elsif (/=/ && /TAR.GZ/) { push @new_conf, "OPTION_COMP=tar.gz"; } } output_p($cfg_file, @new_conf); } sub read_conf_file() { if (-e $cfg_file) { my $conf_version = `grep USE_HD $cfg_file`; upgrade_conf_file() if $conf_version !~ /^USE_HD=1/; %conf = getVarsFromSh($cfg_file); @other_files = conf_to_list($conf{OTHER_FILES}); @user_list = conf_to_list($conf{HOME_FILES}); @sys_files = conf_to_list($conf{SYS_FILES}) if exists($conf{SYS_FILES}); $backup_daemon = 1 if exists($conf{DAEMON_TIME_SPACE}); $conf{PASSWD} = $host_passwd if $conf{REMEMBER_PASS} != 1; read_cron_files(); $cfg_file_exist = 1; } else { $cfg_file_exist = 0; #- these were 1 by default, but that made it so the user could never save the #- inverse behavior. this allows incremental as the default if not configured $conf{SYS_INCREMENTAL_BACKUPS} = 1; $conf{USER_INCREMENTAL_BACKUPS} = 1; } # some basic defaults $conf{SMTP_SERVER} = "localhost" if !exists($conf{SMTP_SERVER}); $conf{MAX_SPACE} = 1000.0 if !exists($conf{MAX_SPACE}); $conf{USE_HD} = 1 if !exists($conf{USE_HD}); $conf{OPTION_COMP} = "tar.gz" if !exists($conf{OPTION_COMP}); # deal with users that may have been deleted from the system check_valid_users() if $cfg_file_exist; $use_hd = !($conf{USE_CD} || $conf{USE_TAPE} || $conf{USE_NET}); } sub verify_mail_setup() { all_user_list() if @user_list_all == (); if ($conf{USER_MAIL} ne "root" && $conf{USER_MAIL} !~ /[\w.-]*\@[\w.-]/ && !member($conf{USER_MAIL}, @user_list_all)) { show_warning("f", N("\"%s\" neither is a valid email nor is an existing local user!", $conf{USER_MAIL})); return 1; } if (member($conf{USER_MAIL}, @user_list_all) && $conf{SMTP_SERVER} ne "localhost") { show_warning("f", N("\"%s\" is a local user, but you did not select a local smtp, so you must use a complete email address!", $conf{USER_MAIL})); return 1; } } sub check_valid_users() { all_user_list(); my @new_user_list = intersection(\@user_list, \@user_list_all); if (@user_list != @new_user_list) { log::l(N("Valid user list changed, rewriting config file.")); if ($DEBUG) { print N("Old user list:\n"); print "@user_list\n"; print N("New user list:\n"); print "@new_user_list\n"; } @user_list = @new_user_list; save_conf_file(); } } sub write_sitecopyrc() { #- FIXME - how to deal with existing sitecopyrc my @cfg_list = ("site drakbackup\n", "\tserver $conf{HOST_NAME}\n", "\tremote /$conf{HOST_PATH}\n", "\tlocal $conf{PATH_TO_SAVE}\n", "\tusername $conf{LOGIN}\n", "\tpassword $conf{PASSWD}\n", "\tprotocol webdav\n" ); output_p("$user_home/.sitecopyrc", @cfg_list); chmod(0600, "$user_home/.sitecopyrc"); -d "$user_home/.sitecopy" or mkdir_p("$user_home/.sitecopy"); chmod(0700, "$user_home/.sitecopy"); } sub write_password_file() { output_p("$cfg_dir/rsync.user", "$conf{PASSWD}\n"); chmod(0600, "$cfg_dir/rsync.user"); } sub show_warning { my ($mode, $warning) = @_; $mode = N("Warning") if $mode eq "w"; $mode = N("Error") if $mode eq "f"; $mode = N("Information") if $mode eq "i"; if ($interactive) { $in->ask_warn($mode, translate($warning)); } else { warn "$mode: $warning\n"; } $log_buff .= "\n$mode: $warning\n"; } sub complete_results() { system_state(); $results .= "***********************************************************************\n\n"; $daemon or $results .= N("\n DrakBackup Report \n"); $daemon and $results .= N("\n DrakBackup Daemon Report\n"); my $datem = `date`; $results .= " $datem\n\n"; $results .= "***********************************************************************\n\n"; $results .= $system_state; $results .= "\n\n***********************************************************************\n\n"; $results .= N("\n DrakBackup Report Details\n\n\n"); $results .= "***********************************************************************\n\n"; } sub ftp_client() { use Net::FTP; my $ftp; foreach (1..5) { $ftp = Net::FTP->new($conf{HOST_NAME}, Debug => 0) or return 1; $ftp && $ftp->login($conf{LOGIN}, $conf{PASSWD}) and last; log::l("ftp login failed, sleeping before trying again"); sleep 5 * $_; $ftp = 0; } return 1 if !$ftp; $ftp->binary; $ftp->cwd($conf{HOST_PATH}); foreach (@file_list_to_send_by_ftp) { $interactive and $pbar->set_fraction(0); $interactive and progress($pbar, $plabel, 0.5, $_); $interactive and $pbar->set_text($_); $ftp->put($_, undef, undef); $interactive and progress($pbar, $plabel, 0.5, $_); $interactive and $pbar->set_text($_); $interactive and progress($pbar3, $plabel3, 1/@file_list_to_send_by_ftp, N("Total progress")); } $ftp->quit; return 0; } sub do_expect { #- Sort of a general purpose expect routine, we use it to backup files to #- a remote server, as well as transfer a key and restore. #- Using the key after it is setup is preferred. my ($mode) = @_; eval { require Expect }; if ($@) { #- should have already been installed during configuration $log_buff .= "perl-Expect not installed!" if check_pkg_needs(); return 1; } #- for debugging set to 1 $Expect::Exp_Internal = 0; #- for debugging set to 1 $Expect::Debug = 0; $Expect::Log_Stdout = 0; my $spawn_ok; my $no_perm; my $bad_passwd; my $bad_dir; my $had_err; my $timeout = 20; my $exp_command; my @send_files = "$backup_key.pub"; #- just bypass progress for sendkey for now my $no_prog = 1; $no_prog = 0 if $mode eq "sendkey"; @send_files = @file_list_to_send_by_ftp if $mode eq "backup"; $interactive && $no_prog and $pbar->set_fraction(0); $interactive && $no_prog and $pbar3->set_fraction(0); $interactive && $no_prog and progress($pbar, $plabel, 0.5, "File Transfer..."); foreach (@send_files) { $exp_command = "scp -P $scp_port $_ $conf{LOGIN}\@$conf{HOST_NAME}:$conf{HOST_PATH}" if $mode eq "backup"; $exp_command = "ssh-copy-id -i $_ $conf{LOGIN}\@$conf{HOST_NAME}" if $mode eq "sendkey"; if (-e $backup_key && $mode eq "sendkey") { if ($in->ask_yesorno(N("Warning"), N("%s exists, delete?\n\nIf you've already done this process you'll probably\n need to purge the entry from authorized_keys on the server.", $backup_key))) { unlink($backup_key); unlink($backup_key . '.pub'); } else { return 0; } } if (!(-e $backup_key) && $mode eq "sendkey") { $in->ask_warn(N("Information"), N("This may take a moment to generate the keys.")); gtkset_mousecursor_wait(); #- not using a passphrase for the moment system("ssh-keygen", "-P", "", "-t", "dsa", "-f", $backup_key); gtkset_mousecursor_normal(); } my $exp = Expect->spawn($exp_command) or $in->ask_warn(N("Error"), N("Cannot spawn %s.", $exp_command)); $interactive && $no_prog and progress($pbar3, $plabel3, 1/@send_files, N("Total progress")); $interactive && $no_prog and $stext->set_text($_); #- run scp, look for some common errors and try to track successful progress for GUI $exp->expect($timeout, [ qr 'password: $', sub { $spawn_ok = 1; my $fh = shift; $fh->send("$conf{PASSWD}\n"); Expect::exp_continue() } ], [ '-re', 'please try again', sub { $bad_passwd = 1; Expect::exp_continue() } ], [ '-re', 'Permission denied', sub { $no_perm = 1; Expect::exp_continue() } ], [ '-re', 'No such file or directory', sub { $bad_dir = 1; Expect::exp_continue() } ], # [ '-re', '%', sub { update_scp_progress(); Expect::exp_continue(); } ], [ eof => sub { if (!$spawn_ok) { show_warning("f", N("No password prompt on %s at port %s", $conf{HOST_NAME}, $scp_port)) } if ($bad_passwd) { show_warning("f", N("Bad password on %s", $conf{HOST_NAME})) } if ($no_perm) { show_warning("f", N("Permission denied transferring %s to %s", $_, $conf{HOST_NAME})) } if ($bad_dir) { show_warning("f", N("Can't find %s on %s", $conf{HOST_PATH}, $conf{HOST_NAME})) } $had_err = !$spawn_ok || $bad_passwd || $no_perm || $bad_dir; } ], [ timeout => sub { show_warning("f", N("%s not responding", $conf{HOST_NAME})) } ], ); my $exit_stat = $exp->exitstatus; $in->ask_warn(N("Information"), N("Transfer successful\nYou may want to verify you can login to the server with:\n\nssh -i %s %s\@%s\n\nwithout being prompted for a password.", $backup_key, $conf{LOGIN}, $conf{HOST_NAME})) if $exit_stat == 0 && !$had_err && $mode eq "sendkey"; $log_buff .= "$_\n" if $exit_stat == 0 && $mode eq "backup"; $exp->hard_close; } $interactive && $no_prog and progress($pbar, $plabel, 0.5, "Done..."); } sub ssh_client() { $DEBUG and print "file list to send: $_\n " foreach @file_list_to_send_by_ftp; my $command; my $value; foreach (@file_list_to_send_by_ftp) { if ($conf{USER_KEYS}) { $command = "scp -P $scp_port $_ $conf{LOGIN}\@$conf{HOST_NAME}:$conf{HOST_PATH}"; } else { $command = "scp -P $scp_port -i $backup_key $_ $conf{LOGIN}\@$conf{HOST_NAME}:$conf{HOST_PATH}"; } $interactive and $pbar->set_fraction(0); $interactive and progress($pbar, $plabel, 0.5, "File Transfer..."); $interactive and $stext->set_text($_); $log_buff .= $command . "\n\n"; my $TMP; open $TMP, "$command 2>&1 |"; while ($value = <$TMP>) { $log_buff .= $value; } close $TMP; $log_buff .= "\n"; $interactive and progress($pbar, $plabel, 0.5, "Done..."); $interactive and progress($pbar3, $plabel3, 1/@file_list_to_send_by_ftp, N("Total progress")); } return 0; } sub webdav_client() { $DEBUG and print "file list to send: $_\n " foreach @file_list_to_send_by_ftp; if (!(-e "$user_home/.sitecopy/drakbackup")) { my $command = "sitecopy -f $conf{HOST_PATH}"; spawn_progress($command, "Initializing sitecopy"); } my $command = "sitecopy -u drakbackup"; spawn_progress($command, "Running sitecopy..."); if ($log_buff =~ /Nothing to do - no changes found/) { show_warning("w", N("WebDAV remote site already in sync!")); return 1; } if ($log_buff !~ /Update completed successfully/) { show_warning("f", N("WebDAV transfer failed!")); return 1; } return 0; } sub rsync_client() { $DEBUG and print "file list to send: $_\n " foreach @file_list_to_send_by_ftp; my $rsync_cmd = "rsync -tv $conf{PATH_TO_SAVE}/* "; $rsync_cmd = $rsync_cmd . "--password-file=$cfg_dir/rsync.user " if $conf{PASSWD}; $rsync_cmd = $rsync_cmd . "$conf{LOGIN}\@" if $conf{LOGIN}; $rsync_cmd = $rsync_cmd . $conf{HOST_NAME} . "::" . $conf{HOST_PATH}; spawn_progress($rsync_cmd, "Running rsync"); return 0; } sub check_for_cd() { #- check for a cd my $command = "cdrecord dev=$conf{CD_DEVICE} -atip"; spawn_progress($command, "Check for media in drive"); if ($log_buff =~ /No disk/) { show_warning("f", N("No CD-R/DVD-R in drive!")); return 1; } if ($log_buff !~ /ATIP info from disk|ATIP start of lead in|Found DVD media/) { show_warning("f", N("Does not appear to be recordable media!")); return 1; } #- non-fatal, just disable erase if (($log_buff =~ /Is not erasable/ || $log_buff =~ /Found DVD media/) && $conf{MEDIA_ERASE}) { show_warning("w", N("Not erasable media!")); $conf{MEDIA_ERASE} = 0; save_conf_file(); } if ($conf{MULTI_SESSION}) { $command = "cdrecord -s dev=$conf{CD_DEVICE} -msinfo"; spawn_progress($command, "Check for previous session status"); #- if we don't find a previous session, start fresh if ($log_buff =~ /Cannot read session offset/) { $conf{MEDIA_ERASE} = 1; return 0; } else { #- extract the session info from $log_buff my $code_loc = rindex($log_buff, "msinfo") + 8; if ($code_loc != -1) { my $bufflen = length($log_buff); $session_offset = substr($log_buff, $code_loc, $bufflen-$code_loc-1); return 0; } return 1; } } } sub write_on_cd() { my $command = "cdrecord -v dev=$conf{CD_DEVICE} -data "; # DVD+RW use -sao $command .= "-sao " if $conf{DVDRW}; #- only blank if it's the first session $command .= "blank=fast " if $conf{MEDIA_ERASE} && $session_offset eq ''; #- multi-session mode $command .= "-multi -pad " if $conf{MULTI_SESSION}; $command .= "$conf{PATH_TO_SAVE}/drakbackup.iso"; spawn_progress($command, "Running cdrecord"); unlink("$conf{PATH_TO_SAVE}/drakbackup.iso"); } sub erase_cdrw() { #- we can only hit this via interactive $interactive = 0; $in->ask_warn(N("Information"), N("This may take a moment to erase the media.")); gtkset_mousecursor_wait(); my $command = "cdrecord dev=$conf{CD_DEVICE} -blank=fast"; spawn_progress($command, "Erasing CDRW..."); gtkset_mousecursor_normal(); $interactive = 1; } sub spawn_progress { my ($command, $descr) = @_; my $value; my $timer; $interactive and progress($pbar3, $plabel3, 0, translate($descr)); $interactive and $pbar3->set_pulse_step(0.1); $interactive and $timer = Glib::Timeout->add(20, sub { $pbar3->pulse }); $log_buff .= "\n" . $descr . ":\n"; $log_buff .= $command . "\n\n"; standalone::explanations("Running $command"); my $TMP; open $TMP, "$command 2>&1 |"; while ($value = <$TMP>) { $log_buff .= $value; if ($interactive) { $stext->set_text($value); gtkflush(); } } close $TMP; $interactive and Glib::Source->remove($timer); } sub get_cd_volname() { my $vol_device = $conf{CD_DEVICE}; $vol_device =~ s/sg/scd/; $vol_name = `volname $vol_device` if $conf{CD_DEVICE}; $vol_name =~ s/[ \t]+\n$//; $vol_name; } sub build_iso() { if ($conf{MULTI_SESSION} && $session_offset) { $vol_name = get_cd_volname(); } else { $vol_name = "Drakbackup" . $the_time; } #this is safe to change the volname on rewrites, as is seems to get ignored anyway my $command = "mkisofs -r -J -T -v -V '$vol_name' "; $command .= "-C $session_offset -M $conf{CD_DEVICE} " if $conf{MULTI_SESSION} && $session_offset; $command .= "-o $conf{PATH_TO_SAVE}/drakbackup.iso @file_list_to_send_by_ftp"; spawn_progress($command, "Running mkisofs..."); } sub build_cd() { if (!check_for_cd()) { build_iso(); if ($log_buff =~ /Permission denied/) { show_warning("f", N("Permission problem accessing CD.")); $media_problem = 1; return 1; } else { write_on_cd(); } } } sub get_tape_label { my ($device) = @_; gtkset_mousecursor_wait(); system("mt -f $device rewind"); system("tar -C $cfg_dir -xf $device"); my @volname = cat_("$cfg_dir/drakbackup.label"); unlink("$cfg_dir/drakbackup.label"); $vol_name = $volname[0]; gtkset_mousecursor_normal(); $vol_name; } sub build_tape() { my $command; #- do we have a tape? $command = "mt -f $conf{TAPE_DEVICE} status"; spawn_progress($command, "Checking for tape"); if ($log_buff =~ /DR_OPEN/) { show_warning("f", N("No tape in %s!", $conf{TAPE_DEVICE})); return 1; } #- try to roll to the end of the data if we're not erasing if (!$conf{MEDIA_ERASE}) { $command = "mt -f $conf{TAPE_DEVICE} rewind"; spawn_progress($command, "Rewind to find tape label"); $command = "tar -tf $conf{TAPE_DEVICE}"; spawn_progress($command, "Check for label"); if ($log_buff =~ /drakbackup.label/) { if ($conf{TAPE_NOREWIND}) { $command = "mt -f $conf{TAPE_DEVICE} rewind"; spawn_progress($command, "Rewind to get tape label"); } $command = "tar -C $cfg_dir -xf $conf{TAPE_DEVICE}"; spawn_progress($command, "Reading tape label"); my @volname = cat_("$cfg_dir/drakbackup.label"); unlink("$cfg_dir/drakbackup.label"); $vol_name = $volname[0]; } $command = "mt -f $conf{TAPE_DEVICE} eod"; spawn_progress($command, "Running mt to find eod"); } else { $command = "mt -f $conf{TAPE_DEVICE} rewind"; spawn_progress($command, "Running mt to rewind"); # make a tape label for the catalog # if we're using the rewinding device, change modes briefly if (!$conf{TAPE_NOREWIND}) { $conf{TAPE_DEVICE} =~ s|/st|/nst|; } $vol_name = "Drakbackup" . $the_time; my $f = "$cfg_dir/drakbackup.label"; output($f, $vol_name); $command = "tar -C $cfg_dir -cf $conf{TAPE_DEVICE} drakbackup.label;"; spawn_progress($command, "Creating tape label"); unlink $f; if (!$conf{TAPE_NOREWIND}) { $conf{TAPE_DEVICE} =~ s|/nst|/st|; } } #- do the backup $command = "tar -cvf $conf{TAPE_DEVICE} "; if ($conf{DIRECT_TAPE}) { ($command, undef) = handle_ignores($command, undef, @files_for_direct_tape); $command .= " @files_for_direct_tape"; } else { $command .= " @file_list_to_send_by_ftp"; } spawn_progress($command, "Running tar to tape"); #- eject the tape? if ($conf{MEDIA_EJECT}) { $command = "mt -f $conf{TAPE_DEVICE} rewoff"; spawn_progress($command, "Running mt to eject tape"); } } sub send_mail { my ($result) = @_; my $datem = `date`; use Mail::Mailer; my $mailer = Mail::Mailer->new('smtp', Server => $conf{SMTP_SERVER}); $mailer->open({ From => 'drakbackup', To => $conf{USER_MAIL}, Subject => "DrakBackup report on $datem", }) or return 1; print $mailer $result; $mailer->close; 0; } sub build_backup_files() { my $path_name; my $tar_cmd; my $more_recent; my $tar_cmd_sys; my $tar_cmd_user; my $tar_cmd_other; my @dir_content; my $incr; my $base; my $find_args = "! -type d -print"; local $_; $results = ""; $log_buff = ""; #- flush these so if the user does 2 runs in a row we don't try to send the same files @file_list_to_send_by_ftp = (); @files_for_direct_tape = (); $interactive and gtkset_mousecursor_wait(); read_conf_file(); $the_time = the_time(); $conf{SEND_MAIL} and complete_results(); -d $conf{PATH_TO_SAVE} or mkdir_p($conf{PATH_TO_SAVE}); $tar_cmd = "tar cp"; $tar_cmd .= "v" if $DEBUG; $tar_cmd .= "j" if $conf{OPTION_COMP} eq "tar.bz2"; $tar_cmd .= "z" if $conf{OPTION_COMP} eq "tar.gz"; $tar_cmd .= " "; my $used_space = check_storage_quota($conf{PATH_TO_SAVE}); if ($used_space) { my $msg = N("Backup quota exceeded!\n%d MB used vs %d MB allocated.", $used_space, $conf{MAX_SPACE}); show_warning("f", $msg); $interactive and gtkset_mousecursor_normal(); $results .= $msg; $interactive and show_status(); results_to_logfile(); return 1; } $tar_cmd_sys = $tar_cmd; $tar_cmd_user = $tar_cmd; $tar_cmd_other = $tar_cmd; $conf{NO_CRITICAL_SYS} and $tar_cmd_sys .= "--exclude passwd --exclude fstab --exclude group --exclude mtab"; $conf{NO_BROWSER_CACHE} and $tar_cmd_user .= "--exclude NewCache --exclude Cache --exclude cache"; $nonroot_user and $tar_cmd_user .= " --exclude .drakbackup"; $conf{BACKUPIGNORE} && -f "/etc/.backupignore" and $tar_cmd_sys .= " -X /etc/.backupignore"; -d $conf{PATH_TO_SAVE} and @dir_content = all($conf{PATH_TO_SAVE}); if ($conf{USE_HD} && !$daemon || $daemon) { $interactive and progress($pbar, $plabel, 0.5, N("Backup system files...")); unless ($conf{NO_SYS_FILES}) { my $find_args_sys = $find_args; my $first_done; $ignore_files_list = ''; $find_args_sys = handle_ignores2("/etc", $find_args_sys) if $conf{BACKUPIGNORE}; if ($conf{SYS_INCREMENTAL_BACKUPS}) { $base = $incr = "incr_sys"; ($base, $incr) = swap_prefix($base, $incr) if $conf{SYS_DIFFERENTIAL_BACKUPS}; $base =~ s/incr/base/ if !any { /^list_incr_sys/ } @dir_content; if (any { /^list_base_sys/ } @dir_content) { $more_recent = get_more_recent($base, @dir_content); my $list_file = name_list_file($incr); do_find($more_recent, $find_args_sys, $list_file, @sys_files); if (check_rm_list($list_file)) { do_tar($tar_cmd_sys, "backup_$incr", $list_file, undef); } $first_done = 1; } else { $incr = "base_sys"; } } else { $incr = "sys"; clean_dest($incr); } if (!$first_done) { my $list_file = name_list_file($incr); do_find(undef, $find_args_sys, $list_file, @sys_files); do_tar($tar_cmd_sys, "backup_$incr", undef, @sys_files); } push_list("list_$incr") if $incr =~ /_sys/; files_to_results($incr); } $interactive and progress($pbar, $plabel, 0.5, N("Backup system files...")); $interactive and progress($pbar3, $plabel3, 0.3, N("Hard Disk Backup files...")); unless ($conf{NO_USER_FILES}) { foreach (@user_list) { my $user = $_; my $tar_cmd_cuser = $tar_cmd_user; $path_name = return_path($user); $conf{BACKUPIGNORE} && -f "$path_name/.backupignore" and $tar_cmd_cuser .= " -X $path_name/.backupignore"; my $find_args_user = $find_args; my $first_done; $ignore_files_list = ''; $find_args_user = handle_ignores2($path_name, $find_args_user) if $conf{BACKUPIGNORE}; if ($conf{USER_INCREMENTAL_BACKUPS}) { $base = $incr = "incr_user_"; ($base, $incr) = swap_prefix($base, $incr) if $conf{USER_DIFFERENTIAL_BACKUPS}; $base =~ s/incr/base/ if !any { /^list_incr_user_$user/ } @dir_content; if (any { /^list_base_user_$user/ } @dir_content) { $more_recent = get_more_recent("$base$user", @dir_content); my $list_file = name_list_file($incr . $user); do_find($more_recent, $find_args_user, $list_file, $path_name); if (check_rm_list($list_file)) { do_tar($tar_cmd_cuser, "backup_$incr$user", $list_file, undef); } $first_done = 1; } else { $incr = "base_user_"; } } else { $incr = "user_"; clean_dest("$incr$user"); } if (!$first_done) { my $list_file = name_list_file($incr . $user); do_find(undef, $find_args_user, $list_file, $path_name); do_tar($tar_cmd_cuser, "backup_$incr$user", undef, $path_name); } push_list("list_$incr$user") if $incr =~ /_user/; files_to_results("$incr$user"); } } $interactive and progress($pbar2, $plabel1, 1, N("Backup User files...")); $interactive and progress($pbar3, $plabel3, 0.4, N("Hard Disk Backup files...")); if ($conf{OTHER_FILES}) { my $find_args_other = $find_args; my $first_done; $ignore_files_list = ''; ($tar_cmd_other, $find_args_other) = handle_ignores($tar_cmd_other, $find_args_other, @other_files) if $conf{BACKUPIGNORE}; if ($conf{OTHER_INCREMENTAL_BACKUPS}) { $base = $incr = "incr_other"; ($base, $incr) = swap_prefix($base, $incr) if $conf{OTHER_DIFFERENTIAL_BACKUPS}; $base =~ s/incr/base/ if !any { /^list_incr_other/ } @dir_content; if (any { /^list_base_other/ } @dir_content) { $more_recent = get_more_recent($base, @dir_content); my $list_file = name_list_file($incr); do_find($more_recent, $find_args_other, $list_file, @other_files); if (check_rm_list($list_file)) { do_tar($tar_cmd_other, "backup_$incr", $list_file, undef); } $first_done = 1; } else { $incr = "base_other"; } } else { $incr = "other"; clean_dest($incr); } if (!$first_done) { my $list_file = name_list_file($incr); do_find(undef, $find_args_other, $list_file, @other_files); do_tar($tar_cmd_other, "backup_$incr", undef, @other_files); } push_list("list_$incr") if $incr =~ /_other/; files_to_results($incr); } $interactive and progress($pbar1, $plabel2, 1, N("Backup Other files...")); $interactive and progress($pbar3, $plabel3, 0.3, N("Hard Disk Backup Progress...")); } my $filecount = @file_list_to_send_by_ftp; if (!$filecount && !$conf{DIRECT_TAPE}) { my $msg = N("No changes to backup!"); show_warning("w", $msg); $interactive and gtkset_mousecursor_normal(); $interactive and interactive_mode_box(); results_to_logfile(); return 1; } #- should hit this block if running daemon mode only if ($daemon && $conf{DAEMON_MEDIA}) { # ftp_client() if $ftp_daemon; rsync_client() if $conf{DAEMON_MEDIA} eq 'rsync'; ssh_client() if $conf{DAEMON_MEDIA} eq 'ssh' && !$conf{USE_EXPECT}; do_expect("backup") if $conf{DAEMON_MEDIA} eq 'ssh' && $conf{USE_EXPECT}; webdav_client() if $conf{DAEMON_MEDIA} eq 'webdav'; build_cd() if $conf{DAEMON_MEDIA} eq 'cd'; build_tape() if $conf{DAEMON_MEDIA} eq 'tape'; $results .= N("\nDrakbackup activities via %s:\n\n", $conf{DAEMON_MEDIA}) if $conf{DAEMON_MEDIA} ne 'hd'; $results .= $log_buff; } #- leave this one alone for now - works well #- integrate with other methods later if (($conf{USE_NET} && !$daemon && $conf{NET_PROTO} eq 'ftp') || $daemon && $conf{DAEMON_MEDIA} eq 'ftp') { $interactive and build_backup_ftp_status(); if (ftp_client()) { $results .= N("\n FTP connection problem: It was not possible to send your backup files by FTP.\n"); $interactive and $in->ask_warn(N("Error"), N("Error during sending file via FTP. Please correct your FTP configuration.")); } else { $results .= N("file list sent by FTP: %s\n", $_) foreach @file_list_to_send_by_ftp; } } #- consolidate all the other methods under here - interactive and --default should land here if (!$daemon) { if ($conf{USE_NET} && $conf{NET_PROTO} && $conf{NET_PROTO} ne 'ftp') { rsync_client() if $conf{NET_PROTO} eq 'rsync'; ssh_client() if $conf{NET_PROTO} eq 'ssh' && !$conf{USE_EXPECT}; do_expect("backup") if $conf{NET_PROTO} eq 'ssh' && $conf{USE_EXPECT}; webdav_client() if $conf{NET_PROTO} eq 'webdav'; $results .= N("\nDrakbackup activities via %s:\n\n", $conf{NET_PROTO}); } if ($conf{USE_CD}) { build_cd(); $results .= N("\nDrakbackup activities via CD:\n\n"); } if ($conf{USE_TAPE}) { build_tape(); $results .= N("\nDrakbackup activities via tape:\n\n"); } $results .= $log_buff; } results_to_logfile(); if ($conf{SEND_MAIL}) { if (send_mail($results)) { $interactive and $in->ask_warn(N("Error"), N("Error sending mail. Your report mail was not sent.")); $interactive or print N(" Error while sending mail. \n"); } } #- write our catalog file if (!$media_problem) { my $catalog = substr($the_time, 1); my $direct_tape = ""; $direct_tape = "Direct" if $conf{DIRECT_TAPE}; if (!$conf{USE_NET} && !$conf{USE_TAPE} && !$conf{USE_CD}) { $catalog .= ":HD:localhost:$conf{PATH_TO_SAVE}"; $conf{NET_PROTO} = ''; } $catalog .= ":$conf{NET_PROTO}:$conf{LOGIN}\@$conf{HOST_NAME}:$conf{HOST_PATH}" if $conf{NET_PROTO}; $catalog .= ":CD:$vol_name:$conf{CD_DEVICE}" if $conf{USE_CD}; $catalog .= ":" . $direct_tape . "Tape:$vol_name:$conf{TAPE_DEVICE}" if $conf{USE_TAPE}; $catalog .= ":System" unless $conf{NO_SYS_FILES}; $catalog .= ":I" if $conf{SYS_INCREMENTAL_BACKUPS} && !$conf{NO_SYS_FILES} && !$conf{SYS_DIFFERENTIAL_BACKUPS}; $catalog .= ":D" if $conf{SYS_INCREMENTAL_BACKUPS} && !$conf{NO_SYS_FILES} && $conf{SYS_DIFFERENTIAL_BACKUPS}; $catalog .= ":F" if !$conf{SYS_INCREMENTAL_BACKUPS} && !$conf{NO_SYS_FILES}; $catalog .= ":Users=(@user_list)" unless $conf{NO_USER_FILES}; $catalog .= ":I" if $conf{USER_INCREMENTAL_BACKUPS} && !$conf{NO_USER_FILES} && !$conf{USER_DIFFERENTIAL_BACKUPS}; $catalog .= ":D" if $conf{USER_INCREMENTAL_BACKUPS} && !$conf{NO_USER_FILES} && $conf{USER_DIFFERENTIAL_BACKUPS}; $catalog .= ":F" if !$conf{USER_INCREMENTAL_BACKUPS} && !$conf{NO_USER_FILES}; $catalog .= ":Other=(@other_files)" if $conf{OTHER_FILES}; $catalog .= ":I" if $conf{OTHER_INCREMENTAL_BACKUPS} && $conf{OTHER_FILES} && !$conf{OTHER_DIFFERENTIAL_BACKUPS}; $catalog .= ":D" if $conf{OTHER_INCREMENTAL_BACKUPS} && $conf{OTHER_FILES} && $conf{OTHER_DIFFERENTIAL_BACKUPS}; $catalog .= ":F" if !$conf{OTHER_INCREMENTAL_BACKUPS} && $conf{OTHER_FILES}; $catalog .= "\n"; append_to_file("$cfg_dir/drakbackup_catalog", $catalog) or show_warning("w", N("Can't create catalog!")); } #- clean up HD files if del_hd_files and media isn't hd if ($conf{DEL_HD_FILES} && ($conf{USE_CD} || $conf{USE_TAPE} || $conf{USE_NET}) && $conf{DAEMON_MEDIA} ne 'hd') { foreach (@file_list_to_send_by_ftp) { unlink($_) if /$conf{OPTION_COMP}$/; } } #- if we had a media problem then get rid of the text log of the backed up files too if ($media_problem) { system("rm $conf{PATH_TO_SAVE}/list*$the_time.txt"); } $interactive and gtkset_mousecursor_normal(); $interactive and show_status(); } sub swap_prefix { my ($base, $incr) = @_; $incr =~ s/incr/diff/; $base =~ s/incr/base/; return $base, $incr; } sub name_list_file { my ($suffix) = @_; return $conf{PATH_TO_SAVE} . "/list_" . $suffix . $the_time . ".txt"; } sub check_rm_list { my ($list_file) = @_; if (!cat_($list_file)) { unlink($list_file); return 0; } else { return 1; } } sub get_more_recent { my ($match, @directory) = @_; $match = "list_" . $match; my @more_recent = grep { /^$match/ } sort @directory; my $more_recent = pop @more_recent; $DEBUG and print "more recent file: $more_recent\n"; return $more_recent; } sub clean_dest { my ($wildcard) = @_; system("cd $conf{PATH_TO_SAVE} && rm -f backup*$wildcard*"); } sub do_find { my ($newer, $more_args, $into, @where) = @_; #- $newer may be undef - if it's defined then "-cnewer $newer" $newer = $conf{PATH_TO_SAVE} . "/" . $newer if defined($newer); defined($newer) ? system("find @where -cnewer $newer $more_args > $into") : system("find @where $more_args > $into"); #- someone on club complained about perms being too open chmod(0600, $into) if -e $into; } sub do_tar { my ($tar_cmd, $dest_file, $list_file, @files) = @_; my $full_dest_file = $conf{PATH_TO_SAVE} . "/" . $dest_file . $the_time . "." . $conf{OPTION_COMP}; if ($conf{DIRECT_TAPE}) { log::explanations("Direct tape backup - tar deferred..."); defined($list_file) ? push @files_for_direct_tape, $list_file : push @files_for_direct_tape, @files; } else { #- if $list_file is undefined, then use the @files list defined($list_file) ? system("$tar_cmd -f $full_dest_file -T $list_file") : system("$tar_cmd -f $full_dest_file @files"); } #- someone on club complained about perms being too open chmod(0600, $full_dest_file) if -e $full_dest_file; push_list($dest_file); } sub push_list { my ($prefix) = @_; my $filename = $conf{PATH_TO_SAVE} . "/" . $prefix . $the_time . "."; $filename .= $conf{OPTION_COMP} if $prefix =~ /^backup/; $filename .= "txt" if $prefix =~ /^list/; push @file_list_to_send_by_ftp, $filename if -e $filename; } sub files_to_results { my ($basename) = @_; if ($conf{DIRECT_TAPE}) { $results .= "\nDirect to tape:\n\n"; } else { $results .= "\nfile: " . $conf{PATH_TO_SAVE} . "/backup_" . $basename . $the_time . "." . $conf{OPTION_COMP} . "\n\n"; $results .= cat_("$conf{PATH_TO_SAVE}/list_" . $basename . $the_time . ".txt"); } $results .= "\nignored:\n" . $ignore_files_list . "\n" if $ignore_files_list; } sub handle_ignores { my ($tar_cmd, $find_args, @list) = @_; foreach my $dir (@list) { if (-d $dir) { -f "$dir/.backupignore" and $tar_cmd .= " -X $dir/.backupignore"; $find_args = handle_ignores2($dir, $find_args); } } return $tar_cmd, $find_args; } sub handle_ignores2 { my ($dir, $find_args) = @_; my @ignore_files = cat_("$dir/.backupignore"); foreach (@ignore_files) { $ignore_files_list .= $_; } $find_args .= " | grep -v -f $dir/.backupignore" if -f "$dir/.backupignore"; return $find_args; } sub require_rpm { my $all_rpms_found = 1; my $res; foreach my $pkg (@_) { $res = system("rpm -q $pkg > /dev/null"); if ($res == 256) { $all_rpms_found = 0; push @list_of_rpm_to_install, $pkg; } } return $all_rpms_found; } sub check_pkg_needs() { my @extra_pkg; @list_of_rpm_to_install = (); if ($conf{USE_NET}) { @extra_pkg = "rsync" if $conf{NET_PROTO} eq 'rsync'; @extra_pkg = ("sitecopy", "wget") if $conf{NET_PROTO} eq 'webdav'; @extra_pkg = "perl-Expect" if $conf{NET_PROTO} eq 'ssh' && ($conf{USE_EXPECT} || $conf{DRAK_KEYS}); } @extra_pkg = "mt-st" if $conf{USE_TAPE}; @extra_pkg = ("mkisofs", "cdrecord") if $conf{USE_CD}; if (@extra_pkg) { if (require_rpm(@extra_pkg)) { return 0; } else { return 1; } } } sub show_status() { my $text = Gtk2::TextView->new; destroy_widget(); my $scrolled_window = Gtk2::ScrolledWindow->new; $scrolled_window->set_border_width(10); $scrolled_window->add_with_viewport($text); gtktext_insert(gtkset_editable($text, 0), [ [ $results ] ]); gtkpack($advanced_box, $table = gtkpack_(Gtk2::VBox->new(0,10), 1, $scrolled_window) ); $central_widget = \$table; $table->show_all; } sub results_to_logfile() { output_p($log_file, $results); } sub conf_to_list { my ($config) = @_; return split(",", $config); } sub list_to_conf { my (@list) = @_; return join(",", @list); } sub filedialog_generic { #- a more generic file dialog #- a title prompt, the widget to get updated my ($prompt, $widget) = @_; my $file_dialog; $file_dialog = gtksignal_connect(Gtk2::FileSelection->new($prompt), destroy => sub { $file_dialog->destroy }); $file_dialog->ok_button->signal_connect(clicked => sub { if (defined($widget)) { $$widget->set_text($file_dialog->get_filename); } else { my $file_name = $file_dialog->get_filename; #- catch files and dirs with spaces $file_name = '"' . $file_name . '"' if $file_name =~ / /; if (!member($file_name, @other_files)) { push(@other_files, $file_name); $list_model->append_set(0, $file_name); } } $file_dialog->destroy; }); $file_dialog->cancel_button->signal_connect(clicked => sub { $file_dialog->destroy }); $file_dialog->show; } ################################################ ADVANCED ################################################ sub check_list { foreach (@_) { my $ref = $_->[1]; $_->[2] ? gtkset_active($_->[0], !$$ref) : gtkset_active($_->[0], $$ref); gtksignal_connect($_->[0], toggled => sub { invbool $ref; destroy_widget(); $current_widget->(); }); } } sub fonction_env { ($central_widget, $current_widget, $previous_widget, $next_widget) = @_; } sub advanced_what_sys() { my $box_what_sys; gtkpack($advanced_box, $box_what_sys = gtkpack_(Gtk2::VBox->new(0, 15), 1, N("\nPlease check all options that you need.\n"), 1, N("These options can backup and restore all files in your /etc directory.\n"), 0, my $check_what_sys = Gtk2::CheckButton->new(N("Backup your System files. (/etc directory)")), 0, my $check_what_versions = Gtk2::CheckButton->new(N("Use Incremental/Differential Backups (do not replace old backups)")), 0, gtkpack__(Gtk2::HBox->new(0,0), my @mode_buttons = gtkradio((N("Use Incremental Backups")) x 2, N("Use Differential Backups")), ), 0, my $check_what_critical = Gtk2::CheckButton->new(N("Do not include critical files (passwd, group, fstab)")), 0, N("With this option you will be able to restore any version\n of your /etc directory."), 1, Gtk2::VBox->new(0, 15), ), ); check_list([$check_what_sys, \$conf{NO_SYS_FILES}, 1], [$check_what_critical, \$conf{NO_CRITICAL_SYS}]); $check_what_versions->set_active($conf{SYS_INCREMENTAL_BACKUPS}); $check_what_versions->signal_connect('toggled' => sub { invbool \$conf{SYS_INCREMENTAL_BACKUPS}; $mode_buttons[0]->set_sensitive($conf{SYS_INCREMENTAL_BACKUPS}); $mode_buttons[1]->set_sensitive($conf{SYS_INCREMENTAL_BACKUPS}); }); $mode_buttons[1]->set_active($conf{SYS_DIFFERENTIAL_BACKUPS}); $mode_buttons[0]->signal_connect('toggled' => sub { $conf{SYS_DIFFERENTIAL_BACKUPS} = $mode_buttons[1]->get_active }); $mode_buttons[0]->set_sensitive($conf{SYS_INCREMENTAL_BACKUPS}); $mode_buttons[1]->set_sensitive($conf{SYS_INCREMENTAL_BACKUPS}); set_help_tip($check_what_versions, 'use_incr_decr'); set_help_tip($mode_buttons[0], 'use_incremental'); set_help_tip($mode_buttons[1], 'use_differential'); fonction_env(\$box_what_sys, \&advanced_what_sys, \&advanced_what); $up_box->show_all; } sub advanced_what_user { my ($previous_function) = @_; my $box_what_user; my %check_what_user; all_user_list(); gtkpack($advanced_box, $box_what_user = gtkpack_(Gtk2::VBox->new(0, 15), 0, N("Please check all users that you want to include in your backup."), 0, Gtk2::HSeparator->new, 1, create_scrolled_window( gtkpack__(Gtk2::VBox->new(0,0), map { my $name = $_; my @user_list_tmp; my $b = Gtk2::CheckButton->new($name); if (any { /^$name$/ } @user_list) { $check_what_user{$_}[1] = 1; gtkset_active($b, 1); } else { $check_what_user{$_}[1] = 0; gtkset_active($b, 0); } $b->signal_connect(toggled => sub { if ($check_what_user{$name}[1]) { $check_what_user{$name}[1] = 0; @user_list_tmp = grep { !/^$name$/ } @user_list; @user_list = @user_list_tmp; } else { $check_what_user{$name}[1] = 1; if (!member($name, @user_list)) { push @user_list, $name } } }); $b } (@user_list_all) ), ), 0, my $check_what_browser = Gtk2::CheckButton->new(N("Do not include the browser cache")), 0, my $check_what_user_versions = Gtk2::CheckButton->new(N("Use Incremental/Differential Backups (do not replace old backups)")), 0, gtkpack__(Gtk2::HBox->new(0,0), my @mode_buttons = gtkradio((N("Use Incremental Backups")) x 2, N("Use Differential Backups")), ), ), ); check_list([$check_what_browser, \$conf{NO_BROWSER_CACHE}]); $check_what_user_versions->set_active($conf{USER_INCREMENTAL_BACKUPS}); $check_what_user_versions->signal_connect('toggled' => sub { invbool \$conf{USER_INCREMENTAL_BACKUPS}; $mode_buttons[0]->set_sensitive($conf{USER_INCREMENTAL_BACKUPS}); $mode_buttons[1]->set_sensitive($conf{USER_INCREMENTAL_BACKUPS}); }); $mode_buttons[1]->set_active($conf{USER_DIFFERENTIAL_BACKUPS}); $mode_buttons[0]->signal_connect('toggled' => sub { $conf{USER_DIFFERENTIAL_BACKUPS} = $mode_buttons[1]->get_active }); $mode_buttons[0]->set_sensitive($conf{USER_INCREMENTAL_BACKUPS}); $mode_buttons[1]->set_sensitive($conf{USER_INCREMENTAL_BACKUPS}); set_help_tip($check_what_user_versions, 'use_incr_decr'); set_help_tip($mode_buttons[0], 'use_incremental'); set_help_tip($mode_buttons[1], 'use_differential'); if ($previous_function) { fonction_env(\$box_what_user, \&advanced_what_user, \&$previous_function, \&$previous_function) } else { fonction_env(\$box_what_user, \&advanced_what_user, \&advanced_what) } $up_box->show_all; } sub advanced_what_other() { my $box_what_other; my $file_iter; my $other_file; $list_model = Gtk2::ListStore->new("Glib::String"); my $list_others = Gtk2::TreeView->new_with_model($list_model); $list_others->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); $list_others->set_headers_visible(0); foreach (@other_files) { $list_model->append_set(0, $_); } $list_others->get_selection->signal_connect(changed => sub { my ($model, $iter) = $_[0]->get_selected; $model && $iter or return; $other_file = $model->get($iter, 0); $file_iter = $iter; }); gtkpack($advanced_box, $box_what_other = gtkpack_(Gtk2::VBox->new(0, 15), 1, gtkpack_(Gtk2::HBox->new(0,4), 1, create_scrolled_window($list_others), ), 0, gtkadd(gtkset_layout(Gtk2::HButtonBox->new, 'spread'), gtksignal_connect(Gtk2::Button->new(N("Add")), clicked => sub { filedialog_generic(N("Select the files or directories and click on 'OK'"), undef) }), gtksignal_connect(Gtk2::Button->new(N("Remove Selected")), clicked => sub { $list_model->remove($file_iter) if $file_iter; my $iindex = 0; foreach (@other_files) { if ($other_files[$iindex] eq $other_file) { splice(@other_files, $iindex, 1); last; } $iindex++; } }), ), 0, my $check_what_other_versions = Gtk2::CheckButton->new(N("Use Incremental/Differential Backups (do not replace old backups)")), 0, gtkpack__(Gtk2::HBox->new(0,0), my @mode_buttons = gtkradio((N("Use Incremental Backups")) x 2, N("Use Differential Backups")), ), ), ); $check_what_other_versions->set_active($conf{OTHER_INCREMENTAL_BACKUPS}); $check_what_other_versions->signal_connect('toggled' => sub { invbool \$conf{OTHER_INCREMENTAL_BACKUPS}; $mode_buttons[0]->set_sensitive($conf{OTHER_INCREMENTAL_BACKUPS}); $mode_buttons[1]->set_sensitive($conf{OTHER_INCREMENTAL_BACKUPS}); }); $mode_buttons[1]->set_active($conf{OTHER_DIFFERENTIAL_BACKUPS}); $mode_buttons[0]->signal_connect('toggled' => sub { $conf{OTHER_DIFFERENTIAL_BACKUPS} = $mode_buttons[1]->get_active }); $mode_buttons[0]->set_sensitive($conf{OTHER_INCREMENTAL_BACKUPS}); $mode_buttons[1]->set_sensitive($conf{OTHER_INCREMENTAL_BACKUPS}); set_help_tip($check_what_other_versions, 'use_incr_decr'); set_help_tip($mode_buttons[0], 'use_incremental'); set_help_tip($mode_buttons[1], 'use_differential'); fonction_env(\$box_what_other, \&advanced_what_other, \&advanced_what); $up_box->show_all; } sub advanced_what() { my $box_what; gtkpack($advanced_box, $box_what = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtksignal_connect(my $button_what_sys = Gtk2::Button->new, clicked => sub { $box_what->destroy; advanced_what_sys() }), 1, gtksignal_connect(my $button_what_user = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_what_user(undef) }), 1, gtksignal_connect(my $button_what_other = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_what_other() }), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); $button_what_sys->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-system-40"), Gtk2::Label->new(N("System")), Gtk2::HBox->new(0, 5) )); $button_what_user->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-users-40"), Gtk2::Label->new(N("Users")), Gtk2::HBox->new(0, 5) )); $button_what_other->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-others-40"), Gtk2::Label->new(N("Other")), Gtk2::HBox->new(0, 5) )); gtkset_sensitive($button_what_sys, !$conf{NO_SYS_FILES}); fonction_env(\$box_what, \&advanced_what, \&advanced_box); $up_box->show_all; } sub advanced_where_net_types { my ($previous_function) = @_; my $box_where_net; gtkpack($advanced_box, $box_where_net = gtkpack_(Gtk2::VBox->new(0, 10), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, my $check_where_use_net = Gtk2::CheckButton->new(N("Use network connection to backup")), 1, Gtk2::HBox->new(0,10), 0, Gtk2::Label->new(N("Net Method:")), 0, gtkset_sensitive(my $entry_net_type = Gtk2::ComboBox->new_text, $conf{USE_NET}), ), 0, gtkpack_(Gtk2::HBox->new(0,5), 0, gtkset_sensitive(my $check_use_expect = Gtk2::CheckButton->new(N("Use Expect for SSH")), ($conf{USE_NET} && $conf{NET_PROTO} eq 'ssh')), 0, gtkset_sensitive(my $check_xfer_keys = Gtk2::CheckButton->new(N("Create/Transfer backup keys for SSH")), ($conf{USE_NET} && $conf{NET_PROTO} eq 'ssh')), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $button_xfer_keys = Gtk2::Button->new(N("Transfer Now")), $conf{DRAK_KEYS}), ), 0, gtkset_sensitive(my $check_user_keys = Gtk2::CheckButton->new(N("Other (not drakbackup) keys in place already")), ($conf{USE_NET} && $conf{NET_PROTO} eq 'ssh')), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Host name or IP.")), $conf{USE_NET}), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $host_name_entry = Gtk2::Entry->new, $conf{USE_NET}), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Directory (or module) to put the backup on this host.")), $conf{USE_NET}), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $host_path_entry = Gtk2::Entry->new, $conf{USE_NET}), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Login name")), $conf{USE_NET}), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $login_user_entry = Gtk2::Entry->new, $conf{USE_NET}), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Password")), $conf{USE_NET}), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $check_remember_pass = Gtk2::CheckButton->new(N("Remember this password")), $conf{USE_NET}), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $passwd_user_entry = Gtk2::Entry->new, $conf{USE_NET}), ), ), ); $entry_net_type->set_popdown_strings('', @net_methods); $entry_net_type->entry->set_text($conf{NET_PROTO}); $button_xfer_keys->signal_connect('clicked', sub { if ($conf{PASSWD} && $conf{LOGIN} && $conf{HOST_NAME}) { if (check_pkg_needs()) { install_rpm(\&advanced_where_net_types, $previous_function); } else { do_expect("sendkey"); } } else { $in->ask_warn(N("Error"), N("Need hostname, username and password!")); } }); $passwd_user_entry->set_visibility(0); $passwd_user_entry->set_text($conf{PASSWD}); $passwd_user_entry->signal_connect('changed', sub { $conf{PASSWD} = $passwd_user_entry->get_text }); $host_path_entry->set_text($conf{HOST_PATH}); $host_name_entry->set_text($conf{HOST_NAME}); $login_user_entry->set_text($conf{LOGIN}); $host_name_entry->signal_connect('changed', sub { $conf{HOST_NAME} = $host_name_entry->get_text }); $host_path_entry->signal_connect('changed', sub { $conf{HOST_PATH} = $host_path_entry->get_text }); $login_user_entry->signal_connect('changed', sub { $conf{LOGIN} = $login_user_entry->get_text }); $entry_net_type->entry->signal_connect('changed', sub { $conf{NET_PROTO} = $entry_net_type->entry->get_text; my $sensitive = 0; $sensitive = 1 if $conf{NET_PROTO} eq 'ssh'; $check_use_expect->set_sensitive($sensitive); $check_xfer_keys->set_sensitive($sensitive); $button_xfer_keys->set_sensitive($sensitive); $check_user_keys->set_sensitive($sensitive); }); check_list([$check_remember_pass, \$conf{REMEMBER_PASS}]); gtksignal_connect(gtkset_active($check_where_use_net, $conf{USE_NET}), toggled => sub { invbool \$conf{USE_NET}; #- assure other methods disabled if ($conf{USE_NET} == 1) { $conf{USE_CD} = 0; $conf{USE_TAPE} = 0; } $conf{NET_PROTO} = '' if $conf{USE_NET} == 0; destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_use_expect, $conf{USE_EXPECT}), toggled => sub { invbool \$conf{USE_EXPECT}; #- assure other methods disabled if ($conf{USE_EXPECT} == 1) { $conf{DRAK_KEYS} = 0; $conf{USER_KEYS} = 0; } destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_xfer_keys, $conf{DRAK_KEYS}), toggled => sub { invbool \$conf{DRAK_KEYS}; #- assure other methods disabled if ($conf{DRAK_KEYS} == 1) { $conf{USE_EXPECT} = 0; $conf{USER_KEYS} = 0; } destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_user_keys, $conf{USER_KEYS}), toggled => sub { invbool \$conf{USER_KEYS}; #- assure other methods disabled if ($conf{USER_KEYS} == 1) { $conf{DRAK_KEYS} = 0; $conf{USE_EXPECT} = 0; } destroy_widget(); $current_widget->($previous_function); }); set_help_tip($check_use_expect, 'use_expect'); set_help_tip($check_remember_pass, 'remember_pass'); set_help_tip($host_path_entry, 'dir_or_module'); if ($previous_function) { fonction_env(\$box_where_net, \&advanced_where_net_types, \&$previous_function, \&wizard_step3); button_box_wizard(); } else { fonction_env(\$box_where_net, \&advanced_where_net_types, \&advanced_where); } $up_box->show_all; } sub advanced_where_cd { my ($previous_function) = @_; my $box_where_cd; my %dev_codes; get_cd_info(); foreach my $key (keys %cd_devices) { $dev_codes{$cd_devices{$key}{rec_dev}} = $key; } my $combo_where_cd_device = Gtk2::ComboBox->new_with_strings(%cd_devices ? [ sort keys %dev_codes ] : \@no_devices); my $combo_where_cd_time = Gtk2::ComboBox->new_with_strings([ "650 MB", "700 MB", "750 MB", "800 MB", "4.7 GB" ]); gtkpack($advanced_box, $box_where_cd = gtkpack_(Gtk2::VBox->new(0, 6), 0, my $check_where_cd = Gtk2::CheckButton->new(N("Use CD-R/DVD-R to backup")), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Choose your CD/DVD device")), $conf{USE_CD}), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive($combo_where_cd_device, $conf{USE_CD}), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Choose your CD/DVD media size")), $conf{USE_CD}), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive($combo_where_cd_time, $conf{USE_CD}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(Gtk2::Label->new(N("Multisession CD")), $conf{USE_CD}), 0, gtkset_sensitive(my $check_multisession = Gtk2::CheckButton->new, $conf{USE_CD}), 0, gtkset_sensitive(Gtk2::Label->new(N("CDRW media")), $conf{USE_CD}), 0, gtkset_sensitive(my $check_cdrw = Gtk2::CheckButton->new, $conf{USE_CD}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(Gtk2::Label->new(N("Erase your RW media (1st Session)")), $conf{CDRW} && $conf{USE_CD}), 0, gtkset_sensitive(my $button_erase_now = Gtk2::Button->new(N(" Erase Now ")), $conf{CDRW}), 0, gtkset_sensitive(my $check_cdrw_erase = Gtk2::CheckButton->new, $conf{CDRW} && $conf{USE_CD}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(Gtk2::Label->new(N("DVD+RW media")), $conf{USE_CD}), 0, gtkset_sensitive(my $check_dvdrw = Gtk2::CheckButton->new, $conf{USE_CD}), 0, gtkset_sensitive(Gtk2::Label->new(N("DVD-R media")), $conf{USE_CD}), 0, gtkset_sensitive(my $check_dvdr = Gtk2::CheckButton->new, $conf{USE_CD}), 0, gtkset_sensitive(Gtk2::Label->new(N("DVDRAM device")), $conf{USE_CD}), 0, gtkset_sensitive(my $check_dvdram = Gtk2::CheckButton->new, $conf{USE_CD}), ), ), ); foreach ([$check_cdrw_erase, \$conf{MEDIA_ERASE}], [$check_dvdrw, \$conf{DVDRW}], [$check_dvdr, \$conf{DVDR}], [$check_dvdram, \$conf{DVDRAM}], [$check_multisession, \$conf{MULTI_SESSION}]) { my $ref = $_->[1]; gtksignal_connect(gtkset_active($_->[0], $$ref), toggled => sub { $$ref = $$ref ? 0 : 1 }); } gtksignal_connect(gtkset_active($check_where_cd, $conf{USE_CD}), toggled => sub { $conf{USE_CD} = $conf{USE_CD} ? 0 : 1; #- toggle where_net, where_tape off if ($conf{USE_CD} == 1) { $conf{USE_NET} = 0; $conf{USE_TAPE} = 0; } destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_cdrw, $conf{CDRW}), toggled => sub { $conf{CDRW} = $conf{CDRW} ? 0 : 1; $conf{MEDIA_ERASE} = $conf{MEDIA_ERASE} ? 0 : 1; $check_cdrw_erase->set_sensitive($conf{CDRW}); destroy_widget(); $current_widget->($previous_function); }); $button_erase_now->signal_connect('clicked', sub { if ($conf{CD_DEVICE}) { erase_cdrw(); } else { $in->ask_warn(N("Error"), N("No CD device defined!")); } }); $combo_where_cd_time->entry->set_text($conf{CD_TIME}) if $conf{CD_TIME}; $combo_where_cd_time->entry->signal_connect('changed', sub { $conf{CD_TIME} = $combo_where_cd_time->entry->get_text }); $combo_where_cd_device->entry->set_text($conf{CD_DEVICE}) if $conf{CD_DEVICE}; $combo_where_cd_device->entry->signal_connect('changed', sub { $conf{CD_DEVICE} = $combo_where_cd_device->entry->get_text; $std_device = $dev_codes{$conf{CD_DEVICE}}; $check_dvdr->set_active($cd_devices{$std_device}{dvdr}); $check_dvdrw->set_active($cd_devices{$std_device}{dvdr}); $check_dvdram->set_active($cd_devices{$std_device}{dvdram}); $check_cdrw->set_active($cd_devices{$std_device}{cdrw}); }); set_help_tip($button_erase_now, 'erase_cdrw'); if ($previous_function) { fonction_env(\$box_where_cd, \&advanced_where_cd, \&$previous_function, \&wizard_step3); button_box_wizard(); } else { fonction_env(\$box_where_cd, \&advanced_where_cd, \&advanced_where); } $up_box->show_all; } sub advanced_where_tape { my ($previous_function) = @_; #- look for tape devices; get_tape_info(); my $combo_where_tape_device = Gtk2::ComboBox->new_with_strings(@tape_devices ? \@tape_devices : \@no_devices); my $box_where_tape; local $_; gtkpack($advanced_box, $box_where_tape = gtkpack_(Gtk2::VBox->new(0, 6), 0, Gtk2::HSeparator->new, 0, my $check_where_tape = Gtk2::CheckButton->new(N("Use tape to backup")), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Device name to use for backup")), $conf{USE_TAPE}), 1, Gtk2::VBox->new(0, 6), 0, gtkset_sensitive($combo_where_tape_device, $conf{USE_TAPE}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Backup directly to tape")), $conf{USE_TAPE}), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(my $direct_to_tape = Gtk2::CheckButton->new, $conf{USE_TAPE}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Don't rewind tape after backup")), $conf{USE_TAPE}), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(my $check_tape_rewind = Gtk2::CheckButton->new, $conf{USE_TAPE}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Erase tape before backup")), $conf{USE_TAPE}), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(my $check_tape_erase = Gtk2::CheckButton->new, $conf{USE_TAPE}), ), 0, Gtk2::VBox->new(0, 5), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Eject tape after the backup")), $conf{USE_TAPE}), 1, Gtk2::VBox->new(0, 5), 0, gtkset_sensitive(my $check_tape_eject = Gtk2::CheckButton->new, $conf{USE_TAPE}), ), 0, Gtk2::VBox->new(0, 6), 0, gtkpack_(Gtk2::HBox->new(0,10),), ), ); gtksignal_connect(gtkset_active($check_where_tape, $conf{USE_TAPE}), toggled => sub { $conf{USE_TAPE} = $conf{USE_TAPE} ? 0 : 1; #- assure other methods are off if ($conf{USE_TAPE} == 1) { $conf{USE_NET} = 0; $conf{USE_CD} = 0; } destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_tape_rewind, $conf{TAPE_NOREWIND}), toggled => sub { $conf{TAPE_NOREWIND} = $conf{TAPE_NOREWIND} ? 0 : 1; $_ = $conf{TAPE_DEVICE}; if ($conf{TAPE_NOREWIND}) { $conf{TAPE_DEVICE} =~ s|/st|/nst|; } else { $conf{TAPE_DEVICE} =~ s|/nst|/st|; } $combo_where_tape_device->entry->set_text($conf{TAPE_DEVICE}); destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($direct_to_tape, $conf{DIRECT_TAPE}), toggled => sub { $conf{DIRECT_TAPE} = $conf{DIRECT_TAPE} ? 0 : 1; destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_tape_erase, $conf{MEDIA_ERASE}), toggled => sub { $conf{MEDIA_ERASE} = $conf{MEDIA_ERASE} ? 0 : 1; destroy_widget(); $current_widget->($previous_function); }); gtksignal_connect(gtkset_active($check_tape_eject, $conf{MEDIA_EJECT}), toggled => sub { $conf{MEDIA_EJECT} = $conf{MEDIA_EJECT} ? 0 : 1; destroy_widget(); $current_widget->($previous_function); }); $combo_where_tape_device->entry->set_text($conf{TAPE_DEVICE}) if $conf{TAPE_DEVICE}; $combo_where_tape_device->entry->signal_connect('changed', sub { $conf{TAPE_DEVICE} = $combo_where_tape_device->entry->get_text; }); if ($previous_function) { fonction_env(\$box_where_tape, \&advanced_where_tape, \&$previous_function, \&wizard_step3); button_box_wizard(); } else { fonction_env(\$box_where_tape, \&advanced_where_tape, \&advanced_where); } $up_box->show_all; } sub advanced_where_hd { my ($previous_function) = @_; my $box_where_hd; my $button; if ($conf{MAX_SPACE} == 1000.0) { $conf{MAX_SPACE} = int(0.8 * get_free_space($conf{PATH_TO_SAVE})) if -d $conf{PATH_TO_SAVE}; } my $adj = Gtk2::Adjustment->new($conf{MAX_SPACE}, 0.0, $conf{MAX_SPACE}, 10.0, 5.0, 0.0); my $spinner; gtkpack($advanced_box, $box_where_hd = gtkpack_(Gtk2::VBox->new(0, 6), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Enter the directory to save to:")), $conf{USE_HD}), 1, Gtk2::VBox->new(0, 6), 0, gtkset_size_request(gtkset_sensitive($save_path_entry = Gtk2::Entry->new, $conf{USE_HD}), 152, 20), 0, gtkset_sensitive($button = gtksignal_connect(Gtk2::Button->new, clicked => sub { filedialog_generic(N("Directory to save to"), \$save_path_entry); }), $conf{USE_HD}), ), 0, Gtk2::VBox->new(0, 6), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Maximum size\n allowed for Drakbackup (MB)")), $conf{USE_HD}), 1, Gtk2::VBox->new(0, 6), 0, gtkset_size_request(gtkset_sensitive($spinner = Gtk2::SpinButton->new($adj, 0, 0), $conf{USE_HD}), 200, 20), ), ), ); $button->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-dossier-32"))); $save_path_entry->set_text($conf{PATH_TO_SAVE}); $spinner->signal_connect('changed', sub { $conf{MAX_SPACE} = $spinner->get_text }); $save_path_entry->signal_connect('changed', sub { $conf{PATH_TO_SAVE} = $save_path_entry->get_text; if (-d $conf{PATH_TO_SAVE}) { $conf{MAX_SPACE} = int(0.8 * get_free_space($conf{PATH_TO_SAVE})); # seems to be the easiest way to avoid the widgets fighting over values # and getting garbage in $max_value destroy_widget(); $current_widget->($previous_function); } }); if ($previous_function) { fonction_env(\$box_where_hd, \&advanced_where_hd, \&$previous_function, \&wizard_step3); button_box_wizard(); } else { fonction_env(\$box_where_hd, \&advanced_where_hd, \&advanced_where); } $up_box->show_all; } sub advanced_where() { my $box_where; gtkpack($advanced_box, $box_where = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtksignal_connect(my $button_where_net = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_where_net_types(undef); }), 1, gtksignal_connect(my $button_where_cd = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_where_cd(undef); }), 1, gtksignal_connect(my $button_where_hd = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_where_hd(undef); }), 1, gtksignal_connect(my $button_where_tape = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_where_tape(undef); }), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); $button_where_net->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-network-40"), Gtk2::Label->new(N("Network")), Gtk2::HBox->new(0, 5) )); $button_where_cd->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-CD-40"), Gtk2::Label->new(N("CD-R / DVD-R")), Gtk2::HBox->new(0, 5) )); $button_where_hd->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-discdurwhat-40"), Gtk2::Label->new(N("HardDrive / NFS")), Gtk2::HBox->new(0, 5) )); $button_where_tape->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-tape-40"), Gtk2::Label->new(N("Tape")), Gtk2::HBox->new(0, 5) )); fonction_env(\$box_where, \&advanced_where, \&advanced_box); $up_box->show_all; } sub advanced_when() { my $box_when; my $allow_custom = $backup_daemon && $custom_cron; my $combo_when_space = Gtk2::ComboBox->new_with_strings([ "", N("hourly"), N("daily"), N("weekly"), N("monthly"), N("custom") ]); my %trans = (N("hourly") => 'hourly', N("daily") => 'daily', N("weekly") => 'weekly', N("monthly") => 'monthly', N("custom") => 'custom'); my %trans2 = ('hourly' => N("hourly"), 'daily' => N("daily"), 'weekly' => N("weekly"), 'monthly' => N("monthly"), 'custom' => N("custom")); set_help_tip($combo_when_space, 'when_space'); #- custom setup - let user specify month, day of month, day of week, hour, minute my @months = ("*", N("January"), N("February"), N("March"), N("April"), N("May"), N("June"), N("July"), N("August"), N("September"), N("October"), N("November"), N("December")); my $combo_month_when = Gtk2::ComboBox->new_with_strings(\@months); my $combo_day_when = Gtk2::ComboBox->new_with_strings([ "*", (1..31) ]); my @weekdays = ("*", N("Sunday"), N("Monday"), N("Tuesday"), N("Wednesday"), N("Thursday"), N("Friday"), N("Saturday")); my $combo_weekday_when = Gtk2::ComboBox->new_with_strings(\@weekdays); my $combo_hour_when = Gtk2::ComboBox->new_text; $combo_hour_when->set_popdown_strings("*", (0..23)); my $combo_minute_when = Gtk2::ComboBox->new_with_strings([ "*", (0..59) ]); my $entry_crontab = Gtk2::Entry->new; gtkset_editable($entry_crontab, 0); my @time_list = split(" ", $time_string); $combo_minute_when->entry->set_text($time_list[0]); $combo_hour_when->entry->set_text($time_list[1]); $combo_day_when->entry->set_text($time_list[2]); if ($time_list[3] =~ /\*/) { $combo_month_when->entry->set_text($time_list[3]); } else { $combo_month_when->entry->set_text($months[$time_list[3]]); } if ($time_list[4] =~ /\*/) { $combo_weekday_when->entry->set_text($time_list[4]); } else { $combo_weekday_when->entry->set_text($weekdays[$time_list[4] + 1]); } #- drop down list of possible media - default to config value my $entry_media_type = Gtk2::ComboBox->new_with_strings([ sort(@net_methods, @media_types) ], $conf{DAEMON_MEDIA}); gtkpack($advanced_box, $box_when = gtkpack_(Gtk2::VBox->new(0, 10), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::HBox->new(0,10), 1, gtkcreate_img("ic82-when-40"), 0, my $check_when_daemon = Gtk2::CheckButton->new(N("Use daemon")), 1, Gtk2::HBox->new(0,10), ), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Please choose the time interval between each backup")), $backup_daemon), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive($combo_when_space, $backup_daemon), ), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Custom setup/crontab entry:")), $allow_custom), 1, gtkset_sensitive($entry_crontab, $allow_custom), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Minute")), $allow_custom), 0, gtkset_sensitive($combo_minute_when, $allow_custom), ), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Hour")), $allow_custom), 0, gtkset_sensitive($combo_hour_when, $allow_custom), ), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Day")), $allow_custom), 0, gtkset_sensitive($combo_day_when, $allow_custom), ), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Month")), $allow_custom), 0, gtkset_sensitive($combo_month_when, $allow_custom), ), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Weekday")), $allow_custom), 0, gtkset_sensitive($combo_weekday_when, $allow_custom), ), ), 0, Gtk2::HSeparator->new, 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Please choose the media for backup.")), $backup_daemon), 1, Gtk2::HBox->new(0,10), 0, gtkpack_(Gtk2::VBox->new(0,10), 0, gtkset_sensitive($entry_media_type, $backup_daemon), ), ), 0, Gtk2::HSeparator->new, 0, gtkset_sensitive(Gtk2::Label->new(N("Please be sure that the cron daemon is included in your services.")), $backup_daemon), 0, gtkset_sensitive(Gtk2::Label->new(N("Note that currently all 'net' media also use the hard drive.")), $backup_daemon), ), ); gtksignal_connect(gtkset_active($check_when_daemon, $backup_daemon), toggled => sub { $backup_daemon = $backup_daemon ? 0 : 1; destroy_widget(); advanced_when(); }); $combo_when_space->entry->set_text($trans2{$conf{DAEMON_TIME_SPACE}}); $combo_when_space->entry->signal_connect('changed', sub { $conf{DAEMON_TIME_SPACE} = $trans{$combo_when_space->entry->get_text}; $custom_cron = $conf{DAEMON_TIME_SPACE} eq "custom" ? 1 : 0; destroy_widget(); advanced_when(); }); if ($custom_cron) { $entry_crontab->set_text("$time_string $exec_string"); } $combo_minute_when->entry->signal_connect('changed', sub { combo_to_cron_string($combo_minute_when->get_text, 0) }); $combo_hour_when->entry->signal_connect('changed', sub { combo_to_cron_string($combo_hour_when->get_text, 1) }); $combo_day_when->entry->signal_connect('changed', sub { combo_to_cron_string($combo_day_when->get_text, 2) }); $combo_month_when->entry->signal_connect('changed', sub { combo_to_cron_string($combo_month_when->get_active, 3) }); $combo_weekday_when->entry->signal_connect('changed', sub { combo_to_cron_string($combo_weekday_when->get_active - 1, 4) }); $entry_media_type->entry->signal_connect('changed', sub { $conf{DAEMON_MEDIA} = $entry_media_type->entry->get_text }); fonction_env(\$box_when, \&advanced_when, \&advanced_box); $up_box->show_all; } sub combo_to_cron_string { my ($field, $location) = @_; $field = "*" if $field == 0 && $location == 3; $field = "*" if $field == -1 && $location == 4; my @time_list = split(" ", $time_string); splice(@time_list, $location, 1, $field); $time_string = join(" ", @time_list); destroy_widget(); advanced_when(); } sub advanced_options() { my $box_options; my $entry_comp_mode = Gtk2::ComboBox->new_with_strings([ "tar", "tar.gz", "tar.bz2" ], $conf{OPTION_COMP}); gtkpack($advanced_box, $box_options = gtkpack_(Gtk2::VBox->new(0, 15), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, Gtk2::Label->new(N("Please choose the compression type")), 1, Gtk2::HBox->new(0,10), 0, $entry_comp_mode, ), 0, my $check_backupignore = Gtk2::CheckButton->new(N("Use .backupignore files")), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, my $check_mail = Gtk2::CheckButton->new(N("Send mail report after each backup to:")), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $mail_entry = Gtk2::Entry->new, $conf{SEND_MAIL}), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::HBox->new(0,10), 0, N("SMTP server for mail:"), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(my $smtp_entry = Gtk2::Entry->new, $conf{SEND_MAIL}), ), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, my $check_del_hd_files = Gtk2::CheckButton->new(N("Delete Hard Drive tar files after backup to other media.")), ), ), ); check_list([$check_mail, \$conf{SEND_MAIL}], [$check_del_hd_files, \$conf{DEL_HD_FILES}], [$check_backupignore, \$conf{BACKUPIGNORE}]); $mail_entry->set_text($conf{USER_MAIL}); $mail_entry->signal_connect('changed', sub { $conf{USER_MAIL} = $mail_entry->get_text }); $smtp_entry->set_text($conf{SMTP_SERVER}); $smtp_entry->signal_connect('changed', sub { $conf{SMTP_SERVER} = $smtp_entry->get_text }); $entry_comp_mode->entry->signal_connect('changed', sub { $conf{OPTION_COMP} = $entry_comp_mode->entry->get_text }); set_help_tip($check_backupignore, 'backupignore'); set_help_tip($check_mail, 'send_mail_to'); set_help_tip($check_del_hd_files, 'delete_files'); fonction_env(\$box_options, \&advanced_options, \&advanced_box); $up_box->show_all; } sub advanced_box() { my $box_adv; gtkpack($advanced_box, $box_adv = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtksignal_connect(my $button_what = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_what() }), 1, gtksignal_connect(my $button_where = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_where() }), 1, gtksignal_connect(my $button_when = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_when() }), 1, gtksignal_connect(my $button_options = Gtk2::Button->new, clicked => sub { destroy_widget(); advanced_options() }), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); $button_what->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-discdurwhat-40"), Gtk2::Label->new(N("What")), Gtk2::HBox->new(0, 5) )); $button_where->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-where-40"), Gtk2::Label->new(N("Where")), Gtk2::HBox->new(0, 5) )); $button_when->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-when-40"), Gtk2::Label->new(N("When")), Gtk2::HBox->new(0, 5) )); $button_options->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-moreoption-40"), Gtk2::Label->new(N("More Options")), Gtk2::HBox->new(0, 5) )); fonction_env(\$box_adv, \&advanced_box, \&interactive_mode_box); $up_box->show_all; } ################################################ WIZARD ################################################ sub wizard_step3() { destroy_widget(); my $no_device = $conf{USE_CD} && $conf{CD_DEVICE} eq '' || $conf{USE_TAPE} && $conf{TAPE_DEVICE} eq '' || $conf{USE_NET} && $conf{NET_PROTO} eq '' && 1; if ($no_device) { show_warning("f", N("Backup destination not configured...")); advanced_where_net_types(\&wizard_step2) if $conf{USE_NET}; advanced_where_cd(\&wizard_step2) if $conf{USE_CD}; advanced_where_tape(\&wizard_step2) if $conf{USE_TAPE}; return; } if (check_pkg_needs()) { install_rpm(\&wizard_step3, undef); return; } my $text = Gtk2::TextView->new; save_conf_file(); read_conf_file(); system_state(); gtktext_insert($text, [ [ $system_state ] ]); button_box_restore_main(); gtkpack($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, N("Drakbackup Configuration"), 1, create_scrolled_window($text), ), ), ); fonction_env(\$box2, \&wizard_step3, \&wizard_step2); button_box_wizard_end(); $up_box->show_all; } sub wizard_step2() { gtkpack($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 0, N("Please choose where you want to backup"), 0, gtkpack_(Gtk2::HBox->new(0, 15), 0, gtkpack__(Gtk2::VBox->new(0, 10), my @where_radio = gtkradio('', N("Hard Drive used to prepare backups for all media"), N("Across Network"), N("On CD-R"), N("On Tape Device")), ), 1, gtkpack_(Gtk2::HBox->new(0,5),), 0, gtkpack_(Gtk2::VBox->new(0,5), 0, gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Configure")), clicked => sub { destroy_widget(); advanced_where_hd(\&wizard_step2); }), $use_hd), 0, gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Configure")), clicked => sub { destroy_widget(); advanced_where_net_types(\&wizard_step2); }), $conf{USE_NET}), 0, gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Configure")), clicked => sub { destroy_widget(); advanced_where_cd(\&wizard_step2); }), $conf{USE_CD}), 0, gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Configure")), clicked => sub { destroy_widget(); advanced_where_tape(\&wizard_step2); }), $conf{USE_TAPE}), ), ), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); my @wheres = ($use_hd, $conf{USE_NET}, $conf{USE_CD}, $conf{USE_TAPE}); foreach my $i (0..3) { $where_radio[$i]->set_active($wheres[$i]); $where_radio[$i]->signal_connect(toggled => sub { if ($where_radio[$i]->get_active) { @wheres = (0, 0, 0, 0); $wheres[$i] = 1; ($use_hd, $conf{USE_NET}, $conf{USE_CD}, $conf{USE_TAPE}) = @wheres; destroy_widget(); wizard_step2(); } }); } fonction_env(\$box2, \&wizard_step2, \&wizard, undef); button_box_wizard(); $up_box->show_all; } sub wizard() { my $user_string = N("Backup Users"); $user_string .= N(" (Default is all users)") if !$nonroot_user; if (!$conf{NO_USER_FILES} && !$manual_user) { @user_list = @user_list_all; } elsif (!$manual_user) { @user_list = (); } gtkpack($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 0, N("Please choose what you want to backup"), 0, my $check_wizard_sys = Gtk2::CheckButton->new(N("Backup System")), 0, my $check_wizard_user = Gtk2::CheckButton->new($user_string), 0, gtksignal_connect(Gtk2::Button->new(N("Select user manually")), clicked => sub { $manual_user = 1; destroy_widget(); advanced_what_user(\&wizard); }), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); foreach ([$check_wizard_sys, \$conf{NO_SYS_FILES}], [$check_wizard_user, \$conf{NO_USER_FILES}]) { my $ref = $_->[1]; gtksignal_connect(gtkset_active($_->[0], !$$ref), toggled => sub { $$ref = $$ref ? 0 : 1; if (!$conf{NO_SYS_FILES} || !$conf{NO_USER_FILES} && @user_list) { $next_widget = \&wizard_step2; } else { $next_widget = \&wizard; } if (!$conf{NO_USER_FILES}) { @user_list = @user_list_all; } else { @user_list = (); } }); } if (!$conf{NO_SYS_FILES} || !$conf{NO_USER_FILES} && @user_list) { fonction_env(\$box2, \&wizard, \&interactive_mode_box, \&wizard_step2); } else { $in->ask_warn(N("Error"), N("Please select data to backup...")); fonction_env(\$box2, \&wizard, \&interactive_mode_box, \&wizard); } button_box_wizard(); $up_box->show_all; } ################################################ RESTORE ################################################ sub find_backup_to_restore() { my @list_backup; my @list_backup_tmp2; my $to_put; my $nom; @sys_backuped = (); local $_; @user_backuped = (); -d $path_to_find_restore and @list_backup_tmp2 = all($path_to_find_restore); foreach (@list_backup_tmp2) { s/_base//gi; s/_incr//gi; push @list_backup , $_; } foreach (grep { /^backup_sys_/ } @list_backup) { ($to_put, undef) = file_to_put($_, "sys"); push @sys_backuped , $to_put; } $restore_step_sys_date = $to_put; foreach (grep { /^backup_other_/ } @list_backup) { ($to_put, undef) = file_to_put($_, "other"); push @other_backuped , $to_put; } $restore_step_other_date = $to_put; foreach (grep { /^backup_user_/ } @list_backup) { ($to_put, $nom) = file_to_put($_, "user"); push @user_backuped , $to_put; any { /^$nom$/ } @user_list_backuped or push @user_list_backuped, $nom; } } sub file_to_put { my ($name, $type) = @_; my $to_put; my ($nom, $date, $heure); local $_ = $name; chomp; $name = "backup_" . $type . "_"; s/^$name//gi; s/.tar|.gz|.bz2$//gi; if ($type eq "user") { ($nom, $date, $heure) = /^(.*)_([^_]*)_([^_]*)$/; } else { ($date, $heure) = /^(.*)_([^_]*)$/; } my $year = substr($date, 0, 4); my $month = substr($date, 4, 2); my $day = substr($date, 6, 2); my $hour = substr($heure, 0, 2); my $min = substr($heure, 2, 2); if ($type eq "user") { $to_put = "$_ user: $nom, date: $day/$month/$year, hour: $hour:$min"; return $to_put, $nom; } else { $to_put = "$day/$month/$year $hour:$min $_"; return $to_put, undef; } } sub system_state() { if ($cfg_file_exist) { $system_state = N("\nBackup Sources: \n"); $conf{NO_SYS_FILES} or $system_state .= N("\n- System Files:\n"); $conf{NO_SYS_FILES} or $system_state .= "\t\t$_\n" foreach @sys_files; $conf{NO_USER_FILES} or $system_state .= N("\n- User Files:\n"); $conf{NO_USER_FILES} or $system_state .= "\t\t$_\n" foreach @user_list; $conf{OTHER_FILES} and $system_state .= N("\n- Other Files:\n"); $conf{OTHER_FILES} and $system_state .= "\t\t$_\n" foreach @other_files; $conf{USE_HD} and $system_state .= N("\n- Save on Hard drive on path: %s\n", $conf{PATH_TO_SAVE}); $conf{USE_HD} and $system_state .= N("\tLimit disk usage to %s MB\n", $conf{MAX_SPACE}); if ($conf{DEL_HD_FILES} && ($conf{USE_CD} || $conf{USE_TAPE} || $conf{USE_NET}) && $conf{DAEMON_MEDIA} ne 'hd') { $system_state .= N("\n- Delete hard drive tar files after backup.\n"); } #- tape and CDRW share some features my $erase_media = $conf{MEDIA_ERASE} && ($conf{USE_CD} || $conf{USE_TAPE}) ? N("Yes") : N("No"); $conf{USE_CD} and $system_state .= N("\n- Burn to CD"); $conf{USE_CD} and $conf{CDRW} and $system_state .= N("RW"); $conf{USE_CD} and $system_state .= N(" on device: %s", $conf{CD_DEVICE}); $conf{USE_CD} && $conf{MULTI_SESSION} and $system_state .= N(" (multi-session)"); $conf{USE_TAPE} and $system_state .= N("\n- Save to Tape on device: %s", $conf{TAPE_DEVICE}); (($conf{USE_CD} || $conf{USE_TAPE}) && $conf{MEDIA_ERASE}) and $system_state .= N("\t\tErase=%s", $erase_media); $conf{USE_CD} || $conf{USE_TAPE} and $system_state .= "\n"; $conf{USE_TAPE} && $conf{DIRECT_TAPE} and $system_state .= N("\tBackup directly to Tape\n"); $conf{USE_NET} and $system_state .= N("\n- Save via %s on host: %s\n", $conf{NET_PROTO}, $conf{HOST_NAME}); $conf{USE_NET} and $system_state .= N("\t\t user name: %s\n\t\t on path: %s \n", $conf{LOGIN}, $conf{HOST_PATH}); $system_state .= N("\n- Options:\n"); $conf{NO_SYS_FILES} and $system_state .= N("\tDo not include System Files\n"); $system_state .= N("\tBackups use tar and bzip2\n") if $conf{OPTION_COMP} eq "tar.bz2"; $system_state .= N("\tBackups use tar and gzip\n") if $conf{OPTION_COMP} eq "tar.gz"; $system_state .= N("\tBackups use tar only\n") if $conf{OPTION_COMP} eq "tar"; $system_state .= N("\tUse .backupignore files\n") if $conf{BACKUPIGNORE}; $system_state .= N("\tSend mail to %s\n", $conf{USER_MAIL}) if $conf{SEND_MAIL}; $system_state .= N("\tUsing SMTP server %s\n", $conf{SMTP_SERVER}) if $conf{SEND_MAIL}; $conf{DAEMON_MEDIA} and $system_state .= N("\n- Daemon, %s via:\n", $conf{DAEMON_TIME_SPACE}); $conf{DAEMON_MEDIA} eq 'hd' and $system_state .= N("\t-Hard drive.\n"); $conf{DAEMON_MEDIA} eq 'cd' and $system_state .= N("\t-CD-R.\n"); $conf{DAEMON_MEDIA} eq 'tape' and $system_state .= N("\t-Tape \n"); $conf{DAEMON_MEDIA} eq 'ftp' and $system_state .= N("\t-Network by FTP.\n"); $conf{DAEMON_MEDIA} eq 'ssh' and $system_state .= N("\t-Network by SSH.\n"); $conf{DAEMON_MEDIA} eq 'rsync' and $system_state .= N("\t-Network by rsync.\n"); $conf{DAEMON_MEDIA} eq 'webdav' and $system_state .= N("\t-Network by webdav.\n"); } else { $system_state = N("No configuration, please click Wizard or Advanced.\n"); } } sub restore_state() { $restore_state = N("List of data to restore:\n\n"); if ($restore_sys) { $restore_state .= N("- Restore System Files.\n"); my @tmp = split(' ', $restore_step_sys_date); $restore_state .= N(" - from date: %s %s\n", $tmp[0], $tmp[1]); } if ($restore_user) { $restore_state .= N("- Restore User Files: \n"); $restore_state .= "\t\t$_\n" foreach @user_list_to_restore2; push @user_list_to_restore, (split(',', $_))[0] foreach @user_list_to_restore2; } if ($restore_other) { $restore_state .= N("- Restore Other Files: \n"); my @tmp = split(' ', $restore_step_other_date); $restore_state .= N(" - from date: %s %s\n", $tmp[0], $tmp[1]); } if ($restore_other_path) { $restore_state .= "- Path to Restore: $restore_path \n"; } } sub select_most_recent_selected_of { my ($user_name) = @_; my @tmp = sort @user_list_to_restore2; my @list_tmp2 = grep { /$user_name/ } sort @tmp; return pop @list_tmp2; } sub select_user_data_to_restore() { my $var_eq = 1; my @list_backup; my @list_tmp; my @list_tmp2; @user_list_to_restore = (); local $_; my @list_backup_tmp2 = -d $path_to_find_restore && grep { /^backup/ } all($path_to_find_restore); @list_tmp2 = @list_backup_tmp2; foreach (@list_backup_tmp2) { s/_base//gi; s/_incr//gi; push @list_backup , $_; } foreach my $var_tmp (@user_list_backuped) { $var_eq = 1; my $more_recent = (split(' ', select_most_recent_selected_of($var_tmp)))[0]; foreach (grep { /^backup_user_$var_tmp/ } sort @list_backup) { s/.$conf{OPTION_COMP}//gi; if ($more_recent) { if (/$more_recent/) { push @list_tmp , $_; $var_eq = 0; } else { #- only if user asked for it - previously this was restoring everything (SB) my $tmp_name = $_; s/backup_user_//gi; foreach my $buff (@user_list_to_restore2) { if (index($buff, $_) >= 0) { $var_eq and push @list_tmp , $tmp_name; } } } } } } foreach my $var_to_restore (@list_tmp) { $var_to_restore =~ s/backup_//gi; foreach my $var_exist (sort @list_tmp2) { if ($var_exist =~ /$var_to_restore/) { push @user_list_to_restore, $var_exist; } } } $DEBUG and print "real user list to restore: $_ \n" foreach @user_list_to_restore; } sub select_sys_data_to_restore() { my $var_eq = 1; my @list_tmp; local $_; -d $path_to_find_restore and @list_tmp = grep { /^backup/ } all($path_to_find_restore); my @more_recent = split(' ', $restore_step_sys_date); my $more_recent = pop @more_recent; foreach my $var_exist (grep { /_sys_/ } sort @list_tmp) { if ($var_exist =~ /$more_recent/) { push @sys_list_to_restore, $var_exist; $var_eq = 0; } else { $var_eq and push @sys_list_to_restore, $var_exist; } } $DEBUG and print "sys list to restore: $_\n " foreach @sys_list_to_restore; } sub select_other_data_to_restore() { my $var_eq = 1; my @list_tmp; local $_; @other_list_to_restore = (); -d $path_to_find_restore and @list_tmp = grep { /^backup/ } all($path_to_find_restore); my @more_recent = split(' ', $restore_step_other_date); my $more_recent = pop @more_recent; foreach my $var_exist (grep { /_other_/ } sort @list_tmp) { if ($var_exist =~ /$more_recent/) { push @other_list_to_restore, $var_exist; $var_eq = 0; } else { $var_eq and push @other_list_to_restore, $var_exist; } } $DEBUG and print "other list to restore: $_\n " foreach @other_list_to_restore; } sub show_backup_details { my ($function, $mode, $name) = @_; my $archive_file_detail; my $value; my $command2; my $tarfile; if ($mode eq "user") { #- we've only got a partial filename in this case $tarfile = "$path_to_find_restore/backup_*" . $name . ".tar*"; } if ($mode eq "sys") { #- funky string here we need to use to reconstruct the filename my @flist = split(/[ \t,]+/, $name); $tarfile = "$path_to_find_restore/backup_*" . $flist[2] . ".tar*"; } my @tarfiles = glob($tarfile); if ($tarfiles[0] eq "") { destroy_widget(); $function->(); } $tarfile = $tarfiles[0]; my $command1 = "stat " . $tarfile; $command2 = "tar -tv"; $command2 = set_tar($command2, $tarfile); $command2 .= " $tarfile"; log::explanations("Running $command1"); $archive_file_detail = `$command1 2>&1` . "\n\n"; log::explanations("Running $command2"); my $TMP; open $TMP, "$command2 2>&1 |"; while ($value = <$TMP>) { #- drop the permissions display for the sake of readability $archive_file_detail .= substr($value, 11); } close $TMP; my $text = Gtk2::TextView->new; my $advanced_box_archive; gtktext_insert(gtkset_editable($text, 0), $archive_file_detail); gtkpack($advanced_box, $advanced_box_archive = gtkpack_(Gtk2::VBox->new(0,10), 1, gtkpack_(Gtk2::HBox->new(0,0), 1, create_scrolled_window($text), ), 0, gtkadd(gtkset_layout(Gtk2::HButtonBox->new, 'spread'), gtksignal_connect(Gtk2::Button->new(N("Done")), clicked => sub { destroy_widget(); $function->() }), ), ) ); $central_widget = \$advanced_box_archive; $up_box->show_all; } sub valid_backup_test { my (@files_list) = @_; @files_corrupted = (); my $is_corrupted = 0; my $comp_test; foreach (@files_list) { $comp_test = set_tar("tar t", $_); if (system("$comp_test $path_to_find_restore/$_ > /dev/null 2>&1") > 1) { push @files_corrupted, $_; $is_corrupted = -1; } } return $is_corrupted; } sub restore_aff_backup_problems() { my $do_restore; my $text = Gtk2::TextView->new; my $restore_pbs_state = N("List of data corrupted:\n\n"); $restore_pbs_state .= "\t\t$_\n" foreach @files_corrupted; $restore_pbs_state .= N("Please uncheck or remove it on next time."); gtktext_insert($text, [ [ $restore_pbs_state ] ]); button_box_restore_main(); gtkpack($advanced_box, $do_restore = gtkpack_(Gtk2::VBox->new(0,10), 0, Gtk2::VBox->new(0,10), 1, gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 0, gtkcreate_img('warning'), 0, N("Backup files are corrupted"), 1, Gtk2::VBox->new(0, 5), ), 0, Gtk2::VBox->new(0,10), 1, create_scrolled_window($text), ), ); button_box_restore_pbs_end(); fonction_env(\$do_restore, \&restore_aff_backup_problems, "restore_pbs"); $up_box->show_all; } sub restore_aff_result() { my $do_restore; my $text = Gtk2::TextView->new; gtktext_insert($text, [ [ $restore_state ] ]); button_box_restore_main(); gtkpack($advanced_box, $do_restore = gtkpack_(Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), 0, N(" All of your selected data have been "), 0, N(" Successfully Restored on %s ", $restore_path), 1, Gtk2::VBox->new(0,10), ), ); button_box_ok_only(); $central_widget = \$do_restore; $up_box->show_all; } sub return_path { my ($username) = @_; my $usr; my $home_dir; my @passwords = cat_("/etc/passwd"); foreach my $line (@passwords) { chomp($line); ($usr, $home_dir) = (split(/:/, $line))[0,5]; last if $usr eq $username; } return $home_dir; } sub restore_backend() { my $untar_cmd = "tar x"; my $exist_problem = 0; my $user_dir; my $username; local $_; -d $restore_path or mkdir_p $restore_path; if ($restore_user) { select_user_data_to_restore(); if (valid_backup_test(@user_list_to_restore) == -1) { $exist_problem = 1; restore_aff_backup_problems(); } else { foreach (@user_list_to_restore) { if ($conf{USER_INCREMENTAL_BACKUPS}) { (undef, $username, undef) = /^(\w+_\w+_user_)(.*)_(\d+_\d+.*)$/; } else { (undef, $username, undef) = /^(\w+_user_)(.*)_(\d+_\d+.*)$/; } $user_dir = return_path($username); -d $user_dir and rm_rf($user_dir) if $remove_user_before_restore; my $user_untar = set_tar($untar_cmd, $_); $DEBUG and print "user name to restore: $username, user directory: $user_dir\n"; system("$user_untar $path_to_find_restore/$_ -C $restore_path"); } #- flush this out for another cycle (SB) @user_list_to_restore2 = (); } } if ($restore_sys) { select_sys_data_to_restore(); if (valid_backup_test(@sys_list_to_restore) == -1) { $exist_problem = 1; restore_aff_backup_problems(); } else { foreach (@sys_list_to_restore) { my $sys_untar = set_tar($untar_cmd, $_); system("$sys_untar $path_to_find_restore/$_ -C $restore_path"); } } } if ($restore_other) { if (valid_backup_test(@other_list_to_restore) == -1) { $exist_problem = 1; restore_aff_backup_problems(); } else { foreach (@other_list_to_restore) { my $other_untar = set_tar($untar_cmd, $_); system("$other_untar $path_to_find_restore/$_ -C $restore_path"); } } } $exist_problem or restore_aff_result(); } sub set_tar { my ($untar_cmd, $filename) = @_; $untar_cmd .= "z" if $filename =~ /tar.gz$/; $untar_cmd .= "j" if $filename =~ /tar.bz2$/; $untar_cmd .= "f"; return $untar_cmd; } sub restore_do() { if ($backup_bef_restore) { if ($restore_sys) { $conf{NO_SYS_FILES} = 0; } else { $conf{NO_SYS_FILES} = 1; } if ($restore_user) { $conf{NO_USER_FILES} = 0; @user_list = @user_list_to_restore; } else { $conf{NO_USER_FILES} = 1; } build_backup_status(); read_conf_file(); build_backup_files(); $table->destroy; } restore_do2(); } sub restore_do2() { destroy_widget(); my $do_restore; my $text = Gtk2::TextView->new; restore_state(); gtktext_insert($text, [ [ $restore_state ] ]); button_box_restore_main(); gtkpack($advanced_box, $do_restore = gtkpack_(Gtk2::VBox->new(0,10), 0, N(" Restore Configuration "), 1, create_scrolled_window($text), ), ); button_box_restore_end(); fonction_env(\$do_restore, \&restore_do2, \&restore_box); $up_box->show_all; } sub restore_step_other() { my $retore_step_other; my $text = Gtk2::TextView->new; my $untar_cmd = "tar tzf"; my $other_rest = ""; select_other_data_to_restore(); if ($restore_other) { foreach (@other_list_to_restore) { if (/tar.bz2$/) { $untar_cmd = "tar tjf"; } $other_rest .= "/" . `$untar_cmd $path_to_find_restore/$_ -C $restore_path`; } } gtktext_insert($text, [ [ $other_rest ] ]); gtkpack($advanced_box, $retore_step_other = gtkpack_(Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), 1, create_scrolled_window($text), 0, my $check_restore_other_sure = Gtk2::CheckButton->new(N("OK to restore the other files.")), 1, Gtk2::VBox->new(0,10), ), ); check_list([$check_restore_other_sure, \$restore_other]); fonction_env(\$retore_step_other, \&restore_step_other, \&restore_step2, \&restore_do); $up_box->show_all; } sub restore_step_user() { my $retore_step_user; my @tmp_list = sort @user_backuped; @user_backuped = @tmp_list; gtkpack($advanced_box, $retore_step_user = gtkpack_(Gtk2::VBox->new(0,10), 0, Gtk2::VBox->new(0,10), 0, N("User list to restore (only the most recent date per user is important)"), 1, create_scrolled_window(gtkpack__(Gtk2::VBox->new(0,0), map { my $name; my $var2; my $name_complet = $_; $name = (split(' ', $name_complet))[0]; my @user_list_tmp; my $restore_row = Gtk2::HBox->new(0,5); my $b = Gtk2::CheckButton->new($name_complet); my $details = Gtk2::Button->new(N("Details")); $restore_row->pack_start($b, 1, 1, 0); $restore_row->pack_end(Gtk2::VBox->new(1,5), 0, 0, 0); $restore_row->pack_end($details, 0, 0, 0); foreach (@user_list_to_restore2) { if ($name_complet eq $_) { gtkset_active($b, 1); $check_user_to_restore{$name_complet}[1] = 1; } else { gtkset_active($b, 0); $check_user_to_restore{$name_complet}[1] = 0; } } $b->signal_connect(toggled => sub { if (!$check_user_to_restore{$name_complet}[1]) { $check_user_to_restore{$name_complet}[1] = 1; if (!any { /$name/ } @user_list_to_restore2) { push @user_list_to_restore2, $name_complet; } } else { $check_user_to_restore{$name_complet}[1] = 0; foreach (@user_list_to_restore2) { $var2 = (split(' ', $_))[0]; if ($name ne $var2) { push @user_list_tmp, $_; } } @user_list_to_restore2 = @user_list_tmp; } }); $details->signal_connect('clicked', sub { destroy_widget(); show_backup_details(\&restore_step_user, "user", $name); }); $restore_row } (@user_backuped) ), ), ), ); if ($restore_other) { fonction_env(\$retore_step_user, \&restore_step_user, "", \&restore_step_other); } elsif ($restore_sys) { fonction_env(\$retore_step_user, \&restore_step_user, \&restore_step_sys, \&restore_step_other); } else { fonction_env(\$retore_step_user, \&restore_step_user, \&restore_step2, \&restore_do); } $up_box->show_all; } sub restore_step_sys() { my $restore_step_sys; my $combo_restore_step_sys = Gtk2::ComboBox->new_with_strings(\@sys_backuped, $restore_step_sys_date); gtkpack($advanced_box, $restore_step_sys = gtkpack_(Gtk2::VBox->new(0,10), 0, N("Please choose the date to restore:"), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::HBox->new(0,10), 0, $combo_restore_step_sys, 0, my $details = Gtk2::Button->new(N("Details")), 1, Gtk2::HBox->new(0,10), ), ), ); $combo_restore_step_sys->entry->signal_connect('changed', sub { $restore_step_sys_date = $combo_restore_step_sys->entry->get_text; }); $details->signal_connect('clicked', sub { #- we're only passing a portion of the filename to #- the subroutine so we need to let it know this $restore_step_sys_date = $combo_restore_step_sys->entry->get_text; destroy_widget(); show_backup_details(\&restore_step_sys, "sys", $restore_step_sys_date); }); fonction_env(\$restore_step_sys, \&restore_step_sys, \&restore_step2, "restore"); if ($restore_user) { fonction_env(\$restore_step_sys, \&restore_step_sys, \&restore_step2, \&restore_step_user); } elsif ($restore_other) { fonction_env(\$restore_step_sys, \&restore_step_sys, \&restore_step2, \&restore_step_other); } else { fonction_env(\$restore_step_sys, \&restore_step_sys, \&restore_step2, \&restore_do); } $up_box->show_all; } sub restore_other_media() { my $box_find_restore; my $button; gtkpack($advanced_box, $box_find_restore = gtkpack_(Gtk2::VBox->new(0, 6), 0, Gtk2::HSeparator->new, 0, my $check_other_media_hd = Gtk2::CheckButton->new(N("Restore from Hard Disk.")), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, gtkset_sensitive(Gtk2::Label->new(N("Enter the directory where backups are stored")), $other_media_hd), 1, Gtk2::VBox->new(0, 6), 0, gtkset_size_request(gtkset_sensitive($restore_find_path_entry = Gtk2::Entry->new, $other_media_hd), 152, 20), 0, gtkset_sensitive($button = gtksignal_connect(Gtk2::Button->new, clicked => sub { filedialog_generic(N("Directory with backups"), \$restore_find_path_entry); }), $other_media_hd), ), 1, Gtk2::VBox->new(0, 6), 0, Gtk2::VBox->new(0, 6), ), ); gtksignal_connect(gtkset_active($check_other_media_hd, $other_media_hd), toggled => sub { $other_media_hd = $other_media_hd ? 0 : 1; destroy_widget(); $current_widget->(); }); $button->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-dossier-32"))); $restore_find_path_entry->set_text($path_to_find_restore); $restore_find_path_entry->signal_connect('changed', sub { $path_to_find_restore = $restore_find_path_entry->get_text }); fonction_env(\$box_find_restore, \&restore_other_media, \&restore_step2, \&restore_do); $up_box->show_all; } sub restore_step2() { my $retore_step2; my $other_exist; my $sys_exist; my $user_exist; local $_; destroy_widget(); my $restore_info_path = $conf{PATH_TO_SAVE}; $restore_info_path = $path_to_find_restore if $conf{USE_HD} || $conf{USE_CD}; my $info_prefix = "backup"; $info_prefix = "list" if $conf{USE_NET} || $conf{USE_TAPE}; if (any { /_other_/ } grep { /^$info_prefix/ } all("$restore_info_path/")) { $other_exist = 1; } else { $other_exist = 0; $restore_other = 0; } if (any { /_sys_/ } grep { /^$info_prefix/ } all("$restore_info_path/")) { $sys_exist = 1; } else { $sys_exist = 0; $restore_sys = 0; } if (any { /_user_/ } grep { /^$info_prefix/ } all("$restore_info_path/")) { $user_exist = 1; } else { $user_exist = 0; $restore_user = 0; } my $restore_path_entry = Gtk2::Entry->new; gtkpack($advanced_box, $retore_step2 = gtkpack_(Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, my $check_restore_other_src = Gtk2::CheckButton->new(N("Select another media to restore from")), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Other Media")), clicked => sub { destroy_widget(); restore_other_media(); }), $restore_other_src), ), 0, gtkset_sensitive(my $check_restore_sys = Gtk2::CheckButton->new(N("Restore system")), $sys_exist), 0, gtkset_sensitive(my $check_restore_user = Gtk2::CheckButton->new(N("Restore Users")), $user_exist), 0, gtkset_sensitive(my $check_restore_other = Gtk2::CheckButton->new(N("Restore Other")), $other_exist), 0, gtkpack_(Gtk2::HBox->new(0,10), 0, my $check_restore_other_path = Gtk2::CheckButton->new(N("Select path to restore (instead of /)")), 1, Gtk2::HBox->new(0,10), 0, gtkset_sensitive($restore_path_entry, $restore_other_path), 0, gtksignal_connect(my $button = Gtk2::Button->new->new, clicked => sub { filedialog_generic(N("Path To Restore To"), \$restore_path_entry); }), ), 0, gtkset_sensitive(my $check_backup_bef_restore = Gtk2::CheckButton->new(N("Do new backup before restore (only for incremental backups.)")), $conf{SYS_INCREMENTAL_BACKUPS} || $conf{USER_INCREMENTAL_BACKUPS}), 0, gtkset_sensitive(my $check_remove_user_dir = Gtk2::CheckButton->new(N("Remove user directories before restore.")), $user_exist), 1, Gtk2::VBox->new(0,10), ), ); foreach ([$check_restore_sys, \$restore_sys], [$check_backup_bef_restore, \$backup_bef_restore], [$check_restore_user, \$restore_user], [$check_remove_user_dir, \$remove_user_before_restore], [$check_restore_other, \$restore_other]) { my $ref = $_->[1]; gtksignal_connect(gtkset_active($_->[0], $$ref), toggled => sub { $$ref = $$ref ? 0 : 1; if (!$restore_sys && !$restore_user && !$restore_other) { $next_widget = \&message_norestore_box; } elsif ($restore_sys && $conf{SYS_INCREMENTAL_BACKUPS}) { $next_widget = \&restore_step_sys; } elsif ($restore_user) { $next_widget = \&restore_step_user; } elsif ($restore_other) { $next_widget = \&restore_step_other; } else { $next_widget = \&restore_do; } }); } gtksignal_connect(gtkset_active($check_restore_other_path, $restore_other_path), toggled => sub { $restore_other_path = $restore_other_path ? 0 : 1; destroy_widget(); $current_widget->(); }); gtksignal_connect(gtkset_active($check_restore_other_src, $restore_other_src), toggled => sub { $restore_other_src = $restore_other_src ? 0 : 1; destroy_widget(); $current_widget->(); }); $central_widget = \$retore_step2; fonction_env(\$retore_step2, \&restore_step2, \&restore_box); if (!$restore_sys && !$restore_user && !$restore_other) { $next_widget = \&message_norestore_box; } elsif ($restore_sys && $conf{SYS_INCREMENTAL_BACKUPS}) { $next_widget = \&restore_step_sys; } elsif ($restore_user) { $next_widget = \&restore_step_user; } elsif ($restore_other) { $next_widget = \&restore_step_other; } else { $next_widget = \&restore_do; } $button->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-dossier-32"))); $restore_path_entry->set_text($restore_path); $restore_path_entry->signal_connect('changed', sub { $restore_path = $restore_path_entry->get_text; $untar_prefix = "tar -C $restore_path -x"; }); $up_box->show_all; } sub find_files_to_restore() { local $_; my $file_restore; my $start_restore; my $files_selected = 0; my @possible_sources; my %catalog_entries; my @files_to_restore; my $cat_entry; my @catalog = cat_("$cfg_dir/drakbackup_catalog"); destroy_widget(); #- file info in tree view my $model = Gtk2::TreeStore->new("Glib::String", "Gtk2::Gdk::Pixbuf", "Glib::Int"); my $file_list = Gtk2::TreeView->new_with_model($model); $file_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); $file_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererPixbuf->new, 'pixbuf' => 1)); $file_list->append_column(my $valcolumn = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 2)); $file_list->set_headers_visible(0); $file_list->get_selection->set_mode('browse'); $valcolumn->set_visible(0); my $unselected = gtkcreate_pixbuf('unselected'); my $selected = gtkcreate_pixbuf('selected'); my $file_wildcard_entry = Gtk2::Entry->new; gtkpack($advanced_box, $file_restore = gtkpack_(Gtk2::VBox->new(0,10), 0, Gtk2::Label->new(N("Filename text substring to search for (empty string matches all):")), 0, gtkpack_(Gtk2::HBox->new(0,10), 1, $file_wildcard_entry, 0, gtksignal_connect(Gtk2::Button->new(N("Search Backups")), clicked => sub { local $_ = $file_wildcard_entry->get_text; s|^\*|\\*|g; my $wildcard = $_; @possible_sources = glob "$conf{PATH_TO_SAVE}/list*"; $model->clear; my $match = 0; foreach my $list (@possible_sources) { my @matches = grep { /$wildcard/ } cat_($list); if (@matches) { my $list_entry = $model->append_set(undef, [ 0 => $list, 2 => '' ]); foreach (@matches) { chop; $model->append_set($list_entry, [ 0 => $_, 1 => $unselected, 2 => 0 ]); } $match = 1; } } show_warning("i", N("No matches found...")) if $match == 0; }), ), 1, create_scrolled_window($file_list), 0, gtkset_sensitive(gtksignal_connect($start_restore = Gtk2::Button->new(N("Restore Selected")), clicked => sub { @files_to_restore = (); my $last_entry = ''; my $catalog_entry; my $restore_file; foreach (sort keys %catalog_entries) { if ($catalog_entries{$_} == 1) { ($catalog_entry, $restore_file) = split("###", $_); $last_entry = $catalog_entry if $last_entry eq ''; if ($catalog_entry ne $last_entry) { restore_catalog_entry($cat_entry, @files_to_restore); @files_to_restore = (); push @files_to_restore, $restore_file; } else { push @files_to_restore, $restore_file; } $last_entry = $catalog_entry; } } restore_catalog_entry($cat_entry, @files_to_restore); destroy_widget(); find_files_to_restore(); }), 0), ), ); $file_list->get_selection->signal_connect(changed => sub { my ($lmodel, $iter) = $_[0]->get_selected; $lmodel && $iter or return; my ($s, $val) = $lmodel->get($iter, 0, 2); if (! any { /$s/ } @possible_sources) { my $parent_iter = Gtk2::TreeModel::iter_parent($lmodel, $iter); my $parent_name = $lmodel->get($parent_iter, 0); $cat_entry = substr($parent_name, -19, 15); my @full_cat_entry = grep { /^$cat_entry/ } @catalog; chop @full_cat_entry; $cat_entry = $full_cat_entry[0]; $val ? $lmodel->set($iter, 1, $unselected, 2, 0) : $lmodel->set($iter, 1, $selected, 2, 1); $val ? $files_selected-- : $files_selected++; $catalog_entries{$cat_entry . "###" . $s} = 1 - $val; $files_selected ? gtkset_sensitive($start_restore, 1) : gtkset_sensitive($start_restore, 0); } }); $central_widget = \$file_restore; } sub catalog_restore { my ($call_method) = @_; my $catalog_box; my $cat_entry; my @restore_files; my $restore_path_entry; destroy_widget(); #- catalog info in tree view my $model = Gtk2::TreeStore->new("Glib::String"); my $tree_catalog = Gtk2::TreeView->new_with_model($model); $tree_catalog->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); $tree_catalog->set_headers_visible(0); $tree_catalog->get_selection->set_mode('single'); # file details in list widget my $lmodel = Gtk2::ListStore->new("Glib::String"); my $tree_files = Gtk2::TreeView->new_with_model($lmodel); $tree_files->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); $tree_files->set_headers_visible(0); $tree_files->get_selection->set_mode('multiple'); #- read the catalog my @catalog = cat_("$cfg_dir/drakbackup_catalog"); foreach (@catalog) { chop; my @line_data = split(':', $_); my $t = $line_data[0]; my $t_catalog = $model->append_set(undef, [ 0 => $t ]); my $indexer = 0; foreach (@line_data) { if ($indexer != 0) { my $m; $m = "Media: " if $indexer == 1; $m = "Label or Host: " if $indexer == 2; $m = "Device or Path: " if $indexer == 3; $m = "Type: Incremental" if $_ eq "I"; $m = "Type: Differential" if $_ eq "D"; $m = "Type: Full" if $_ eq "F"; $m .= $_ if $_ ne "I" && $_ ne "F" && $_ ne "D"; $model->append_set($t_catalog, [ 0 => $m ]); } $indexer++; } } $tree_catalog->get_selection->signal_connect(changed => sub { my ($model, $iter) = $_[0]->get_selected; $model && $iter or return; $cat_entry = $model->get($iter, 0); my $parent_iter = Gtk2::TreeModel::iter_parent($model, $iter); if ($parent_iter) { $cat_entry = ''; return; } gtkset_mousecursor_wait(); @restore_files = (); $lmodel->clear; foreach my $filename (glob("$conf{PATH_TO_SAVE}/list*$cat_entry.txt")) { my @contents = cat_($filename); foreach (@contents) { chop; my $s = $_; $lmodel->append_set(0, $s); } } gtkset_mousecursor_normal(); my @full_cat_entry = grep { /^$cat_entry/ } @catalog; $cat_entry = $full_cat_entry[0]; }); $tree_files->get_selection->signal_connect(changed => sub { my (@what) = $_[0]->get_selected_rows; @restore_files = (); foreach (@what) { my $iter = $lmodel->get_iter($_); my $s = $lmodel->get($iter, 0); push @restore_files, $s; } }); gtkpack($advanced_box, $catalog_box = gtkpack_(Gtk2::HBox->new(0,10), 1, gtkpack_(Gtk2::VBox->new(0,5), 0, N("Click date/time to see backup files.\nCtrl-Click files to select multiple files."), 1, gtkpack_(Gtk2::VBox->new(0, 10), 1, create_scrolled_window($tree_catalog), 1, create_scrolled_window($tree_files), ), 0, gtkpack_(Gtk2::HBox->new(1, 10), 1, gtksignal_connect(Gtk2::Button->new(N("Restore Selected\nCatalog Entry")), clicked => sub { if ($cat_entry) { my $media_check = restore_catalog_entry($cat_entry, ()); if (! $media_check) { destroy_widget(); interactive_mode_box(); } } }), 1, gtksignal_connect(Gtk2::Button->new(N("Restore Selected\nFiles")), clicked => sub { my $files = @restore_files; #- grab the array before the widget clears it my @passed_files = @restore_files; if ($cat_entry && $files) { my $media_check = restore_catalog_entry($cat_entry, @passed_files); if (! $media_check) { destroy_widget(); interactive_mode_box(); } } }), 1, gtkpack_(Gtk2::VBox->new(0, 5), 0, Gtk2::Label->new("Restore To Path"), 0, $restore_path_entry = Gtk2::Entry->new, ), 0, gtksignal_connect(my $button = Gtk2::Button->new, clicked => sub { filedialog_generic(N("Path To Restore To"), \$restore_path_entry); }), ), 0, Gtk2::VBox->new(0,10), ), 0, Gtk2::VBox->new(0,10), ), ); $restore_path_entry->set_text($restore_path); gtksignal_connect($restore_path_entry, changed => sub { $restore_path = $restore_path_entry->get_text; $untar_prefix = "tar -C $restore_path -x"; }); $button->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-dossier-32"))); button_box_restore(); fonction_env(\$catalog_box, \&catalog_restore, \&restore_find_media_box, \&catalog_restore) if $call_method eq "need media"; fonction_env(\$catalog_box, \&catalog_restore, \&restore_box, \&catalog_restore) if $call_method eq "button"; $central_widget = \$catalog_box; $up_box->show_all; } sub restore_catalog_entry { restore_status(); my ($cat_entry, @restore_files) = @_; my $username; my $userpass = $conf{PASSWD}; my $restore_result = 1; my @line_data = split(':', $cat_entry); my $backup_time = $line_data[0]; #- use our own variables here so we don't trash a saved config accidentally my $media = $line_data[1]; #- can be a volume name or a host name my $vol_host = $line_data[2]; #- see if we have a username embedded in the host if (index($vol_host, "@") != -1) { my @user_host = split("@", $vol_host); $username = $user_host[0]; $vol_host = $user_host[1]; } else { $username = $conf{LOGIN}; } #- create a restore work directory if we don't have one -d "$cfg_dir/restores" or mkdir_p "$cfg_dir/restores"; #- can be a device name or a path my $dev_path = $line_data[3]; if ($media eq 'HD') { #- shouldn't really happen, should have just browsed #- to the $conf{PATH_TO_SAVE} in the previous step - deal with it anyway my @restore_tar_files = glob("$dev_path/*$backup_time*$conf{OPTION_COMP}"); my $matches = @restore_tar_files; if ($matches == 0) { show_warning("f", N("Backup files not found at %s.", $dev_path)); return 0; } else { my $save_path_org = $conf{PATH_TO_SAVE}; $conf{PATH_TO_SAVE} = $dev_path; $restore_result = restore_hd_or_cd($cat_entry, $dev_path, @restore_files); $conf{PATH_TO_SAVE} = $save_path_org; } } if ($media eq 'CD') { #- we know the cdrecord device, and the label #- prompt the user for the right CD $in->ask_okcancel(N("Restore From CD"), N("Insert the CD with volume label %s\n in the CD drive under mount point /mnt/cdrom", $vol_host) ,1) ? ($vol_name = get_cd_volname()) : return 0; if ($vol_name ne $vol_host) { show_warning("f", N("Not the correct CD label. Disk is labelled %s.", $vol_name)); return 0; } else { $restore_result = restore_hd_or_cd($cat_entry, '/mnt/cdrom', @restore_files); } } if ($media =~ /^DirectTape|^Tape/) { #- a little more complicated, we need to check if other backups #- were done on this tape, and try to find the offset to this one $in->ask_okcancel(N("Restore From Tape"), N("Insert the tape with volume label %s\n in the tape drive device %s", $vol_host, $dev_path) ,1) ? ($vol_name = get_tape_label($dev_path)) : return 0; if ($vol_name ne $vol_host) { show_warning("f", N("Not the correct tape label. Tape is labelled %s.", $vol_name)); return 0; } else { $restore_result = restore_tape($media, $cat_entry, $dev_path, @restore_files); } } if ($media eq 'ftp' || $media eq 'webdav' || $media eq 'ssh' || $media eq 'rsync') { #- show the user what we know of the connection from the catalog #- and the config file, let them override if necessary $in->ask_from(N("Restore Via Network"), N("Restore Via Network Protocol: %s", $media), [ { label => N("Host Name"), val => \$vol_host }, { label => N("Host Path or Module"), val => \$dev_path }, { label => N("Username"), val => \$username }, { label => N("Password"), val => \$userpass, hidden => 1 }, ]) or goto return 0; if ($media eq 'ftp' || $media eq 'rsync') { if ($userpass eq '') { show_warning("f", N("Password required")); return 0; } } if ($media eq 'ftp' || $media eq 'rsync' || $media eq 'ssh') { if ($username eq '') { show_warning("f", N("Username required")); return 0; } elsif ($vol_host eq '') { show_warning("f", N("Hostname required")); return 0; } } if ($dev_path eq '') { show_warning("f", N("Path or Module required")); return 0; } $restore_result = restore_ftp($cat_entry, $vol_host, $dev_path, $username, $userpass, @restore_files) if $media eq 'ftp'; $restore_result = restore_rsync_ssh_webdav($cat_entry, $vol_host, $dev_path, $username, $media, @restore_files) if $media eq 'rsync' || $media eq 'ssh' || $media eq 'webdav'; } # cleanup our restore dir - unlink fails here? system("rm -fr $cfg_dir/restores/*"); if (!$restore_result) { show_warning("i", N("Files Restored...")); return 0; } else { show_warning("f", N("Restore Failed...")); return 1; } } sub untar { my ($cmd, $msg, $tarfile, $restorefile) = @_; my $message = "Untarring from \n$tarfile \nto $restore_path."; $message = "Untarring \n$restorefile from \n$tarfile \nto $restore_path." if $msg eq "files"; my $untar_cmd = set_tar($untar_prefix, $tarfile); my $command = "$untar_cmd $cmd"; spawn_progress($command, $message); } sub no_tarfile { my ($tarfile) = @_; if (!-e "$cfg_dir/restores/$tarfile") { show_warning("f", N("%s not retrieved...", $tarfile)); return 1; } } sub restore_hd_or_cd { my ($cat_entry, $tarfile_dir, @restore_files) = @_; my $indv_files = @restore_files; my $wild_card = catalog_to_wildcard($cat_entry); if ($indv_files == 0) { #- full catalog specified foreach (wildcard_to_tarfile($wild_card)) { untar("$tarfile_dir/$_", "all", $_, undef); } } else { #- individual files - pull from appropriate catalog foreach (@restore_files) { my ($restorefile, $tarfile) = file_to_tarfile($_, $wild_card); untar("$tarfile_dir/$tarfile $restorefile", "files", $tarfile, $restorefile); } } return 0; } sub restore_tape { my ($media, $cat_entry, $dev_path, @restore_files) = @_; my $indv_files = @restore_files; my $wild_card = catalog_to_wildcard($cat_entry); $dev_path =~ s|/st|/nst|; my $command = "tar -C $restore_path -xf $dev_path"; if ($media eq "DirectTape") { position_tape($cat_entry, $dev_path); if ($indv_files != 0) { foreach (@restore_files) { $command .= " " . substr($_, 1); } } spawn_progress($command, "Restoring files from $dev_path to $restore_path."); return 0; } if ($indv_files == 0) { #- full catalog specified foreach (wildcard_to_tarfile($wild_card)) { position_tape($cat_entry, $dev_path); get_tarfile_from_tape($dev_path); return 1 if no_tarfile("$conf{PATH_TO_SAVE}/$_"); untar("$cfg_dir/restores/$conf{PATH_TO_SAVE}/$_", "all", $_, undef); } } else { #- individual files - pull from appropriate catalog foreach (@restore_files) { my ($restorefile, $tarfile) = file_to_tarfile($_, $wild_card); if (!-e "$cfg_dir/restores/$tarfile") { position_tape($cat_entry, $dev_path); get_tarfile_from_tape($dev_path); } return 1 if no_tarfile($tarfile); untar("$cfg_dir/restores/$tarfile $restorefile", "files", $tarfile, $restorefile); } } return 0; } sub restore_ftp { use Net::FTP; my $ftp; my ($cat_entry, $hostname, $hostpath, $username, $userpass, @restore_files) = @_; my $indv_files = @restore_files; $DEBUG and print "file list to retrieve: $cat_entry\n "; if ($DEBUG && $interactive) { $ftp = Net::FTP->new($hostname, Debug => 1) or return 1 } elsif ($interactive) { $ftp = Net::FTP->new($hostname, Debug => 0) or return 1 } else { $ftp = Net::FTP->new($hostname, Debug => 0) or return 1 } $ftp->login($username, $userpass); $ftp->cwd($hostpath); $ftp->binary; my $wild_card = catalog_to_wildcard($cat_entry); if ($indv_files == 0) { #- full catalog specified foreach (wildcard_to_tarfile($wild_card)) { $ftp->get($_, "$cfg_dir/restores/$_"); return 1 if no_tarfile($_); untar("$cfg_dir/restores/$_", "all", $_, undef); } } else { #- individual files - pull from appropriate catalog foreach (@restore_files) { my ($restorefile, $tarfile) = file_to_tarfile($_, $wild_card); if (!-e "$cfg_dir/restores/$tarfile") { $ftp->get($tarfile, "$cfg_dir/restores/$tarfile"); } return 1 if no_tarfile($tarfile); untar("$cfg_dir/restores/$tarfile $restorefile", "files", $tarfile, $restorefile); } } $ftp->quit; return 0; } sub restore_rsync_ssh_webdav { my ($cat_entry, $hostname, $hostpath, $username, $mode, @restore_files) = @_; my $indv_files = @restore_files; my $wild_card = catalog_to_wildcard($cat_entry); if ($indv_files == 0) { #- full catalog specified foreach (wildcard_to_tarfile($wild_card)) { get_file_from_net($mode, $_, $hostname, $hostpath, $username); return 1 if no_tarfile($_); untar("$cfg_dir/restores/$_", "all", $_, undef); } } else { #- individual files - pull from appropriate catalog foreach (@restore_files) { my ($restorefile, $tarfile) = file_to_tarfile($_, $wild_card); get_file_from_net($mode, $tarfile, $hostname, $hostpath, $username) if !-e "$cfg_dir/restores/$tarfile"; return 1 if no_tarfile($tarfile); untar("$cfg_dir/restores/$tarfile $restorefile", "files", $tarfile, $restorefile); } } return 0; } sub get_file_from_net { my ($mode, $tarfile, $hostname, $hostpath, $username) = @_; my $command; if ($mode eq 'ssh') { $command = "scp $username\@$hostname:$hostpath/$tarfile $cfg_dir/restores/"; } elsif ($mode eq 'rsync') { $command = "rsync --password-file=$cfg_dir/rsync.user $username\@$hostname" . "::" . "$hostpath/$tarfile $cfg_dir/restores/"; } else { $command = "wget http://$hostname/$hostpath/$tarfile -P $cfg_dir/restores/"; } spawn_progress($command, "Retrieving backup file \n$tarfile \nvia $mode."); } sub catalog_to_wildcard { my ($cat_entry) = @_; my @line_data = split(':', $cat_entry); my $wildcard = $line_data[0]; $wildcard; } sub wildcard_to_tarfile { my ($wildcard) = @_; my (@tarfile) = glob("$conf{PATH_TO_SAVE}/*$wildcard.txt"); foreach (@tarfile) { $_ = basename($_); s/txt/$conf{OPTION_COMP}/; s/list/backup/; } @tarfile; } sub file_to_tarfile { my ($restore_file, $wildcard) = @_; #- remove leading "/" $restore_file = substr($restore_file, 1); #- filename with spaces $restore_file = "'" . $restore_file . "'" if $restore_file =~ / /; my $tarfile = `grep -l $restore_file $conf{PATH_TO_SAVE}/*$wildcard.txt`; chop $tarfile; $tarfile = basename($tarfile); $tarfile =~ s/txt/$conf{OPTION_COMP}/; $tarfile =~ s/list/backup/; $restore_file, $tarfile; } sub find_tape_offset { my ($cat_entry) = @_; my @line_data = split(':', $cat_entry); my $label = $line_data[2]; my @catalog = cat_("$cfg_dir/drakbackup_catalog"); # always off by 1 for tape label. my $offset = 1; foreach (@catalog) { if (index($_, $label)) { if (!index($_, $cat_entry)) { # tar seems to need 2 of these to get located $offset++; $offset++; } else { return $offset; } } } } sub position_tape { my ($cat_entry, $dev_path) = @_; my $offset = find_tape_offset($cat_entry); spawn_progress("mt -f $dev_path rewind", "Rewinding tape on $dev_path."); spawn_progress("mt -f $dev_path fsf $offset", "Moving forward $offset file records."); } sub get_tarfile_from_tape { my ($dev_path) = @_; spawn_progress("tar -C $cfg_dir/restores -xf $dev_path", "Untarring from $dev_path to work directory."); } sub restore_box() { destroy_widget(); if ($good_restore_path) { $path_to_find_restore = $conf{PATH_TO_SAVE} if $conf{USE_HD}; $path_to_find_restore = "/mnt/cdrom" if $conf{USE_CD}; } find_backup_to_restore(); button_box_restore_main(); if (@other_backuped || @sys_backuped || @user_backuped) { gtkpack($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0,1), 1, Gtk2::VBox->new(0,10), 1, gtkpack_(Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), 1, gtksignal_connect(Gtk2::Button->new(N("Search for files to restore")), clicked => sub { button_box_file_restore(); find_files_to_restore(); }), 1, gtksignal_connect(Gtk2::Button->new(N("Restore all backups")), clicked => sub { button_box_restore(); @user_list_to_restore2 = sort @user_backuped; $restore_sys = 1; $restore_other = 1; $restore_user = 1; restore_do(); }), 1, gtksignal_connect(Gtk2::Button->new(N("Custom Restore")), clicked => sub { button_box_restore(); restore_step2(); }), 1, gtksignal_connect(Gtk2::Button->new(N("Restore From Catalog")), clicked => sub { catalog_restore("button"); }), 1, Gtk2::VBox->new(0,10), 1, Gtk2::VBox->new(0,10), ), 1, Gtk2::HBox->new(0,10), ), ); } else { destroy_widget(); restore_find_media_box(); } fonction_env(\$box2, \&restore_box, \&interactive_mode_box); $central_widget = \$box2; $up_box->show_all; } sub restore_find_media_box() { my $mount_media = 1; $good_restore_path = 0; my $message = N("Unable to find backups to restore...\n"); $message .= N("Verify that %s is the correct path", $path_to_find_restore) if $conf{USE_HD} && $conf{USE_CD}; $message .= N(" and the CD is in the drive") if $conf{USE_CD}; if ($conf{USE_TAPE} || $conf{NET_PROTO}) { $message .= N("Backups on unmountable media - Use Catalog to restore"); $mount_media = 0; } $message .= "."; gtkpack($advanced_box, $box2 = gtkpack_(Gtk2::VBox->new(0, 5), 1, gtkpack(Gtk2::HBox->new(0, 15), Gtk2::VBox->new(0, 5), gtkcreate_img('warning'), translate($message), Gtk2::VBox->new(0, 5), ), 1, gtkpack(Gtk2::HBox->new(0, 15), Gtk2::VBox->new(0, 5), gtkpack(Gtk2::VBox->new(0, 10), gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("CD in place - continue.")), clicked => sub { $good_restore_path = 1; interactive_mode_box("restore"); }), $mount_media), $new_path_entry = gtkset_sensitive(Gtk2::Entry->new, $mount_media), gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Browse to new restore repository.")), clicked => sub { filedialog_generic(N("Directory To Restore From"), \$new_path_entry); }), $mount_media), gtksignal_connect(Gtk2::Button->new(N("Restore From Catalog")), clicked => sub { $box2->destroy; catalog_restore("need media"); }), gtksignal_connect(Gtk2::Button->new(N("Search for files to restore")), clicked => sub { $box2->destroy; button_box_file_restore(); find_files_to_restore(); }), ), Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); $new_path_entry->set_text($path_to_find_restore); $new_path_entry->signal_connect('changed', sub { $path_to_find_restore = $new_path_entry->get_text }); $central_widget = \$box2; button_box_find_media($mount_media); $up_box->show_all; } sub restore_status() { destroy_widget(); $pbar3 = Gtk2::ProgressBar->new; $stext = Gtk2::Label->new(""); gtkpack($advanced_box, $table = gtkpack(Gtk2::VBox->new(0, 5), Gtk2::HBox->new(0,5), create_packtable({ col_spacings => 10, row_spacings => 5 }, [""], [""], [""], [""], [N("Restore Progress")], [""], [""], [$pbar3], [""], [""], [$plabel3 = Gtk2::Label->new(' ')], [""], ), $stext, ), ); $central_widget = \$table; $up_box->show_all; gtkflush(); } ################################################ BUTTON_BOX ################################################ sub hbutton() { 0, gtksignal_connect(Gtk2::Button->new(N("Help")), clicked => \&adv_help); } sub cbutton() { 0, gtksignal_connect(Gtk2::Button->new(N("Cancel")), clicked => \&interactive_mode_box); } sub cbuttonr() { 0, gtksignal_connect(Gtk2::Button->new(N("Cancel")), clicked => \&restore_box); } sub ibutton { my ($msg) = @_; 0, gtksignal_connect(Gtk2::Button->new($msg), clicked => \&interactive_mode_box); } sub pbutton() { 0, gtksignal_connect(Gtk2::Button->new(N("Previous")), clicked => sub { destroy_widget(); $previous_widget->(); }); } sub hspace() { 1, Gtk2::HBox->new(0, 1); } sub button_box_adv() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbutton(), hbutton(), hspace(), pbutton(), 0, gtksignal_connect(Gtk2::Button->new(N("Save")), clicked => sub { if (check_pkg_needs()) { install_rpm(\&$current_widget, undef); } else { if (!save_conf_file()) { destroy_widget(); $previous_widget->(); } } }), ), ); } sub button_box_restore_main() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbutton(), hbutton(), hspace(), ibutton(N("Previous")), ibutton(N("Next")), ), ); } sub button_box_file_restore() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, 0, gtksignal_connect(Gtk2::Button->new(N("Cancel")), clicked => \&restore_box), hbutton(), hspace(), ), ); } sub button_box_ok_only() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, hspace(), ibutton(N("Ok")), ), ); } sub button_box_backup_end() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbutton(), hbutton(), hspace(), pbutton(), 0, gtksignal_connect(Gtk2::Button->new(N("Build Backup")), clicked => sub { destroy_widget(); build_backup_status(); build_backup_files(); }), ), ); } sub button_box_wizard_end() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbutton(), hbutton(), hspace(), pbutton(), 0, gtksignal_connect(Gtk2::Button->new(N("Save")), clicked => sub { save_conf_file(); interactive_mode_box(); }), ), ); } sub button_box_restore_end() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbuttonr(), hbutton(), hspace(), pbutton(), 0, gtksignal_connect(Gtk2::Button->new(N("Restore")), clicked => sub { destroy_widget(); restore_backend(); }), ), ); } sub button_box_restore_pbs_end() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, hspace(), hbutton(), ibutton(N("Ok")), ), ); } sub button_box_restore() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbuttonr(), hbutton(), hspace(), pbutton(), 0, gtksignal_connect(Gtk2::Button->new(N("Next")), clicked => sub { destroy_widget(); $next_widget->(); }), ), ); } sub button_box_find_media { my ($mount_media) = @_; $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbutton(), hbutton(), hspace(), ibutton(N("Previous")), 0, gtkset_sensitive(gtksignal_connect(Gtk2::Button->new(N("Next")), clicked => sub { interactive_mode_box("restore"); }), $mount_media), ), ); } sub button_box_wizard() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, cbutton(), hbutton(), hspace(), 0, gtksignal_connect(Gtk2::Button->new($previous_widget ? N("Previous") : N("Ok")), clicked => sub { destroy_widget(); $previous_widget ? $previous_widget->() : $next_widget->(); }), if_($next_widget, 0, gtksignal_connect(Gtk2::Button->new(N("Next")), clicked => sub { destroy_widget(); $next_widget ? $next_widget->() : $previous_widget->(); })), ), ); } sub button_box_main() { $button_box_tmp->destroy; gtkpack($button_box, $button_box_tmp = gtkpack_(Gtk2::HButtonBox->new, hbutton(), hspace(), 0, gtksignal_connect(Gtk2::Button->new(N("Close")), clicked => sub { ugtk2->exit(0) }), ), ); } ################################################ MESSAGES ################################################ sub install_rpm { my ($calling_widget, $previous) = @_; destroy_widget(); gtkpack($advanced_box, my $rpm_box = gtkpack_(Gtk2::VBox->new(0, 15), 0, N("The following packages need to be installed:\n") . join(' ', @list_of_rpm_to_install), 0, Gtk2::HSeparator->new, 0, gtksignal_connect(Gtk2::Button->new(N("Install")), clicked => sub { my $installed = system("/usr/sbin/urpmi --X @list_of_rpm_to_install"); if ($installed == 0) { destroy_widget(); $calling_widget->($previous); } else { #- no string for the moment - too late for translators $in->ask_warn(N("Error"), @list_of_rpm_to_install); } }), ), ); $central_widget = \$rpm_box; $up_box->show_all; } sub message_norestore_box() { $box2->destroy; gtkadd($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack(Gtk2::HBox->new(0, 15), Gtk2::VBox->new(0, 5), gtkcreate_img('warning'), N("Please select data to restore..."), Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); button_box_restore_main(); $central_widget = \$box2; $up_box->show_all; } ################################################ BUILD_BACKUP ################################################ sub progress { my ($progressbar, $plabel, $incr, $label_text) = @_; my ($new_val) = $progressbar->get_fraction; $new_val += $incr; if ($new_val > 1) { $new_val = 1 } $progressbar->set_fraction($new_val); $plabel->set_text($label_text); gtkflush(); } sub build_backup_status() { $pbar = Gtk2::ProgressBar->new; $pbar1 = Gtk2::ProgressBar->new; $pbar2 = Gtk2::ProgressBar->new; $pbar3 = Gtk2::ProgressBar->new; $plabel = Gtk2::Label->new(" "); $plabel1 = Gtk2::Label->new(" "); $plabel2 = Gtk2::Label->new(" "); $plabel3 = Gtk2::Label->new(" "); $stext = Gtk2::Label->new(""); button_box_ok_only(); my $table = Gtk2::Table->new(10, 2, 1); $table->set_row_spacings(5); $table->set_col_spacings(10); $table->attach_defaults(Gtk2::Label->new(N("Backup system files")), 0, 1, 0, 1); $table->attach_defaults($pbar, 0, 1, 1, 2); $table->attach_defaults($plabel, 1, 2, 1, 2); $table->attach_defaults(Gtk2::Label->new(N("Backup user files")), 0, 1, 2, 3); $table->attach_defaults($pbar1, 0, 1, 3, 4); $table->attach_defaults($plabel1, 1, 2, 3, 4); $table->attach_defaults(Gtk2::Label->new(N("Backup other files")), 0, 1, 4, 5); $table->attach_defaults($pbar2, 0, 1, 5, 6); $table->attach_defaults($plabel2, 1, 2, 5, 6); $table->attach_defaults(Gtk2::Label->new(N("Total Progress")), 0, 1, 6, 7); $table->attach_defaults($pbar3, 0, 1, 7, 8); $table->attach_defaults($plabel3, 1, 2, 7, 8); gtkpack($advanced_box, my $tbox = gtkpack(Gtk2::VBox->new(0, 5), $table, $stext, ), ); $central_widget = \$tbox; $up_box->show_all; gtkflush(); } sub build_backup_ftp_status() { $pbar = Gtk2::ProgressBar->new; $pbar3 = Gtk2::ProgressBar->new; destroy_widget(); button_box_ok_only(); $pbar->set_fraction(0); $pbar3->set_fraction(0); gtkpack($advanced_box, $table = gtkpack_(Gtk2::VBox->new(0, 15), 1, N("Sending files by FTP"), 1, Gtk2::VBox->new(0, 15), 1, create_packtable({ col_spacings => 10, row_spacings => 5 }, [N("Sending files...")], [""], [ $plabel = Gtk2::Label->new(' ') ], [ $pbar ], [""], [N("Total Progress")], [ $plabel3 = Gtk2::Label->new(' ') ], [$pbar3], ), 1, Gtk2::VBox->new(0, 15), ), ); $central_widget = \$table; $up_box->show_all; gtkflush(); } sub build_backup_box_see_conf { my ($caller) = @_; my $text = Gtk2::TextView->new; read_conf_file(); system_state(); gtktext_insert($text, [ [ $system_state ] ]); button_box_restore_main(); gtkpack($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, gtkpack_(Gtk2::VBox->new(0,10), 0, N("Drakbackup Configuration"), 1, create_scrolled_window($text), ), ), ); button_box_backup_end(); $central_widget = \$box2; $current_widget = \&build_backup_box_see_conf; if ($caller eq "interactive") { $previous_widget = \&interactive_mode_box; } else { $previous_widget = \&build_backup_box; } $up_box->show_all; } sub build_backup_box() { destroy_widget(); gtkadd($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtksignal_connect(my $button_from_conf_file = Gtk2::Button->new, clicked => sub { destroy_widget(); build_backup_status(); build_backup_files(); }), 0, Gtk2::VBox->new(0, 5), 1, gtksignal_connect(my $button_see_conf = Gtk2::Button->new, clicked => sub { destroy_widget(); build_backup_box_see_conf(undef); }), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); $button_from_conf_file->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-discdurwhat-40"), Gtk2::Label->new(N("Backup Now from configuration file")), Gtk2::HBox->new(0, 5) )); $button_see_conf->add(gtkpack(Gtk2::HBox->new(0,10), gtkcreate_img("ic82-moreoption-40"), Gtk2::Label->new(N("View Backup Configuration.")), Gtk2::HBox->new(0, 5) )); button_box_restore_main(); fonction_env(\$box2, \&build_backup_box, \&interactive_mode_box); $up_box->show_all; } ################################################ INTERACTIVE ################################################ sub interactive_mode_box { my ($o_mode) = @_; if ($o_mode eq "restore") { $central_widget = \$box2; restore_box(); return 0; } destroy_widget(); gtkadd($advanced_box, $box2 = gtkpack_(Gtk2::HBox->new(0, 15), 1, Gtk2::VBox->new(0, 5), 1, gtkpack_(Gtk2::VBox->new(0, 5), 1, Gtk2::VBox->new(0, 5), 0, gtksignal_connect(Gtk2::Button->new(N("Wizard Configuration")), clicked => sub { destroy_widget(); read_conf_file(); wizard(); }), 0, gtksignal_connect(Gtk2::Button->new(N("Advanced Configuration")), clicked => sub { button_box_adv(); destroy_widget(); advanced_box(); }), 0, gtksignal_connect(Gtk2::Button->new(N("View Configuration")), clicked => sub { destroy_widget(); build_backup_box_see_conf("interactive"); }), 0, gtksignal_connect(Gtk2::Button->new(N("View Last Log")), clicked => sub { $results = cat_($log_file); button_box_ok_only(); show_status(); }), 0, gtksignal_connect(Gtk2::Button->new(N("Backup Now")), clicked => sub { if ($cfg_file_exist) { build_backup_box(); } else { $in->ask_warn(N("Error"), N("No configuration file found \nplease click Wizard or Advanced.")); } }), 0, gtksignal_connect(Gtk2::Button->new(N("Restore")), clicked => sub { restore_box(); }), 1, Gtk2::VBox->new(0, 5), ), 1, Gtk2::VBox->new(0, 5), ), ); $central_widget = \$box2; button_box_main(); $up_box->show_all; } sub interactive_mode() { $interactive = 1; $in = 'interactive'->vnew; $::Wizard_title = N("Drakbackup"); $::Wizard_pix_up = "ic82-back-up-48.png"; $in->isa('interactive::gtk') and $::isWizard = 1; $my_win = ugtk2->new(N("Drakbackup")); $window1 = $my_win->{window}; $my_win->{rwindow}->signal_connect(delete_event => sub { ugtk2->exit(0) }); read_conf_file(); gtkadd($window1, gtkpack(Gtk2::VBox->new(0,0), gtkpack($up_box = Gtk2::VBox->new(0, 5), gtkpack_(Gtk2::VBox->new(0, 3), 1, gtkpack_(Gtk2::HBox->new(0, 3), 1, $advanced_box = Gtk2::HBox->new(0, 15), ), 0, Gtk2::HSeparator->new, 0, $button_box = gtkpack(Gtk2::VBox->new(0, 15), $button_box_tmp = gtkpack(Gtk2::VBox->new(0, 0),), ), ), ), ), ); setup_tooltips(); interactive_mode_box(); button_box_main(); $central_widget = \$box2; $window1->realize; $window1->show_all; $my_win->main; $my_win->exit(0); } sub adv_help() { exec("drakhelp --id drakbackup") unless fork(); } sub destroy_widget() { if ($central_widget ne '') { $$central_widget->destroy; $central_widget = ''; } }