#!/usr/bin/perl ################################################################################ # Backup Applet # # # # Copyright (C) 2008 Mandriva # # # # Thierry Vignaud # # # # 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 strict; use POSIX ":sys_wait_h"; use Config; use lib qw(/usr/lib/libDrakX); use standalone; # for explanations use interactive; use common; use run_program; use detect_devices; use dbus_object; use feature 'state'; use Gtk2::Pango; BEGIN { unshift @::textdomains, 'draksnapshot' } use mygtk2 qw(gtknew); #- do not import gtkadd which conflicts with ugtk2 version use ugtk2 qw(:all); use lib qw(/usr/lib/libDrakX/drakfirsttime); use Gtk2::Notify '-init', 'draksnapshot'; if (my $pid = is_running('draksnapshot-ap')) { die "draksnapshot-applet already running ($pid)\n"; } ugtk2::add_icon_path("/usr/share/draksnapshot/pixmaps/"); my $menu; my $localfile = "$ENV{HOME}/.draksnapshot"; my $insensitive_while_running_a_child; my %state = ( okay => { menu => [], do_not_use_bubble => 1, }, disk_found => { menu => [ 'configure' ], tt => [ N_("USB discs are available for backups") ] }, config_in_progress => { menu => [], do_not_use_bubble => 1, tt => [ N_("Configurator is currently running") ] }, ); my %actions = ( 'configure' => { name => N("Configure"), launch => \&configure } ); # create status icon: my $icon = Gtk2::StatusIcon->new; $icon->signal_connect(popup_menu => sub { my ($_icon, $button, $time) = @_; $menu and $menu->popup(undef, undef, undef, undef, $button, $time); }); $icon->signal_connect(activate => \&configure); my $hal_dn = 'org.freedesktop.Hal'; my $hal_manager = "$hal_dn.Manager"; my $manager_path = '/org/freedesktop/Hal/Manager'; my $dbus = dbus_object::system_bus(); my $do = dbus_object->new($dbus, $hal_dn, $manager_path, $hal_manager); my $con = $dbus->{connection}; my $old_time; $con->add_filter(sub { my ($_bus, $msg) = @_; return 0 if $msg->get_member ne 'DeviceAdded'; my $hal = $dbus->get_service($hal_dn); my $bool; eval { my $device = $hal->get_object(($msg->get_args_list)[0], "$hal_dn.Device"); $bool = $device->QueryCapability('volume') && !$device->GetProperty('volume.is_disc') && $device->GetProperty('volume.is_mounted'); }; if ($bool) { my $time = time(); go2State('disk_found') if 5 < abs($time - $old_time); $old_time = $time; } 0; }); $con->add_match("type='signal',interface='$hal_manager'"); my ($opt) = @ARGV; if ($opt eq '--force' || $opt eq '-f') { setAutoStart('TRUE') } shouldStart() or die "$localfile should be set to TRUE: please use --force or -f option to launch applet\n"; go2State('okay'); gtkflush(); Glib::Timeout->add(($::testing ? 1 : 30) * 1000, sub { firstCheck(); 0 }); $SIG{USR1} = 'IGNORE'; $SIG{USR2} = 'IGNORE'; $SIG{CHLD} = \&harvester; run_program::raw({ detach => 1 }, 'ionice', '-p', $$, '-n7'); $do->set_gtk2_watch; Gtk2->main; ugtk2::exit(0); my $config_pid; # Signal management sub harvester { my ($_signame, $_clean) = @_; my ($childpid, @pids); do { $childpid = waitpid(-1, &WNOHANG); if ($config_pid && $config_pid == $childpid) { undef $config_pid; # we should better check it has really been configured indeed. } push @pids, $childpid; WIFEXITED($?) and refresh_gui(1); } while $childpid > 0; return @pids; } sub fork_exec { my $pid = run_program::raw({ detach => 1 }, @_); refresh_gui(1); return $pid; } sub refresh_gui { my ($sens) = @_; #!$conf_launched and silentCheck(); $conf_launched = 0; my $w = $::main_window ? $::main_window->window : undef; $insensitive_while_running_a_child = !$sens; $sens ? gtkset_mousecursor_normal($w) : gtkset_mousecursor_wait($w); gtkflush(); } sub configure() { $config_pid = fork_exec('/usr/sbin/draksnapshot-config'); go2State('config_in_progress'); } sub firstCheck() { my @discs = grep { $_->{usb_bus} && detect_devices::may_be_a_hd($_) } detect_devices::getSCSI(); # find root fs: my $root; foreach (cat_("/etc/mtab"), cat_("/proc/mounts")) { my ($dev, $mntpoint) = split; if ($mntpoint eq '/') { $root = $dev; last; } } # we just want the basename of the root disc: $root =~ s!.*/!!; $root =~ s!\d*$!!; # exclude root fs from found USB discs: @discs = grep { $_ ne $root } map { $_->{device} } @discs; go2State(@discs ? 'disk_found' : 'okay'); } sub go2State { my $state = shift; $menu->destroy if $menu; $menu = setState($state); } sub shouldStart() { my %p = getVarsFromSh($localfile); to_bool($p{AUTOSTART} ne 'FALSE'); } sub setState { my ($state_type) = @_; my $checkme; my $arr = $state{$state_type}{menu}; state $pixbuf; if (!$pixbuf) { $pixbuf = gtkcreate_pixbuf('draksnapshot'); $icon->set_from_pixbuf($pixbuf); } $icon->set_tooltip(formatAlaTeX(translate($state{$state_type}{tt}[0]))); $icon->set_visible($state_type ne 'okay'); gtkflush(); # so that bubbles are displayed on right icon select(undef, undef, undef, 0.1); #- hackish :-( if ($state{$state_type}{tt}[0] && $icon->isa('Gtk2::StatusIcon') && !$state{$state_type}{do_not_use_bubble}) { my $bubble = Gtk2::Notify->new_with_status_icon(N("Info"), formatAlaTeX(translate($state{$state_type}{tt}[0])) . "\n", '/usr/share/icons/draksnapshot.png', $icon); $bubble->set_timeout(5000); eval { $bubble->show }; } my $menu = Gtk2::Menu->new; foreach (@$arr) { $menu->append(gtksignal_connect(gtkshow(Gtk2::MenuItem->new_with_label($actions{$_}{name})), activate => $actions{$_}{launch})); } $menu->append(gtkshow(Gtk2::SeparatorMenuItem->new)); $menu->append( gtksignal_connect( gtkshow(Gtk2::MenuItem->new_with_label(N("About..."))), activate => sub { my $ver = 1; # automatically set from spec file my $w = gtknew('AboutDialog', name => N("DrakSnapshot %s", $ver), copyright => N("Copyright (C) %s by Mandriva", '2008'), license => join('', cat_('/usr/share/common-licenses/GPL')), icon => '/usr/share/icons/mini/draksnapshot.png', comments => N("DrakSnapshot enables to backup your machine through periodic snapshots."), website => 'http://www.mandriva.com', website_label => N("Mandriva WebSite"), authors => 'Thierry Vignaud ', translator_credits => #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith ") N("_: Translator(s) name(s) & email(s)\n"), transient_for => $::main_window, modal => 1, position_policy => 'center-on-parent', ); $w->show_all; $w->run; return 1; })); $menu->append(gtksignal_connect(gtkset_active($checkme = Gtk2::CheckMenuItem->new_with_label(N("Always launch on startup")), shouldStart()), toggled => sub { setAutoStart(uc(bool2text($checkme->get_active))) })); $checkme->show; $menu->append(gtksignal_connect(gtkshow(Gtk2::MenuItem->new_with_label(N("Quit"))), activate => sub { mainQuit() })); $menu; } sub logIt { my $log = shift; log::explanations($log); } sub setVar { my ($file, $var, $st) = @_; my %s = getVarsFromSh($file); $s{$var} = $st; setVarsInSh($file, \%s); } sub setAutoStart { my ($state) = @_; if (-f $localfile) { setVar($localfile, 'AUTOSTART', $state); } else { output_p($localfile, "AUTOSTART=$state\n"); } } sub mainQuit() { Gtk2->main_quit; }