%CFG::targets = (
'qemu' => '--qemu',
"tud" => '--server=erwin.inf.tu-dresden.de:~sojka/boot/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --grub --grub-prefix=(nd)/tftpboot/sojka/novaboot --grub-preamble="timeout 0" --concat --iprelay=141.76.48.80:2324 --scriptmod=s/\\\\bhostserial\\\\b/hostserialpci/g',
- "novabox" => '--server=rtime.felk.cvut.cz:/srv/tftp/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --pulsar --iprelay=147.32.87.159:2324',
+ "novabox" => '--server=novabox@rtime.felk.cvut.cz:/srv/tftp/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --pulsar --iprelay-cmd="ssh -tt novabox@rtime.felk.cvut.cz nc localhost 2324"',
"localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog=" set root=\'(hd0,msdos1)\'"',
"ryu" => '--uboot --uboot-init="mw f0000b00 \${psc_cfg}; sleep 1" --uboot-addr kernel=800000 --uboot-addr ramdisk=b00000 --uboot-addr fdt=7f0000',
"ryuglab" => '--target ryu --server=pc-sojkam.felk.cvut.cz:/srv/tftp --remote-cmd="ssh -tt pc-sojkam.felk.cvut.cz \"sterm -d -s 115200 /dev/ttyUSB0\""',
my $explicit_target = $ENV{'NOVABOOT_TARGET'};
GetOptions ("target|t=s" => \$explicit_target);
-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, $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, $reset, $reset_cmd, $reset_send, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
+# 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 ($target_reset, $target_power_on, $target_power_off);
# Default values of certain command line options
%uboot_addr = (
$interaction = 1; # Perform target interaction by default
$final_eol = 1;
$netif = 'eth0';
+$remote_expect_timeout = 180;
my @expect_seen = ();
sub handle_expect
@expect_seen = ();
}
-my %opt_spec;
-%opt_spec = (
+# Options which can be safely specified on the server (via --ssh),
+# i.e. which cannot cause unwanted local code execution etc.
+my %opt_spec_safe = (
+ "grub|g:s" => \$grub_config,
+ "grub-preamble=s"=> \$grub_preamble,
+ "grub2-prolog=s" => \$grub2_prolog,
+ "grub2:s" => \$grub2_config,
+ "prefix|grub-prefix=s" => \$grub_prefix,
+ "pulsar-root=s" => \$pulsar_root,
+ "pulsar|p:s" => \$pulsar,
+ "uboot-addr=s" => \%uboot_addr,
+ "uboot-cmd=s" => \$uboot_cmd,
+ "uboot-init=s" => sub { push @uboot_init, { command => $_[1] }; },
+ "uboot:s" => \$uboot,
+ );
+
+my %opt_spec = (
+ %opt_spec_safe,
"amt=s" => \$amt,
"append|a=s" => \@append,
"bender|b" => \$bender,
"expect-raw=s" => sub { my ($n, $v) = @_; unshift(@expect_raw, eval($v)); },
"final-eol!" => \$final_eol,
"gen-only" => \$gen_only,
- "grub|g:s" => \$grub_config,
- "grub-preamble=s"=> \$grub_preamble,
- "prefix|grub-prefix=s" => \$grub_prefix,
- "grub2:s" => \$grub2_config,
- "grub2-prolog=s" => \$grub2_prolog,
"ider" => \$ider,
"interaction!" => \$interaction,
"iprelay=s" => \$iprelay,
+ "iprelay-cmd=s" => \$iprelay_cmd,
"iso:s" => \$iso_image,
"kernel|k=s" => \$kernel_opt,
"interactive|i" => \$interactive,
"no-file-gen" => \$no_file_gen,
"off" => \$off_opt,
"on" => \$on_opt,
- "pulsar|p:s" => \$pulsar,
- "pulsar-root=s" => \$pulsar_root,
"qemu|Q:s" => \$qemu,
"qemu-append=s" => \$qemu_append,
"qemu-flags|q=s" => \$qemu_flags_cmd,
"remote-cmd=s" => \$remote_cmd,
"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-send=s" => \$reset_send,
"sendcont=s" => \&handle_send,
"serial|s:s" => \$serial,
"server:s" => \$server,
+ "ssh:s" => \&handle_novaboot_server,
"strip-rom" => sub { $rom_prefix = ''; },
"stty=s" => \$stty,
"tftp" => \$tftp,
"tftp-port=i" => \$tftp_port,
- "uboot:s" => \$uboot,
"no-uboot" => sub { undef $uboot; },
- "uboot-addr=s" => \%uboot_addr,
- "uboot-cmd=s" => \$uboot_cmd,
- "uboot-init=s" => sub { push @uboot_init, { command => $_[1] }; },
"h" => \$help,
"help" => \$man,
);
+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";
+
+ $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/@.*// if index($val, '@') != -1;
+ $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"); };
+
+ my $cmd = "ssh '${val}' get-config";
+ print STDERR "novaboot: Running: $cmd\n";
+ my @target_config = qx($cmd < /dev/null);
+ if ($?) { die("Cannot get target configuration from the server"); }
+ printf "novaboot: Received configuration from the server:%s\n", (!@target_config) ? " empty" : "";
+ foreach (@target_config) { chomp; print " $_\n"; }
+
+ GetOptionsFromArray(\@target_config, %opt_spec_safe) or die("Error processing configuration from the server");
+ if (scalar @target_config) { die "Unsuported configuration received from the server: ".join(", ", @target_config); }
+}
+
# First process target options
{
my $t = defined($explicit_target) ? $explicit_target : $CFG::default_target;
$t = $explicit_target;
}
- (undef, my @args) = @ARGV;
- @args = (@target_expanded, @args);
+ my @args = (@target_expanded, @ARGV);
print STDERR "novaboot: Effective options: @args\n";
Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
{
my %input_opts = ('--iprelay' => \$iprelay,
+ '--iprelay-cmd'=> \$iprelay_cmd,
'--serial' => \$serial,
'--remote-cmd' => \$remote_cmd,
'--amt' => \$amt);
my $exp; # Expect object to communicate with the target over serial line
-my ($target_reset, $target_power_on, $target_power_off);
my ($amt_user, $amt_password, $amt_host, $amt_port);
-if (defined $iprelay) {
- my $IPRELAY;
- $iprelay =~ /([.0-9]+)(:([0-9]+))?/;
- my $addr = $1;
- my $port = $3 || 23;
- my $paddr = sockaddr_in($port, inet_aton($addr));
- my $proto = getprotobyname('tcp');
- socket($IPRELAY, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
- print STDERR "novaboot: Connecting to IP relay... ";
- connect($IPRELAY, $paddr) || die "connect: $!";
- print STDERR "done\n";
- $exp = Expect->init(\*$IPRELAY);
- $exp->log_stdout(1);
+if (defined $iprelay || defined $iprelay_cmd) {
+ if (defined $iprelay) {
+ my $IPRELAY;
+ $iprelay =~ /([.0-9]+)(:([0-9]+))?/;
+ my $addr = $1;
+ my $port = $3 || 23;
+ my $paddr = sockaddr_in($port, inet_aton($addr));
+ my $proto = getprotobyname('tcp');
+ socket($IPRELAY, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
+ print STDERR "novaboot: Connecting to IP relay... ";
+ connect($IPRELAY, $paddr) || die "connect: $!";
+ print STDERR "done\n";
+ $exp = Expect->init(\*$IPRELAY);
+ $exp->log_stdout(1);
+ }
+ if (defined $iprelay_cmd) {
+ print STDERR "novaboot: Running: $iprelay_cmd\n";
+ $exp = new Expect;
+ $exp->raw_pty(1);
+ $exp->spawn($iprelay_cmd);
+ }
while (1) {
print $exp "\xFF\xF6"; # AYT
my $connected = $exp->expect(20, # Timeout in seconds
'<iprelayd: connected>',
- '-re', '<WEB51 HW[^>]*>');
+ '-re', '<WEB51 HW[^>]*>')
+ || die "iprelay connection: " . ($! || "timeout");
last if $connected;
}
$exp->log_stdout(0);
print $exp relaycmd($relay, $onoff);
my $confirmed = $exp->expect(20, # Timeout in seconds
- relayconf($relay, $onoff));
+ relayconf($relay, $onoff))
+ || die "iprelay command: " . ($! || "timeout");
if (!$confirmed) {
if ($can_giveup) {
print("Relay confirmation timeout - ignoring\n");
my $cmd = "amtterm -u $amt_user -p $amt_password $amt_host $amt_port";
print STDERR "novaboot: Running: $cmd\n" =~ s/\Q$amt_password\E/???/r;
$exp = Expect->spawn($cmd);
- $exp->expect(10, "RUN_SOL") || die "Expect for 'RUN_SOL' timed out";
-
+ $exp->expect(10, "RUN_SOL") || die "Expect for 'RUN_SOL': " . ($! || "timeout");
}
if (defined $remote_expect_silent) {
$exp->log_stdout(0);
}
- $exp->expect(180, $remote_expect) || die "Expect for '$remote_expect' timed out";
+ $exp->expect($remote_expect_timeout >= 0 ? $remote_expect_timeout : undef,
+ $remote_expect) || die "Expect for '$remote_expect':" . ($! || "timeout");;
if (defined $remote_expect_silent) {
$exp->log_stdout($log);
print $exp->after() if $log;
$exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
$exp->expect(10,
[qr/##/, sub { exp_continue; }],
- $uboot_prompt) || die "Kernel load timeout";
+ $uboot_prompt) || die "Kernel load: " . ($! || "timeout");
}
if (defined $dtb) {
die "No '--uboot-addr fdt' given" unless $uboot_addr{fdt};
$exp->send("tftpboot $uboot_addr{fdt} $prefix$dtb\n");
$exp->expect(10,
[qr/##/, sub { exp_continue; }],
- $uboot_prompt) || die "Device tree load timeout";
+ $uboot_prompt) || die "Device tree load: " . ($! || "timeout");
+ } else {
+ $uboot_addr{fdt} = '';
}
if (defined $initrd) {
die "No '--uboot-addr ramdisk' given" unless $uboot_addr{ramdisk};
$exp->send("tftpboot $uboot_addr{ramdisk} $prefix$initrd\n");
$exp->expect(10,
[qr/##/, sub { exp_continue; }],
- $uboot_prompt) || die "Initrd load timeout";
+ $uboot_prompt) || die "Initrd load: " . ($! || "timeout");
} else {
$uboot_addr{ramdisk} = '-';
}
$kcmd //= '';
$exp->send("setenv bootargs $kcmd\n");
- $exp->expect(5, $uboot_prompt) || die "U-Boot prompt timeout";
+ $exp->expect(5, $uboot_prompt) || die "U-Boot prompt: " . ($! || "timeout");
}
$uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
$uboot_cmd =~ s/\$fdt_addr/$uboot_addr{fdt}/g;
$exp->send($uboot_cmd . "\n");
- $exp->expect(5, "\n") || die "U-Boot command timeout";
+ $exp->expect(5, "\n") || die "U-Boot command: " . ($! || "timeout");
}
}
print STDERR "novaboot: Serial line interaction (press $interrupt to interrupt)...\n";
$exp->log_stdout(1);
if (@exiton) {
- $exp->expect($exiton_timeout, @expect_raw, @exiton) || die("exiton timeout");
+ $exp->expect($exiton_timeout, @expect_raw, @exiton) || die("exiton: " . ($! || "timeout"));
} else {
my @inputs = ($exp);
my $infile = new IO::File;
to use from either B<NOVABOOT_TARGET> environment variable or
B<$default_target> configuration file variable.
+=item --ssh=I<user@hostname>
+
+Configures novaboot to control the target via C<novaboot-shell>
+running remotely via SSH.
+
+Using this option is the same as specifying B<--remote-cmd>,
+B<--remote-expect>, B<--server> B<--rsync-flags>, B<--prefix> and
+B<--reset-cmd> manually in a way compatible with V<novaboot-shell>.
+
+Currently, this in an initial experimental implementation. We plan to
+change/extend this feature soon!
+
=back
=head2 Script preprocessing phase
Note: This option is supposed to work with HWG-ER02a IP relays.
+=item --iprelay-cmd=I<command>
+
+Similar to B<--iprelay> but uses I<command> to talk to the iprelay
+rather than direct network connection.
+
=item -s, --serial[=device]
Target's serial line is connected to host's serial line (device). The
matched string is printed to stdout, so you may want to include line
end characters in the I<string> as well.
+=item --remote-expect-timeout=I<seconds>
+
+Timeout in seconds for B<--remote-expect> or
+B<--remote-expect-seconds>. When negative, waits forever. The default
+is 180 seconds.
+
=back
=head2 File deployment phase
=item --on, --off
Switch on/off the target machine and exit. The script (if any) is
-completely ignored. Currently, it works only with B<--iprelay> or
-B<--amt>.
+completely ignored. Currently, it works only with the following
+options: B<--iprelay>, B<--amt>, B<--ssh>.
=item -Q, --qemu[=I<qemu-binary>]
Interact with U-Boot bootloader to boot the thing described in the
novaboot script. I<prompt> specifies the U-Boot's prompt (default is
"=> ", other common prompts are "U-Boot> " or "U-Boot# ").
-Implementation of this option is currently tied to a particular board
-that we use. It may be subject to changes in the future!
=item --no-uboot
=item -i, --interactive
-Setup things for the interactive use of the target. Your terminal will be
-switched to raw mode. In raw mode, your system does not process input
-in any way (no echoing of entered characters, no interpretation
-special characters). This, among others, means that Ctrl-C is passed
-to the target and does no longer interrupt novaboot. Use "~~."
-sequence to exit novaboot.
+Setup things for the interactive use of the target. Your terminal will
+be switched to raw mode. In raw mode, your local terminal does not
+process input in any way (no echoing of entered characters, no
+interpretation of special characters). This, among others, means that
+Ctrl-C is passed to the target and does not interrupt novaboot. To
+exit from novaboot interactive mode type "~~.".
=item --no-interaction, --interaction