3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 use warnings (exists $ENV{NOVABOOT_TEST} ? (FATAL => 'all') : ());
21 use Getopt::Long qw(GetOptionsFromString GetOptionsFromArray);
26 use Time::HiRes("usleep");
30 use POSIX qw(:errno_h sysconf);
31 use Cwd qw(getcwd abs_path);
37 my $invocation_dir = $ENV{PWD} || getcwd();
39 ## Configuration file handling
41 # Default configuration
42 $CFG::hypervisor = "";
43 $CFG::hypervisor_params = "serial";
44 $CFG::genisoimage = "genisoimage";
45 $CFG::qemu = 'qemu-system-i386 -cpu coreduo -smp 2';
46 $CFG::default_target = '';
49 "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',
50 "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.86.92:2324',
51 "localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog=" set root=\'(hd0,msdos1)\'"',
52 "ryu" => '--uboot --uboot-init="mw f0000b00 \${psc_cfg}; sleep 1" --uboot-addr kernel=800000 --uboot-addr ramdisk=b00000 --uboot-addr fdt=7f0000',
53 "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\""',
54 "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"',
59 $const{linux}->{_SC_NPROCESSORS_CONF} = 83;
60 my $nproc = sysconf($const{$^O}->{_SC_NPROCESSORS_CONF});
62 $CFG::scons = "scons -j$nproc";
63 $CFG::make = "make -j$nproc";
71 package CFG; # Put config data into a separate namespace
77 die("ERROR: Failure compiling '$cfg' - $@");
78 } elsif (! defined($rc)) {
79 die("ERROR: Failure reading '$cfg' - $!");
81 die("ERROR: Failure processing '$cfg'");
84 $builddir = File::Spec->rel2abs($CFG::builddir, dirname($cfg)) if defined $CFG::builddir;
85 print STDERR "novaboot: Read $cfg\n";
90 # We don't use $0 here, because it points to the novaboot itself and
91 # not to the novaboot script. The problem with this approach is that
92 # when a script is run as "novaboot <options> <script>" then $ARGV[0]
93 # contains the first option. Hence the -f check.
94 my $dir = File::Spec->rel2abs($ARGV[0] && -f $ARGV[0] ? dirname($ARGV[0]) : '', $invocation_dir);
95 while ((-d $dir || -l $dir ) && $dir ne "/") {
96 push @cfgs, "$dir/.novaboot" if -r "$dir/.novaboot";
97 my @dirs = File::Spec->splitdir($dir);
98 $dir = File::Spec->catdir(@dirs[0..$#dirs-1]);
100 @cfgs = reverse @cfgs;
102 my $xdg_config_home = $ENV{'XDG_CONFIG_HOME'} || $ENV{'HOME'}.'/.config';
103 unshift(@cfgs, "$xdg_config_home/novaboot") if -r "$xdg_config_home/novaboot";
105 $dir = $ENV{'NOVABOOT_CONFIG_DIR'} || '/etc/novaboot.d';
106 if (opendir(my $dh, $dir)) {
107 my @etccfg = map { "$dir/$_" } grep { /^[-_a-zA-Z0-9]+$/ && -f "$dir/$_" } readdir($dh);
109 @etccfg = sort(@etccfg);
110 @cfgs = ( @etccfg, @cfgs );
113 my $cfg = $ENV{'NOVABOOT_CONFIG'};
114 Getopt::Long::Configure(qw/no_ignore_case pass_through/);
115 GetOptions ("config|c=s" => \$cfg);
116 read_config($_) foreach $cfg or @cfgs;
118 ## Command line handling
120 my $explicit_target = $ENV{'NOVABOOT_TARGET'};
121 GetOptions ("target|t=s" => \$explicit_target);
123 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);
125 # Default values of certain command line options
127 'kernel' => '${kernel_addr_r}',
128 'ramdisk' => '${ramdisk_addr_r}',
129 'fdt' => '${fdt_addr_r}',
132 $rom_prefix = 'rom://';
133 $stty = 'raw -crtscts -onlcr 115200';
134 $reset = 1; # Reset target by default
135 $interaction = 1; # Perform target interaction by default
139 my @expect_seen = ();
143 push(@expect_seen, '-re') if $n eq "expect-re";
144 push(@expect_seen, $v);
150 unless (@expect_seen) { die("No --expect before --send"); }
151 my $ret = ($n eq "sendcont") ? exp_continue : 0;
152 unshift(@expect_raw, sub { shift->send(eval("\"$v\"")); $ret; });
153 unshift(@expect_raw, @expect_seen);
160 "append|a=s" => \@append,
161 "bender|b" => \$bender,
162 "build-dir=s" => sub { my ($n, $v) = @_; $builddir = File::Spec->rel2abs($v); },
163 "concat" => \$concat,
164 "chainloader=s" => \@chainloaders,
165 "dhcp-tftp|d" => \$dhcp_tftp,
166 "dump" => \$dump_opt,
167 "dump-config" => \$dump_config,
168 "exiton=s" => \@exiton,
169 "exiton-timeout=i"=> \$exiton_timeout,
170 "exiton-re=s" => sub { my ($n, $v) = @_; push(@exiton, '-re', $v); },
171 "expect=s" => \&handle_expect,
172 "expect-re=s" => \&handle_expect,
173 "expect-raw=s" => sub { my ($n, $v) = @_; unshift(@expect_raw, eval($v)); },
174 "final-eol!" => \$final_eol,
175 "gen-only" => \$gen_only,
176 "grub|g:s" => \$grub_config,
177 "grub-preamble=s"=> \$grub_preamble,
178 "prefix|grub-prefix=s" => \$grub_prefix,
179 "grub2:s" => \$grub2_config,
180 "grub2-prolog=s" => \$grub2_prolog,
182 "interaction!" => \$interaction,
183 "iprelay=s" => \$iprelay,
184 "iso:s" => \$iso_image,
185 "kernel|k=s" => \$kernel_opt,
186 "interactive|i" => \$interactive,
187 "name=s" => \$config_name_opt,
188 "make|m:s" => \$make,
189 "netif=s" => \$netif,
190 "no-file-gen" => \$no_file_gen,
193 "pulsar|p:s" => \$pulsar,
194 "pulsar-root=s" => \$pulsar_root,
195 "qemu|Q:s" => \$qemu,
196 "qemu-append=s" => \$qemu_append,
197 "qemu-flags|q=s" => \$qemu_flags_cmd,
198 "remote-cmd=s" => \$remote_cmd,
199 "remote-expect=s"=> \$remote_expect,
200 "remote-expect-silent=s"=> sub { $remote_expect=$_[1]; $remote_expect_silent=1; },
202 "reset-cmd=s" => \$reset_cmd,
203 "reset-send=s" => \$reset_send,
204 "rsync-flags=s" => \$rsync_flags,
205 "scons:s" => \$scons,
206 "scriptmod=s" => \@scriptmod,
207 "send=s" => \&handle_send,
208 "sendcont=s" => \&handle_send,
209 "serial|s:s" => \$serial,
210 "server:s" => \$server,
211 "strip-rom" => sub { $rom_prefix = ''; },
214 "tftp-port=i" => \$tftp_port,
215 "uboot:s" => \$uboot,
216 "no-uboot" => sub { undef $uboot; },
217 "uboot-addr=s" => \%uboot_addr,
218 "uboot-cmd=s" => \$uboot_cmd,
219 "uboot-init=s" => \@uboot_init,
224 # First process target options
226 my $t = defined($explicit_target) ? $explicit_target : $CFG::default_target;
228 Getopt::Long::Configure(qw/no_ignore_case pass_through/);
230 exists $CFG::targets{$t} or die("Unknown target '$t' (valid targets are: ".join(", ", sort keys(%CFG::targets)).")");
232 undef $explicit_target;
233 my ($ret, $remaining_args) = GetOptionsFromString ($CFG::targets{$t}, ("target|t=s" => \$explicit_target));
234 if (!$ret) { die "Error parsing target $t option"; }
235 push(@target_expanded, @$remaining_args);
236 $t = $explicit_target;
238 Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
239 GetOptionsFromArray(\@target_expanded, %opt_spec) or die ("Error in target definition");
242 # Then process other command line options - some of them may override
243 # what was specified by the target
244 GetOptions %opt_spec or die("Error in command line arguments");
245 pod2usage(1) if $help;
246 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
248 ### Dump sanitized configuration (if requested)
252 $Data::Dumper::Indent=1;
253 print "# This file is in perl syntax.\n";
254 foreach my $key(sort(keys(%CFG::))) { # See "Symbol Tables" in perlmod(1)
255 if (defined ${$CFG::{$key}}) { print Data::Dumper->Dump([${$CFG::{$key}}], ["*$key"]); }
256 if ( @{$CFG::{$key}}) { print Data::Dumper->Dump([\@{$CFG::{$key}}], ["*$key"]); }
257 if ( %{$CFG::{$key}}) { print Data::Dumper->Dump([\%{$CFG::{$key}}], ["*$key"]); }
263 ### Sanitize configuration
265 if ($interactive && !-t STDIN) {
266 die("novaboot: Interactive mode not supported when not on terminal");
269 if (defined $config_name_opt && scalar(@ARGV) > 1) { die "You cannot use --name with multiple scripts"; }
272 $iso_image //= ''; # IDE-R needs an ISO image
273 if (!defined $amt) { die "Error: --ider requires --amt"; }
277 my %input_opts = ('--iprelay' => \$iprelay,
278 '--serial' => \$serial,
279 '--remote-cmd' => \$remote_cmd,
281 my @opts = grep(defined(${$input_opts{$_}}) , keys %input_opts);
283 die("novaboot: More than one target connection option: ".join(', ', @opts)) if scalar @opts > 1;
287 if (defined $serial) {
288 $serial ||= "/dev/ttyUSB0";
289 $ENV{NB_SERIAL} = $serial;
291 if (defined $grub_config) { $grub_config ||= "menu.lst"; }
292 if (defined $grub2_config) { $grub2_config ||= "grub.cfg"; }
294 ## Parse the novaboot script(s)
299 my ($modules, $variables, $generated, $copy, $continuation) = ([], {}, [], []);
300 my $skip_reading = defined($on_opt) || defined($off_opt);
301 while (!$skip_reading && ($_ = <>)) {
302 if ($ARGV ne $last_fn) { # New script
303 die "Missing EOF in $last_fn" if $file;
304 die "Unfinished line in $last_fn" if $continuation;
306 push @scripts, { 'filename' => $ARGV,
307 'modules' => $modules = [],
308 'variables' => $variables = {},
309 'generated' => $generated = [],
310 'copy' => $copy = [],
315 next if /^#/ || /^\s*$/; # Skip comments and empty lines
317 $_ =~ s/^[[:space:]]*// if ($continuation);
319 if (/\\$/) { # Line continuation
320 $continuation .= substr($_, 0, length($_)-1);
324 if ($continuation) { # Last continuation line
325 $_ = $continuation . $_;
329 foreach my $mod(@scriptmod) { eval $mod; }
331 if ($file && $_ eq $EOF) { # Heredoc end
335 if ($file) { # Heredoc content
336 push @{$file}, "$_\n";
339 if (/^([A-Z_]+)=(.*)$/) { # Internal variable
340 $$variables{$1} = $2;
341 push(@exiton, $2) if ($1 eq "EXITON");
344 sub process_load_copy($) {
345 die("novaboot: '$last_fn' line $.: Missing file name\n") unless /^[^ <]+/;
346 if (/^([^ ]*)(.*?)[[:space:]]*<<([^ ]*)$/) { # Heredoc start
348 push @$generated, {filename => $1, content => $file};
352 if (/^([^ ]*)(.*?)[[:space:]]*< ?(.*)$/) { # Command substitution
353 push @$generated, {filename => $1, command => $3};
358 if (s/^load *//) { # Load line
359 push @$modules, process_load_copy($_);
362 if (s/^copy *//) { # Copy line
363 push @$copy, process_load_copy($_);
366 if (/^run (.*)/) { # run line
367 push @$generated, {command => $1};
370 if (/^uboot(?::([0-9]+)s)? (.*)/) { # uboot line
371 # TODO: If U-Boot supports some interactive menu, it might
372 # make sense to store uboot lines per novaboot script.
373 if ($1) { # Command with explicit timeout
374 push @uboot_init, { command => $2,
376 } else { # Command without explicit timeout
377 push @uboot_init, $2;
382 die("novaboot: Cannot parse script '$last_fn' line $.. Didn't you forget 'load' keyword?\n");
385 # print Dumper(\@scripts);
387 foreach my $script (@scripts) {
388 $modules = $$script{modules};
389 @$modules[0] =~ s/^[^ ]*/$kernel_opt/ if $kernel_opt;
390 @$modules[0] .= ' ' . join(' ', @append) if @append;
393 if (exists $variables->{KERNEL}) {
394 $kernel = $variables->{KERNEL};
396 if ($CFG::hypervisor) {
397 $kernel = $CFG::hypervisor . " ";
398 if (exists $variables->{HYPERVISOR_PARAMS}) {
399 $kernel .= $variables->{HYPERVISOR_PARAMS};
401 $kernel .= $CFG::hypervisor_params;
405 @$modules = ($kernel, @$modules) if $kernel;
406 @$modules = (@chainloaders, @$modules);
407 @$modules = ("bin/boot/bender", @$modules) if ($bender || defined $ENV{'NOVABOOT_BENDER'});
411 foreach my $script (@scripts) {
412 print join("\n", @{$$script{modules}})."\n";
419 sub generate_configs($$$) {
420 my ($base, $generated, $filename) = @_;
421 if ($base) { $base = "$base/"; };
422 foreach my $g(@$generated) {
423 if (exists $$g{content}) {
424 my $config = $$g{content};
425 my $fn = $$g{filename};
426 open(my $f, '>', $fn) || die("$fn: $!");
427 map { s|\brom://([^ ]*)|$rom_prefix$base$1|g; print $f "$_"; } @{$config};
429 print STDERR "novaboot: Created $fn\n";
430 } elsif (exists $$g{command} && ! $no_file_gen) {
431 $ENV{SRCDIR} = dirname(File::Spec->rel2abs( $filename, $invocation_dir ));
432 if (exists $$g{filename}) {
433 system_verbose("( $$g{command} ) > $$g{filename}");
435 system_verbose($$g{command});
441 sub generate_grub_config($$$$;$)
443 my ($filename, $title, $base, $modules_ref, $preamble) = @_;
444 if ($base) { $base = "$base/"; };
445 open(my $fg, '>', $filename) or die "$filename: $!";
446 print $fg "$preamble\n" if $preamble;
447 print $fg "title $title\n" if $title;
448 #print $fg "root $base\n"; # root doesn't really work for (nd)
450 foreach (@$modules_ref) {
453 my ($kbin, $kcmd) = split(' ', $_, 2);
454 $kcmd = '' if !defined $kcmd;
455 print $fg "kernel ${base}$kbin $kcmd\n";
457 s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
458 print $fg "module $base$_\n";
462 print("novaboot: Created $builddir/$filename\n");
466 sub generate_syslinux_config($$$$)
468 my ($filename, $title, $base, $modules_ref) = @_;
469 if ($base && $base !~ /\/$/) { $base = "$base/"; };
470 open(my $fg, '>', $filename) or die "$filename: $!";
471 print $fg "LABEL $title\n";
472 #TODO print $fg "MENU LABEL $human_readable_title\n";
474 my ($kbin, $kcmd) = split(' ', @$modules_ref[0], 2);
476 if (system("file $kbin|grep 'Linux kernel'") == 0) {
477 my $initrd = @$modules_ref[1];
478 die('To many "load" lines for Linux kernel') if (scalar @$modules_ref > 2);
479 print $fg "LINUX $base$kbin\n";
480 print $fg "APPEND $kcmd\n";
481 print $fg "INITRD $base$initrd\n";
483 print $fg "KERNEL mboot.c32\n";
485 foreach (@$modules_ref) {
486 s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
487 push @append, "$base$_";
488 print $fg "APPEND ".join(' --- ', @append)."\n";
491 #TODO print $fg "TEXT HELP\n";
492 #TODO print $fg "some help here\n";
493 #TODO print $fg "ENDTEXT\n";
495 print("novaboot: Created $builddir/$filename\n");
499 sub generate_grub2_config($$$$;$$)
501 my ($filename, $title, $base, $modules_ref, $preamble, $prolog) = @_;
502 if ($base && substr($base,-1,1) ne '/') { $base = "$base/"; };
503 open(my $fg, '>', $filename) or die "$filename: $!";
504 print $fg "$preamble\n" if $preamble;
505 $title ||= 'novaboot';
506 print $fg "menuentry $title {\n";
507 print $fg "$prolog\n" if $prolog;
509 foreach (@$modules_ref) {
512 my ($kbin, $kcmd) = split(' ', $_, 2);
513 $kcmd = '' if !defined $kcmd;
514 print $fg " multiboot ${base}$kbin $kcmd\n";
517 # GRUB2 doesn't pass filename in multiboot info so we have to duplicate it here
518 $_ = join(' ', ($args[0], @args));
519 s|\brom://|$rom_prefix|g; # We do not need to translate path for GRUB2
520 print $fg " module $base$_\n";
525 print("novaboot: Created $builddir/$filename\n");
529 sub generate_pulsar_config($$)
531 my ($filename, $modules_ref) = @_;
532 open(my $fg, '>', $filename) or die "$filename: $!";
533 print $fg "root $pulsar_root\n" if defined $pulsar_root;
536 foreach (@$modules_ref) {
539 ($kbin, $kcmd) = split(' ', $_, 2);
540 $kcmd = '' if !defined $kcmd;
543 s|\brom://|$rom_prefix|g;
544 print $fg "load $_\n";
547 # Put kernel as last - this is needed for booting Linux and has no influence on non-Linux OSes
548 print $fg "exec $kbin $kcmd\n";
550 print("novaboot: Created $builddir/$filename\n");
554 sub shell_cmd_string(@)
556 return join(' ', map((/^[-_=a-zA-Z0-9\/\.\+]+$/ ? "$_" : "'$_'"), @_));
561 print STDERR "novaboot: Running: ".shell_cmd_string(@_)."\n";
563 exit(1); # should not be reached
566 sub system_verbose($)
569 print STDERR "novaboot: Running: $cmd\n";
570 my $ret = system($cmd);
571 if ($ret & 0x007f) { die("Command terminated by a signal"); }
572 if ($ret & 0xff00) {die("Command exit with non-zero exit code"); }
573 if ($ret) { die("Command failure $ret"); }
578 $str =~ s/^\s+|\s+$//g;
584 if (exists $variables->{WVDESC}) {
585 print STDERR "Testing \"$variables->{WVDESC}\" in $last_fn:\n";
586 } elsif ($last_fn =~ /\.wv$/) {
587 print STDERR "Testing \"all\" in $last_fn:\n";
590 ## Connect to the target and check whether it is not occupied
592 # We have to do this before file generation phase, because file
593 # generation is intermixed with file deployment phase and we want to
594 # check whether the target is not used by somebody else before
595 # deploying files. Otherwise, we may rewrite other user's files on a
598 my $exp; # Expect object to communicate with the target over serial line
600 my ($target_reset, $target_power_on, $target_power_off);
601 my ($amt_user, $amt_password, $amt_host, $amt_port);
603 if (defined $iprelay) {
605 $iprelay =~ /([.0-9]+)(:([0-9]+))?/;
608 my $paddr = sockaddr_in($port, inet_aton($addr));
609 my $proto = getprotobyname('tcp');
610 socket($IPRELAY, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
611 print STDERR "novaboot: Connecting to IP relay... ";
612 connect($IPRELAY, $paddr) || die "connect: $!";
613 print STDERR "done\n";
614 $exp = Expect->init(\*$IPRELAY);
618 print $exp "\xFF\xF6"; # AYT
619 my $connected = $exp->expect(20, # Timeout in seconds
620 '<iprelayd: connected>',
621 '-re', '<WEB51 HW[^>]*>');
626 my ($relay, $onoff) = @_;
627 die unless ($relay == 1 || $relay == 2);
629 my $cmd = ($relay == 1 ? 0x5 : 0x6) | ($onoff ? 0x20 : 0x10);
630 return "\xFF\xFA\x2C\x32".chr($cmd)."\xFF\xF0";
634 my ($relay, $onoff) = @_;
635 die unless ($relay == 1 || $relay == 2);
636 my $cmd = ($relay == 1 ? 0xdf : 0xbf) | ($onoff ? 0x00 : 0xff);
637 return "\xFF\xFA\x2C\x97".chr($cmd)."\xFF\xF0";
641 my ($relay, $onoff, $can_giveup) = @_;
642 my $confirmation = '';
644 print $exp relaycmd($relay, $onoff);
645 my $confirmed = $exp->expect(20, # Timeout in seconds
646 relayconf($relay, $onoff));
649 print("Relay confirmation timeout - ignoring\n");
651 die "Relay confirmation timeout";
657 $target_reset = sub {
658 relay(2, 1, 1); # Reset the machine
663 $target_power_off = sub {
664 relay(1, 1); # Press power button
665 usleep(6000000); # Long press to switch off
669 $target_power_on = sub {
670 relay(1, 1); # Press power button
671 usleep(100000); # Short press
677 system_verbose("stty -F $serial $stty");
678 open($CONN, "+<", $serial) || die "open $serial: $!";
679 $exp = Expect->init(\*$CONN);
681 elsif ($remote_cmd) {
682 print STDERR "novaboot: Running: $remote_cmd\n";
683 $exp = Expect->spawn($remote_cmd);
685 elsif (defined $amt) {
686 require LWP::UserAgent;
687 require LWP::Authen::Digest;
690 my ($host, $username, $password, $schema, $className, $pstate) = @_;
691 #AMT numbers for PowerStateChange (MNI => bluescreen on windows;-)
692 my %pstates = ("on" => 2,
699 <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
700 <s:Header><a:To>http://$host:16992/wsman</a:To>
701 <w:ResourceURI s:mustUnderstand="true">$schema</w:ResourceURI>
702 <a:ReplyTo><a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>
703 <a:Action s:mustUnderstand="true">$schema$className</a:Action>
704 <w:MaxEnvelopeSize s:mustUnderstand="true">153600</w:MaxEnvelopeSize>
705 <a:MessageID>uuid:709072C9-609C-4B43-B301-075004043C7C</a:MessageID>
706 <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
707 <w:OperationTimeout>PT60.000S</w:OperationTimeout>
708 <w:SelectorSet><w:Selector Name="Name">Intel(r) AMT Power Management Service</w:Selector></w:SelectorSet>
710 <p:RequestPowerStateChange_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService">
711 <p:PowerState>$pstates{$pstate}</p:PowerState>
712 <p:ManagedElement><a:Address>http://$host:16992/wsman</a:Address>
713 <a:ReferenceParameters><w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</w:ResourceURI>
714 <w:SelectorSet><w:Selector Name="Name">ManagedSystem</w:Selector></w:SelectorSet>
715 </a:ReferenceParameters></p:ManagedElement>
716 </p:RequestPowerStateChange_INPUT>
717 </s:Body></s:Envelope>
722 my ($host, $username, $password, $content) = @_;
724 my $ua = LWP::UserAgent->new();
725 $ua->agent("novaboot");
727 my $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
728 my $res = $ua->request($req);
729 die ("Unexpected AMT response: " . $res->status_line) unless $res->code == 401;
731 my ($realm) = $res->header("WWW-Authenticate") =~ /Digest realm="(.*?)"/;
732 $ua->credentials("$host:16992", $realm, $username => $password);
735 $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
736 $req->content_type('application/x-www-form-urlencoded');
737 $req->content($content);
738 $res = $ua->request($req);
739 die ("AMT power change request failed: " . $res->status_line) unless $res->is_success;
740 $res->content() =~ /<g:ReturnValue>(\d+)<\/g:ReturnValue>/;
745 my ($host, $username, $password, $pstate)=@_;
746 my $schema="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService";
747 my $className="/RequestPowerStateChange";
748 my $content = genXML($host, $username, $password ,$schema, $className, $pstate);
749 return sendPOST($host, $username, $password, $content);
752 ($amt_user,$amt_password,$amt_host,$amt_port) = ($amt =~ /(?:(.*?)(?::(.*))?@)?([^:]*)(?::([0-9]*))?/);;
753 $amt_user ||= "admin";
754 $amt_password ||= $ENV{'AMT_PASSWORD'} || die "AMT password not specified";
755 $amt_host || die "AMT host not specified";
759 $target_power_off = sub {
761 my $result = powerChange($amt_host,$amt_user,$amt_password, "off");
762 die "AMT power off failed (ReturnValue $result)" if $result != 0;
765 $target_power_on = sub {
766 my $result = powerChange($amt_host,$amt_user,$amt_password, "on");
767 die "AMT power on failed (ReturnValue $result)" if $result != 0;
770 $target_reset = sub {
771 my $result = powerChange($amt_host,$amt_user,$amt_password, "reset");
773 print STDERR "Warning: Cannot reset $amt_host, trying power on. ";
774 $result = powerChange($amt_host,$amt_user,$amt_password, "on");
776 die "AMT reset failed (ReturnValue $result)" if $result != 0;
779 my $cmd = "amtterm -u $amt_user -p $amt_password $amt_host $amt_port";
780 print STDERR "novaboot: Running: $cmd\n" =~ s/\Q$amt_password\E/???/r;
781 $exp = Expect->spawn($cmd);
782 $exp->expect(10, "RUN_SOL") || die "Expect for 'RUN_SOL' timed out";
787 if ($remote_expect) {
788 $exp || die("No serial line connection");
789 my $log = $exp->log_stdout;
790 if (defined $remote_expect_silent) {
793 $exp->expect(180, $remote_expect) || die "Expect for '$remote_expect' timed out";
794 if (defined $remote_expect_silent) {
795 $exp->log_stdout($log);
796 print $exp->after() if $log;
800 if (defined $reset_cmd) {
801 $target_reset = sub {
802 system_verbose($reset_cmd);
806 if (defined $reset_send) {
807 $target_reset = sub {
808 $reset_send =~ s/\\n/\n/g;
809 $exp->send($reset_send);
813 if (defined $on_opt && defined $target_power_on) {
817 if (defined $off_opt && defined $target_power_off) {
818 print STDERR "novaboot: Switching the target off...\n";
819 &$target_power_off();
823 $builddir ||= dirname(File::Spec->rel2abs( ${$scripts[0]}{filename})) if scalar @scripts;
824 if (defined $builddir) {
825 chdir($builddir) or die "Can't change directory to $builddir: $!";
826 print STDERR "novaboot: Entering directory `$builddir'\n";
828 $builddir = $invocation_dir;
831 ## File generation phase
832 my (%files_iso, $menu_iso, $filename);
833 my $config_name = '';
836 foreach my $script (@scripts) {
837 $filename = $$script{filename};
838 $modules = $$script{modules};
839 $generated = $$script{generated};
840 $variables = $$script{variables};
842 ($config_name = $filename) =~ s#.*/##;
843 $config_name = $config_name_opt if (defined $config_name_opt);
845 if (exists $variables->{BUILDDIR}) {
846 $builddir = File::Spec->rel2abs($variables->{BUILDDIR});
847 chdir($builddir) or die "Can't change directory to $builddir: $!";
848 print STDERR "novaboot: Entering directory `$builddir'\n";
852 $prefix = $grub_prefix;
853 $prefix =~ s/\$NAME/$config_name/;
854 $prefix =~ s/\$BUILDDIR/$builddir/;
856 # TODO: use $grub_prefix as first parameter if some switch is given
857 generate_configs('', $generated, $filename);
859 ### Generate bootloader configuration files
860 my @bootloader_configs;
861 push @bootloader_configs, generate_grub_config($grub_config, $config_name, $prefix, $modules, $grub_preamble) if (defined $grub_config);
862 push @bootloader_configs, generate_grub2_config($grub2_config, $config_name, $prefix, $modules, $grub_preamble, $grub2_prolog) if (defined $grub2_config);
863 push @bootloader_configs, generate_pulsar_config('config-'.($pulsar||'novaboot'), $modules) if (defined $pulsar);
865 ### Run scons or make
867 my @files = map({ ($file) = m/([^ ]*)/; $file; } @$modules);
868 # Filter-out generated files
869 my @to_build = grep({ my $file = $_; !scalar(grep($file eq ($$_{filename} || ''), @$generated)) } @files);
871 system_verbose($scons || $CFG::scons." ".join(" ", @to_build)) if (defined $scons);
872 system_verbose($make || $CFG::make ." ".join(" ", @to_build)) if (defined $make);
875 ### Copy files (using rsync)
876 if (defined $server && !defined($gen_only)) {
877 (my $real_server = $server) =~ s/\$NAME/$config_name/;
879 my ($hostname, $path) = split(":", $real_server, 2);
880 if (! defined $path) {
884 my $files = join(" ", map({ ($file) = m/([^ ]*)/; $file; } ( @$modules, @bootloader_configs, @$copy)));
885 map({ my $file = (split)[0]; die "$file: $!" if ! -f $file; } @$modules);
886 my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb';
887 my $progress = $istty ? "--progress" : "";
889 system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
890 if ($server =~ m|/\$NAME$| && $concat) {
891 my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
892 system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
897 ### Prepare ISO image generation
898 if (defined $iso_image) {
899 generate_configs("(cd)", $generated, $filename);
901 generate_syslinux_config(\$menu, $config_name, "/", $modules);
902 $menu_iso .= "$menu\n";
903 map { ($file,undef) = split; $files_iso{$file} = 1; } @$modules;
907 ## Generate ISO image
908 if (defined $iso_image) {
909 system_verbose("mkdir -p isolinux");
912 if (-f '/usr/lib/ISOLINUX/isolinux.bin') {
913 # Newer ISOLINUX version
914 @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);
916 # Older ISOLINUX version
917 @files = qw(/usr/lib/syslinux/isolinux.bin /usr/lib/syslinux/mboot.c32 /usr/lib/syslinux/menu.c32);
919 system_verbose("cp @files isolinux");
920 open(my $fh, ">isolinux/isolinux.cfg");
922 print $fh "TIMEOUT 50\n";
923 print $fh "DEFAULT menu\n";
925 print $fh "DEFAULT $config_name\n";
927 print $fh "$menu_iso";
930 my $files = join(" ", map("$_=$_", (keys(%files_iso), 'isolinux/isolinux.cfg', map(s|.*/|isolinux/|r, @files))));
931 $iso_image ||= "$config_name.iso";
933 # Note: We use -U flag below to "Allow 'untranslated' filenames,
934 # completely violating the ISO9660 standards". Without this
935 # option, isolinux is not able to read files names for example
937 system_verbose("$CFG::genisoimage -R -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -hide-rr-moved -U -o $iso_image -graft-points $files");
938 print("ISO image created: $builddir/$iso_image\n");
941 exit(0) if defined $gen_only;
943 ## Boot the system using various methods and send serial output to stdout
945 if (scalar(@scripts) > 1 && ( defined $dhcp_tftp || defined $serial || defined $iprelay)) {
946 die "You cannot do this with multiple scripts simultaneously";
949 if ($variables->{WVTEST_TIMEOUT}) {
950 print STDERR "wvtest: timeout ", $variables->{WVTEST_TIMEOUT}, "\n";
957 $qemu ||= $variables->{QEMU} || $CFG::qemu;
958 my @qemu_flags = split(" ", $qemu);
959 $qemu = shift(@qemu_flags);
961 @qemu_flags = split(/ +/, trim($variables->{QEMU_FLAGS})) if exists $variables->{QEMU_FLAGS};
962 @qemu_flags = split(/ +/, trim($qemu_flags_cmd)) if $qemu_flags_cmd;
963 push(@qemu_flags, split(/ +/, trim($qemu_append || '')));
965 if (defined $iso_image) {
966 # Boot NOVA with grub (and test the iso image)
967 push(@qemu_flags, ('-cdrom', $iso_image));
969 # Boot NOVA without GRUB
971 # Non-patched qemu doesn't like commas, but NUL can live with pluses instead of commans
972 foreach (@$modules) {s/,/+/g;}
973 generate_configs("", $generated, $filename);
975 if (scalar @$modules) {
976 my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
977 $kcmd = '' if !defined $kcmd;
979 @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
980 my $initrd = join ",", @$modules;
982 push(@qemu_flags, ('-kernel', $kbin, '-append', $kcmd));
983 push(@qemu_flags, ('-initrd', $initrd)) if $initrd;
984 push(@qemu_flags, ('-dtb', $dtb)) if $dtb;
987 push(@qemu_flags, qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
988 unshift(@qemu_flags, ('-name', $config_name));
989 print STDERR "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
990 $exp = Expect->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
993 ### Local DHCPD and TFTPD
995 my ($dhcpd_pid, $tftpd_pid);
997 $tftp=1 if $tftp_port;
999 if (defined $dhcp_tftp)
1001 generate_configs("(nd)", $generated, $filename);
1002 system_verbose('mkdir -p tftpboot');
1003 generate_grub_config("tftpboot/os-menu.lst", $config_name, "(nd)", \@$modules, "timeout 0");
1004 open(my $fh, '>', 'dhcpd.conf');
1005 my $mac = `cat /sys/class/net/$netif/address`;
1007 print $fh "subnet 10.23.23.0 netmask 255.255.255.0 {
1008 range 10.23.23.10 10.23.23.100;
1009 filename \"bin/boot/grub/pxegrub.pxe\";
1010 next-server 10.23.23.1;
1013 hardware ethernet $mac;
1014 fixed-address 10.23.23.1;
1017 system_verbose("sudo ip a add 10.23.23.1/24 dev $netif;
1018 sudo ip l set dev $netif up;
1019 sudo touch dhcpd.leases");
1021 # We run servers by forking ourselves, because the servers end up
1022 # in our process group and get killed by signals sent to the
1023 # process group (e.g. Ctrl-C on terminal).
1024 $dhcpd_pid = fork();
1025 exec_verbose("sudo dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid") if ($dhcpd_pid == 0);
1028 if (defined $dhcp_tftp || defined $tftp) {
1030 # Unfortunately, tftpd requires root privileges even with
1031 # non-privileged (>1023) port due to initgroups().
1032 system_verbose("sudo in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid --address :$tftp_port $builddir");
1034 # Kill server when we die
1035 $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid') if (defined $dhcp_tftp);
1036 system_verbose('sudo pkill --pidfile=tftpd.pid'); };
1038 # We have to kill tftpd explicitely, because it is not in our process group
1039 $SIG{INT} = sub { system_verbose('sudo pkill --pidfile=tftpd.pid'); exit(0); };
1043 if (defined $ider) {
1044 my $ider_cmd= "amtider -c $iso_image -u $amt_user -p $amt_password $amt_host $amt_port" ;
1045 print STDERR "novaboot: Running: $ider_cmd\n" =~ s/\Q$amt_password\E/???/r;
1046 my $ider_pid = fork();
1047 if ($ider_pid == 0) {
1049 die "IDE redirection failed";
1051 # FIXME: This collides with --tftp option. Hopefully, nobody needs
1052 # to use both simultaneously.
1053 $SIG{__DIE__} = sub { system_verbose('kill $ider_pid'); };
1056 ### Reset target (IP relay, AMT, ...)
1058 if (defined $target_reset && $reset) {
1059 print STDERR "novaboot: Reseting the test box... ";
1061 print STDERR "done\n";
1063 # We don't want to output anything printed by the target
1064 # before reset so we clear the buffers now. This is, however,
1065 # not ideal because we may loose some data that were sent
1066 # after the reset. If this is a problem, one should reset and
1067 # connect to serial line in atomic manner. For example, if
1068 # supported by hardware, use --remote-cmd 'sterm -d ...' and
1069 # do not use separate --reset-cmd.
1070 my $log = $exp->log_stdout;
1071 $exp->log_stdout(0);
1072 $exp->expect(0); # Read data from target
1073 $exp->clear_accum(); # Clear the read data
1074 $exp->log_stdout($log);
1078 ### U-boot conversation
1079 if (defined $uboot) {
1080 my $uboot_prompt = $uboot || '=> ';
1081 print STDERR "novaboot: Waiting for U-Boot prompt...\n";
1082 $exp || die("No serial line connection");
1083 $exp->log_stdout(1);
1084 #$exp->exp_internal(1);
1086 [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
1087 $uboot_prompt) || die "No U-Boot prompt deteceted";
1088 foreach my $cmdspec (@uboot_init) {
1089 my ($cmd, $timeout);
1090 if (ref($cmdspec) eq "HASH") {
1091 $cmd = $cmdspec->{command};
1092 $timeout = $cmdspec->{timeout};
1097 if ($cmd =~ /\$NB_MYIP/) {
1098 my $ip = (grep /inet /, `ip addr show $netif`)[0] || die "Problem determining IP address of $netif";
1099 $ip =~ s/\s*inet ([0-9.]*).*/$1/;
1100 $cmd =~ s/\$NB_MYIP/$ip/g;
1102 if ($cmd =~ /\$NB_PREFIX/) {
1105 $cmd =~ s/\$NB_PREFIX/$p/g;
1108 $exp->send("$cmd\n");
1110 my ($matched_pattern_position, $error,
1111 $successfully_matching_string,
1112 $before_match, $after_match) =
1113 $exp->expect($timeout, $uboot_prompt);
1114 die "No U-Boot prompt: $error" if $error;
1115 #print "\n>>>>$before_match<<<<\n";
1118 # Load files if there are some load lines in the script
1119 if (scalar(@$modules) > 0 && !$variables->{NO_BOOT}) {
1120 my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
1122 @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
1123 my $initrd = shift @$modules;
1125 if (defined $kbin) {
1126 die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel};
1127 $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
1129 [qr/##/, sub { exp_continue; }],
1130 $uboot_prompt) || die "Kernel load timeout";
1133 die "No '--uboot-addr fdt' given" unless $uboot_addr{fdt};
1134 $exp->send("tftpboot $uboot_addr{fdt} $prefix$dtb\n");
1136 [qr/##/, sub { exp_continue; }],
1137 $uboot_prompt) || die "Device tree load timeout";
1139 if (defined $initrd) {
1140 die "No '--uboot-addr ramdisk' given" unless $uboot_addr{ramdisk};
1141 $exp->send("tftpboot $uboot_addr{ramdisk} $prefix$initrd\n");
1143 [qr/##/, sub { exp_continue; }],
1144 $uboot_prompt) || die "Initrd load timeout";
1146 $uboot_addr{ramdisk} = '-';
1150 $exp->send("setenv bootargs $kcmd\n");
1151 $exp->expect(5, $uboot_prompt) || die "U-Boot prompt timeout";
1154 $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
1155 if (!$variables->{NO_BOOT} && $uboot_cmd ne '') {
1156 $uboot_cmd =~ s/\$kernel_addr/$uboot_addr{kernel}/g;
1157 $uboot_cmd =~ s/\$ramdisk_addr/$uboot_addr{ramdisk}/g;
1158 $uboot_cmd =~ s/\$fdt_addr/$uboot_addr{fdt}/g;
1160 $exp->send($uboot_cmd . "\n");
1161 $exp->expect(5, "\n") || die "U-Boot command timeout";
1165 ### Serial line interaction
1166 if ($interaction && defined $exp) {
1167 # Serial line of the target is available
1168 my $interrupt = 'Ctrl-C';
1169 if ($interactive && !@exiton) {
1170 $interrupt = '"~~."';
1172 print STDERR "novaboot: Serial line interaction (press $interrupt to interrupt)...\n";
1173 $exp->log_stdout(1);
1175 $exp->expect($exiton_timeout, @expect_raw, @exiton) || die("exiton timeout");
1177 my @inputs = ($exp);
1178 my $infile = new IO::File;
1179 $infile->IO::File::fdopen(*STDIN,'r');
1180 my $in_object = Expect->exp_init($infile);
1181 $in_object->set_group($exp);
1184 $in_object->set_seq('~~\.', sub { print STDERR "novaboot: Escape sequence detected\r\n"; undef; });
1185 $in_object->manual_stty(0); # Use raw terminal mode
1187 $in_object->manual_stty(1); # Do not modify terminal settings
1189 push(@inputs, $in_object);
1191 #print Dumper(\@expect_raw);
1192 $exp->expect(undef, @expect_raw) if @expect_raw;
1193 Expect::interconnect(@inputs) unless defined($exp->exitstatus);
1197 ## Kill dhcpc or tftpd
1198 if (defined $dhcp_tftp || defined $tftp) {
1199 die("novaboot: This should kill servers on background\n");
1202 # Always finish novaboot output with newline
1203 print "\n" if $final_eol;
1210 novaboot - Boots a locally compiled operating system on a remote
1217 B<novaboot> [option]... [--] script...
1219 B<./script> [option]...
1223 Novaboot makes booting of a locally compiled operating system (OS)
1224 (e.g. NOVA or Linux) on remote targets as simple as running a program
1225 locally. It automates things like copying OS images to a TFTP server,
1226 generation of bootloader configuration files, resetting of target
1227 hardware or redirection of target's serial line to stdin/out. Novaboot
1228 is highly configurable and makes it easy to boot a single image on
1229 different targets or different images on a single target.
1231 Novaboot operation is controlled by configuration files, command line
1232 options and by a so called novaboot script, which can be thought as a
1233 generalization of bootloader configuration files (see L</"NOVABOOT
1234 SCRIPT SYNTAX">). Typical way of using novaboot is to make the
1235 novaboot script executable and set its first line to I<#!/usr/bin/env
1236 novaboot>. Then, booting a particular OS configuration becomes the
1237 same as executing a local program – the novaboot script.
1239 Novaboot uses configuration files to, among other things, define
1240 command line options needed for different targets. Users typically use
1241 only the B<-t>/B<--target> command line option to select the target.
1242 Internally, this option expands to the pre-configured options.
1243 Configuration files are searched at multiple places, which allows to
1244 have per-system, per-user or per-project configurations. Configuration
1245 file syntax is described in section L</"CONFIGURATION FILES">.
1247 Simple examples of using C<novaboot>:
1253 Run an OS in Qemu. This can be specified with the B<--qemu> option.
1256 novaboot --qemu myos
1258 (or C<./myos --qemu> as described above) will run Qemu and make it
1259 boot the configuration specified in the F<myos> script.
1263 Create a bootloader configuration file (currently supported
1264 bootloaders are GRUB, GRUB2, ISOLINUX, Pulsar and U-Boot) and copy it
1265 with all other files needed for booting to a remote boot server. Then
1266 use a TCP/IP-controlled relay/serial-to-TCP converter to reset the
1267 target and receive its serial output.
1269 ./myos --grub2 --server=192.168.1.1:/tftp --iprelay=192.168.1.2
1271 Alternatively, you can put these switches to the configuration file
1274 ./myos --target mytarget
1278 Run DHCP and TFTP server on developer's machine to boot the target
1283 This is useful when no network infrastructure is in place and
1284 the target is connected directly to developer's box.
1288 Create bootable ISO image.
1290 novaboot --iso -- script1 script2
1292 The created ISO image will have ISOLINUX bootloader installed on it
1293 and the boot menu will allow selecting between I<script1> and
1294 I<script2> configurations.
1298 =head1 PHASES AND OPTIONS
1300 Novaboot performs its work in several phases. Each phase can be
1301 influenced by several command line options, certain phases can be
1302 skipped. The list of phases (in the execution order) is as follows.
1306 =item 1. L<Configuration reading|/Configuration reading phase>
1308 =item 2. L<Command line processing|/Command line processing phase>
1310 =item 3. L<Script preprocessing|/Script preprocessing phase>
1312 =item 4. L<File generation|/File generation phase>
1314 =item 5. L<Target connection|/Target connection check>
1316 =item 6. L<File deployment|/File deployment phase>
1318 =item 7. L<Target power-on and reset|/Target power-on and reset phase>
1320 =item 8. L<Interaction with the bootloader|/Interaction with the bootloader on the target>
1322 =item 9. L<Target interaction|/Target interaction phase>
1326 Each phase is described in the following sections together with the
1327 command line options that control it.
1329 =head2 Configuration reading phase
1331 After starting, novaboot reads configuration files. Their content is
1332 described in section L</"CONFIGURATION FILES">. By default,
1333 configuration is read from multiple locations. First from the system
1334 configuration directory (F</etc/novaboot.d/>), second from the user
1335 configuration file (F<~/.config/novaboot>) and third from F<.novaboot>
1336 files along the path to the current directory. Alternatively, a single
1337 configuration file specified with the B<-c> switch or with the
1338 C<NOVABOOT_CONFIG> environment variable is read. The latter read files
1339 override settings from the former ones.
1341 The system configuration directory is determined by the content of
1342 NOVABOOT_CONFIG_DIR environment variable and defaults to
1343 F</etc/novaboot.d>. Files in this directory with names consisting
1344 solely of English letters, numbers, dashes '-' and underscores '_'
1345 (note that dot '.' is not included) are read in alphabetical order.
1347 Then, the user configuration file is read from
1348 F<$XDG_CONFIG_HOME/novaboot>. If C<$XDG_CONFIG_HOME> environemnt
1349 variable is not set F<~/.config/novaboot> is read instead.
1351 Finally, novaboot searches for files named F<.novaboot> starting from the
1352 directory of the novaboot script (or working directory, see bellow)
1353 and continuing upwards up to the root directory. The found
1354 configuration files are then read in the opposite order (i.e. from the
1355 root directory downwards). This allows to have, for example, a project
1356 specific configuration in F<~/project/.novaboot>.
1358 Note the difference between F<~/.config/novaboot> and F<~/.novaboot>.
1359 The former one is read always, whereas the latter only when novaboot
1360 script or working directory is under the C<$HOME> directory.
1362 In certain cases, the location of the novaboot script cannot be
1363 determined in this early phase. This happens either when the script is
1364 read from the standard input or when novaboot is invoked explicitly as
1365 in the example L</"4."> above. In this case the current working
1366 directory is used as a starting point for configuration file search
1367 instead of the novaboot script directory.
1371 =item -c, --config=I<filename>
1373 Use the specified configuration file instead of the default one(s).
1377 =head2 Command line processing phase
1383 Dump the current configuration to stdout end exit. Useful as an
1384 initial template for a configuration file.
1388 Print short (B<-h>) or long (B<--help>) help.
1390 =item -t, --target=I<target>
1392 This option serves as a user configurable shortcut for other novaboot
1393 options. The effect of this option is the same as specifying the
1394 options stored in the C<%targets> configuration variable under key
1395 I<target>. See also L</"CONFIGURATION FILES">.
1397 When this option is not given, novaboot tries to determine the target
1398 to use from either B<NOVABOOT_TARGET> environment variable or from
1399 B<$default_target> configuration file variable.
1403 =head2 Script preprocessing phase
1405 This phases allows to modify the parsed novaboot script before it is
1406 used in the later phases.
1410 =item -a, --append=I<parameters>
1412 Append a string to the first C<load> line in the novaboot script. This
1413 can be used to append parameters to the kernel's or root task's
1414 command line. This option can appear multiple times.
1418 Use L<bender|https://github.com/TUD-OS/morbo/blob/master/standalone/bender.c>
1419 chainloader. Bender scans the PCI bus for PCI serial ports and stores
1420 the information about them in the BIOS data area for use by the
1423 =item --chainloader=I<chainloader>
1425 Specifies a chainloader that is loaded before the kernel and other
1426 files specified in the novaboot script. E.g. 'bin/boot/bender
1431 Print the modules to boot and their parameters after this phase
1432 finishes. Then exit. This is useful for seeing the effect of other
1433 options in this section.
1435 =item -k, --kernel=F<file>
1437 Replace the first word on the first C<load> line in the novaboot
1438 script with F<file>.
1440 =item --scriptmod=I<perl expression>
1442 When novaboot script is read, I<perl expression> is executed for every
1443 line (in $_ variable). For example, C<novaboot
1444 --scriptmod=s/sigma0/omega6/g> replaces every occurrence of I<sigma0>
1445 in the script with I<omega6>.
1447 When this option is present, it overrides I<$script_modifier> variable
1448 from the configuration file, which has the same effect. If this option
1449 is given multiple times all expressions are evaluated in the command
1454 =head2 File generation phase
1456 In this phase, files needed for booting are generated in a so called
1457 I<build directory> (see L</--build-dir>). In most cases configuration
1458 for a bootloader is generated automatically by novaboot. It is also
1459 possible to generate other files using I<heredoc> or I<"<"> syntax in
1460 novaboot scripts. Finally, binaries can be generated in this phases by
1461 running C<scons> or C<make>.
1465 =item --build-dir=I<directory>
1467 Overrides the default build directory location.
1469 The default build directory location is determined as follows: If the
1470 configuration file defines the C<$builddir> variable, its value is
1471 used. Otherwise, it is the directory that contains the first processed
1474 See also L</BUILDDIR> variable.
1476 =item -g, --grub[=I<filename>]
1478 Generates grub bootloader menu file. If the I<filename> is not
1479 specified, F<menu.lst> is used. The I<filename> is relative to the
1480 build directory (see B<--build-dir>).
1482 =item --grub-preamble=I<prefix>
1484 Specifies the I<preable> that is at the beginning of the generated
1485 GRUB or GRUB2 config files. This is useful for specifying GRUB's
1488 =item --prefix=I<prefix>
1490 Specifies I<prefix> (e.g. F</srv/tftp>) that is put in front of every
1491 file name in generated bootloader configuration files (or in U-Boot
1494 If the I<prefix> contains string $NAME, it will be replaced with the
1495 name of the novaboot script (see also B<--name>).
1497 If the I<prefix> contains string $BUILDDIR, it will be replaced with
1498 the build directory (see also B<--build-dir>).
1502 Alias for B<--prefix>.
1504 =item --grub2[=I<filename>]
1506 Generate GRUB2 menu entry in I<filename>. If I<filename> is not
1507 specified F<grub.cfg> is used. The content of the menu entry can be
1508 customized with B<--grub-preamble>, B<--grub2-prolog> or
1509 B<--grub_prefix> options.
1511 In order to use the the generated menu entry on your development
1512 machine that uses GRUB2, append the following snippet to
1513 F</etc/grub.d/40_custom> file and regenerate your grub configuration,
1514 i.e. run update-grub on Debian/Ubuntu.
1516 if [ -f /path/to/nul/build/grub.cfg ]; then
1517 source /path/to/nul/build/grub.cfg
1520 =item --grub2-prolog=I<prolog>
1522 Specifies text that is put at the beginning of the GRUB2 menu entry.
1524 =item -m, --make[=make command]
1526 Runs C<make> to build files that are not generated by novaboot itself.
1528 =item --name=I<string>
1530 Use the name I<string> instead of the name of the novaboot script.
1531 This name is used for things like a title of grub menu or for the
1532 server directory where the boot files are copied to.
1536 Do not run external commands to generate files (i.e. "<" syntax and
1537 C<run> keyword). This switch does not influence generation of files
1538 specified with "<<WORD" syntax.
1540 =item -p, --pulsar[=mac]
1542 Generates pulsar bootloader configuration file named F<config-I<mac>>
1543 The I<mac> string is typically a MAC address and defaults to
1546 =item --scons[=scons command]
1548 Runs C<scons> to build files that are not generated by novaboot
1553 Strip I<rom://> prefix from command lines and generated config files.
1554 The I<rom://> prefix is used by NUL. For NRE, it has to be stripped.
1558 Exit novaboot after file generation phase.
1562 =head2 Target connection check
1564 If supported by the target, the connection to it is made and it is
1565 checked whether the target is not occupied by another novaboot
1570 =item --amt=I<"[user[:password]@]host[:port]>
1572 Use Intel AMT technology to control the target machine. WS management
1573 is used to powercycle it and Serial-Over-Lan (SOL) for input/output.
1574 The hostname or (IP address) is given by the I<host> parameter. If
1575 I<password> is not specified, environment variable AMT_PASSWORD is
1576 used. The I<port> specifies a TCP port for SOL. If not specified, the
1577 default is 16992. Default I<user> is admin.
1579 =item --iprelay=I<addr[:port]>
1581 Use TCP/IP relay and serial port to access the target's serial port
1582 and powercycle it. The IP address of the relay is given by I<addr>
1583 parameter. If I<port> is not specified, it default to 23.
1585 Note: This option is supposed to work with HWG-ER02a IP relays.
1587 =item -s, --serial[=device]
1589 Target's serial line is connected to host's serial line (device). The
1590 default value for device is F</dev/ttyUSB0>.
1592 The value of this option is exported in NB_NOVABOOT environment
1593 variable to all subprocesses run by C<novaboot>.
1595 =item --stty=I<settings>
1597 Specifies settings passed to C<stty> invoked on the serial line
1598 specified with B<--serial> option. If this option is not given,
1599 C<stty> is called with C<raw -crtscts -onlcr 115200> settings.
1601 =item --remote-cmd=I<cmd>
1603 Command that mediates connection to the target's serial line. For
1604 example C<ssh server 'cu -l /dev/ttyS0'>.
1606 =item --remote-expect=I<string>
1608 Wait for reception of I<string> after establishing the remote
1611 =item --remote-expect-silent=I<string>
1613 The same as B<--remote-expect> except that the remote output is not
1614 echoed to stdout while waiting for the I<string>. Everything after the
1615 matched string is printed to stdout, so you may want to include line
1616 end characters in the I<string> as well.
1620 =head2 File deployment phase
1622 In some setups, it is necessary to copy the files needed for booting
1623 to a particular location, e.g. to a TFTP boot server or to the
1628 =item -d, --dhcp-tftp
1630 Turns your workstation into a DHCP and TFTP server so that the OS can
1631 be booted via PXE BIOS (or similar mechanism) on the test machine
1632 directly connected by a plain Ethernet cable to your workstation.
1634 The DHCP and TFTP servers requires root privileges and C<novaboot>
1635 uses C<sudo> command to obtain those. You can put the following to
1636 I</etc/sudoers> to allow running the necessary commands without asking
1639 Cmnd_Alias NOVABOOT = /bin/ip a add 10.23.23.1/24 dev eth0, /bin/ip l set dev eth0 up, /usr/sbin/dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid, /usr/sbin/in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/touch dhcpd.leases, /usr/bin/pkill --pidfile=dhcpd.pid, /usr/bin/pkill --pidfile=tftpd.pid
1640 your_login ALL=NOPASSWD: NOVABOOT
1644 Starts a TFTP server on your workstation. This is similar to
1645 B<--dhcp-tftp> except that DHCP server is not started.
1647 The TFTP server require root privileges and C<novaboot> uses C<sudo>
1648 command to obtain those. You can put the following to I</etc/sudoers>
1649 to allow running the necessary commands without asking for password.
1651 Cmnd_Alias NOVABOOT = /usr/sbin/in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/pkill --pidfile=tftpd.pid
1652 your_login ALL=NOPASSWD: NOVABOOT
1654 =item --tftp-port=I<port>
1656 Port to run the TFTP server on. Implies B<--tftp>.
1658 =item --netif=I<network interface>
1660 Network interface used to deploy files to the target. Default value is
1661 I<eth0>. This influences the configuration of the DHCP server started
1662 by B<--dhcp-tftp> and the value that B<$NB_MYIP> get replaced with in
1663 U-Boot conversation.
1665 =item --iso[=filename]
1667 Generates the ISO image that boots NOVA system via GRUB. If no filename
1668 is given, the image is stored under I<NAME>.iso, where I<NAME> is the name
1669 of the novaboot script (see also B<--name>).
1671 =item --server[=[[user@]server:]path]
1673 Copy all files needed for booting to another location. The files will
1674 be copied (by B<rsync> tool) to the directory I<path>. If the I<path>
1675 contains string $NAME, it will be replaced with the name of the
1676 novaboot script (see also B<--name>).
1678 =item --rsync-flags=I<flags>
1680 Specifies which I<flags> are appended to F<rsync> command line when
1681 copying files as a result of I<--server> option.
1685 If B<--server> is used and its value ends with $NAME, then after
1686 copying the files, a new bootloader configuration file (e.g. menu.lst)
1687 is created at I<path-wo-name>, i.e. the path specified by B<--server>
1688 with $NAME part removed. The content of the file is created by
1689 concatenating all files of the same name from all subdirectories of
1690 I<path-wo-name> found on the "server".
1694 Use Intel AMT technology for IDE redirection. This allows the target
1695 machine to boot from novaboot created ISO image. Implies B<--iso>.
1697 The experimental C<amtider> utility needed by this option can be
1698 obtained from https://github.com/wentasah/amtterm.
1702 =head2 Target power-on and reset phase
1704 At this point, the target is reset (or switched on/off). There is
1705 several ways how this can be accomplished. Resetting a physical target
1706 can currently be accomplished by the following options: B<--amt>,
1707 B<--iprelay>, B<--reset-cmd> and B<--reset-send>.
1713 Switch on/off the target machine and exit. The script (if any) is
1714 completely ignored. Currently it works only with B<--iprelay> or
1717 =item -Q, --qemu[=I<qemu-binary>]
1719 Boot the configuration in qemu. Optionally, the name of qemu binary
1720 can be specified as a parameter.
1722 =item --qemu-append=I<flags>
1724 Append I<flags> to the default qemu flags (QEMU_FLAGS variable or
1725 C<-cpu coreduo -smp 2>).
1727 =item -q, --qemu-flags=I<flags>
1729 Replace the default qemu flags (QEMU_FLAGS variable or C<-cpu coreduo
1730 -smp 2>) with I<flags> specified here.
1732 =item --reset-cmd=I<cmd>
1734 Command that resets the target.
1736 =item --reset-send=I<string>
1738 Reset the target by sending the given I<string> to the remote serial
1739 line. "\n" sequences are replaced with newline character.
1741 =item --no-reset, --reset
1743 Disable/enable resetting of the target.
1747 =head2 Interaction with the bootloader on the target
1751 =item --uboot[=I<prompt>]
1753 Interact with U-Boot bootloader to boot the thing described in the
1754 novaboot script. I<prompt> specifies the U-Boot's prompt (default is
1755 "=> ", other common prompts are "U-Boot> " or "U-Boot# ").
1756 Implementation of this option is currently tied to a particular board
1757 that we use. It may be subject to changes in the future!
1761 Disable U-Boot interaction previously enabled with B<--uboot>.
1765 Command(s) to send the U-Boot bootloader before loading the images and
1766 booting them. This option can be given multiple times. After sending
1767 commands from each option novaboot waits for U-Boot I<prompt>.
1769 If the command contains string I<$NB_MYIP> then this string is
1770 replaced by IPv4 address of eth0 interface (see also B<--netif>).
1771 Similarly I<$NB_PREFIX> is replaced with prefix given by B<--prefix>.
1773 See also C<uboot> keyword in L</"NOVABOOT SCRIPT SYNTAX">).
1775 =item --uboot-addr I<name>=I<address>
1777 Load address of U-Boot's C<tftpboot> command for loading I<name>,
1778 where name is one of I<kernel>, I<ramdisk> or I<fdt> (flattened device
1781 The default addresses are ${I<name>_addr_r}, i.e. U-Boot environment
1782 variables used by convention for this purpose.
1784 =item --uboot-cmd=I<command>
1786 Specifies U-Boot command used to execute the OS. If the command
1787 contains strings C<$kernel_addr>, C<$ramdisk_addr>, C<$fdt_addr>,
1788 these are replaced with the addresses configured with B<--uboot-addr>.
1790 The default value is
1792 bootm $kernel_addr $ramdisk_addr $fdt_addr
1794 or the C<UBOOT_CMD> variable if defined in the novaboot script.
1798 =head2 Target interaction phase
1800 In this phase, target's serial output is redirected to stdout and if
1801 stdin is a TTY, it is redirected to the target's serial input allowing
1802 interactive work with the target.
1806 =item --exiton=I<string>
1808 When I<string> is sent by the target, novaboot exits. This option can
1809 be specified multiple times, in which case novaboot exits whenever
1810 either of the specified strings is sent.
1812 If I<string> is C<-re>, then the next B<--exiton>'s I<string> is
1813 treated as regular expression. For example:
1815 --exiton -re --exiton 'error:.*failed'
1817 =item --exiton-re=I<regex>
1819 The same as --exiton -re --exiton I<regex>.
1821 =item --exiton-timeout=I<seconds>
1823 By default B<--exiton> waits for the string match forever. When this
1824 option is specified, "exiton" timeouts after the specifies number of
1825 seconds and novaboot returns non-zero exit code.
1827 =item -i, --interactive
1829 Setup things for interactive use of target. Your terminal will be
1830 switched to raw mode. In raw mode, your system does not process input
1831 in any way (no echoing of entered characters, no interpretation
1832 special characters). This, among others, means that Ctrl-C is passed
1833 to the target and does no longer interrupt novaboot. Use "~~."
1834 sequence to exit novaboot.
1836 =item --no-interaction, --interaction
1838 Skip resp. force target interaction phase. When skipped, novaboot exits
1839 immediately when boot is initiated.
1841 =item --expect=I<string>
1843 When I<string> is received from the target, send the string specified
1844 with the subsequent B<--send*> option to the target.
1846 =item --expect-re=I<regex>
1848 When target's output matches regular expression I<regex>, send the
1849 string specified with the subsequent B<--send*> option to the target.
1851 =item --expect-raw=I<perl-code>
1853 Provides direct control over Perl's Expect module.
1855 =item --send=I<string>
1857 Send I<string> to the target after the previously specified
1858 B<--expect*> was matched in the target's output. The I<string> may
1859 contain escape sequences such as "\n".
1861 Note that I<string> is actually interpreted by Perl, so it can contain
1862 much more that escape sequences. This behavior may change in the
1865 Example: C<--expect='login: ' --send='root\n'>
1867 =item --sendcont=I<string>
1869 Similar to B<--send> but continue expecting more input.
1871 Example: C<--expect='Continue?' --sendcont='yes\n'>
1873 =item --final-eol, --no-final-eol
1875 By default, B<novaboot> always prints an end-of-line character at the
1876 end of its execution in order to ensure that the output of programs
1877 started after novaboot appears at the beginning of the line. When this
1878 is not desired B<--no-final-eol> option can be used to override this
1883 =head1 NOVABOOT SCRIPT SYNTAX
1885 The syntax tries to mimic POSIX shell syntax. The syntax is defined
1886 with the following rules.
1888 Lines starting with "#" and empty lines are ignored.
1890 Lines that end with "\" are concatenated with the following line after
1891 removal of the final "\" and leading whitespace of the following line.
1893 Lines of the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
1894 expression) assign values to internal variables. See L</VARIABLES>
1897 Otherwise, the first word on the line defines the meaning of the line.
1898 The following keywords are supported:
1904 These lines represent modules to boot. The
1905 word after C<load> is a file name (relative to the build directory
1906 (see B<--build-dir>) of the module to load and the remaining words are
1907 passed to it as the command line parameters.
1909 When the C<load> line ends with "<<WORD" then the subsequent lines
1910 until the line containing solely WORD are copied literally to the file
1911 named on that line. This is similar to shell's heredoc feature.
1913 When the C<load> line ends with "< CMD" then command CMD is executed
1914 with F</bin/sh> and its standard output is stored in the file named on
1915 that line. The SRCDIR variable in CMD's environment is set to the
1916 absolute path of the directory containing the interpreted novaboot
1921 These lines are similar to C<load> lines. The
1922 file mentioned there is copied to the same place as in case of C<load>
1923 (e.g. tftp server), but the file is not used in the bootloader
1924 configuration. Such a file can be used by the target for other
1925 purposed than booting, e.g. at OS runtime or for firmware update.
1929 Lines starting with C<run> keyword contain shell commands that are run
1930 during file generation phase. This is the same as the "< CMD" syntax
1931 for C<load> keyboard except that the command's output is not
1932 redirected to a file. The ordering of commands is the same as they
1933 appear in the novaboot script.
1937 Lines starting with C<uboot> represent U-Boot commands that are sent
1938 to the target if B<--uboot> option is given. Having a U-Boot line in
1939 the novaboot script is the same as passing an equivalent
1940 B<--uboot-init> option to novaboot. The C<uboot> keyword can be
1941 suffixed with timeout specification. The syntax is C<uboot:Ns>, where
1942 C<N> is the whole number of seconds. If the U-Boot command prompt does
1943 not appear before the timeout, novaboot fails. The default timeout is
1950 #!/usr/bin/env novaboot
1951 load bzImage console=ttyS0,115200
1952 run make -C buildroot
1953 load rootfs.cpio < gen_cpio buildroot/images/rootfs.cpio "myapp->/etc/init.d/S99myapp"
1955 Example (NOVA User Land - NUL):
1957 #!/usr/bin/env novaboot
1958 WVDESC=Example program
1959 load bin/apps/sigma0.nul S0_DEFAULT script_start:1,1 \
1960 verbose hostkeyb:0,0x60,1,12,2
1961 load bin/apps/hello.nul
1962 load hello.nulconfig <<EOF
1963 sigma0::mem:16 name::/s0/log name::/s0/timer name::/s0/fs/rom ||
1964 rom://bin/apps/hello.nul
1967 This example will load three modules: F<sigma0.nul>, F<hello.nul> and
1968 F<hello.nulconfig>. sigma0 receives some command line parameters and
1969 F<hello.nulconfig> file is generated on the fly from the lines between
1970 C<<<EOF> and C<EOF>.
1974 The following variables are interpreted in the novaboot script:
1980 Novaboot chdir()s to this directory before file generation phase. The
1981 directory name specified here is relative to the build directory
1982 specified by other means (see L</--build-dir>).
1986 Assigning this variable has the same effect as specifying L</--exiton>
1989 =item HYPERVISOR_PARAMS
1991 Parameters passed to hypervisor. The default value is "serial", unless
1992 overridden in configuration file.
1996 The kernel to use instead of the hypervisor specified in the
1997 configuration file with the C<$hypervisor> variable. The value should
1998 contain the name of the kernel image as well as its command line
1999 parameters. If this variable is defined and non-empty, the variable
2000 HYPERVISOR_PARAMS is not used.
2004 If this variable is 1, the system is not booted. This is currently
2005 only implemented for U-Boot bootloader where it is useful for
2006 interacting with the bootloader without booting the system - e.g. for
2011 Use a specific qemu binary (can be overridden with B<-Q>) and flags
2012 when booting this script under qemu. If QEMU_FLAGS variable is also
2013 specified flags specified in QEMU variable are replaced by those in
2018 Use specific qemu flags (can be overridden with B<-q>).
2022 See L</--uboot-cmd>.
2026 Description of the WvTest-compliant program.
2028 =item WVTEST_TIMEOUT
2030 The timeout in seconds for WvTest harness. If no complete line appears
2031 in the test output within the time specified here, the test fails. It
2032 is necessary to specify this for long running tests that produce no
2033 intermediate output.
2037 =head1 CONFIGURATION FILES
2039 Novaboot can read its configuration from one or more files. By
2040 default, novaboot looks for files in F</etc/novaboot.d>, file
2041 F<~/.config/novaboot> and files named F<.novaboot> as described in
2042 L</Configuration reading phase>. Alternatively, configuration file
2043 location can be specified with the B<-c> switch or with the
2044 NOVABOOT_CONFIG environment variable. The configuration file has Perl
2045 syntax (i.e. it is better to put C<1;> as the last line) and should set
2046 values of certain Perl variables. The current configuration can be
2047 dumped with the B<--dump-config> switch. Some configuration variables
2048 can be overridden by environment variables (see below) or by command
2051 Supported configuration variables include:
2057 Build directory location relative to the location of the configuration
2060 =item $default_target
2062 Default target (see below) to use when no target is explicitly
2063 specified with the B<--target> command line option or
2064 B<NOVABOOT_TARGET> environment variable.
2068 Hash of target definitions to be used with the B<--target> option. The
2069 key is the identifier of the target, the value is the string with
2070 command line options. For instance, if the configuration file contains:
2072 $targets{'mybox'} = '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
2074 then the following two commands are equivalent:
2076 ./myos --server=boot:/tftproot --serial=/dev/ttyUSB0 --grub
2081 =head1 ENVIRONMENT VARIABLES
2083 Some options can be specified not only via config file or command line
2084 but also through environment variables. Environment variables override
2085 the values from configuration file and command line parameters
2086 override the environment variables.
2090 =item NOVABOOT_CONFIG
2092 Name of the novaboot configuration file to use instead of the default
2095 =item NOVABOOT_CONFIG_DIR
2097 Name of the novaboot configuration directory. When not specified
2098 F</etc/novaboot.d> is used.
2100 =item NOVABOOT_TARGET
2102 Name of the novaboot target to use. This overrides the value of
2103 B<$default_target> from the configuration file and can be overriden
2104 with the B<--target> command line option.
2106 =item NOVABOOT_BENDER
2108 Defining this variable has the same effect as using B<--bender>
2115 Michal Sojka <sojka@os.inf.tu-dresden.de>
2117 Latest novaboot version can be found at
2118 L<https://github.com/wentasah/novaboot>.
2122 # LocalWords: novaboot Novaboot NOVABOOT TFTP PXE DHCP filename stty
2123 # LocalWords: chainloader stdout Qemu qemu preprocessing ISOLINUX bootable
2124 # LocalWords: config subprocesses sudo sudoers tftp dhcp IDE stdin
2125 # LocalWords: subdirectories TTY whitespace heredoc POSIX WvTest