summaryrefslogtreecommitdiffstats
path: root/draksnapshot-config
diff options
context:
space:
mode:
Diffstat (limited to 'draksnapshot-config')
-rwxr-xr-xdraksnapshot-config308
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)),
+ );
+}