]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - novaboot
server: Store target configuration in a variable, not in a shell function
[novaboot.git] / novaboot
index 893d7b87c59c2c12ad2edad08df516030fe29036..8e991dc742488173348f2edb33d40cbeb2a32e01 100755 (executable)
--- a/novaboot
+++ b/novaboot
@@ -121,7 +121,7 @@ my $explicit_target = $ENV{'NOVABOOT_TARGET'};
 GetOptions ("target|t=s" => \$explicit_target);
 
 # Variables for command line options
-my ($amt, @append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, @exiton, $exiton_timeout, @expect_raw, $final_eol, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $interaction, $iprelay, $iprelay_cmd, $iso_image, $interactive, $kernel_opt, $make, $man, $netif, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, $remote_cmd, $remote_expect, $remote_expect_silent, $remote_expect_timeout, $reset, $reset_cmd, $reset_send, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
+my ($amt, @append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, @exiton, $exiton_timeout, @expect_raw, $final_eol, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $interaction, $iprelay, $iprelay_cmd, $iso_image, $interactive, $kernel_opt, $make, $man, $netif, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, @remote_cmd, $remote_expect, $remote_expect_silent, $remote_expect_timeout, $reset, @reset_cmd, $reset_send, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
 
 my ($target_reset, $target_power_on, $target_power_off);
 
@@ -209,12 +209,12 @@ my %opt_spec = (
     "qemu|Q:s"              => \$qemu,
     "qemu-append=s"  => \$qemu_append,
     "qemu-flags|q=s" => \$qemu_flags_cmd,
-    "remote-cmd=s"   => \$remote_cmd,
+    "remote-cmd=s"   => sub { @remote_cmd = ($_[1]); },
     "remote-expect=s"=> \$remote_expect,
     "remote-expect-silent=s"=> sub { $remote_expect=$_[1]; $remote_expect_silent=1; },
     "remote-expect-timeout=i"=> \$remote_expect_timeout,
     "reset!"         => \$reset,
-    "reset-cmd=s"    => \$reset_cmd,
+    "reset-cmd=s"    => sub { @reset_cmd = ($_[1]); },
     "reset-send=s"   => \$reset_send,
     "rsync-flags=s"  => \$rsync_flags,
     "scons:s"       => \$scons,
@@ -237,17 +237,17 @@ sub handle_novaboot_server
 {
     my ($n, $val) = @_;
     my $xdg_runtime_dir = $ENV{XDG_RUNTIME_DIR} || '/var/run';
-    my $ssh_ctl_path = "${xdg_runtime_dir}/novaboot-%r@%h%p";
+    my $ssh_ctl_path = "${xdg_runtime_dir}/novaboot$$";
 
-    $remote_cmd = "ssh -tt -M -S '${ssh_ctl_path}' '${val}' console";
+    @remote_cmd = ('ssh', '-tt', '-M', '-S', $ssh_ctl_path, $val, 'console');
     $remote_expect = "novaboot-shell: Connected";
     $server = "$val:";
     $rsync_flags = "--rsh='ssh -S \'${ssh_ctl_path}\''";
     ($grub_prefix = $val) =~ s|(.*)@.*|\/$1\/| if index($val, '@') != -1;
-    $reset_cmd = "ssh -tt -S '${ssh_ctl_path}' '${val}' reset";
+    @reset_cmd = ('ssh', '-tt', '-S', $ssh_ctl_path, $val, 'reset');
 
-    $target_power_off = sub { system_verbose("ssh -tt -S '${ssh_ctl_path}' '${val}' off"); };
-    $target_power_on  = sub { system_verbose("ssh -tt -S '${ssh_ctl_path}' '${val}' on"); };
+    $target_power_off = sub { system_verbose('ssh', '-tt', '-S', $ssh_ctl_path, $val, 'off'); };
+    $target_power_on  = sub { system_verbose('ssh', '-tt', '-S', $ssh_ctl_path, $val, 'on'); };
 
     my $cmd = "ssh '${val}' get-config";
     print STDERR "novaboot: Running: $cmd\n";
@@ -323,7 +323,7 @@ if ($ider) {
     my %input_opts = ('--iprelay'    => \$iprelay,
                      '--iprelay-cmd'=> \$iprelay_cmd,
                      '--serial'     => \$serial,
-                     '--remote-cmd' => \$remote_cmd,
+                     '--remote-cmd' => (@remote_cmd ? \$remote_cmd[0] : undef),
                      '--amt'        => \$amt);
     my @opts = grep(defined(${$input_opts{$_}}) , keys %input_opts);
 
@@ -613,7 +613,11 @@ sub generate_pulsar_config($$$)
 
 sub shell_cmd_string(@)
 {
-    return join(' ', map((/^[-_=a-zA-Z0-9\/\.\+]+$/ ? "$_" : "'$_'"), @_));
+    if (scalar(@_) == 1) {
+       return $_[0];
+    } else {
+       return join(' ', map((/^[-_=a-zA-Z0-9\/\.\+]+$/ ? "$_" : "'$_'"), @_));
+    }
 }
 
 sub exec_verbose(@)
@@ -623,11 +627,10 @@ sub exec_verbose(@)
     exit(1); # should not be reached
 }
 
-sub system_verbose($)
+sub system_verbose
 {
-    my $cmd = shift;
-    print STDERR "novaboot: Running: $cmd\n";
-    my $ret = system($cmd);
+    print STDERR "novaboot: Running: ".shell_cmd_string(@_)."\n";
+    my $ret = system(@_);
     if ($ret & 0x007f) { die("Command terminated by a signal"); }
     if ($ret & 0xff00) {die("Command exit with non-zero exit code"); }
     if ($ret) { die("Command failure $ret"); }
@@ -657,6 +660,30 @@ if (exists $variables->{WVDESC}) {
 
 my $exp; # Expect object to communicate with the target over serial line
 
+sub kill_exp_on_signal() {
+    # Sometimes, under unclear circumstances (e.g. when running under
+    # both Jenkins and Robotframework), novaboot does not terminate
+    # console command when killed. The console is then blocked by the
+    # stale process forever. Theoretically, this should not happen,
+    # because when novaboot is killed, console command's controlling
+    # terminal sends SIGHUP to the console command and the command
+    # should terminate. It seems that at least SSH sometimes ignores
+    # HUP and does not terminate. The code below seems to work around
+    # that problem by killing the process immediately with SIGTERM,
+    # which is not ignored.
+
+    sub kill_console { kill TERM => $exp->pid if $exp->pid; die "Terminated by SIG$_[0]"; };
+
+    # For our Jenkins/Robotframework use case, it was sufficient to
+    # handle the TERM signal, but to be on the safe side, we also
+    # catch other signals.
+    $SIG{TERM} = \&kill_console;
+    $SIG{HUP} = \&kill_console;
+    $SIG{INT} = \&kill_console;
+    $SIG{QUIT} = \&kill_console;
+}
+
+
 my ($amt_user, $amt_password, $amt_host, $amt_port);
 
 if (defined $iprelay || defined $iprelay_cmd) {
@@ -679,6 +706,7 @@ if (defined $iprelay || defined $iprelay_cmd) {
        $exp = new Expect;
        $exp->raw_pty(1);
        $exp->spawn($iprelay_cmd);
+       kill_exp_on_signal();
     }
 
     while (1) {
@@ -747,9 +775,10 @@ elsif ($serial) {
     open($CONN, "+<", $serial) || die "open $serial: $!";
     $exp = Expect->init(\*$CONN);
 }
-elsif ($remote_cmd) {
-    print STDERR "novaboot: Running: $remote_cmd\n";
-    $exp = Expect->spawn($remote_cmd);
+elsif (@remote_cmd) {
+    print STDERR "novaboot: Running: ".shell_cmd_string(@remote_cmd)."\n";
+    $exp = Expect->spawn(@remote_cmd);
+    kill_exp_on_signal();
 }
 elsif (defined $amt) {
     require LWP::UserAgent;
@@ -866,9 +895,9 @@ if ($remote_expect) {
     }
 }
 
-if (defined $reset_cmd) {
+if (@reset_cmd) {
     $target_reset = sub {
-       system_verbose($reset_cmd);
+       system_verbose(@reset_cmd);
     };
 }
 
@@ -1282,6 +1311,10 @@ if ($interaction && defined $exp) {
     }
 }
 
+# When exp-spawned command ignores SIGHUP, Expect waits 5 seconds
+# before killing it. We kill it by SIGTERM immediately.
+kill TERM => $exp->pid if defined $exp && $exp->pid;
+
 ## Kill dhcpc or tftpd
 if (defined $dhcp_tftp || defined $tftp) {
     die("novaboot: This should kill servers on background\n");