Applications-BackupAndRestore
view release on metacpan or search on metacpan
lib/Applications/BackupAndRestore.pm view on Meta::CPAN
package Applications::BackupAndRestore;
use strict;
use warnings;
our $VERSION = 0.021;
our $DEBUG = 1;
=head1 NAME
Applications::BackupAndRestore - a linux frontend for tar
=head1 DESCRIPTION
BackupAndRestore is a backup utility for making incremental backups by using GNU Tar.
Core features:
=over
=item *
Incremental backup with quick and easy restoration of files
=item *
Handels different backup locations
=item *
Full support for excluding files and folders and even file patterns (shell regex)
=item *
Handels different store locations
=item *
Log with all relevant information
=head1 REQUIREMENTS
A Linux
L<GNU Tar|http://www.gnu.org/software/tar/>
L<bzip2|http://www.bzip.org/>
A archive browser like file-roller (for now BackupAndRestore only supports this)
=head1 GUI
=head2 Backup & Restore
The Backup & Restore utility is illustrated in Figure 1-1.
=head3 Figure 1-1. Backup & Restore
=begin html
<img src="BackupAndRestore/pod/BackupAndRestore.png"><br>
=end html
=head1
To start up Backup & Restore from a terminal window, type B<BackupAndRestore> and then press C<Enter>.
Backup & Restore has a List View where you see every single backup with time, date, changed files and the exact space required on your harddrive.
Above the list view there is a File Chooser Button where you can select a folder to backup. Position the cursor over File Chooser Button and press the right mouse button. A pop-up menu appears. Choose a folder from the pop-up menu. Drag a folder ico...
Right hand to the File Chooser Button there is a Recycle Button. The recycle button keeps a list of folders you have saved. For example, place the cursor over the recycle button on a Backup & Restore window; then press the left mouse button to see a ...
Below the list view there is a backup button.
=head2 Backup In Progress Notification
The Backup In Progress Notification is illustrated in Figure 1-2.
=head3 Figure 1-2. Backup In Progress Notification
=begin html
<img src="BackupAndRestore/pod/BackupInProgressNotification.png"><br>
=end html
=head2 Restore Dialog
The Restore Dialog is illustrated in Figure 1-3.
=head3 Figure 1-3. Restore Dialog
=begin html
<img src="BackupAndRestore/pod/RestoreDialog.png"><br>
=end html
=head2 Restore In Progress Notification
The Restore Dialog is illustrated in Figure 1-4.
=head3 Figure 1-4. Restore In Progress Notification
=begin html
<img src="BackupAndRestore/pod/RestoreInProgressNotification.png"><br>
=end html
=head2 Remove Backup Dialog
The Remove Backup Dialog is illustrated in Figure 1-5.
=head3 Figure 1-5. Remove Backup Dialog
=begin html
<img src="BackupAndRestore/pod/RemoveBackupDialog.png"><br>
=end html
=head1 Perl
=head2 Synopsis
use Applications::BackupAndRestore -run;
use Applications::BackupAndRestore;
=head2 Functions
=cut
lib/Applications/BackupAndRestore.pm view on Meta::CPAN
=head1 ARRANGED BY
HOOO@cpan.org
=head1 COPYRIGHT
This is free software; you can redistribute it and/or modify it
under the same terms as L<Perl|perl> itself.
=cut
# gconf
#
sub gconf {
my ( $this, $key, $value ) = @_;
my $app_key = "/apps/" . $ApplicationName . "/$key";
$this->{client}->set( $app_key, { type => 'string', 'value' => $value } )
if defined $value;
return $this->{client}->get_string($app_key);
}
# gui init
#
sub init {
my ($this) = @_;
print "init $this\n" if $DEBUG > 0;
$SIG{$_} = sub { $this->gtk_main_quit }
foreach @SIGS;
# GUI init
$this->{init} = TRUE;
my $WindowIcon = "../share/BackupAndRestore/BackupAndRestore.svg";
$this->window->set_icon_from_file ($WindowIcon)
if -e $WindowIcon;
$this->exclude_combo->set_active(0); # Gtk2::GladeXML macht es nicht
my $button;
$this->{folder_recycle_button} = new Gtk2::Ex::FileLocator::RecycleButton;
$this->{folder_recycle_button}->show;
$this->folder_box->pack_start( $this->{folder_recycle_button}, FALSE, FALSE, 0 );
$this->{folder_recycle_button}->signal_connect( 'selection-changed', sub { $this->on_folder_recycle_button(@_) } );
#configure;
$this->restore_extract_modifiction_time->set_active(
defined $this->gconf("restore-extract-modification-time")
? $this->gconf("restore-extract-modification-time")
: 1
);
$this->restore_folder->set_current_folder( $this->gconf("restore-folder") || $ENV{HOME} );
$this->store_folder->set_current_folder( $this->gconf("store-folder") || $ENV{HOME} );
$this->store_folder_name->set_text( $this->gconf("store-folder-name") || "Backup" );
$this->folder->set_current_folder( $this->gconf("current-backup-folder") || $ENV{HOME} );
$this->configure_expander;
$this->build_tree;
$this->log_init;
$this->log_add_text( "*** $ApplicationName\n", "Version: $VERSION\n", "\n", );
$this->log_add_text( "*** ", $this->get_tar_version );
$this->{init} = FALSE;
$this->fill_tree;
print "init done $this\n" if $DEBUG > 0;
}
# build_tree
#
sub build_tree {
my ($this) = @_;
print "build_tree\n" if $DEBUG > 0;
#this will create a treeview, specify $tree_store as its model
my $tree_view = $this->tree_view;
#
#create a Gtk2::TreeViewColumn to add
#to $tree_view
my $tree_column = Gtk2::TreeViewColumn->new();
$tree_column->set_title( __ "Time" );
$tree_column->set_visible(FALSE);
#create a renderer
my $renderer = Gtk2::CellRendererText->new;
$tree_column->pack_start( $renderer, FALSE );
$tree_column->add_attribute( $renderer, text => COL_TIME );
#$tree_column->set_sort_column_id(COL_TIME);
#add $tree_column to the treeview
$tree_view->append_column($tree_column);
#
#create a Gtk2::TreeViewColumn to add
#to $tree_view
$tree_column = Gtk2::TreeViewColumn->new();
$tree_column->set_title( __ "Date" );
#create a renderer
$renderer = Gtk2::CellRendererPixbuf->new;
$renderer->set( 'icon-name' => 'tgz' );
$tree_column->pack_start( $renderer, FALSE );
#create a renderer
$renderer = Gtk2::CellRendererText->new;
$tree_column->pack_start( $renderer, FALSE );
$tree_column->add_attribute( $renderer, text => COL_NAME );
$tree_column->add_attribute( $renderer, weight_set => COL_LAST_BACKUP );
lib/Applications/BackupAndRestore.pm view on Meta::CPAN
#
#create a Gtk2::TreeViewColumn to add
#to $tree_view
$tree_column = Gtk2::TreeViewColumn->new();
$tree_column->set_title( __ "Real size in bytes" );
$tree_column->set_visible(FALSE);
#create a renderer
$renderer = Gtk2::CellRendererText->new;
$tree_column->pack_start( $renderer, FALSE );
$tree_column->add_attribute( $renderer, text => COL_REAL_SIZE );
#$tree_column->set_sort_column_id(COL_SIZE);
#add $tree_column to the treeviewGtk2::CellRenderer
$tree_view->append_column($tree_column);
#
#create a Gtk2::TreeViewColumn to add
#to $tree_view
$tree_column = Gtk2::TreeViewColumn->new();
$tree_column->set_title( __ "Label" );
#$tree_column->set_visible(FALSE);
#create a renderer
$renderer = Gtk2::CellRendererText->new;
$tree_column->pack_start( $renderer, FALSE );
$tree_column->add_attribute( $renderer, text => COL_LABEL );
$tree_column->add_attribute( $renderer, weight_set => COL_LAST_BACKUP );
$tree_column->add_attribute( $renderer, weight => COL_WEIGHT );
#$tree_column->set_sort_column_id(COL_PATH);
#add $tree_column to the treeviewGtk2::CellRenderer
$tree_view->append_column($tree_column);
#
#create a Gtk2::TreeViewColumn to add
#to $tree_view
$tree_column = Gtk2::TreeViewColumn->new();
$tree_column->set_title( __ "Path" );
$tree_column->set_visible(FALSE);
#create a renderer
$renderer = Gtk2::CellRendererText->new;
$tree_column->pack_start( $renderer, FALSE );
$tree_column->add_attribute( $renderer, text => COL_PATH );
#$tree_column->set_sort_column_id(COL_PATH);
#add $tree_column to the treeviewGtk2::CellRenderer
$tree_view->append_column($tree_column);
}
#backup
#
sub on_backup_folder_changed {
my ($this) = @_;
return unless $this->folder->get_filename;
return if abs_path( $this->folder->get_filename ) eq $this->gconf("current-backup-folder");
printf "on_backup_folder_changed %s\n", abs_path $this->folder->get_filename if $DEBUG > 0;
$this->gconf( "current-backup-folder", abs_path $this->folder->get_filename );
$this->fill_tree;
return;
}
sub on_folder_recycle_button {
my ($this) = @_;
return unless $this->{folder_recycle_button}->get_filename;
return if $this->folder->get_filename eq abs_path( $this->{folder_recycle_button}->get_filename );
printf " on_folder_recycle_button %s\n", abs_path $this->{folder_recycle_button}->get_filename
if $DEBUG > 0;
$this->folder->set_current_folder( abs_path $this->{folder_recycle_button}->get_filename );
return;
}
#tree
#
use Tie::DataDumper;
sub fill_tree {
my ($this) = @_;
return if $this->{init};
$this->restore_button->set_sensitive(FALSE);
#fill it with arbitry data
my $folder = $this->get_store_folder;
printf "fill_tree %s\n", $folder if $DEBUG > 0;
my $tree_store = Gtk2::TreeStore->new(@ColumnTypes);
if ( -e $folder ) {
my ( $day_iter, $day, $day_folder_size, $day_folder_real_size, $day_folder_files ) = ( undef, "", 0, 0, 0 );
my $current_dat = "$folder/$CurrentDat";
my $date_of_last_backup = $this->fetch_restore_date($folder);
#printf "%s\n", $date_of_last_backup;
my @filenames = reverse grep { m/\.tar\.bz2$/ } get_files($folder);
foreach my $filename (@filenames) {
# get basename
my $basename = basename( $filename, ".tar.bz2" );
# calculate size
my $tardat = "$folder/$basename.dat.bz2";
my $size = ( -s $filename ) + ( -s $tardat );
$size += -s $current_dat unless $day;
################################################################
my $infofile = "$folder/$basename.info.txt";
my $info = $this->get_backup_info( $filename, $infofile );
################################################################
# append day folder
my ( $date, $time ) = split / /o, $basename;
if ( $date ne $day ) {
$tree_store->set(
$day_iter,
(
COL_SIZE, $day_folder_size, COL_REAL_SIZE, $day_folder_real_size, COL_HSIZE,
( sprintf "%sB (%sB)", format_bytes($day_folder_size), format_bytes($day_folder_real_size) ),
COL_FILES, $day_folder_files,
)
) if ref $day_iter;
$day = $date;
$day_iter = $tree_store->append(undef);
$tree_store->set(
$day_iter,
(
COL_DATE, $date, COL_HDATE, format_date($date),
COL_TIME, $time, COL_NAME, format_date($date),
COL_SIZE, $day_folder_size, COL_FILES, $day_folder_files,
COL_PATH, $filename, COL_LABEL, "",
COL_LAST_BACKUP, FALSE, COL_WEIGHT, 600,
)
);
$day_folder_size = 0;
$day_folder_real_size = 0;
$day_folder_files = 0;
}
#printf "%s\n", $tardat unless -s $tardat if $DEBUG > 3;
$day_folder_size += $size;
$day_folder_real_size += $info->{size};
$day_folder_files += scalar @{ $info->{files} };
# day-time column
my $iter = $tree_store->append($day_iter);
$tree_store->set(
$iter,
(
COL_DATE, $date,
COL_HDATE,
format_date($date),
COL_TIME, $time, COL_NAME, $time, COL_SIZE, $size,
COL_HSIZE,
( sprintf "%sB (%sB)", format_bytes($size), format_bytes( $info->{size} ) ),
COL_FILES,
scalar @{ $info->{files} },
COL_LABEL,
__("$info->{label}")
. ( "$info->{label}" ? ", " : "" )
. (
$date_of_last_backup eq $basename
? __("Last backup.")
: ""
),
COL_PATH,
$filename,
COL_LAST_BACKUP,
$date_of_last_backup eq $basename,
COL_WEIGHT,
800,
)
);
}
# append last day folder
$tree_store->set(
$day_iter,
(
COL_SIZE, $day_folder_size, COL_HSIZE,
( sprintf "%sB (%sB)", format_bytes($day_folder_size), format_bytes($day_folder_real_size) ),
COL_FILES, $day_folder_files,
)
) if ref $day_iter;
}
$this->size_all->set_text( format_bytes( folder_size($folder) ) );
#this will create a treeview, specify $tree_store as its model
$this->tree_view->set_model($tree_store);
$this->exclude_configure;
#$this->window->set_sensitive(TRUE);
}
sub get_backup_info {
my ( $this, $filename, $infoname ) = @_;
tie my %info, 'Tie::DataDumper', $infoname
or warn "Problem tying %info: $!";
$info{folders} = 0 unless exists $info{folders};
$info{files} = [] unless exists $info{files};
$info{label} = '' unless exists $info{label};
$info{size} = 0 unless exists $info{size};
return \%info;
}
#sub get_changed_files {
# my ( $this, $filename ) = @_;
# my $cmd = qq{ env LANG=en_GB.utf8 nice --adjustment=17 \\
# env LANG=en_GB.utf8 tar --list \\
# --file "$filename" \\
# | nice --adjustment=17 grep -E "[^//]\$"
# };
# printf "cmd %s\n", $cmd if $DEBUG > 0;
#
# my @changed_files = `$cmd`;
# chomp @changed_files;
#
# printf "changed_files %s\n", scalar @changed_files if $DEBUG > 3;
# return @changed_files;
#}
sub get_store_folder {
my ($this) = @_;
return sprintf "%s%s", abs_path( $this->get_main_store_folder || "" ), abs_path( $this->folder->get_filename || "" );
}
sub on_tree_view_button_press_event {
my ( $this, $widget, $event ) = @_;
#print "on_tree_view_button_press_event $this, $widget", $event->type, "\n" if $DEBUG > 3;
$this->restore_button->set_sensitive(TRUE);
$this->{tree_view_2button_press} = $event->type eq "2button-press";
return;
}
sub on_tree_view_button_release_event {
my ( $this, $widget, $event ) = @_;
#print "on_tree_view_button_release_event %s %s %s\n", $this, $widget, $this->{tree_view_2button_press}, "" if $DEBUG > 3;
my $selected = $this->tree_view->get_selection->get_selected;
if ( ref $selected ) {
if ( $this->{tree_view_2button_press} ) # double click
{
my $path = $this->tree_view->get_model->get( $selected, COL_PATH );
printf "*** %s\n", $path if $DEBUG > 3;
system $TarOpenCmd, $path;
}
else {
my $last_backup = $this->tree_view->get_model->get( $selected, COL_LAST_BACKUP );
printf "*** %s\n", $last_backup ? 1 : 0 if $DEBUG > 3;
if ($last_backup) {
$this->backup_remove_button->set_sensitive(TRUE);
}
else {
$this->backup_remove_button->set_sensitive(FALSE);
}
}
}
return;
}
#backup
#
sub on_backup_button_clicked {
my ($this) = @_;
print "on_backup_button_clicked $this\n" if $DEBUG > 3;
$this->window->set_sensitive(FALSE);
$this->backup_changed_files_label->set_text(0);
$this->backup_folders_label->set_text(0);
$this->backup_elapsed_time_label->set_text( sprintf "%s", strtime(0) );
$this->backup_estimated_time_label->set_text( sprintf "%s / %s", map { strtime(0) } ( 0, 0 ) );
$this->backup_file_label->set_text("");
$this->backup_progress(0);
$this->backup_notification->present;
$this->backup_folder;
$this->{folder_recycle_button}->set_uri( $this->folder->get_uri );
$this->fill_tree;
$this->backup_notification->hide;
$this->window->set_sensitive(TRUE);
return;
}
sub rmdir_p {
my ($folder) = @_;
while ( rmdir $folder ) {
$folder = dirname $folder;
}
return;
}
sub backup_folder {
my ($this) = @_;
#$this->{backup_folder} = TRUE;
my $date = strftime( "%F %X", localtime );
$this->log_add_text( sprintf "\n%s\n", "*" x 42 );
$this->log_add_text( sprintf __("%s Starting backup . . .\n"), $date );
$this->backup_progress(0);
$this->backup_notification->{startTime} = time;
my $folder = abs_path $this->folder->get_filename;
my $store = $this->get_store_folder;
my $mainstore = $this->get_main_store_folder;
my $current_dat = "$store/$CurrentDat";
my $process_dat = "$store/$ProcessDat";
my $archive = "$store/$date.tar.bz2";
my $tardat = "$store/$date.dat.bz2";
my $excludes = "$store/$ExcludesFile";
my $first = -e $excludes;
$this->{cleanup} = sub {
print "cleanup backup @_\n" if $DEBUG > 3;
unlink $tardat;
unlink $process_dat;
unlink $archive;
unless ( -s $current_dat ) {
unlink $current_dat;
unlink $excludes;
rmdir_p $store;
}
if (@_) {
$this->gtk_main_quit;
exit;
}
};
$SIG{$_} = $this->{cleanup} foreach @SIGS;
system "mkdir", "-p", $store;
system "cp", $current_dat, $process_dat if -e $current_dat;
system "touch", $current_dat;
$this->save_excludes;
my $cmd = qq{ env LANG=en_GB.utf8 nice --adjustment=17 \\
env LANG=en_GB.utf8 tar --create \\
--verbose \\
--directory "$folder" \\
--file "$archive" \\
--listed-incremental "$process_dat" \\
--preserve-permissions \\
--ignore-failed-read \\
--exclude "$mainstore" \\
--exclude-from "$excludes" \\
--bzip2 \\
./ 2>&1 \\
};
printf "cmd %s\n", $cmd if $DEBUG > 3;
die $! unless $this->{tarpid} = open TAR, "-|", $cmd;
my $size = 1;
my $files = {};
my $total_size = 1;
my $folders = 0;
my $utf8 = Unicode::UTF8simple->new;
while (<TAR>) {
print $_ if $DEBUG > 3;
#last unless $this->{backup_folder};
chomp;
$_ = $utf8->fromUTF8( "iso-8859-1", $_ );
if (s|^\./||o) {
my $path = "$folder/$_";
if ( -d $path ) {
$this->backup_folders_label->set_text( ++$folders );
Gtk2->main_iteration while Gtk2->events_pending;
}
else {
unless ( exists $files->{$path} ) {
$total_size += $files->{$path} = ( -s $path ) || 0;
$this->backup_changed_files_label->set_text( sprintf "%d [%sB]", scalar keys %$files,
format_bytes($total_size) );
}
$files->{$path} = 0
unless defined $files->{$path}; # wegen: -s link = 0
my @times =
map { strtime($_) } estimated_time( $this->backup_notification->{startTime}, $size, $total_size );
$this->backup_elapsed_time_label->set_text( sprintf "%s", $times[0] );
$this->backup_estimated_time_label->set_text( sprintf "%s / %s", @times[ 1, 2 ] );
$this->backup_file_label->set_text( sprintf "%s [%sB]", $path, format_bytes( $files->{$path} ) );
$this->backup_progress( $size / $total_size );
$size += $files->{$path};
}
}
elsif ( m|^tar: \./(.*?): Directory is new$|o
|| m|^tar: \./(.*?): Directory has been renamed from.*$|o )
{
#print "$1\n" if $DEBUG > 3;
$files->{$_} = -s $_ foreach get_files("$folder/$1");
$total_size += folder_size("$folder/$1");
$this->backup_changed_files_label->set_text( sprintf "%d [%sB]", scalar keys %$files, format_bytes($total_size) );
Gtk2->main_iteration while Gtk2->events_pending;
}
elsif (/^(tar: ).*?(Warning:)/o) {
print "$_\n" if $DEBUG > 3;
$this->log_add_text( $_, "\n" );
}
elsif (/^(tar: )?(Terminated|Killed|Hangup)$/o) {
print "$_\n" if $DEBUG > 3;
$this->log_add_text( $_, "\n" );
}
else {
print "$_\n" if $DEBUG > 3;
}
}
close TAR;
# are there some heavy errors
if ($?) {
$this->log_add_text( sprintf __("Tar exited with status %s\n"), $? );
if ( $? == 512 ) {
$this->log_add_text( __("The 512 exit status means tar thinks it failed for some reason.\n") );
$this->log_add_text( __("This could be caused by files with no permission.\n") );
$? = FALSE;
}
}
if ($?) { # cancel / heavy error
&{ $this->{cleanup} }();
}
else { # everything is fine
my $retval = system qq{ nice --adjustment=17 \\
bzip2 -c9 "$process_dat" > "$tardat" \\
};
printf "bzip2 returned %s\n", $retval if $DEBUG > 3;
$SIG{$_} = 'IGNORE' foreach @SIGS;
system "cp", $process_dat, $current_dat;
unlink $process_dat;
#store date of current backup
$this->store_restore_date($archive);
#store size
my $infofile = "$store/$date.info.txt";
my $info = $this->get_backup_info( $archive, $infofile );
$info->{label} = "First Backup" unless $first;
$info->{folders} = $folders;
$info->{files} = [ keys %$files ];
$info->{size} = $total_size;
tied(%$info)->save;
}
#$this->{backup_folder} = FALSE;
$this->{tarpid} = 0;
if ( $this->backup_notification->{startTime} ) {
$this->log_add_text( sprintf __("Changed files: %d\n"), scalar keys %$files );
$this->log_add_text( sprintf __("Folders: %d\n"), $folders );
$this->log_add_text( sprintf __("Total size: %s\n"), format_bytes($total_size) );
$this->log_add_text( sprintf __("Total time: %s\n"),
strtime( localtime( time - $this->backup_notification->{startTime} ) ) );
}
$this->log_add_text( sprintf __("%s Backup done.\n"), strftime( "%F %X", localtime ) )
if $this->backup_notification->{startTime};
$SIG{$_} = sub { $this->gtk_main_quit }
foreach @SIGS;
}
sub backup_progress {
my ( $this, $fraction ) = @_;
$this->backup_progressbar->set_fraction($fraction);
$this->backup_progressbar->set_text( sprintf "%.2f %%", $fraction * 100 );
#$this->backup_notification->set_title( sprintf "Backup in progress %.2f %%", $fraction * 100 );
Gtk2->main_iteration while Gtk2->events_pending;
return;
}
sub on_cancel_backup {
my ($this) = @_;
printf "on_cancel_backup %s\n", $this->{tarpid} if $DEBUG > 3;
system "pkill", "-P", $this->{tarpid};
$this->backup_notification->{startTime} = 0;
$this->backup_progressbar->set_fraction(1);
$this->backup_progressbar->set_text( __ "Canceling Backup ..." );
#$this->{backup_folder} = FALSE;
$this->log_add_text( sprintf __("%s Backup canceled.\n"), strftime( "%F %X", localtime ) );
return 1;
}
#exclude
#
sub exclude_configure {
my ($this) = @_;
#printf "exclude_configure %s\n", $this->get_excludes_filename if $DEBUG > 3;
$this->exclude_clear;
my $folder = abs_path $this->folder->get_filename || "";
my $excludes = $this->get_excludes_filename;
#unlink $excludes;
if ( -e $excludes ) {
my @excludes = `cat "$excludes"`;
foreach (@excludes) {
chomp;
next unless $_;
if ( -f "$folder/$_" ) {
$this->exclude_add( EXCLUDE_FILE, "$folder/$_" );
}
elsif ( -d "$folder/$_" ) {
$this->exclude_add( EXCLUDE_FOLDER, "$folder/$_" );
}
else {
$this->exclude_add( EXCLUDE_PATTERN, $_ );
}
}
}
elsif ( $ENV{HOME} =~ /^\Q$folder/ ) {
$this->exclude_add( EXCLUDE_PATTERN, "*Trash*" );
$this->exclude_add( EXCLUDE_FILE, "$ENV{HOME}/.xsession-errors" );
}
}
sub exclude_clear {
my ($this) = @_;
$this->exclude_box->remove($_) foreach $this->exclude_box->get_children;
return;
}
sub on_exclude_add {
my ($this) = @_;
#printf "on_exclude_add %s\n", $this->exclude_combo->get_active if $DEBUG > 3;
$this->exclude_add( $this->exclude_combo->get_active );
return;
}
sub exclude_add {
my ( $this, $index, @values ) = @_;
lib/Applications/BackupAndRestore.pm view on Meta::CPAN
$widget->signal_connect(
'changed',
sub {
$widget->{pattern} = $widget->get_text;
$this->save_excludes;
}
);
return $widget;
}
sub save_excludes {
my ($this) = @_;
return unless -e $this->get_store_folder . "/$CurrentDat";
my $folder = abs_path $this->folder->get_filename;
my $excludes = $this->get_excludes_filename;
#printf "save_excludes\n" if $DEBUG > 3;
open( EXCLUDES, ">", $excludes ) || die $!;
printf EXCLUDES "%s\n", join "\n", map { s/^\Q$folder\E\/?//; $_ }
grep { $_ }
map { ( $_->get_children )[1]->{pattern} } $this->exclude_box->get_children;
close EXCLUDES;
return;
}
sub get_excludes_filename {
my ($this) = @_;
my $store = $this->get_store_folder;
my $excludes = "$store/$ExcludesFile";
return $excludes;
}
#restore
#
sub on_restore_folder_changed {
my ($this) = @_;
printf "on_restore_folder_changed %s\n", abs_path $this->restore_folder->get_filename if $DEBUG > 3;
$this->gconf( "restore-folder", abs_path $this->restore_folder->get_filename );
return;
}
sub on_restore_extract_modification_time_changed {
my ($this) = @_;
printf "on_restore_extract_modification_time_changed %s\n", $this->restore_extract_modifiction_time->get_active ? 1 : 0
if $DEBUG > 3;
$this->gconf( "restore-extract-modification-time", $this->restore_extract_modifiction_time->get_active ? 1 : 0 );
return;
}
sub on_restore_button_clicked {
my ($this) = @_;
print "on_restore_button_clicked $this\n" if $DEBUG > 3;
my $selected = $this->tree_view->get_selection->get_selected;
my ( $hdate, $time ) = $this->tree_view->get_model->get( $selected, COL_HDATE, COL_TIME );
$this->restore_backup_from_label->set_text("$hdate $time");
$this->window->set_sensitive(FALSE);
$this->restore_dialog->show;
return;
}
sub on_restore_dialog_cancel {
my ( $this, $widget ) = @_;
print "on_restore_folder_dialog_cancel $this\n" if $DEBUG > 3;
$this->restore_dialog->hide;
$this->window->set_sensitive(TRUE);
return 1;
}
sub on_restore_dialog_ok {
my ( $this, $widget ) = @_;
$this->restore_dialog->hide;
$this->restore_notification->show;
$this->restore_backup;
$this->fill_tree;
$this->restore_notification->hide;
$this->window->set_sensitive(TRUE);
return;
}
sub restore_backup {
my ($this) = @_;
my $restore_to_folder = abs_path $this->restore_folder->get_filename;
my @files = $this->get_files_to_restore;
$this->log_add_text( sprintf "\n%s\n", "*" x 42 );
$this->log_add_text( sprintf __("%s Starting restore . . .\n"), strftime( "%F %X", localtime ) );
$this->restore_progress(0);
$this->restore_notification->{startTime} = time;
my $store = $this->get_store_folder;
my $utf8 = Unicode::UTF8simple->new;
my $backup = 0;
my $counter = 0;
my $numFiles = 0;
my $size = 0;
my $elapsedSize = 0;
my $totalSize = 0;
foreach my $filename (@files) {
my $infofile = "$store/" . basename( $filename, ".tar.bz2" ) . ".info.txt";
my $info = $this->get_backup_info( $filename, $infofile );
$numFiles += @{ $info->{files} };
$totalSize += $info->{size};
$this->log_add_text( sprintf "%s %d\n", $infofile, $info->{size} );
}
$this->log_add_text( sprintf "total size %d\n", $totalSize );
printf "***restore_backup to folder: %s %d\n", $restore_to_folder, $totalSize
if $DEBUG > 3;
foreach my $filename (@files) {
printf "file: %s\n", $filename if $DEBUG > 3;
##########################################################################
my $infofile = "$store/" . basename( $filename, ".tar.bz2" ) . ".info.txt";
my $info = $this->get_backup_info( $filename, $infofile );
##########################################################################
$this->log_add_text( sprintf __("restoring backup from %s\n"), basename( $filename, ".tar.bz2" ) );
$this->restore_process_backup_label->set_text( sprintf __("%d / %d"), ++$backup, scalar @files );
Gtk2->main_iteration while Gtk2->events_pending;
my $touch =
$this->restore_extract_modifiction_time->get_active
? ""
: "--touch";
my $cmd = qq{ env LANG=en_GB.utf8 nice --adjustment=17 \\
env LANG=en_GB.utf8 tar --extract \\
--verbose \\
--directory "$restore_to_folder" \\
--file "$filename" \\
--preserve-permissions \\
$touch \\
--listed-incremental /dev/null \\
./ 2>&1 \\
};
printf "cmd %s\n", $cmd if $DEBUG > 4;
die $! unless $this->{tarpid} = open TAR, "-|", $cmd;
while (<TAR>) {
print $_ if $DEBUG > 0;
chomp;
$_ = $utf8->fromUTF8( "iso-8859-1", $_ );
my $path = "$restore_to_folder/$_";
if ( -d $path ) {
}
elsif ( -f $path ) {
$size = -s $path;
$elapsedSize += $size;
my @times =
map { strtime($_) } estimated_time( $this->restore_notification->{startTime}, $elapsedSize, $totalSize );
$this->restore_elapsed_time_label->set_text( sprintf "%s", $times[0] );
$this->restore_estimated_time_label->set_text( sprintf "%s / %s", @times[ 1, 2 ] );
$this->restore_file_label->set_text( sprintf "%s [%sB]", $_, format_bytes($size) );
$this->restore_progress( $elapsedSize / $totalSize );
Gtk2->main_iteration while Gtk2->events_pending;
}
}
close TAR;
printf "tar returned %s\n", $? if $DEBUG > 3;
if ($?) { # cancel / error ...
if ( $? == 512 ) {
#$this->log_add_text();
$? = FALSE;
}
else {
$this->log_add_text( sprintf __("Tar exited with status %s\n"), $? );
last;
}
}
else { # everything is fine
}
}
#store date
$this->store_restore_date( $files[$#files] );
#finish
$this->{tarpid} = 0;
$this->log_add_text( sprintf __("%s Restore done . . .\n"), strftime( "%F %X", localtime ) );
}
sub get_files_to_restore {
my ($this) = @_;
my @files = ();
my $selected = $this->tree_view->get_selection->get_selected;
my $file = $this->tree_view->get_model->get( $selected, COL_PATH );
my $folder = dirname $file;
#printf "***get_files_to_restore file: %s\n", $file if $DEBUG > 3;
#printf "***get_files_to_restore folder %s\n", $folder if $DEBUG > 3;
foreach my $filename ( grep { m/\.tar\.bz2$/ } get_files($folder) ) {
push @files, $filename;
last if $filename eq $file;
}
return @files;
}
sub on_cancel_restore {
my ($this) = @_;
printf "on_cancel_restore %s\n", $this->{tarpid} if $DEBUG > 3;
system "pkill", "-P", $this->{tarpid};
$this->restore_notification->{startTime} = 0;
$this->restore_progressbar->set_fraction(1);
$this->restore_progressbar->set_text( __ "Canceling Restore ..." );
$this->log_add_text( sprintf __("%s Restore canceled.\n"), strftime( "%F %X", localtime ) );
return 1;
}
sub restore_progress {
my ( $this, $fraction ) = @_;
$this->restore_progressbar->set_fraction($fraction);
$this->restore_progressbar->set_text( sprintf "%.2f %%", $fraction * 100 );
#$this->backup_notification->set_title( sprintf "Backup in progress %.2f %%", $fraction * 100 );
return;
}
# remove backup
#
sub on_backup_remove_button_clicked {
my ($this) = @_;
print "on_backup_remove_button_clicked $this\n" if $DEBUG > 3;
my $selected = $this->tree_view->get_selection->get_selected;
my ( $hdate, $time ) = $this->tree_view->get_model->get( $selected, COL_HDATE, COL_TIME );
$this->backup_remove_from_label->set_text("$hdate $time");
$this->window->set_sensitive(FALSE);
$this->remove_dialog->show;
return;
}
sub on_backup_remove_dialog_cancel {
my ( $this, $widget ) = @_;
print "on_restore_folder_dialog_cancel $this\n" if $DEBUG > 3;
$this->remove_dialog->hide;
$this->window->set_sensitive(TRUE);
return 1;
}
sub on_backup_remove_dialog_ok {
my ( $this, $widget ) = @_;
$this->remove_dialog->hide;
$this->remove_backup;
$this->fill_tree;
$this->window->set_sensitive(TRUE);
$this->backup_remove_button(FALSE);
return;
}
sub remove_backup {
my ($this) = @_;
my $restore_to_folder = abs_path $this->restore_folder->get_filename;
my @files = $this->get_files_to_restore;
my $selected = $this->tree_view->get_selection->get_selected;
my ( $hdate, $date, $time, $file ) = $this->tree_view->get_model->get( $selected, COL_HDATE, COL_DATE, COL_TIME, COL_PATH );
my $folder = dirname $file;
my $store = $this->get_store_folder;
$this->log_add_text( sprintf "\n%s\n", "*" x 42 );
$this->log_add_text( sprintf __("%s removing backup from %s %s\n"), strftime( "%F %X", localtime ), $hdate, $time );
{
my $archive = "$store/$date $time.tar.bz2";
my $tardat = "$store/$date $time.dat.bz2";
my $infofile = "$folder/$date $time.info.txt";
print "$archive\n";
print "$tardat\n";
print "$infofile\n";
unlink( $archive, $tardat, $infofile );
pop @files;
}
{
my $current_dat = "$store/$CurrentDat";
if (@files) {
my $date = basename( $files[$#files], ".tar.bz2" );
my $tardat = "$store/$date.dat.bz2";
# print "bzip2 -c -d '$tardat' >'$current_dat'\n";
system "bzip2 -c -d '$tardat' >'$current_dat'";
$this->store_restore_date( $files[$#files] );
}
else {
my $excludes = "$store/$ExcludesFile";
my $dateTxt = "$store/$DateTxt";
unlink $current_dat, $excludes, $dateTxt;
rmdir_p $store;
}
}
$this->log_add_text( sprintf __("%s remove done . . .\n"), strftime( "%F %X", localtime ) );
}
# schedule
#
sub on_schedule_enabled_button_toggled {
my ( $this, $widget ) = @_;
print "on_schedule_enabled_button_toggled $this\n" if $DEBUG > 3;
$this->time_hbox->set_sensitive( $widget->get_active );
$this->wdays_hbox->set_sensitive( $widget->get_active );
return;
}
#store
#
sub on_store_folder_changed {
my ($this) = @_;
printf "on_store_folder_changed %s\n", $this->get_main_store_folder
if $DEBUG > 3;
my $store = abs_path $this->get_main_store_folder;
#system "mkdir", "-p", $store;
$this->gconf( 'store-folder', abs_path $this->store_folder->get_filename );
( run in 0.581 second using v1.01-cache-2.11-cpan-39bf76dae61 )