]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - novaboot
server: Refactor console setup to a separate function
[novaboot.git] / novaboot
index e8eb27d6451902d6987d753e5844221888e3102d..44d4a310daf7cb882f1eb06830ca511651ebf248 100755 (executable)
--- a/novaboot
+++ b/novaboot
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
 
 # 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
@@ -52,6 +52,7 @@ $CFG::hypervisor_params = "serial";
 $CFG::genisoimage = "genisoimage";
 $CFG::qemu = 'qemu-system-i386 -cpu coreduo -smp 2';
 $CFG::default_target = '';
+$CFG::netif = 'eth0';
 %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',
@@ -129,7 +130,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, $uboot_stop_key);
 
 my ($target_reset, $target_power_on, $target_power_off);
 
@@ -141,11 +142,11 @@ my ($target_reset, $target_power_on, $target_power_off);
     );
 $rsync_flags = '';
 $rom_prefix = 'rom://';
-$stty = 'raw -crtscts -onlcr 115200';
+$stty = 'raw -crtscts -onlcr -echo 115200';
 $reset = 1;                    # Reset target by default
 $interaction = 1;              # Perform target interaction by default
 $final_eol = 1;
-$netif = 'eth0';
+$netif = $CFG::netif;
 $remote_expect_timeout = -1;
 
 my @expect_seen = ();
@@ -176,8 +177,12 @@ my %opt_spec_safe = (
     "prefix|grub-prefix=s" => \$grub_prefix,
     "pulsar-root=s"  => \$pulsar_root,
     "pulsar|p:s"     => \$pulsar,
+    "remote-expect=s"=> \$remote_expect,
+    "remote-expect-silent=s"=> sub { $remote_expect=$_[1]; $remote_expect_silent=1; },
+    "remote-expect-timeout=i"=> \$remote_expect_timeout,
     "uboot-addr=s"   => \%uboot_addr,
     "uboot-cmd=s"    => \$uboot_cmd,
+    "uboot-stop-key=s" => \$uboot_stop_key,
     "uboot-init=s"   => sub { push @uboot_init, { command => $_[1] }; },
     "uboot:s"       => \$uboot,
     );
