diff options
Diffstat (limited to 'draksnapshot-config')
-rwxr-xr-x | draksnapshot-config | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/draksnapshot-config b/draksnapshot-config new file mode 100755 index 0000000..18ca4ce --- /dev/null +++ b/draksnapshot-config @@ -0,0 +1,308 @@ +#!/usr/bin/perl +################################################################################ +# Backup Configuration Tool # +# # +# Copyright (C) 2008 Mandriva # +# # +# Thierry Vignaud <tvignaud at mandriva dot com> # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# 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 common; +use interactive; +use MDV::Snapshot::Common; +use mygtk2 qw(gtknew); #- do not import gtkadd which conflicts with ugtk2 version +use ugtk2 qw(:create :dialogs :helpers :wrappers); +use Gtk2::SimpleList; +use interactive; + +require_root_capability(); + +ugtk2::add_icon_path("/usr/share/draksnapshot/pixmaps/"); + +######### read config + +my @ordered_intervals = qw(hourly daily weekly monthly); + +my ($backup_list, $exclude_list) = map { + my $key = $_; + my $list = Gtk2::SimpleList->new('' => 'text'); + # properly size when not embedded: + $list->set_size_request($::isEmbedded ? -1 : 500, 100); + $list->set_headers_visible(0); + @{$list->{data}} = map { [ (split(/\s/, $_, 3))[1] ] } grep { /^$key\s/ } cat_($config_file); + $list; +} qw(backup exclude); + + +my %default_intervals = ( + map { + if (my ($type, $interval) = /^interval\s*(\S*)\s*(\S*)/) { + $type => $interval; + } + } grep { /^interval\s/ } cat_($config_file) +); + +# initialize commented out fields: +$default_intervals{$_} ||= undef foreach @ordered_intervals; + + +######### GUI + +$ugtk2::wm_icon = "draksnapshot-big"; +my $my_win = ugtk2->new(N("Backup snapshots configuration")); + +unless ($::isEmbedded) { + $my_win->{window}->set_border_width(5); + #$my_win->{window}->set_default_size(540,460); +} +$my_win->{window}->signal_connect(delete_event => \&quit); + + +### menus definition +# the menus are not shown +# but they provides shiny shortcut like C-q +my @menu_items = ( + { path => N("/_File"), item_type => '<Branch>' }, + { path => N("/File/_Quit"), accelerator => N("<control>Q"), callback => \&quit }, + { path => N("/_Help"), item_type => '<LastBranch>' }, + { path => N("/Help/_About...") } +); +my $_menubar = $::isEmbedded ? create_factory_menu($my_win->{rwindow}, @menu_items) : undef; +######### menus end + + +my %interval_titles = ( + 'hourly' => N("Hourly interval"), + 'daily' => N("Daily interval"), + 'weekly' => N("Weekly interval"), + 'monthly' => N("Monthly interval"), +); + + +my (%entries, $where); + +gtkadd($my_win->{window}, + gtknew('VBox', children => [ + if_(!$::isEmbedded, 0, Gtk2::Banner->new('draksnapshot-big', + N("Backup snapshots configuration"))), + 0, gtknew('Title1', label => N("Settings")), + 0, gtknew('HBox', spacing => 5, children => [ + 0, gtknew('Label_Left', text => N("Where to backup")), + 1, $where = + gtknew('Entry', text => $backup_directory), + 0, gtknew('Button', text => N("Browse"), + clicked => sub { + my $file_dlg; + $file_dlg = + gtknew('FileSelection', title => N("Path selection"), + ok_button => { + clicked => sub { + $where->set_text($file_dlg->get_filename); + $file_dlg->destroy; + }, + }, + cancel_button => { + clicked => sub { $file_dlg->destroy }, + }, + ); + $file_dlg->set_transient_for($my_win->{real_window}); + $file_dlg->set_modal(1); + $file_dlg->set_filename($where->get_text); + $file_dlg->show; + }, + ), + ]), + + 0, gtknew('Title2', label => N("Intervals")), + 0, gtknew('Table', col_spacings => 10, row_spacings => 5, homogeneous => 1, children => [ + map { + [ + gtknew('Label_Left', text => $interval_titles{$_}), + $entries{$_} = gtknew('Entry', text => $default_intervals{$_}) + ]; + } @ordered_intervals + ]), + 0, gtknew('Title2', label => N("Backup list")), + 1, format_list($backup_list, 1), + 0, gtknew('Title2', label => N("Exclude list")), + 1, format_list($exclude_list), + 0, gtknew('HButtonBox', layout => 'end', border_width => 5, spacing => 5, children_loose => [ + gtknew('Button', text => N("Apply"), clicked => \&save), + gtknew('Button', text => $::isEmbedded ? N("Cancel") : N("Close"), clicked => sub { save(); quit() }) + ]) + ]) + ); + +$my_win->{window}->show_all; +$my_win->main; + + +######### callbacks & helpers + +sub format_list { + my ($list, $o_check) = @_; + gtknew('HBox', + children => [ + 0, gtkset_size_request(Gtk2::Alignment->new(0, 0, 0, 0), 35, 1), + 1, gtknew('ScrolledWindow', child => $list), + 0, gtknew('VBox', border_width => 5, spacing => 5, + children_tight => [ + # FIXME: add "up" & "down" buttons? "edit" button? + gtknew('Button', text => N("Add"), clicked => sub { + add($list, $o_check); + }), + gtknew('Button', text => N("Remove"), clicked => sub { + my ($tree, $iter) = $list->get_selection->get_selected; + return if !$iter; + #my $removed_idx = $tree->get($iter, 5); + $tree->remove($iter); + #sensitive_buttons(0); + #$modified++; + }), + ]), + ], + ); +} + +sub quit() { ugtk2->exit(0) } + + +sub add { + my ($list, $check, $o_iter) = @_; + my $model = $list->get_model; + my $dlg = gtknew('Dialog', transient_for => $my_win->{real_window}, title => N("Add")); + my $browse = gtknew('Button', text => N("browse")); + my $file = gtknew('Entry', $o_iter ? (text => $model->get($o_iter, 1)) : ()); + + my $alrd_exsts = defined $o_iter; + + $browse->signal_connect(clicked => sub { + my $file_dlg = Gtk2::FileSelection->new(N("Path selection")); + $file_dlg->set_modal(1); + $file_dlg->set_transient_for($dlg); + $file_dlg->show; + $file_dlg->set_filename($file->get_text); + $file_dlg->cancel_button->signal_connect(clicked => sub { $file_dlg->destroy }); + $file_dlg->ok_button->signal_connect(clicked => sub { + $file->set_text($file_dlg->get_filename); + $file_dlg->destroy; + }); + }); + + gtkpack_($dlg->vbox, + 0, gtknew('Title2', label => N("Path")), + 0, gtknew('HBox', border_width => 18, children => [ + 1, $file, + 0, $browse + ]), + ); + + #$dlg->set_has_separator(0); + + gtkadd($dlg->action_area, + create_okcancel(my $w = + { + cancel_clicked => sub { $dlg->destroy }, + ok_clicked => sub { + my $path = $file->get_text; + if ($check && $path !~ m!^/!) { + err_dialog(N("Warning"), + N("The first character of the path must be a slash (\"/\"):\n\"%s\"", $path)); + return 1; + } + # create new item if needed (that is when adding a new one) at end of list + if (!$o_iter) { + push @{$list->{data}}, $path; + } + $dlg->destroy; + } + }, + ), + ); + + $w->{ok}->set_sensitive(!$model->get($o_iter, 0)) if $alrd_exsts; + $dlg->show_all; + +} + +sub save() { + save_keyword('interval', map { + my $val = $entries{$_}->get_text; + if_($val, join("\t", 'interval', $_, $val)); + } @ordered_intervals); + + save_keyword('backup', map { join("\t", 'backup', @$_[0], 'localhost/') } @{$backup_list->{data}}); + save_keyword('exclude', map { join("\t", 'exclude', @$_[0]) } @{$exclude_list->{data}}); + save_keyword('snapshot_root', join("\t", 'snapshot_root', $where->get_text)); + generate_cron_entry(); +} + +sub save_keyword { + my ($keyword, @values) = @_; + my ($removed, $done); + my $new_val = join('', map { "$_\n" }@values); + substInFile { + if (/^$keyword/) { + undef $_; + $removed++; + } + if ($removed == 1 && !$done) { + $done++; + $_ .= $new_val; + } + } $config_file; + # be safe: + append_to_file($config_file, $new_val) if !$done; +} + + + +sub generate_cron_entry() { + my %minutes = ( + 'hourly' => 0, + 'daily' => 50, + 'weekly' => 40, + 'monthly' => 30, + ); + + my $cron_file = "$::prefix/etc/cron.d/rsnapshot"; + + output($cron_file, + qq(# WARNING: This file is autogenerated from /etc/rsnapshot.conf. +# WARNING: Please alter /etc/rsnapshot.conf instead of $cron_file +# +# $cron_file: crontab fragment for rsnapshot + +), + (map { + if (my ($type, $interval) = /^interval\s*(\S*)\s*(\S*)/) { + # crontab: minute hour day_month month day_of_week user command + join("\t", + $minutes{$type}, + ($type eq 'hourly' ? "*/" . 24/$interval : 4), + ($type eq 'monthly' ? 1 : '*'), + '*' , + ($type eq 'weekly' ? 6 : '*'), + 'root', + "/usr/bin/rsnapshot $type" + ), "\n"; + } + } grep { /^interval\s/ } cat_($config_file)), + ); +} |