-msva-perl (0.6~pre) unstable; urgency=low
+msva-perl (0.6~pre) upstream;
- * add new element to JSON syntax allowing request to override
+ * Add new element to JSON syntax allowing request to override
keyserver_policy (closes MS #2542)
- * do not kill off child handling processes on HUP -- let them finish
+ * Do not kill off child handling processes on HUP -- let them finish
their queries.
+ * Refactor logging code
+ * If we have Gtk2, Linux::Inotify2, and AnyEvent, we should monitor for
+ updates and prompt the user when we notice one. (closes MS #2540)
- -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 14 Oct 2010 16:30:54 -0400
-
-msva-perl (0.5) unstable; urgency=low
+ -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Tue, 26 Oct 2010 22:49:40 -0400
+
+msva-perl (0.5) upstream;
* If ${MSVA_KEYSERVER} is unset or blank, default to using keyserver
from ${GNUPGHOME}/gpg.conf if that file exists. (addresses MS #2080)
use Config::General;
use Crypt::Monkeysphere::MSVA::MarginalUI;
use Crypt::Monkeysphere::MSVA::Logger;
+ use Crypt::Monkeysphere::MSVA::Monitor;
use JSON;
use POSIX qw(strftime);
my $self = shift;
my $cgi = shift;
+ # This is part of a spawned child process. We don't want the
+ # child process to destroy the update monitor when it terminates.
+ $self->{updatemonitor}->forget();
my $clientinfo = get_client_info(select);
my $clientuid = $clientinfo->{uid};
msvalog('debug', "Subprocess %d terminated.\n", $pid);
- if (exists $self->{child_pid} &&
- ($self->{child_pid} == 0 ||
- $self->{child_pid} == $pid)) {
+ if (exists $self->{updatemonitor} &&
+ defined $self->{updatemonitor}->getchildpid() &&
+ $self->{updatemonitor}->getchildpid() == $pid) {
+ my $exitstatus = POSIX::WEXITSTATUS($?);
+ msvalog('verbose', "Update monitoring process (%d) terminated with code %d.\n", $pid, $exitstatus);
+ if (0 == $exitstatus) {
+ msvalog('info', "Reloading MSVA due to update request.\n");
+ # sending self a SIGHUP:
+ kill(1, $$);
+ } else {
+ msvalog('error', "Update monitoring process (%d) died unexpectedly with code %d.\nNo longer monitoring for updates; please send HUP manually.\n", $pid, $exitstatus);
+ # it died for some other weird reason; should we respawn it?
+
+ # FIXME: i'm worried that re-spawning would create a
+ # potentially abusive loop, if there are legit, repeatable
+ # reasons for the failure.
+
+# $self->{updatemonitor}->spawn();
+
+ # instead, we'll just avoid trying to kill the next process with this PID:
+ $self->{updatemonitor}->forget();
+ }
+ } elsif (exists $self->{child_pid} &&
+ ($self->{child_pid} == 0 ||
+ $self->{child_pid} == $pid)) {
my $exitstatus = POSIX::WEXITSTATUS($?);
msvalog('verbose', "Subprocess %d terminated; exiting %d.\n", $pid, $exitstatus);
$server->set_exit_status($exitstatus);
if ((exists $ENV{MSVA_CHILD_PID}) && ($ENV{MSVA_CHILD_PID} ne '')) {
# this is most likely a re-exec.
- msvalog('info', "This appears to be a re-exec, monitoring child pid %d\n", $ENV{MSVA_CHILD_PID});
+ msvalog('info', "This appears to be a re-exec, continuing with child pid %d\n", $ENV{MSVA_CHILD_PID});
$self->{child_pid} = $ENV{MSVA_CHILD_PID} + 0;
} elsif ($#ARGV >= 0) {
$self->{child_pid} = 0; # indicate that we are planning to fork.
# ssh-agent. maybe avoid backgrounding by setting
# MSVA_NO_BACKGROUND.
};
+
+ $self->{updatemonitor} = Crypt::Monkeysphere::MSVA::Monitor->new($logger);
}
sub extracerts {
--- /dev/null
+#----------------------------------------------------------------------
+# Monkeysphere Validation Agent, Perl version
+# Marginal User Interface for reasonable prompting
+# Copyright © 2010 Daniel Kahn Gillmor <dkg@fifthhorseman.net>,
+# Matthew James Goins <mjgoins@openflows.com>,
+# Jameson Graef Rollins <jrollins@finestructure.net>,
+# Elliot Winard <enw@caveteen.com>
+#
+# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
+#
+#----------------------------------------------------------------------
+
+{ package Crypt::Monkeysphere::MSVA::Monitor;
+
+ use strict;
+ use warnings;
+
+ sub createwindow {
+ my $self = shift;
+
+ require Gtk2;
+ Gtk2->init();
+ $self->{dialog} = Gtk2::Dialog->new("Monkeysphere Validation Agent updated!",
+ undef,
+ [],
+ 'gtk-no' => 'cancel',
+ 'gtk-yes' => 'ok');
+
+ my $icon_file = '/usr/share/pixmaps/monkeysphere-icon.png';
+
+ $self->{dialog}->set_default_icon_from_file($icon_file)
+ if (-r $icon_file);
+ $self->{dialog}->set_default_response('ok');
+ my $label = Gtk2::Label->new("Some components of the running Monkeysphere
+Validation Agent have been updated.
+
+Would you like to restart the validation agent?");
+ $label->show();
+ $self->{dialog}->get_content_area()->add($label);
+ $self->{dialog}->signal_connect(response => sub { my ($dialog,$resp) = @_; $self->button_clicked($resp); });
+ $self->{dialog}->signal_connect(delete_event => sub { $self->button_clicked('cancel'); return 1; });
+ }
+
+ sub button_clicked {
+ my $self = shift;
+ my $resp = shift;
+ if ($resp eq 'ok') {
+ # if the user wants to restart the validation agent, we should terminate
+ # so that our parent gets a SIGCHLD.
+ exit 0;
+ } else {
+ $self->{dialog}->hide();
+ }
+ }
+
+ sub prompt {
+ my $self = shift;
+ $self->{dialog}->show();
+ }
+
+ sub spawn {
+ my $self = shift;
+ if (! Module::Load::Conditional::can_load('modules' => { 'Gtk2' => undef,
+ 'AnyEvent' => undef,
+ 'Linux::Inotify2' => undef,
+ })) {
+ $self->{logger}->log('info', "Not spawning a monitoring process; issue 'kill -s HUP %d' to restart after upgrades.\nInstall Perl modules Gtk2, AnyEvent, and Linux::Inotify2 for automated restarts on upgrades.\n", $$);
+ return;
+ }
+ my $fork = fork();
+ if (! defined $fork) {
+ $self->{logger}->log('error', "Failed to spawn monitoring process\n");
+ return;
+ }
+ if ($fork) {
+ $self->{monitorpid} = $fork;
+ $self->{logger}->log('debug', "spawned monitoring process pid %d\n", $self->{monitorpid});
+ return;
+ } else {
+ $self->childmain();
+ }
+ }
+
+ sub childmain {
+ my $self = shift;
+
+ $self->{files} = [ $0, values(%INC) ];
+ $self->{logger}->log('debug3', "setting up monitoring on these files:\n%s\n", join("\n", @{$self->{files}}));
+
+ # close all filedescriptors except for std{in,out,err}:
+ # see http://markmail.org/message/mlbnvfa7ds25az2u
+ close $_ for map { /^(?:ARGV|std(?:err|out|in)|STD(?:ERR|OUT|IN))$/ ? () : *{$::{$_}}{IO} || () } keys %::;
+
+ $self->createwindow();
+
+ require Linux::Inotify2;
+
+ $self->{inotify} = new Linux::Inotify2
+ or die "unable to create new inotify object: $!";
+
+ my $flags = 0xc06;
+ # FIXME: couldn't figure out how to get these to work in "strict subs" mode:
+ # my $flags = Linux::Inotify2::IN_MODIFY |
+ # Linux::Inotify2::IN_ATTRIB |
+ # Linux::Inotify2::IN_DELETE_SELF |
+ # Linux::Inotify2::IN_MOVE_SELF;
+
+ foreach my $file (@{$self->{files}}) {
+ $self->{inotify}->watch($file,
+ $flags,
+ sub {
+ $self->prompt();
+ });
+ }
+
+ require AnyEvent;
+ my $inotify_w = AnyEvent->io (
+ fh => $self->{inotify}->fileno,
+ poll => 'r',
+ cb => sub { $self->changed },
+ );
+ my $w = AnyEvent->signal(signal => 'TERM', cb => sub { exit 1; });
+
+ Gtk2->main();
+ $self->{logger}->log('error', "Got to the end of the monitor process somehow\n");
+ # if we get here, we want to terminate with non-zero
+ exit 1;
+ }
+
+
+ sub changed {
+ my $self = shift;
+
+ $self->{logger}->log('debug', "changed!\n");
+ $self->{inotify}->poll();
+ }
+
+ # forget about cleaning up the monitoring child (e.g. we only want
+ # the parent process to know about this)
+ sub forget {
+ my $self = shift;
+ undef $self->{monitorpid};
+ }
+
+ sub getchildpid {
+ my $self = shift;
+ return $self->{monitorpid};
+ }
+
+ sub DESTROY {
+ my $self = shift;
+ if (defined $self->{monitorpid}) {
+ # SIGTERM is 15:
+ kill(15, $self->{monitorpid});
+ waitpid($self->{monitorpid}, 0);
+ undef($self->{monitorpid});
+ }
+ }
+
+ sub new {
+ my $class = shift;
+ my $logger = shift;
+
+ my $self = { monitorpid => undef,
+ logger => $logger,
+ };
+
+ bless ($self, $class);
+
+ $self->spawn();
+ return $self;
+ }
+
+ 1;
+}