@@ -218,9 +223,6 @@ my %opt_spec = (
     "qemu-append=s"  => \$qemu_append,
     "qemu-flags|q=s" => \$qemu_flags_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"    => sub { @reset_cmd = ($_[1]); },
     "reset-send=s"   => \$reset_send,
@@ -994,7 +996,7 @@ foreach my $script (@scripts) {
            $hostname = "";
        }
        my $files = join(" ", map({ ($file) = m/([^ ]*)/; $file; } ( @$modules, @bootloader_configs, @$copy)));
-       map({ my $file = (split)[0]; die "$file: $!" if ! -f $file; } @$modules);
+       map({ my $file = (split)[0]; die "Not a file: $file: $!" if ! -e $file || -d $file; } @$modules);
        my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb';
        my $progress = $istty ? "--progress" : "";
        if ($files) {
@@ -1024,11 +1026,17 @@ if (defined $iso_image) {
     if (-f '/usr/lib/ISOLINUX/isolinux.bin') {
        # Newer ISOLINUX version
        @files = qw(/usr/lib/ISOLINUX/isolinux.bin /usr/lib/syslinux/modules/bios/mboot.c32 /usr/lib/syslinux/modules/bios/libcom32.c32 /usr/lib/syslinux/modules/bios/menu.c32 /usr/lib/syslinux/modules/bios/ldlinux.c32);
-    } else {
+    } elsif (-f '/usr/lib/syslinux/isolinux.bin') {
        # Older ISOLINUX version
        @files = qw(/usr/lib/syslinux/isolinux.bin /usr/lib/syslinux/mboot.c32 /usr/lib/syslinux/menu.c32);
+    } else {
+       # NixOS and maybe others
+       my $syslinux = `which syslinux` || die "Cannot find syslinux";
+       chomp $syslinux;
+       $syslinux =~ s,/bin/syslinux$,,;
+       @files = ("$syslinux/share/syslinux/isolinux.bin", "$syslinux/share/syslinux/mboot.c32", "$syslinux/share/syslinux/libcom32.c32", "$syslinux/share/syslinux/menu.c32", "$syslinux/share/syslinux/ldlinux.c32");
     }
-    system_verbose("cp @files isolinux");
+    system_verbose("cp @files isolinux && chmod +w isolinux/*");
     open(my $fh, ">isolinux/isolinux.cfg");
     if ($#scripts) {
        print $fh "TIMEOUT 50\n";
@@ -1170,7 +1178,7 @@ if (defined $ider) {
 ### Reset target (IP relay, AMT, ...)
 
 if (defined $target_reset && $reset) {
-    print STDERR "novaboot: Reseting the test box... ";
+    print STDERR "novaboot: Resetting the test box... ";
     &$target_reset();
     print STDERR "done\n";
     if (defined $exp) {
@@ -1197,7 +1205,9 @@ if (defined $uboot) {
     $exp->log_stdout(1);
     #$exp->exp_internal(1);
     $exp->expect(20,
-                [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
+                [qr/Hit any key to stop autoboot:/, sub {
+                    $exp->send($uboot_stop_key // "\n");
+                    exp_continue; }],
                 $uboot_prompt) || die "No U-Boot prompt deteceted";
     foreach my $cmdspec (@uboot_init) {
        my ($cmd, $timeout);
@@ -1243,7 +1253,7 @@ if (defined $uboot) {
        @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
        my $initrd = shift @$modules;
 
-       if (defined $kbin) {
+       if (defined $kbin && $kbin ne '/dev/null') {
            die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel};
            $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
            $exp->expect(15,
@@ -1295,7 +1305,7 @@ if ($interaction && defined $exp) {
     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, @exiton, @expect_raw) || die("exiton: " . ($! || "timeout"));
     } else {
        my @inputs = ($exp);
        my $infile = new IO::File;
@@ -1376,6 +1386,40 @@ having per-system, per-user or per-project configurations.
 Configuration file syntax is described in section L</"CONFIGURATION
 FILES">.
 
+Novaboot newcomers may be confused by a large number of configuration
+options. Understanding all these options is not always needed,
+depending on the used setup. The L<figure from the doc directory
+|https://github.com/wentasah/novaboot/blob/master/doc/typical-setups.svg>
+shows different setups that vary in how much effort is needed
+configure novaboot for them. The setups are:
+
+=over 3
+
+=item A: Laptop and target device only
+
+This requires to configure everything on the laptop side, including a
+serial line connection (L</--serial>, L</--remote-cmd>, ...), power
+on/off/reset commands (L</--reset-cmd>, ...), TFTP server
+(L</--server>, L</--prefix>...), device IP addresses, etc.
+
+=item B: Laptop, target device and external TFTP server
+
+Like the previous setup, but the TFTP (and maybe DHCP) configuration
+is handled by a server. Novaboot users need to understand where to
+copy their files to the TFTP server (L</--server>) and which IP
+addresses their target will get, but do not need to configure the
+servers themselves.
+
+=item C: Novaboot server running novaboot-shell
+
+With this setup, the configuration is done on the server. Users only
+need to know the SSH account (L</--ssh>) used to communicate between
+novaboot and novaboot server. The server is implemented as a
+restricted shell (L<novaboot-shell(1)>) on the server. No need to give
+full shell access to novaboot users on the server.
+
+=back
+
 =head2 Simple examples of using C<novaboot>:
 
 To boot Linux (files F<bzImage> and F<rootfs.cpio> in current
@@ -1449,9 +1493,9 @@ I<script2> configurations.
 
 =back
 
-=head1 PHASES AND OPTIONS
+=head1 OPTIONS AND PHASES
 
-Novaboot performs its work in several phases. Command like options
+Novaboot performs its work in several phases. Command line options
 described bellow influence the execution of each phase or allow their
 skipping. The list of phases (in the execution order) is as follows.
 
@@ -1779,10 +1823,23 @@ example C<ssh server 'cu -l /dev/ttyS0'>.
 
 =item --remote-expect=I<string>
 
-Wait for reception of I<string> after establishing the remote
-connection. This option is needed when novaboot should wait for
-confirmation before deploying files to the target, e.g. to not
-overwrite other user's files when they are using the target.
+Wait for reception of I<string> after establishing the remote serial
+line connection. Novaboot assumes that after establishing the serial
+line connection, the user running novaboot has exclusive access to the
+target. If establishing of the serial line connection happens
+asynchronously (e.g. running a command remotely via SSH), we need this
+option to wait until the exclusive access is confirmed by the remote
+side.
+
+Depending on target configuration, this option can solve two practical
+problems: 1) Overwriting of files deployed by another user currently
+using the target. 2) Resetting the target board before serial line
+connection is established and thus missing bootloader interaction.
+
+Example of usage with the L<sterm
+tool|https://rtime.felk.cvut.cz/gitweb/sojka/sterm.git>:
+
+  --remote-cmd='ssh -tt example.com sterm -v /dev/ttyUSB0' --remote-expect='sterm: Connected'
 
 =item --remote-expect-silent=I<string>
 
@@ -1839,10 +1896,11 @@ Port to run the TFTP server on. Implies B<--tftp>.
 
 =item --netif=I<network interface>
 
-Network interface used to deploy files to the target. The default value is
-I<eth0>. This option influences the configuration of the DHCP server started
-by B<--dhcp-tftp> and the value that B<$NB_MYIP> get replaced with during
-U-Boot conversation.
+Network interface used to deploy files to the target. This option
+influences the configuration of the DHCP server started by
+B<--dhcp-tftp> and the value that B<$NB_MYIP> get replaced with during
+U-Boot conversation. The default value is C<$netif> variable from
+configuration files, which defaults to I<eth0>.
 
 =item --iso[=filename]
 
@@ -1940,6 +1998,12 @@ novaboot script. I<prompt> specifies the U-Boot's prompt (default is
 
 Disable U-Boot interaction previously enabled with B<--uboot>.
 
+=item --uboot-stop-key=I<key>
+
+Character, which is sent as a response to U-Boot's "Hit any key to
+stop autoboot" message. The default value is newline, but some devices
+(e.g. TP-Link TD-W8970) require a specific key to be pressed.
+
 =item --uboot-init
 
 Command(s) to send the U-Boot bootloader before loading the images and
@@ -2001,7 +2065,7 @@ The same as --exiton -re --exiton I<regex>.
 =item --exiton-timeout=I<seconds>
 
 By default B<--exiton> waits for the string match forever. When this
-option is specified, "exiton" timeouts after the specifies the number of
+option is specified, "exiton" timeouts after the specified number of
 seconds and novaboot returns non-zero exit code.
 
 =item -i, --interactive
@@ -2086,9 +2150,16 @@ word after C<load> is a file name (relative to the build directory
 (see B<--build-dir>) of the module to load and the remaining words are
 passed to it as the command line parameters.
 
+When booting Linux, the first C<load> line usually refers to the
+kernel image and its command line parameters (unless you use some
+special pre-loader). Other C<load> lines may refer to an initramfs
+image and/or a device tree blob. Their order is not important, as the
+device tree is recognized as the file name ending with C<.dtb>.
+
 When the C<load> line ends with "<<WORD" then the subsequent lines
 until the line containing solely WORD are copied literally to the file
-named on that line. This is similar to shell's heredoc feature.
+named on that line. This is similar to the heredoc feature of UNIX
+shells.
 
 When the C<load> line ends with "< CMD" then command CMD is executed
 with F</bin/sh> and its standard output is stored in the file named on
@@ -2296,6 +2367,11 @@ Default target (see below) to use when no target is explicitly
 specified with the B<--target> command line option or
 B<NOVABOOT_TARGET> environment variable.
 
+=item $netif
+
+Default value for the B<--netif> option. If not specified, it defaults
+to I<eth0>.
+
 =item %targets
 
 Hash of target definitions to be used with the B<--target> option. The