# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+## Initialization
+
use strict;
use warnings;
use warnings (exists $ENV{NOVABOOT_TEST} ? (FATAL => 'all') : ());
use Socket;
use FileHandle;
use IPC::Open2;
-use POSIX qw(:errno_h);
+use POSIX qw(:errno_h sysconf);
use Cwd qw(getcwd abs_path);
use Expect;
$CFG::hypervisor_params = "serial";
$CFG::genisoimage = "genisoimage";
$CFG::qemu = 'qemu-system-i386 -cpu coreduo -smp 2';
-$CFG::default_target = 'qemu';
+$CFG::default_target = '';
%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',
"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\""',
"ryulocal" => '--target ryu --dhcp-tftp --serial --reset-cmd="if which dtrrts; then dtrrts $NB_SERIAL 0 1; sleep 0.1; dtrrts $NB_SERIAL 1 1; fi"',
);
-chomp(my $nproc = `nproc`);
-$CFG::scons = "scons -j$nproc";
-$CFG::make = "make -j$nproc";
+
+{
+ my %const;
+ $const{linux}->{_SC_NPROCESSORS_CONF} = 83;
+ my $nproc = sysconf($const{$^O}->{_SC_NPROCESSORS_CONF});
+
+ $CFG::scons = "scons -j$nproc";
+ $CFG::make = "make -j$nproc";
+}
my $builddir;
## Command line handling
-my $explicit_target;
+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, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $iprelay, $iso_image, $interactive, $kernel_opt, $make, $man, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, $remote_cmd, $remote_expect, $reset, $reset_cmd, $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, $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, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
+# Default values of certain command line options
%uboot_addr = (
'kernel' => '${kernel_addr_r}',
'ramdisk' => '${ramdisk_addr_r}',
'fdt' => '${fdt_addr_r}',
);
-
$rsync_flags = '';
$rom_prefix = 'rom://';
$stty = 'raw -crtscts -onlcr 115200';
$reset = 1; # Reset target by default
+$interaction = 1; # Perform target interaction by default
+$final_eol = 1;
+$netif = 'eth0';
my @expect_seen = ();
sub handle_expect
"expect=s" => \&handle_expect,
"expect-re=s" => \&handle_expect,
"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,
"grub2:s" => \$grub2_config,
"grub2-prolog=s" => \$grub2_prolog,
"ider" => \$ider,
+ "interaction!" => \$interaction,
"iprelay=s" => \$iprelay,
"iso:s" => \$iso_image,
"kernel|k=s" => \$kernel_opt,
"interactive|i" => \$interactive,
"name=s" => \$config_name_opt,
"make|m:s" => \$make,
+ "netif=s" => \$netif,
"no-file-gen" => \$no_file_gen,
"off" => \$off_opt,
"on" => \$on_opt,
"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; },
"reset!" => \$reset,
"reset-cmd=s" => \$reset_cmd,
"rsync-flags=s" => \$rsync_flags,
"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" => \@uboot_init,
open(my $f, '>', $fn) || die("$fn: $!");
map { s|\brom://([^ ]*)|$rom_prefix$base$1|g; print $f "$_"; } @{$config};
close($f);
- print "novaboot: Created $fn\n";
+ print STDERR "novaboot: Created $fn\n";
} elsif (exists $$g{command} && ! $no_file_gen) {
$ENV{SRCDIR} = dirname(File::Spec->rel2abs( $filename, $invocation_dir ));
if (exists $$g{filename}) {
sub exec_verbose(@)
{
- print "novaboot: Running: ".shell_cmd_string(@_)."\n";
+ print STDERR "novaboot: Running: ".shell_cmd_string(@_)."\n";
exec(@_);
exit(1); # should not be reached
}
sub system_verbose($)
{
my $cmd = shift;
- print "novaboot: Running: $cmd\n";
+ print STDERR "novaboot: Running: $cmd\n";
my $ret = system($cmd);
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"); }
}
+sub trim($) {
+ my ($str) = @_;
+ $str =~ s/^\s+|\s+$//g;
+ return $str
+}
+
## WvTest headline
if (exists $variables->{WVDESC}) {
- print "Testing \"$variables->{WVDESC}\" in $last_fn:\n";
+ print STDERR "Testing \"$variables->{WVDESC}\" in $last_fn:\n";
} elsif ($last_fn =~ /\.wv$/) {
- print "Testing \"all\" in $last_fn:\n";
+ print STDERR "Testing \"all\" in $last_fn:\n";
}
## Connect to the target and check whether it is not occupied
my $paddr = sockaddr_in($port, inet_aton($addr));
my $proto = getprotobyname('tcp');
socket($IPRELAY, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
- print "novaboot: Connecting to IP relay... ";
+ print STDERR "novaboot: Connecting to IP relay... ";
connect($IPRELAY, $paddr) || die "connect: $!";
- print "done\n";
+ print STDERR "done\n";
$exp = Expect->init(\*$IPRELAY);
$exp->log_stdout(1);
$exp = Expect->init(\*$CONN);
}
elsif ($remote_cmd) {
- print "novaboot: Running: $remote_cmd\n";
+ print STDERR "novaboot: Running: $remote_cmd\n";
$exp = Expect->spawn($remote_cmd);
}
elsif (defined $amt) {
};
my $cmd = "amtterm -u $amt_user -p $amt_password $amt_host $amt_port";
- print "novaboot: Running: $cmd\n" =~ s/\Q$amt_password\E/???/r;
+ 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";
if ($remote_expect) {
$exp || die("No serial line connection");
+ my $log = $exp->log_stdout;
+ if (defined $remote_expect_silent) {
+ $exp->log_stdout(0);
+ }
$exp->expect(180, $remote_expect) || die "Expect for '$remote_expect' timed out";
+ if (defined $remote_expect_silent) {
+ $exp->log_stdout($log);
+ print $exp->after() if $log;
+ }
}
if (defined $reset_cmd) {
exit;
}
if (defined $off_opt && defined $target_power_off) {
- print "novaboot: Switching the target off...\n";
+ print STDERR "novaboot: Switching the target off...\n";
&$target_power_off();
exit;
}
$builddir ||= dirname(File::Spec->rel2abs( ${$scripts[0]}{filename})) if scalar @scripts;
if (defined $builddir) {
chdir($builddir) or die "Can't change directory to $builddir: $!";
- print "novaboot: Entering directory `$builddir'\n";
+ print STDERR "novaboot: Entering directory `$builddir'\n";
} else {
$builddir = $invocation_dir;
}
if (exists $variables->{BUILDDIR}) {
$builddir = File::Spec->rel2abs($variables->{BUILDDIR});
chdir($builddir) or die "Can't change directory to $builddir: $!";
- print "novaboot: Entering directory `$builddir'\n";
+ print STDERR "novaboot: Entering directory `$builddir'\n";
}
if ($grub_prefix) {
map({ my $file = (split)[0]; die "$file: $!" if ! -f $file; } @$modules);
my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb';
my $progress = $istty ? "--progress" : "";
- system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
- if ($server =~ m|/\$NAME$| && $concat) {
- my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
- system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
+ if ($files) {
+ system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
+ if ($server =~ m|/\$NAME$| && $concat) {
+ my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
+ system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
+ }
}
}
}
if ($variables->{WVTEST_TIMEOUT}) {
- print "wvtest: timeout ", $variables->{WVTEST_TIMEOUT}, "\n";
-}
-
-sub trim($) {
- my ($str) = @_;
- $str =~ s/^\s+|\s+$//g;
- return $str
+ print STDERR "wvtest: timeout ", $variables->{WVTEST_TIMEOUT}, "\n";
}
### Start in Qemu
}
push(@qemu_flags, qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
unshift(@qemu_flags, ('-name', $config_name));
- print "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
+ print STDERR "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
$exp = Expect->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
}
system_verbose('mkdir -p tftpboot');
generate_grub_config("tftpboot/os-menu.lst", $config_name, "(nd)", \@$modules, "timeout 0");
open(my $fh, '>', 'dhcpd.conf');
- my $mac = `cat /sys/class/net/eth0/address`;
+ my $mac = `cat /sys/class/net/$netif/address`;
chomp $mac;
print $fh "subnet 10.23.23.0 netmask 255.255.255.0 {
range 10.23.23.10 10.23.23.100;
fixed-address 10.23.23.1;
}";
close($fh);
- system_verbose("sudo ip a add 10.23.23.1/24 dev eth0;
- sudo ip l set dev eth0 up;
+ system_verbose("sudo ip a add 10.23.23.1/24 dev $netif;
+ sudo ip l set dev $netif up;
sudo touch dhcpd.leases");
# We run servers by forking ourselves, because the servers end up
### AMT IDE-R
if (defined $ider) {
my $ider_cmd= "amtider -c $iso_image -u $amt_user -p $amt_password $amt_host $amt_port" ;
- print "novaboot: Running: $ider_cmd\n" =~ s/\Q$amt_password\E/???/r;
+ print STDERR "novaboot: Running: $ider_cmd\n" =~ s/\Q$amt_password\E/???/r;
my $ider_pid = fork();
if ($ider_pid == 0) {
exec($ider_cmd);
### Reset target (IP relay, AMT, ...)
if (defined $target_reset && $reset) {
- print "novaboot: Reseting the test box... ";
+ print STDERR "novaboot: Reseting the test box... ";
&$target_reset();
- print "done\n";
+ print STDERR "done\n";
+ if (defined $exp) {
+ # We don't want to output anything printed by the target
+ # before reset so we clear the buffers now. This is, however,
+ # not ideal because we may loose some data that were sent
+ # after the reset. If this is a problem, one should reset and
+ # connect to serial line in atomic manner. For example, if
+ # supported by hardware, use --remote-cmd 'sterm -d ...' and
+ # do not use separate --reset-cmd.
+ my $log = $exp->log_stdout;
+ $exp->log_stdout(0);
+ $exp->expect(0); # Read data from target
+ $exp->clear_accum(); # Clear the read data
+ $exp->log_stdout($log);
+ }
}
### U-boot conversation
if (defined $uboot) {
my $uboot_prompt = $uboot || '=> ';
- print "novaboot: Waiting for U-Boot prompt...\n";
+ print STDERR "novaboot: Waiting for U-Boot prompt...\n";
$exp || die("No serial line connection");
$exp->log_stdout(1);
#$exp->exp_internal(1);
$timeout = 10;
}
if ($cmd =~ /\$NB_MYIP/) {
- my $ip = (grep /inet /, `ip addr show eth0`)[0] || die "Problem determining our IP address";
+ my $ip = (grep /inet /, `ip addr show $netif`)[0] || die "Problem determining our IP address";
$ip =~ s/\s*inet ([0-9.]*).*/$1/;
$cmd =~ s/\$NB_MYIP/$ip/g;
}
$exp->expect($timeout, $uboot_prompt) || die "U-Boot prompt timeout";
}
- # Boot the system if there are some load lines in the script
- if (scalar(@$modules) > 0 && !$variables->{NO_BOOT}) {
+ # Load files if there are some load lines in the script
+ if (scalar(@$modules) > 0 && !$variables->{NO_BOOT}) {
my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
my $dtb;
@$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
my $initrd = shift @$modules;
- die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel};
- $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
- $exp->expect(10,
- [qr/##/, sub { exp_continue; }],
- $uboot_prompt) || die "Kernel load timeout";
+ if (defined $kbin) {
+ die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel};
+ $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
+ $exp->expect(10,
+ [qr/##/, sub { exp_continue; }],
+ $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->send("setenv bootargs $kcmd\n");
$exp->expect(5, $uboot_prompt) || die "U-Boot prompt timeout";
- $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
+ }
+ $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
+ if (!$variables->{NO_BOOT} && $uboot_cmd ne '') {
$uboot_cmd =~ s/\$kernel_addr/$uboot_addr{kernel}/g;
$uboot_cmd =~ s/\$ramdisk_addr/$uboot_addr{ramdisk}/g;
$uboot_cmd =~ s/\$fdt_addr/$uboot_addr{fdt}/g;
}
### Serial line interaction
-if (defined $exp) {
+if ($interaction && defined $exp) {
# Serial line of the target is available
my $interrupt = 'Ctrl-C';
if ($interactive && !@exiton) {
$interrupt = '"~~."';
}
- my $note = (-t STDIN) ? '' : '- only target->host ';
- print "novaboot: Serial line interaction $note(press $interrupt to interrupt)...\n";
+ 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");
- print "\n";
} else {
my @inputs = ($exp);
- if (-t STDIN) { # Set up bi-directional communication if we run on terminal
- my $infile = new IO::File;
- $infile->IO::File::fdopen(*STDIN,'r');
- my $in_object = Expect->exp_init($infile);
- $in_object->set_group($exp);
-
- if ($interactive) {
- $in_object->set_seq('~~\.', sub { print "novaboot: Escape sequence detected\r\n"; undef; });
- $in_object->manual_stty(0); # Use raw terminal mode
- } else {
- $in_object->manual_stty(1); # Do not modify terminal settings
- }
- push(@inputs, $in_object);
+ my $infile = new IO::File;
+ $infile->IO::File::fdopen(*STDIN,'r');
+ my $in_object = Expect->exp_init($infile);
+ $in_object->set_group($exp);
+
+ if ($interactive) {
+ $in_object->set_seq('~~\.', sub { print STDERR "novaboot: Escape sequence detected\r\n"; undef; });
+ $in_object->manual_stty(0); # Use raw terminal mode
+ } else {
+ $in_object->manual_stty(1); # Do not modify terminal settings
}
+ push(@inputs, $in_object);
#use Data::Dumper;
#print Dumper(\@expect_raw);
$exp->expect(undef, @expect_raw) if @expect_raw;
die("novaboot: This should kill servers on background\n");
}
+# Always finish novaboot output with newline
+print "\n" if $final_eol;
+
## Documentation
=head1 NAME
=head1 DESCRIPTION
-This program makes booting of a locally compiled operating system (OS)
+Novaboot makes booting of a locally compiled operating system (OS)
(e.g. NOVA or Linux) on remote targets as simple as running a program
locally. It automates things like copying OS images to a TFTP server,
generation of bootloader configuration files, resetting of target
hardware or redirection of target's serial line to stdin/out. Novaboot
-is highly configurable and it makes it easy to boot a single image on
+is highly configurable and makes it easy to boot a single image on
different targets or different images on a single target.
-Novaboot operation is controlled by command line options and by a so
-called novaboot script, which can be thought as a generalization of
-bootloader configuration files (see L</"NOVABOOT SCRIPT SYNTAX">).
-Typical way of using novaboot is to make the novaboot script
-executable and set its first line to I<#!/usr/bin/env novaboot>. Then,
-booting a particular OS configuration becomes the same as executing a
-local program - the novaboot script.
+Novaboot operation is controlled by configuration files, command line
+options and by a so called novaboot script, which can be thought as a
+generalization of bootloader configuration files (see L</"NOVABOOT
+SCRIPT SYNTAX">). Typical way of using novaboot is to make the
+novaboot script executable and set its first line to I<#!/usr/bin/env
+novaboot>. Then, booting a particular OS configuration becomes the
+same as executing a local program – the novaboot script.
Novaboot uses configuration files to, among other things, define
command line options needed for different targets. Users typically use
Internally, this option expands to the pre-configured options.
Configuration files are searched at multiple places, which allows to
have per-system, per-user or per-project configurations. Configuration
-file syntax is described in section L</"CONFIGURATION FILE">.
+file syntax is described in section L</"CONFIGURATION FILES">.
Simple examples of using C<novaboot>:
=item 1.
-Run an OS in Qemu. This is the default action when no other action is
-specified by command line switches. Thus running C<novaboot myos> (or
-C<./myos> as described above) will run Qemu and make it boot the
-configuration specified in the F<myos> script.
+Run an OS in Qemu. This is can be specified with the B<--qemu> option.
+Thus running C<novaboot --qemu myos> (or C<./myos --qemu> as described
+above) will run Qemu and make it boot the configuration specified in
+the F<myos> script.
=item 2.
Novaboot performs its work in several phases. Each phase can be
influenced by several command line options, certain phases can be
-skipped. The list of phases (in the execution order) and the
-corresponding options follows.
+skipped. The list of phases (in the execution order) is as follows.
+
+=over
+
+=item 1. L<Configuration reading|/Configuration reading phase>
+
+=item 2. L<Command line processing|/Command line processing phase>
+
+=item 3. L<Script preprocessing|/Script preprocessing phase>
+
+=item 4. L<File generation|/File generation phase>
+
+=item 5. L<Target connection|/Target connection check>
+
+=item 6. L<File deployment|/File deployment phase>
+
+=item 7. L<Target power-on and reset|/Target power-on and reset phase>
+
+=item 8. L<Interaction with the bootloader|/Interaction with the bootloader on the target>
+
+=item 9. L<Target interaction|/Target interaction phase>
+
+=back
+
+Each phase is described in the following sections together with the
+command line options that control it.
=head2 Configuration reading phase
After starting, novaboot reads configuration files. Their content is
-described in section L</"CONFIGURATION FILE">. By default,
+described in section L</"CONFIGURATION FILES">. By default,
configuration is read from multiple locations. First from the system
configuration directory (F</etc/novaboot.d/>), second from the user
configuration file (F<~/.config/novaboot>) and third from F<.novaboot>
=item -t, --target=I<target>
This option serves as a user configurable shortcut for other novaboot
-options. The effect of this option is the same as the options stored
-in the C<%targets> configuration variable under key I<target>. See
-also L</"CONFIGURATION FILE">.
+options. The effect of this option is the same as specifying the
+options stored in the C<%targets> configuration variable under key
+I<target>. See also L</"CONFIGURATION FILES">.
+
+When this option is not given, novaboot tries to determine the target
+to use from either B<NOVABOOT_TARGET> environment variable or from
+B<$default_target> configuration file variable.
=back
=item --remote-expect=I<string>
-Wait for reception of I<string> after establishing the the remote
-connection before continuing.
+Wait for reception of I<string> after establishing the remote
+connection.
+
+=item --remote-expect-silent=I<string>
+The same as B<--remote-expect> except that the remote output is not
+echoed to stdout while waiting for the I<string>. Everything after the
+matched string is printed to stdout, so you may want to include line
+end characters in the I<string> as well.
=back
Port to run the TFTP server on. Implies B<--tftp>.
+=item --netif=I<network interface>
+
+Network interface used to deploy files to the target. Default value is
+I<eth0>. This influences the configuration of the DHCP server started
+by B<--dhcp-tftp> and the value that B<$NB_MYIP> get replaced with in
+U-Boot conversation.
+
=item --iso[=filename]
Generates the ISO image that boots NOVA system via GRUB. If no filename
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
+
+Disable U-Boot interaction previously enabled with B<--uboot>.
+
=item --uboot-init
Command(s) to send the U-Boot bootloader before loading the images and
commands from each option novaboot waits for U-Boot I<prompt>.
If the command contains string I<$NB_MYIP> then this string is
-replaced by IPv4 address of eth0 interface. Similarly I<$NB_PREFIX> is
-replaced with prefix given by B<--prefix>.
+replaced by IPv4 address of eth0 interface (see also B<--netif>).
+Similarly I<$NB_PREFIX> is replaced with prefix given by B<--prefix>.
See also C<uboot> keyword in L</"NOVABOOT SCRIPT SYNTAX">).
to the target and does no longer interrupt novaboot. Use "~~."
sequence to exit novaboot.
+=item --no-interaction, --interaction
+
+Skip resp. force target interaction phase. When skipped, novaboot exits
+immediately when boot is initiated.
+
=item --expect=I<string>
When I<string> is received from the target, send the string specified
Example: C<--expect='Continue?' --sendcont='yes\n'>
+=item --final-eol, --no-final-eol
+
+By default, B<novaboot> always prints an end-of-line character at the
+end of its execution in order to ensure that the output of programs
+started after novaboot appears at the beginning of the line. When this
+is not desired B<--no-final-eol> option can be used to override this
+behavior.
+
=back
=head1 NOVABOOT SCRIPT SYNTAX
=back
-=head1 CONFIGURATION FILE
+=head1 CONFIGURATION FILES
Novaboot can read its configuration from one or more files. By
default, novaboot looks for files in F</etc/novaboot.d>, file
=item $default_target
Default target (see below) to use when no target is explicitly
-specified on command line with the B<--target> option.
+specified with the B<--target> command line option or
+B<NOVABOOT_TARGET> environment variable.
=item %targets
Name of the novaboot configuration directory. When not specified
F</etc/novaboot.d> is used.
+=item NOVABOOT_TARGET
+
+Name of the novaboot target to use. This overrides the value of
+B<$default_target> from the configuration file and can be overriden
+with the B<--target> command line option.
+
=item NOVABOOT_BENDER
Defining this variable has the same meaning as B<--bender> option.