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" => sub { push @uboot_init, { command => $_[1] }; },
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 push @uboot_init, { command => $2 ? "" : $3,
374 system => $2 ? $3 : "",
375 timeout => $1 // 10 };
379 die("novaboot: Cannot parse script '$last_fn' line $.. Didn't you forget 'load' keyword?\n");
382 # print Dumper(\@scripts);
384 foreach my $script (@scripts) {
385 $modules = $$script{modules};
386 @$modules[0] =~ s/^[^ ]*/$kernel_opt/ if $kernel_opt;
387 @$modules[0] .= ' ' . join(' ', @append) if @append;
390 if (exists $variables->{KERNEL}) {
391 $kernel = $variables->{KERNEL};
393 if ($CFG::hypervisor) {
394 $kernel = $CFG::hypervisor . " ";
395 if (exists $variables->{HYPERVISOR_PARAMS}) {
396 $kernel .= $variables->{HYPERVISOR_PARAMS};
398 $kernel .= $CFG::hypervisor_params;
402 @$modules = ($kernel, @$modules) if $kernel;
403 @$modules = (@chainloaders, @$modules);
404 @$modules = ("bin/boot/bender", @$modules) if ($bender || defined $ENV{'NOVABOOT_BENDER'});
408 foreach my $script (@scripts) {
409 print join("\n", @{$$script{modules}})."\n";
416 sub generate_configs($$$) {
417 my ($base, $generated, $filename) = @_;
418 if ($base) { $base = "$base/"; };
419 foreach my $g(@$generated) {
420 if (exists $$g{content}) {
421 my $config = $$g{content};
422 my $fn = $$g{filename};
423 open(my $f, '>', $fn) || die("$fn: $!");
424 map { s|\brom://([^ ]*)|$rom_prefix$base$1|g; print $f "$_"; } @{$config};
426 print STDERR "novaboot: Created $fn\n";
427 } elsif (exists $$g{command} && ! $no_file_gen) {
428 $ENV{SRCDIR} = dirname(File::Spec->rel2abs( $filename, $invocation_dir ));
429 if (exists $$g{filename}) {
430 system_verbose("( $$g{command} ) > $$g{filename}");
432 system_verbose($$g{command});
438 sub generate_grub_config($$$$;$)
440 my ($filename, $title, $base, $modules_ref, $preamble) = @_;
441 if ($base) { $base = "$base/"; };
442 open(my $fg, '>', $filename) or die "$filename: $!";
443 print $fg "$preamble\n" if $preamble;
444 print $fg "title $title\n" if $title;
445 #print $fg "root $base\n"; # root doesn't really work for (nd)
447 foreach (@$modules_ref) {
450 my ($kbin, $kcmd) = split(' ', $_, 2);
451 $kcmd = '' if !defined $kcmd;
452 print $fg "kernel ${base}$kbin $kcmd\n";
454 s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
455 print $fg "module $base$_\n";
459 print("novaboot: Created $builddir/$filename\n");
463 sub generate_syslinux_config($$$$)
465 my ($filename, $title, $base, $modules_ref) = @_;
466 if ($base && $base !~ /\/$/) { $base = "$base/"; };
467 open(my $fg, '>', $filename) or die "$filename: $!";
468 print $fg "LABEL $title\n";
469 #TODO print $fg "MENU LABEL $human_readable_title\n";
471 my ($kbin, $kcmd) = split(' ', @$modules_ref[0], 2);
473 if (system("file $kbin|grep 'Linux kernel'") == 0) {
474 my $initrd = @$modules_ref[1];
475 die('To many "load" lines for Linux kernel') if (scalar @$modules_ref > 2);
476 print $fg "LINUX $base$kbin\n";
477 print $fg "APPEND $kcmd\n";
478 print $fg "INITRD $base$initrd\n";
480 print $fg "KERNEL mboot.c32\n";
482 foreach (@$modules_ref) {
483 s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
484 push @append, "$base$_";
485 print $fg "APPEND ".join(' --- ', @append)."\n";
488 #TODO print $fg "TEXT HELP\n";
489 #TODO print $fg "some help here\n";
490 #TODO print $fg "ENDTEXT\n";
492 print("novaboot: Created $builddir/$filename\n");
496 sub generate_grub2_config($$$$;$$)
498 my ($filename, $title, $base, $modules_ref, $preamble, $prolog) = @_;
499 if ($base && substr($base,-1,1) ne '/') { $base = "$base/"; };
500 open(my $fg, '>', $filename) or die "$filename: $!";
501 print $fg "$preamble\n" if $preamble;
502 $title ||= 'novaboot';
503 print $fg "menuentry $title {\n";
504 print $fg "$prolog\n" if $prolog;
506 foreach (@$modules_ref) {
509 my ($kbin, $kcmd) = split(' ', $_, 2);
510 $kcmd = '' if !defined $kcmd;
511 print $fg " multiboot ${base}$kbin $kcmd\n";
514 # GRUB2 doesn't pass filename in multiboot info so we have to duplicate it here
515 $_ = join(' ', ($args[0], @args));
516 s|\brom://|$rom_prefix|g; # We do not need to translate path for GRUB2
517 print $fg " module $base$_\n";
522 print("novaboot: Created $builddir/$filename\n");
526 sub generate_pulsar_config($$)
528 my ($filename, $modules_ref) = @_;
529 open(my $fg, '>', $filename) or die "$filename: $!";
530 print $fg "root $pulsar_root\n" if defined $pulsar_root;
533 foreach (@$modules_ref) {
536 ($kbin, $kcmd) = split(' ', $_, 2);
537 $kcmd = '' if !defined $kcmd;
540 s|\brom://|$rom_prefix|g;
541 print $fg "load $_\n";
544 # Put kernel as last - this is needed for booting Linux and has no influence on non-Linux OSes
545 print $fg "exec $kbin $kcmd\n";
547 print("novaboot: Created $builddir/$filename\n");
551 sub shell_cmd_string(@)
553 return join(' ', map((/^[-_=a-zA-Z0-9\/\.\+]+$/ ? "$_" : "'$_'"), @_));
558 print STDERR "novaboot: Running: ".shell_cmd_string(@_)."\n";
560 exit(1); # should not be reached
563 sub system_verbose($)
566 print STDERR "novaboot: Running: $cmd\n";
567 my $ret = system($cmd);
568 if ($ret & 0x007f) { die("Command terminated by a signal"); }
569 if ($ret & 0xff00) {die("Command exit with non-zero exit code"); }
570 if ($ret) { die("Command failure $ret"); }
575 $str =~ s/^\s+|\s+$//g;
581 if (exists $variables->{WVDESC}) {
582 print STDERR "Testing \"$variables->{WVDESC}\" in $last_fn:\n";
583 } elsif ($last_fn =~ /\.wv$/) {
584 print STDERR "Testing \"all\" in $last_fn:\n";
587 ## Connect to the target and check whether it is not occupied
589 # We have to do this before file generation phase, because file
590 # generation is intermixed with file deployment phase and we want to
591 # check whether the target is not used by somebody else before
592 # deploying files. Otherwise, we may rewrite other user's files on a
595 my $exp; # Expect object to communicate with the target over serial line
597 my ($target_reset, $target_power_on, $target_power_off);
598 my ($amt_user, $amt_password, $amt_host, $amt_port);
600 if (defined $iprelay) {
602 $iprelay =~ /([.0-9]+)(:([0-9]+))?/;
605 my $paddr = sockaddr_in($port, inet_aton($addr));
606 my $proto = getprotobyname('tcp');
607 socket($IPRELAY, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
608 print STDERR "novaboot: Connecting to IP relay... ";
609 connect($IPRELAY, $paddr) || die "connect: $!";
610 print STDERR "done\n";
611 $exp = Expect->init(\*$IPRELAY);
615 print $exp "\xFF\xF6"; # AYT
616 my $connected = $exp->expect(20, # Timeout in seconds
617 '<iprelayd: connected>',
618 '-re', '<WEB51 HW[^>]*>');
623 my ($relay, $onoff) = @_;
624 die unless ($relay == 1 || $relay == 2);
626 my $cmd = ($relay == 1 ? 0x5 : 0x6) | ($onoff ? 0x20 : 0x10);
627 return "\xFF\xFA\x2C\x32".chr($cmd)."\xFF\xF0";
631 my ($relay, $onoff) = @_;
632 die unless ($relay == 1 || $relay == 2);
633 my $cmd = ($relay == 1 ? 0xdf : 0xbf) | ($onoff ? 0x00 : 0xff);
634 return "\xFF\xFA\x2C\x97".chr($cmd)."\xFF\xF0";
638 my ($relay, $onoff, $can_giveup) = @_;
639 my $confirmation = '';
641 print $exp relaycmd($relay, $onoff);
642 my $confirmed = $exp->expect(20, # Timeout in seconds
643 relayconf($relay, $onoff));
646 print("Relay confirmation timeout - ignoring\n");
648 die "Relay confirmation timeout";
654 $target_reset = sub {
655 relay(2, 1, 1); # Reset the machine
660 $target_power_off = sub {
661 relay(1, 1); # Press power button
662 usleep(6000000); # Long press to switch off
666 $target_power_on = sub {
667 relay(1, 1); # Press power button
668 usleep(100000); # Short press
674 system_verbose("stty -F $serial $stty");
675 open($CONN, "+<", $serial) || die "open $serial: $!";
676 $exp = Expect->init(\*$CONN);
678 elsif ($remote_cmd) {
679 print STDERR "novaboot: Running: $remote_cmd\n";
680 $exp = Expect->spawn($remote_cmd);
682 elsif (defined $amt) {
683 require LWP::UserAgent;
684 require LWP::Authen::Digest;
687 my ($host, $username, $password, $schema, $className, $pstate) = @_;
688 #AMT numbers for PowerStateChange (MNI => bluescreen on windows;-)
689 my %pstates = ("on" => 2,
696 <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">
697 <s:Header><a:To>http://$host:16992/wsman</a:To>
698 <w:ResourceURI s:mustUnderstand="true">$schema</w:ResourceURI>
699 <a:ReplyTo><a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>
700 <a:Action s:mustUnderstand="true">$schema$className</a:Action>
701 <w:MaxEnvelopeSize s:mustUnderstand="true">153600</w:MaxEnvelopeSize>
702 <a:MessageID>uuid:709072C9-609C-4B43-B301-075004043C7C</a:MessageID>
703 <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
704 <w:OperationTimeout>PT60.000S</w:OperationTimeout>
705 <w:SelectorSet><w:Selector Name="Name">Intel(r) AMT Power Management Service</w:Selector></w:SelectorSet>
707 <p:RequestPowerStateChange_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService">
708 <p:PowerState>$pstates{$pstate}</p:PowerState>
709 <p:ManagedElement><a:Address>http://$host:16992/wsman</a:Address>
710 <a:ReferenceParameters><w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</w:ResourceURI>
711 <w:SelectorSet><w:Selector Name="Name">ManagedSystem</w:Selector></w:SelectorSet>
712 </a:ReferenceParameters></p:ManagedElement>
713 </p:RequestPowerStateChange_INPUT>
714 </s:Body></s:Envelope>
719 my ($host, $username, $password, $content) = @_;
721 my $ua = LWP::UserAgent->new();
722 $ua->agent("novaboot");
724 my $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
725 my $res = $ua->request($req);
726 die ("Unexpected AMT response: " . $res->status_line) unless $res->code == 401;
728 my ($realm) = $res->header("WWW-Authenticate") =~ /Digest realm="(.*?)"/;
729 $ua->credentials("$host:16992", $realm, $username => $password);
732 $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
733 $req->content_type('application/x-www-form-urlencoded');
734 $req->content($content);
735 $res = $ua->request($req);
736 die ("AMT power change request failed: " . $res->status_line) unless $res->is_success;
737 $res->content() =~ /<g:ReturnValue>(\d+)<\/g:ReturnValue>/;
742 my ($host, $username, $password, $pstate)=@_;
743 my $schema="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService";
744 my $className="/RequestPowerStateChange";
745 my $content = genXML($host, $username, $password ,$schema, $className, $pstate);
746 return sendPOST($host, $username, $password, $content);
749 ($amt_user,$amt_password,$amt_host,$amt_port) = ($amt =~ /(?:(.*?)(?::(.*))?@)?([^:]*)(?::([0-9]*))?/);;
750 $amt_user ||= "admin";
751 $amt_password ||= $ENV{'AMT_PASSWORD'} || die "AMT password not specified";
752 $amt_host || die "AMT host not specified";
756 $target_power_off = sub {
758 my $result = powerChange($amt_host,$amt_user,$amt_password, "off");
759 die "AMT power off failed (ReturnValue $result)" if $result != 0;
762 $target_power_on = sub {
763 my $result = powerChange($amt_host,$amt_user,$amt_password, "on");
764 die "AMT power on failed (ReturnValue $result)" if $result != 0;
767 $target_reset = sub {
768 my $result = powerChange($amt_host,$amt_user,$amt_password, "reset");
770 print STDERR "Warning: Cannot reset $amt_host, trying power on. ";
771 $result = powerChange($amt_host,$amt_user,$amt_password, "on");
773 die "AMT reset failed (ReturnValue $result)" if $result != 0;
776 my $cmd = "amtterm -u $amt_user -p $amt_password $amt_host $amt_port";
777 print STDERR "novaboot: Running: $cmd\n" =~ s/\Q$amt_password\E/???/r;
778 $exp = Expect->spawn($cmd);
779 $exp->expect(10, "RUN_SOL") || die "Expect for 'RUN_SOL' timed out";
784 if ($remote_expect) {
785 $exp || die("No serial line connection");
786 my $log = $exp->log_stdout;
787 if (defined $remote_expect_silent) {
790 $exp->expect(180, $remote_expect) || die "Expect for '$remote_expect' timed out";
791 if (defined $remote_expect_silent) {
792 $exp->log_stdout($log);
793 print $exp->after() if $log;
797 if (defined $reset_cmd) {
798 $target_reset = sub {
799 system_verbose($reset_cmd);
803 if (defined $reset_send) {
804 $target_reset = sub {
805 $reset_send =~ s/\\n/\n/g;
806 $exp->send($reset_send);
810 if (defined $on_opt && defined $target_power_on) {
814 if (defined $off_opt && defined $target_power_off) {
815 print STDERR "novaboot: Switching the target off...\n";
816 &$target_power_off();
820 $builddir ||= dirname(File::Spec->rel2abs( ${$scripts[0]}{filename})) if scalar @scripts;
821 if (defined $builddir) {
822 chdir($builddir) or die "Can't change directory to $builddir: $!";
823 print STDERR "novaboot: Entering directory `$builddir'\n";
825 $builddir = $invocation_dir;
828 ## File generation phase
829 my (%files_iso, $menu_iso, $filename);
830 my $config_name = '';
833 foreach my $script (@scripts) {
834 $filename = $$script{filename};
835 $modules = $$script{modules};
836 $generated = $$script{generated};
837 $variables = $$script{variables};
839 ($config_name = $filename) =~ s#.*/##;
840 $config_name = $config_name_opt if (defined $config_name_opt);
842 if (exists $variables->{BUILDDIR}) {
843 $builddir = File::Spec->rel2abs($variables->{BUILDDIR});
844 chdir($builddir) or die "Can't change directory to $builddir: $!";
845 print STDERR "novaboot: Entering directory `$builddir'\n";
849 $prefix = $grub_prefix;
850 $prefix =~ s/\$NAME/$config_name/;
851 $prefix =~ s/\$BUILDDIR/$builddir/;
853 # TODO: use $grub_prefix as first parameter if some switch is given
854 generate_configs('', $generated, $filename);
856 ### Generate bootloader configuration files
857 my @bootloader_configs;
858 push @bootloader_configs, generate_grub_config($grub_config, $config_name, $prefix, $modules, $grub_preamble) if (defined $grub_config);
859 push @bootloader_configs, generate_grub2_config($grub2_config, $config_name, $prefix, $modules, $grub_preamble, $grub2_prolog) if (defined $grub2_config);
860 push @bootloader_configs, generate_pulsar_config('config-'.($pulsar||'novaboot'), $modules) if (defined $pulsar);
862 ### Run scons or make
864 my @files = map({ ($file) = m/([^ ]*)/; $file; } @$modules);
865 # Filter-out generated files
866 my @to_build = grep({ my $file = $_; !scalar(grep($file eq ($$_{filename} || ''), @$generated)) } @files);
868 system_verbose($scons || $CFG::scons." ".join(" ", @to_build)) if (defined $scons);
869 system_verbose($make || $CFG::make ." ".join(" ", @to_build)) if (defined $make);
872 ### Copy files (using rsync)
873 if (defined $server && !defined($gen_only)) {
874 (my $real_server = $server) =~ s/\$NAME/$config_name/;
876 my ($hostname, $path) = split(":", $real_server, 2);
877 if (! defined $path) {
881 my $files = join(" ", map({ ($file) = m/([^ ]*)/; $file; } ( @$modules, @bootloader_configs, @$copy)));
882 map({ my $file = (split)[0]; die "$file: $!" if ! -f $file; } @$modules);
883 my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb';
884 my $progress = $istty ? "--progress" : "";
886 system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
887 if ($server =~ m|/\$NAME$| && $concat) {
888 my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
889 system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
894 ### Prepare ISO image generation
895 if (defined $iso_image) {
896 generate_configs("(cd)", $generated, $filename);
898 generate_syslinux_config(\$menu, $config_name, "/", $modules);
899 $menu_iso .= "$menu\n";
900 map { ($file,undef) = split; $files_iso{$file} = 1; } @$modules;
904 ## Generate ISO image
905 if (defined $iso_image) {
906 system_verbose("mkdir -p isolinux");
909 if (-f '/usr/lib/ISOLINUX/isolinux.bin') {
910 # Newer ISOLINUX version
911 @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);
913 # Older ISOLINUX version
914 @files = qw(/usr/lib/syslinux/isolinux.bin /usr/lib/syslinux/mboot.c32 /usr/lib/syslinux/menu.c32);
916 system_verbose("cp @files isolinux");
917 open(my $fh, ">isolinux/isolinux.cfg");
919 print $fh "TIMEOUT 50\n";
920 print $fh "DEFAULT menu\n";
922 print $fh "DEFAULT $config_name\n";
924 print $fh "$menu_iso";
927 my $files = join(" ", map("$_=$_", (keys(%files_iso), 'isolinux/isolinux.cfg', map(s|.*/|isolinux/|r, @files))));
928 $iso_image ||= "$config_name.iso";
930 # Note: We use -U flag below to "Allow 'untranslated' filenames,
931 # completely violating the ISO9660 standards". Without this
932 # option, isolinux is not able to read files names for example
934 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");
935 print("ISO image created: $builddir/$iso_image\n");
938 exit(0) if defined $gen_only;
940 ## Boot the system using various methods and send serial output to stdout
942 if (scalar(@scripts) > 1 && ( defined $dhcp_tftp || defined $serial || defined $iprelay)) {
943 die "You cannot do this with multiple scripts simultaneously";
946 if ($variables->{WVTEST_TIMEOUT}) {
947 print STDERR "wvtest: timeout ", $variables->{WVTEST_TIMEOUT}, "\n";
954 $qemu ||= $variables->{QEMU} || $CFG::qemu;
955 my @qemu_flags = split(" ", $qemu);
956 $qemu = shift(@qemu_flags);
958 @qemu_flags = split(/ +/, trim($variables->{QEMU_FLAGS})) if exists $variables->{QEMU_FLAGS};
959 @qemu_flags = split(/ +/, trim($qemu_flags_cmd)) if $qemu_flags_cmd;
960 push(@qemu_flags, split(/ +/, trim($qemu_append || '')));
962 if (defined $iso_image) {
963 # Boot NOVA with grub (and test the iso image)
964 push(@qemu_flags, ('-cdrom', $iso_image));
966 # Boot NOVA without GRUB
968 # Non-patched qemu doesn't like commas, but NUL can live with pluses instead of commans
969 foreach (@$modules) {s/,/+/g;}
970 generate_configs("", $generated, $filename);
972 if (scalar @$modules) {
973 my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
974 $kcmd = '' if !defined $kcmd;
976 @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
977 my $initrd = join ",", @$modules;
979 push(@qemu_flags, ('-kernel', $kbin, '-append', $kcmd));
980 push(@qemu_flags, ('-initrd', $initrd)) if $initrd;
981 push(@qemu_flags, ('-dtb', $dtb)) if $dtb;
984 push(@qemu_flags, qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
985 unshift(@qemu_flags, ('-name', $config_name));
986 print STDERR "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
987 $exp = Expect->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
990 ### Local DHCPD and TFTPD
992 my ($dhcpd_pid, $tftpd_pid);
994 $tftp=1 if $tftp_port;
996 if (defined $dhcp_tftp)
998 generate_configs("(nd)", $generated, $filename);
999 system_verbose('mkdir -p tftpboot');
1000 generate_grub_config("tftpboot/os-menu.lst", $config_name, "(nd)", \@$modules, "timeout 0");
1001 open(my $fh, '>', 'dhcpd.conf');
1002 my $mac = `cat /sys/class/net/$netif/address`;
1004 print $fh "subnet 10.23.23.0 netmask 255.255.255.0 {
1005 range 10.23.23.10 10.23.23.100;
1006 filename \"bin/boot/grub/pxegrub.pxe\";
1007 next-server 10.23.23.1;
1010 hardware ethernet $mac;
1011 fixed-address 10.23.23.1;
1014 system_verbose("sudo ip a add 10.23.23.1/24 dev $netif;
1015 sudo ip l set dev $netif up;
1016 sudo touch dhcpd.leases");
1018 # We run servers by forking ourselves, because the servers end up
1019 # in our process group and get killed by signals sent to the
1020 # process group (e.g. Ctrl-C on terminal).
1021 $dhcpd_pid = fork();
1022 exec_verbose("sudo dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid") if ($dhcpd_pid == 0);
1025 if (defined $dhcp_tftp || defined $tftp) {
1027 # Unfortunately, tftpd requires root privileges even with
1028 # non-privileged (>1023) port due to initgroups().
1029 system_verbose("sudo in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid --address :$tftp_port $builddir");
1031 # Kill server when we die
1032 $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid') if (defined $dhcp_tftp);
1033 system_verbose('sudo pkill --pidfile=tftpd.pid'); };
1035 # We have to kill tftpd explicitely, because it is not in our process group
1036 $SIG{INT} = sub { system_verbose('sudo pkill --pidfile=tftpd.pid'); exit(0); };
1040 if (defined $ider) {
1041 my $ider_cmd= "amtider -c $iso_image -u $amt_user -p $amt_password $amt_host $amt_port" ;
1042 print STDERR "novaboot: Running: $ider_cmd\n" =~ s/\Q$amt_password\E/???/r;
1043 my $ider_pid = fork();
1044 if ($ider_pid == 0) {
1046 die "IDE redirection failed";
1048 # FIXME: This collides with --tftp option. Hopefully, nobody needs
1049 # to use both simultaneously.
1050 $SIG{__DIE__} = sub { system_verbose('kill $ider_pid'); };
1053 ### Reset target (IP relay, AMT, ...)
1055 if (defined $target_reset && $reset) {
1056 print STDERR "novaboot: Reseting the test box... ";
1058 print STDERR "done\n";
1060 # We don't want to output anything printed by the target
1061 # before reset so we clear the buffers now. This is, however,
1062 # not ideal because we may loose some data that were sent
1063 # after the reset. If this is a problem, one should reset and
1064 # connect to serial line in atomic manner. For example, if
1065 # supported by hardware, use --remote-cmd 'sterm -d ...' and
1066 # do not use separate --reset-cmd.
1067 my $log = $exp->log_stdout;
1068 $exp->log_stdout(0);
1069 $exp->expect(0); # Read data from target
1070 $exp->clear_accum(); # Clear the read data
1071 $exp->log_stdout($log);
1075 ### U-boot conversation
1076 if (defined $uboot) {
1077 my $uboot_prompt = $uboot || '=> ';
1078 print STDERR "novaboot: Waiting for U-Boot prompt...\n";
1079 $exp || die("No serial line connection");
1080 $exp->log_stdout(1);
1081 #$exp->exp_internal(1);
1083 [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
1084 $uboot_prompt) || die "No U-Boot prompt deteceted";
1085 foreach my $cmdspec (@uboot_init) {
1086 my ($cmd, $timeout);
1087 die "Internal error - please report a bug" unless ref($cmdspec) eq "HASH";
1089 if ($cmdspec->{system}) {
1090 $cmd = `$cmdspec->{system}`;
1092 $cmd = $cmdspec->{command};
1094 $timeout = $cmdspec->{timeout} // 10;
1096 if ($cmd =~ /\$NB_MYIP/) {
1097 my $ip = (grep /inet /, `ip addr show $netif`)[0] || die "Problem determining IP address of $netif";
1098 $ip =~ s/\s*inet ([0-9.]*).*/$1/;
1099 $cmd =~ s/\$NB_MYIP/$ip/g;
1101 if ($cmd =~ /\$NB_PREFIX/) {
1104 $cmd =~ s/\$NB_PREFIX/$p/g;
1107 $exp->send("$cmd\n");
1109 my ($matched_pattern_position, $error,
1110 $successfully_matching_string,
1111 $before_match, $after_match) =
1112 $exp->expect($timeout, $uboot_prompt);
1113 die "No U-Boot prompt: $error" if $error;
1114 #print "\n>>>>$before_match<<<<\n";
1117 # Load files if there are some load lines in the script
1118 if (scalar(@$modules) > 0 && !$variables->{NO_BOOT}) {
1119 my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
1121 @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
1122 my $initrd = shift @$modules;
1124 if (defined $kbin) {
1125 die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel};
1126 $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
1128 [qr/##/, sub { exp_continue; }],
1129 $uboot_prompt) || die "Kernel load timeout";
1132 die "No '--uboot-addr fdt' given" unless $uboot_addr{fdt};
1133 $exp->send("tftpboot $uboot_addr{fdt} $prefix$dtb\n");
1135 [qr/##/, sub { exp_continue; }],
1136 $uboot_prompt) || die "Device tree load timeout";
1138 if (defined $initrd) {
1139 die "No '--uboot-addr ramdisk' given" unless $uboot_addr{ramdisk};
1140 $exp->send("tftpboot $uboot_addr{ramdisk} $prefix$initrd\n");
1142 [qr/##/, sub { exp_continue; }],
1143 $uboot_prompt) || die "Initrd load timeout";
1145 $uboot_addr{ramdisk} = '-';
1149 $exp->send("setenv bootargs $kcmd\n");
1150 $exp->expect(5, $uboot_prompt) || die "U-Boot prompt timeout";
1153 $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
1154 if (!$variables->{NO_BOOT} && $uboot_cmd ne '') {
1155 $uboot_cmd =~ s/\$kernel_addr/$uboot_addr{kernel}/g;
1156 $uboot_cmd =~ s/\$ramdisk_addr/$uboot_addr{ramdisk}/g;
1157 $uboot_cmd =~ s/\$fdt_addr/$uboot_addr{fdt}/g;
1159 $exp->send($uboot_cmd . "\n");
1160 $exp->expect(5, "\n") || die "U-Boot command timeout";
1164 ### Serial line interaction
1165 if ($interaction && defined $exp) {
1166 # Serial line of the target is available
1167 my $interrupt = 'Ctrl-C';
1168 if ($interactive && !@exiton) {
1169 $interrupt = '"~~."';
1171 print STDERR "novaboot: Serial line interaction (press $interrupt to interrupt)...\n";
1172 $exp->log_stdout(1);
1174 $exp->expect($exiton_timeout, @expect_raw, @exiton) || die("exiton timeout");
1176 my @inputs = ($exp);
1177 my $infile = new IO::File;
1178 $infile->IO::File::fdopen(*STDIN,'r');
1179 my $in_object = Expect->exp_init($infile);
1180 $in_object->set_group($exp);
1183 $in_object->set_seq('~~\.', sub { print STDERR "novaboot: Escape sequence detected\r\n"; undef; });
1184 $in_object->manual_stty(0); # Use raw terminal mode
1186 $in_object->manual_stty(1); # Do not modify terminal settings
1188 push(@inputs, $in_object);
1190 #print Dumper(\@expect_raw);
1191 $exp->expect(undef, @expect_raw) if @expect_raw;
1192 Expect::interconnect(@inputs) unless defined($exp->exitstatus);
1196 ## Kill dhcpc or tftpd
1197 if (defined $dhcp_tftp || defined $tftp) {
1198 die("novaboot: This should kill servers on background\n");
1201 # Always finish novaboot output with newline
1202 print "\n" if $final_eol;
1209 novaboot - Boots a locally compiled operating system on a remote
1216 B<novaboot> [option]... [--] script...
1218 B<./script> [option]...
1222 Novaboot makes booting of a locally compiled operating system (OS)
1223 (e.g. NOVA or Linux) on remote targets as simple as running a program
1224 locally. It automates things like copying OS images to a TFTP server,
1225 generation of bootloader configuration files, resetting of target
1226 hardware or redirection of target's serial line to stdin/out. Novaboot
1227 is highly configurable and makes it easy to boot a single image on
1228 different targets or different images on a single target.
1230 Novaboot operation is controlled by configuration files, command line
1231 options and by a so called novaboot script, which can be thought as a
1232 generalization of bootloader configuration files (see L</"NOVABOOT
1233 SCRIPT SYNTAX">). Typical way of using novaboot is to make the
1234 novaboot script executable and set its first line to I<#!/usr/bin/env
1235 novaboot>. Then, booting a particular OS configuration becomes the
1236 same as executing a local program – the novaboot script.
1238 Novaboot uses configuration files to, among other things, define
1239 command line options needed for different targets. Users typically use
1240 only the B<-t>/B<--target> command line option to select the target.
1241 Internally, this option expands to the pre-configured options.
1242 Configuration files are searched at multiple places, which allows to
1243 have per-system, per-user or per-project configurations. Configuration
1244 file syntax is described in section L</"CONFIGURATION FILES">.
1246 Simple examples of using C<novaboot>:
1252 Run an OS in Qemu. This can be specified with the B<--qemu> option.
1255 novaboot --qemu myos
1257 (or C<./myos --qemu> as described above) will run Qemu and make it
1258 boot the configuration specified in the F<myos> script.
1262 Create a bootloader configuration file (currently supported
1263 bootloaders are GRUB, GRUB2, ISOLINUX, Pulsar and U-Boot) and copy it
1264 with all other files needed for booting to a remote boot server. Then
1265 use a TCP/IP-controlled relay/serial-to-TCP converter to reset the
1266 target and receive its serial output.
1268 ./myos --grub2 --server=192.168.1.1:/tftp --iprelay=192.168.1.2
1270 Alternatively, you can put these switches to the configuration file
1273 ./myos --target mytarget
1277 Run DHCP and TFTP server on developer's machine to boot the target
1282 This is useful when no network infrastructure is in place and
1283 the target is connected directly to developer's box.
1287 Create bootable ISO image.
1289 novaboot --iso -- script1 script2
1291 The created ISO image will have ISOLINUX bootloader installed on it
1292 and the boot menu will allow selecting between I<script1> and
1293 I<script2> configurations.
1297 =head1 PHASES AND OPTIONS
1299 Novaboot performs its work in several phases. Each phase can be
1300 influenced by several command line options, certain phases can be
1301 skipped. The list of phases (in the execution order) is as follows.
1305 =item 1. L<Configuration reading|/Configuration reading phase>
1307 =item 2. L<Command line processing|/Command line processing phase>
1309 =item 3. L<Script preprocessing|/Script preprocessing phase>
1311 =item 4. L<File generation|/File generation phase>
1313 =item 5. L<Target connection|/Target connection check>
1315 =item 6. L<File deployment|/File deployment phase>
1317 =item 7. L<Target power-on and reset|/Target power-on and reset phase>
1319 =item 8. L<Interaction with the bootloader|/Interaction with the bootloader on the target>
1321 =item 9. L<Target interaction|/Target interaction phase>
1325 Each phase is described in the following sections together with the
1326 command line options that control it.
1328 =head2 Configuration reading phase
1330 After starting, novaboot reads configuration files. Their content is
1331 described in section L</"CONFIGURATION FILES">. By default,
1332 configuration is read from multiple locations. First from the system
1333 configuration directory (F</etc/novaboot.d/>), second from the user
1334 configuration file (F<~/.config/novaboot>) and third from F<.novaboot>
1335 files along the path to the current directory. Alternatively, a single
1336 configuration file specified with the B<-c> switch or with the
1337 C<NOVABOOT_CONFIG> environment variable is read. The latter read files
1338 override settings from the former ones.
1340 The system configuration directory is determined by the content of
1341 NOVABOOT_CONFIG_DIR environment variable and defaults to
1342 F</etc/novaboot.d>. Files in this directory with names consisting
1343 solely of English letters, numbers, dashes '-' and underscores '_'
1344 (note that dot '.' is not included) are read in alphabetical order.
1346 Then, the user configuration file is read from
1347 F<$XDG_CONFIG_HOME/novaboot>. If C<$XDG_CONFIG_HOME> environemnt
1348 variable is not set F<~/.config/novaboot> is read instead.
1350 Finally, novaboot searches for files named F<.novaboot> starting from the
1351 directory of the novaboot script (or working directory, see bellow)
1352 and continuing upwards up to the root directory. The found
1353 configuration files are then read in the opposite order (i.e. from the
1354 root directory downwards). This allows to have, for example, a project
1355 specific configuration in F<~/project/.novaboot>.
1357 Note the difference between F<~/.config/novaboot> and F<~/.novaboot>.
1358 The former one is read always, whereas the latter only when novaboot
1359 script or working directory is under the C<$HOME> directory.
1361 In certain cases, the location of the novaboot script cannot be
1362 determined in this early phase. This happens either when the script is
1363 read from the standard input or when novaboot is invoked explicitly as
1364 in the example L</"4."> above. In this case the current working
1365 directory is used as a starting point for configuration file search
1366 instead of the novaboot script directory.
1370 =item -c, --config=I<filename>
1372 Use the specified configuration file instead of the default one(s).
1376 =head2 Command line processing phase
1382 Dump the current configuration to stdout end exit. Useful as an
1383 initial template for a configuration file.
1387 Print short (B<-h>) or long (B<--help>) help.
1389 =item -t, --target=I<target>
1391 This option serves as a user configurable shortcut for other novaboot
1392 options. The effect of this option is the same as specifying the
1393 options stored in the C<%targets> configuration variable under key
1394 I<target>. See also L</"CONFIGURATION FILES">.
1396 When this option is not given, novaboot tries to determine the target
1397 to use from either B<NOVABOOT_TARGET> environment variable or from
1398 B<$default_target> configuration file variable.
1402 =head2 Script preprocessing phase
1404 This phases allows to modify the parsed novaboot script before it is
1405 used in the later phases.
1409 =item -a, --append=I<parameters>
1411 Append a string to the first C<load> line in the novaboot script. This
1412 can be used to append parameters to the kernel's or root task's
1413 command line. This option can appear multiple times.
1417 Use L<bender|https://github.com/TUD-OS/morbo/blob/master/standalone/bender.c>
1418 chainloader. Bender scans the PCI bus for PCI serial ports and stores
1419 the information about them in the BIOS data area for use by the
1422 =item --chainloader=I<chainloader>
1424 Specifies a chainloader that is loaded before the kernel and other
1425 files specified in the novaboot script. E.g. 'bin/boot/bender
1430 Print the modules to boot and their parameters after this phase
1431 finishes. Then exit. This is useful for seeing the effect of other
1432 options in this section.
1434 =item -k, --kernel=F<file>
1436 Replace the first word on the first C<load> line in the novaboot
1437 script with F<file>.
1439 =item --scriptmod=I<perl expression>
1441 When novaboot script is read, I<perl expression> is executed for every
1442 line (in $_ variable). For example, C<novaboot
1443 --scriptmod=s/sigma0/omega6/g> replaces every occurrence of I<sigma0>
1444 in the script with I<omega6>.
1446 When this option is present, it overrides I<$script_modifier> variable
1447 from the configuration file, which has the same effect. If this option
1448 is given multiple times all expressions are evaluated in the command
1453 =head2 File generation phase
1455 In this phase, files needed for booting are generated in a so called
1456 I<build directory> (see L</--build-dir>). In most cases configuration
1457 for a bootloader is generated automatically by novaboot. It is also
1458 possible to generate other files using I<heredoc> or I<"<"> syntax in
1459 novaboot scripts. Finally, binaries can be generated in this phases by
1460 running C<scons> or C<make>.
1464 =item --build-dir=I<directory>
1466 Overrides the default build directory location.
1468 The default build directory location is determined as follows: If the
1469 configuration file defines the C<$builddir> variable, its value is
1470 used. Otherwise, it is the directory that contains the first processed
1473 See also L</BUILDDIR> variable.
1475 =item -g, --grub[=I<filename>]
1477 Generates grub bootloader menu file. If the I<filename> is not
1478 specified, F<menu.lst> is used. The I<filename> is relative to the
1479 build directory (see B<--build-dir>).
1481 =item --grub-preamble=I<prefix>
1483 Specifies the I<preable> that is at the beginning of the generated
1484 GRUB or GRUB2 config files. This is useful for specifying GRUB's
1487 =item --prefix=I<prefix>
1489 Specifies I<prefix> (e.g. F</srv/tftp>) that is put in front of every
1490 file name in generated bootloader configuration files (or in U-Boot
1493 If the I<prefix> contains string $NAME, it will be replaced with the
1494 name of the novaboot script (see also B<--name>).
1496 If the I<prefix> contains string $BUILDDIR, it will be replaced with
1497 the build directory (see also B<--build-dir>).
1501 Alias for B<--prefix>.
1503 =item --grub2[=I<filename>]
1505 Generate GRUB2 menu entry in I<filename>. If I<filename> is not
1506 specified F<grub.cfg> is used. The content of the menu entry can be
1507 customized with B<--grub-preamble>, B<--grub2-prolog> or
1508 B<--grub_prefix> options.
1510 In order to use the the generated menu entry on your development
1511 machine that uses GRUB2, append the following snippet to
1512 F</etc/grub.d/40_custom> file and regenerate your grub configuration,
1513 i.e. run update-grub on Debian/Ubuntu.
1515 if [ -f /path/to/nul/build/grub.cfg ]; then
1516 source /path/to/nul/build/grub.cfg
1519 =item --grub2-prolog=I<prolog>
1521 Specifies text that is put at the beginning of the GRUB2 menu entry.
1523 =item -m, --make[=make command]
1525 Runs C<make> to build files that are not generated by novaboot itself.
1527 =item --name=I<string>
1529 Use the name I<string> instead of the name of the novaboot script.
1530 This name is used for things like a title of grub menu or for the
1531 server directory where the boot files are copied to.
1535 Do not run external commands to generate files (i.e. "<" syntax and
1536 C<run> keyword). This switch does not influence generation of files
1537 specified with "<<WORD" syntax.
1539 =item -p, --pulsar[=mac]
1541 Generates pulsar bootloader configuration file named F<config-I<mac>>
1542 The I<mac> string is typically a MAC address and defaults to
1545 =item --scons[=scons command]
1547 Runs C<scons> to build files that are not generated by novaboot
1552 Strip I<rom://> prefix from command lines and generated config files.
1553 The I<rom://> prefix is used by NUL. For NRE, it has to be stripped.
1557 Exit novaboot after file generation phase.
1561 =head2 Target connection check
1563 If supported by the target, the connection to it is made and it is
1564 checked whether the target is not occupied by another novaboot
1569 =item --amt=I<"[user[:password]@]host[:port]>
1571 Use Intel AMT technology to control the target machine. WS management
1572 is used to powercycle it and Serial-Over-Lan (SOL) for input/output.
1573 The hostname or (IP address) is given by the I<host> parameter. If
1574 I<password> is not specified, environment variable AMT_PASSWORD is
1575 used. The I<port> specifies a TCP port for SOL. If not specified, the
1576 default is 16992. Default I<user> is admin.
1578 =item --iprelay=I<addr[:port]>
1580 Use TCP/IP relay and serial port to access the target's serial port
1581 and powercycle it. The IP address of the relay is given by I<addr>
1582 parameter. If I<port> is not specified, it default to 23.
1584 Note: This option is supposed to work with HWG-ER02a IP relays.
1586 =item -s, --serial[=device]
1588 Target's serial line is connected to host's serial line (device). The
1589 default value for device is F</dev/ttyUSB0>.
1591 The value of this option is exported in NB_NOVABOOT environment
1592 variable to all subprocesses run by C<novaboot>.
1594 =item --stty=I<settings>
1596 Specifies settings passed to C<stty> invoked on the serial line
1597 specified with B<--serial> option. If this option is not given,
1598 C<stty> is called with C<raw -crtscts -onlcr 115200> settings.
1600 =item --remote-cmd=I<cmd>
1602 Command that mediates connection to the target's serial line. For
1603 example C<ssh server 'cu -l /dev/ttyS0'>.
1605 =item --remote-expect=I<string>
1607 Wait for reception of I<string> after establishing the remote
1610 =item --remote-expect-silent=I<string>
1612 The same as B<--remote-expect> except that the remote output is not
1613 echoed to stdout while waiting for the I<string>. Everything after the
1614 matched string is printed to stdout, so you may want to include line
1615 end characters in the I<string> as well.
1619 =head2 File deployment phase
1621 In some setups, it is necessary to copy the files needed for booting
1622 to a particular location, e.g. to a TFTP boot server or to the
1627 =item -d, --dhcp-tftp
1629 Turns your workstation into a DHCP and TFTP server so that the OS can
1630 be booted via PXE BIOS (or similar mechanism) on the test machine
1631 directly connected by a plain Ethernet cable to your workstation.
1633 The DHCP and TFTP servers requires root privileges and C<novaboot>
1634 uses C<sudo> command to obtain those. You can put the following to
1635 I</etc/sudoers> to allow running the necessary commands without asking
1638 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
1639 your_login ALL=NOPASSWD: NOVABOOT
1643 Starts a TFTP server on your workstation. This is similar to
1644 B<--dhcp-tftp> except that DHCP server is not started.
1646 The TFTP server require root privileges and C<novaboot> uses C<sudo>
1647 command to obtain those. You can put the following to I</etc/sudoers>
1648 to allow running the necessary commands without asking for password.
1650 Cmnd_Alias NOVABOOT = /usr/sbin/in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/pkill --pidfile=tftpd.pid
1651 your_login ALL=NOPASSWD: NOVABOOT
1653 =item --tftp-port=I<port>
1655 Port to run the TFTP server on. Implies B<--tftp>.
1657 =item --netif=I<network interface>
1659 Network interface used to deploy files to the target. Default value is
1660 I<eth0>. This influences the configuration of the DHCP server started
1661 by B<--dhcp-tftp> and the value that B<$NB_MYIP> get replaced with in
1662 U-Boot conversation.
1664 =item --iso[=filename]
1666 Generates the ISO image that boots NOVA system via GRUB. If no filename
1667 is given, the image is stored under I<NAME>.iso, where I<NAME> is the name
1668 of the novaboot script (see also B<--name>).
1670 =item --server[=[[user@]server:]path]
1672 Copy all files needed for booting to another location. The files will
1673 be copied (by B<rsync> tool) to the directory I<path>. If the I<path>
1674 contains string $NAME, it will be replaced with the name of the
1675 novaboot script (see also B<--name>).
1677 =item --rsync-flags=I<flags>
1679 Specifies which I<flags> are appended to F<rsync> command line when
1680 copying files as a result of I<--server> option.
1684 If B<--server> is used and its value ends with $NAME, then after
1685 copying the files, a new bootloader configuration file (e.g. menu.lst)
1686 is created at I<path-wo-name>, i.e. the path specified by B<--server>
1687 with $NAME part removed. The content of the file is created by
1688 concatenating all files of the same name from all subdirectories of
1689 I<path-wo-name> found on the "server".
1693 Use Intel AMT technology for IDE redirection. This allows the target
1694 machine to boot from novaboot created ISO image. Implies B<--iso>.
1696 The experimental C<amtider> utility needed by this option can be
1697 obtained from https://github.com/wentasah/amtterm.
1701 =head2 Target power-on and reset phase
1703 At this point, the target is reset (or switched on/off). There is
1704 several ways how this can be accomplished. Resetting a physical target
1705 can currently be accomplished by the following options: B<--amt>,
1706 B<--iprelay>, B<--reset-cmd> and B<--reset-send>.
1712 Switch on/off the target machine and exit. The script (if any) is
1713 completely ignored. Currently it works only with B<--iprelay> or
1716 =item -Q, --qemu[=I<qemu-binary>]
1718 Boot the configuration in qemu. Optionally, the name of qemu binary
1719 can be specified as a parameter.
1721 =item --qemu-append=I<flags>
1723 Append I<flags> to the default qemu flags (QEMU_FLAGS variable or
1724 C<-cpu coreduo -smp 2>).
1726 =item -q, --qemu-flags=I<flags>
1728 Replace the default qemu flags (QEMU_FLAGS variable or C<-cpu coreduo
1729 -smp 2>) with I<flags> specified here.
1731 =item --reset-cmd=I<cmd>
1733 Command that resets the target.
1735 =item --reset-send=I<string>
1737 Reset the target by sending the given I<string> to the remote serial
1738 line. "\n" sequences are replaced with newline character.
1740 =item --no-reset, --reset
1742 Disable/enable resetting of the target.
1746 =head2 Interaction with the bootloader on the target
1750 =item --uboot[=I<prompt>]
1752 Interact with U-Boot bootloader to boot the thing described in the
1753 novaboot script. I<prompt> specifies the U-Boot's prompt (default is
1754 "=> ", other common prompts are "U-Boot> " or "U-Boot# ").
1755 Implementation of this option is currently tied to a particular board
1756 that we use. It may be subject to changes in the future!
1760 Disable U-Boot interaction previously enabled with B<--uboot>.
1764 Command(s) to send the U-Boot bootloader before loading the images and
1765 booting them. This option can be given multiple times. After sending
1766 commands from each option novaboot waits for U-Boot I<prompt>.
1768 If the command contains string I<$NB_MYIP> then this string is
1769 replaced by IPv4 address of eth0 interface (see also B<--netif>).
1770 Similarly I<$NB_PREFIX> is replaced with prefix given by B<--prefix>.
1772 See also C<uboot> keyword in L</"NOVABOOT SCRIPT SYNTAX">).
1774 =item --uboot-addr I<name>=I<address>
1776 Load address of U-Boot's C<tftpboot> command for loading I<name>,
1777 where name is one of I<kernel>, I<ramdisk> or I<fdt> (flattened device
1780 The default addresses are ${I<name>_addr_r}, i.e. U-Boot environment
1781 variables used by convention for this purpose.
1783 =item --uboot-cmd=I<command>
1785 Specifies U-Boot command used to execute the OS. If the command
1786 contains strings C<$kernel_addr>, C<$ramdisk_addr>, C<$fdt_addr>,
1787 these are replaced with the addresses configured with B<--uboot-addr>.
1789 The default value is
1791 bootm $kernel_addr $ramdisk_addr $fdt_addr
1793 or the C<UBOOT_CMD> variable if defined in the novaboot script.
1797 =head2 Target interaction phase
1799 In this phase, target's serial output is redirected to stdout and if
1800 stdin is a TTY, it is redirected to the target's serial input allowing
1801 interactive work with the target.
1805 =item --exiton=I<string>
1807 When I<string> is sent by the target, novaboot exits. This option can
1808 be specified multiple times, in which case novaboot exits whenever
1809 either of the specified strings is sent.
1811 If I<string> is C<-re>, then the next B<--exiton>'s I<string> is
1812 treated as regular expression. For example:
1814 --exiton -re --exiton 'error:.*failed'
1816 =item --exiton-re=I<regex>
1818 The same as --exiton -re --exiton I<regex>.
1820 =item --exiton-timeout=I<seconds>
1822 By default B<--exiton> waits for the string match forever. When this
1823 option is specified, "exiton" timeouts after the specifies number of
1824 seconds and novaboot returns non-zero exit code.
1826 =item -i, --interactive
1828 Setup things for interactive use of target. Your terminal will be
1829 switched to raw mode. In raw mode, your system does not process input
1830 in any way (no echoing of entered characters, no interpretation
1831 special characters). This, among others, means that Ctrl-C is passed
1832 to the target and does no longer interrupt novaboot. Use "~~."
1833 sequence to exit novaboot.
1835 =item --no-interaction, --interaction
1837 Skip resp. force target interaction phase. When skipped, novaboot exits
1838 immediately when boot is initiated.
1840 =item --expect=I<string>
1842 When I<string> is received from the target, send the string specified
1843 with the subsequent B<--send*> option to the target.
1845 =item --expect-re=I<regex>
1847 When target's output matches regular expression I<regex>, send the
1848 string specified with the subsequent B<--send*> option to the target.
1850 =item --expect-raw=I<perl-code>
1852 Provides direct control over Perl's Expect module.
1854 =item --send=I<string>
1856 Send I<string> to the target after the previously specified
1857 B<--expect*> was matched in the target's output. The I<string> may
1858 contain escape sequences such as "\n".
1860 Note that I<string> is actually interpreted by Perl, so it can contain
1861 much more that escape sequences. This behavior may change in the
1864 Example: C<--expect='login: ' --send='root\n'>
1866 =item --sendcont=I<string>
1868 Similar to B<--send> but continue expecting more input.
1870 Example: C<--expect='Continue?' --sendcont='yes\n'>
1872 =item --final-eol, --no-final-eol
1874 By default, B<novaboot> always prints an end-of-line character at the
1875 end of its execution in order to ensure that the output of programs
1876 started after novaboot appears at the beginning of the line. When this
1877 is not desired B<--no-final-eol> option can be used to override this
1882 =head1 NOVABOOT SCRIPT SYNTAX
1884 The syntax tries to mimic POSIX shell syntax. The syntax is defined
1885 with the following rules.
1887 Lines starting with "#" and empty lines are ignored.
1889 Lines that end with "\" are concatenated with the following line after
1890 removal of the final "\" and leading whitespace of the following line.
1892 Lines of the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
1893 expression) assign values to internal variables. See L</VARIABLES>
1896 Otherwise, the first word on the line defines the meaning of the line.
1897 The following keywords are supported:
1903 These lines represent modules to boot. The
1904 word after C<load> is a file name (relative to the build directory
1905 (see B<--build-dir>) of the module to load and the remaining words are
1906 passed to it as the command line parameters.
1908 When the C<load> line ends with "<<WORD" then the subsequent lines
1909 until the line containing solely WORD are copied literally to the file
1910 named on that line. This is similar to shell's heredoc feature.
1912 When the C<load> line ends with "< CMD" then command CMD is executed
1913 with F</bin/sh> and its standard output is stored in the file named on
1914 that line. The SRCDIR variable in CMD's environment is set to the
1915 absolute path of the directory containing the interpreted novaboot
1920 These lines are similar to C<load> lines. The
1921 file mentioned there is copied to the same place as in case of C<load>
1922 (e.g. tftp server), but the file is not used in the bootloader
1923 configuration. Such a file can be used by the target for other
1924 purposed than booting, e.g. at OS runtime or for firmware update.
1928 Lines starting with C<run> keyword contain shell commands that are run
1929 during file generation phase. This is the same as the "< CMD" syntax
1930 for C<load> keyboard except that the command's output is not
1931 redirected to a file. The ordering of commands is the same as they
1932 appear in the novaboot script.
1936 These lines represent U-Boot commands that are sent to the target if
1937 B<--uboot> option is given. Having a U-Boot line in the novaboot
1938 script is the same as giving B<--uboot-init> option to novaboot. The
1939 following syntax variants are supported:
1942 uboot[:<timeout>] <string>
1943 uboot[:<timeout>] < <shell>
1945 C<string> is the literal U-Boot command.
1947 The C<uboot> keyword can be suffixed with timeout specification. The
1948 syntax is C<uboot:Ns>, where C<N> is the whole number of seconds. If
1949 the U-Boot command prompt does not appear before the timeout, novaboot
1950 fails. The default timeout is 10 seconds.
1952 In the second variant with the C<<> character the shell code is
1953 executed and its standard output is sent to U-Boot. Example:
1955 uboot < printf "mmc write \$loadaddr 1 %x" $(($(/usr/bin/stat -c%s rootfs.ext4) / 512))
1961 #!/usr/bin/env novaboot
1962 load bzImage console=ttyS0,115200
1963 run make -C buildroot
1964 load rootfs.cpio < gen_cpio buildroot/images/rootfs.cpio "myapp->/etc/init.d/S99myapp"
1966 Example (NOVA User Land - NUL):
1968 #!/usr/bin/env novaboot
1969 WVDESC=Example program
1970 load bin/apps/sigma0.nul S0_DEFAULT script_start:1,1 \
1971 verbose hostkeyb:0,0x60,1,12,2
1972 load bin/apps/hello.nul
1973 load hello.nulconfig <<EOF
1974 sigma0::mem:16 name::/s0/log name::/s0/timer name::/s0/fs/rom ||
1975 rom://bin/apps/hello.nul
1978 This example will load three modules: F<sigma0.nul>, F<hello.nul> and
1979 F<hello.nulconfig>. sigma0 receives some command line parameters and
1980 F<hello.nulconfig> file is generated on the fly from the lines between
1981 C<<<EOF> and C<EOF>.
1985 The following variables are interpreted in the novaboot script:
1991 Novaboot chdir()s to this directory before file generation phase. The
1992 directory name specified here is relative to the build directory
1993 specified by other means (see L</--build-dir>).
1997 Assigning this variable has the same effect as specifying L</--exiton>
2000 =item HYPERVISOR_PARAMS
2002 Parameters passed to hypervisor. The default value is "serial", unless
2003 overridden in configuration file.
2007 The kernel to use instead of the hypervisor specified in the
2008 configuration file with the C<$hypervisor> variable. The value should
2009 contain the name of the kernel image as well as its command line
2010 parameters. If this variable is defined and non-empty, the variable
2011 HYPERVISOR_PARAMS is not used.
2015 If this variable is 1, the system is not booted. This is currently
2016 only implemented for U-Boot bootloader where it is useful for
2017 interacting with the bootloader without booting the system - e.g. for
2022 Use a specific qemu binary (can be overridden with B<-Q>) and flags
2023 when booting this script under qemu. If QEMU_FLAGS variable is also
2024 specified flags specified in QEMU variable are replaced by those in
2029 Use specific qemu flags (can be overridden with B<-q>).
2033 See L</--uboot-cmd>.
2037 Description of the WvTest-compliant program.
2039 =item WVTEST_TIMEOUT
2041 The timeout in seconds for WvTest harness. If no complete line appears
2042 in the test output within the time specified here, the test fails. It
2043 is necessary to specify this for long running tests that produce no
2044 intermediate output.
2048 =head1 CONFIGURATION FILES
2050 Novaboot can read its configuration from one or more files. By
2051 default, novaboot looks for files in F</etc/novaboot.d>, file
2052 F<~/.config/novaboot> and files named F<.novaboot> as described in
2053 L</Configuration reading phase>. Alternatively, configuration file
2054 location can be specified with the B<-c> switch or with the
2055 NOVABOOT_CONFIG environment variable. The configuration file has Perl
2056 syntax (i.e. it is better to put C<1;> as the last line) and should set
2057 values of certain Perl variables. The current configuration can be
2058 dumped with the B<--dump-config> switch. Some configuration variables
2059 can be overridden by environment variables (see below) or by command
2062 Supported configuration variables include:
2068 Build directory location relative to the location of the configuration
2071 =item $default_target
2073 Default target (see below) to use when no target is explicitly
2074 specified with the B<--target> command line option or
2075 B<NOVABOOT_TARGET> environment variable.
2079 Hash of target definitions to be used with the B<--target> option. The
2080 key is the identifier of the target, the value is the string with
2081 command line options. For instance, if the configuration file contains:
2083 $targets{'mybox'} = '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
2085 then the following two commands are equivalent:
2087 ./myos --server=boot:/tftproot --serial=/dev/ttyUSB0 --grub
2092 =head1 ENVIRONMENT VARIABLES
2094 Some options can be specified not only via config file or command line
2095 but also through environment variables. Environment variables override
2096 the values from configuration file and command line parameters
2097 override the environment variables.
2101 =item NOVABOOT_CONFIG
2103 Name of the novaboot configuration file to use instead of the default
2106 =item NOVABOOT_CONFIG_DIR
2108 Name of the novaboot configuration directory. When not specified
2109 F</etc/novaboot.d> is used.
2111 =item NOVABOOT_TARGET
2113 Name of the novaboot target to use. This overrides the value of
2114 B<$default_target> from the configuration file and can be overriden
2115 with the B<--target> command line option.
2117 =item NOVABOOT_BENDER
2119 Defining this variable has the same effect as using B<--bender>
2126 Michal Sojka <sojka@os.inf.tu-dresden.de>
2128 Latest novaboot version can be found at
2129 L<https://github.com/wentasah/novaboot>.
2133 # LocalWords: novaboot Novaboot NOVABOOT TFTP PXE DHCP filename stty
2134 # LocalWords: chainloader stdout Qemu qemu preprocessing ISOLINUX bootable
2135 # LocalWords: config subprocesses sudo sudoers tftp dhcp IDE stdin
2136 # LocalWords: subdirectories TTY whitespace heredoc POSIX WvTest