]> rtime.felk.cvut.cz Git - novaboot.git/blob - novaboot
Run qemu only when --qemu option is present
[novaboot.git] / novaboot
1 #!/usr/bin/perl -w
2
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.
7
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.
12
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/>.
15
16 use strict;
17 use warnings;
18 use warnings (exists $ENV{NOVABOOT_TEST} ? (FATAL => 'all') : ());
19 use Getopt::Long qw(GetOptionsFromString);
20 use Pod::Usage;
21 use File::Basename;
22 use File::Spec;
23 use IO::Handle;
24 use Time::HiRes("usleep");
25 use Socket;
26 use FileHandle;
27 use IPC::Open2;
28 use POSIX qw(:errno_h);
29 use Cwd qw(getcwd abs_path);
30 use Expect;
31
32 # always flush
33 $| = 1;
34
35 my $invocation_dir = getcwd();
36
37 ## Configuration file handling
38
39 # Default configuration
40 $CFG::hypervisor = "";
41 $CFG::hypervisor_params = "serial";
42 $CFG::genisoimage = "genisoimage";
43 $CFG::qemu = 'qemu -cpu coreduo -smp 2';
44 %CFG::targets = (
45     "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',
46     "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',
47     "localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog="  set root=\'(hd0,msdos1)\'"',
48     "ryuglab" => '--server=pc-sojkam.felk.cvut.cz:/srv/tftp --uboot --uboot-init="mw f0000b00 \${psc_cfg}" --remote-cmd="ssh pc-sojkam.felk.cvut.cz \"cu -l /dev/ttyUSB0 -s 115200\"" --reset-cmd="ssh pc-sojkam.felk.cvut.cz \"dtrrts /dev/ttyUSB0 1 1\""',
49     "ryulocal" => '--dhcp-tftp --serial --uboot --uboot-init="dhcp; mw f0000b00 \${psc_cfg}" --reset-cmd="if which dtrrts; then dtrrts $NB_SERIAL 0 1; sleep 0.1; dtrrts $NB_SERIAL 1 1; fi"',
50
51     );
52 chomp(my $nproc = `nproc`);
53 $CFG::scons = "scons -j$nproc";
54 $CFG::make = "make -j$nproc";
55
56 my $builddir;
57
58 sub read_config($) {
59     my ($cfg) = @_;
60     {
61         package CFG; # Put config data into a separate namespace
62         my $rc = do($cfg);
63
64         # Check for errors
65         if ($@) {
66             die("ERROR: Failure compiling '$cfg' - $@");
67         } elsif (! defined($rc)) {
68             die("ERROR: Failure reading '$cfg' - $!");
69         } elsif (! $rc) {
70             die("ERROR: Failure processing '$cfg'");
71         }
72     }
73     $builddir = File::Spec->rel2abs($CFG::builddir, dirname($cfg)) if defined $CFG::builddir;
74     print STDERR "novaboot: Read $cfg\n";
75 }
76
77 my @cfgs;
78 {
79     # We don't use $0 here, because it points to the novaboot itself and
80     # not to the novaboot script. The problem with this approach is that
81     # when a script is run as "novaboot <options> <script>" then $ARGV[0]
82     # contains the first option. Hence the -f check.
83     my $dir = File::Spec->rel2abs($ARGV[0] && -f $ARGV[0] ? dirname($ARGV[0]) : '', $invocation_dir);
84     while (-d $dir && $dir ne "/") {
85         push @cfgs, "$dir/.novaboot" if -r "$dir/.novaboot";
86         $dir = abs_path($dir."/..");
87     }
88 }
89 my $cfg = $ENV{'NOVABOOT_CONFIG'};
90 Getopt::Long::Configure(qw/no_ignore_case pass_through/);
91 GetOptions ("config|c=s" => \$cfg);
92 read_config($_) foreach $cfg or reverse @cfgs;
93
94 ## Command line handling
95
96 my ($append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $iprelay, $iso_image, $make, $man, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, $remote_cmd, $reset_cmd, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $uboot, $uboot_init);
97
98 $rsync_flags = '';
99 $rom_prefix = 'rom://';
100 $stty = 'raw -crtscts -onlcr 115200';
101
102 Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
103 my %opt_spec;
104 %opt_spec = (
105     "append|a=s"     => \$append,
106     "bender|b"       => \$bender,
107     "build-dir=s"    => sub { my ($n, $v) = @_; $builddir = File::Spec->rel2abs($v); },
108     "concat"         => \$concat,
109     "chainloader=s"  => \@chainloaders,
110     "dhcp-tftp|d"    => \$dhcp_tftp,
111     "dump"           => \$dump_opt,
112     "dump-config"    => \$dump_config,
113     "gen-only"       => \$gen_only,
114     "grub|g:s"       => \$grub_config,
115     "grub-preamble=s"=> \$grub_preamble,
116     "grub-prefix=s"  => \$grub_prefix,
117     "grub2:s"        => \$grub2_config,
118     "grub2-prolog=s" => \$grub2_prolog,
119     "iprelay=s"      => \$iprelay,
120     "iso|i:s"        => \$iso_image,
121     "name=s"         => \$config_name_opt,
122     "make|m:s"       => \$make,
123     "no-file-gen"    => \$no_file_gen,
124     "off"            => \$off_opt,
125     "on"             => \$on_opt,
126     "pulsar|p:s"     => \$pulsar,
127     "pulsar-root=s"  => \$pulsar_root,
128     "qemu|Q:s"       => \$qemu,
129     "qemu-append=s"  => \$qemu_append,
130     "qemu-flags|q=s" => \$qemu_flags_cmd,
131     "remote-cmd=s"   => \$remote_cmd,
132     "reset-cmd=s"    => \$reset_cmd,
133     "rsync-flags=s"  => \$rsync_flags,
134     "scons:s"        => \$scons,
135     "scriptmod=s"    => \@scriptmod,
136     "serial|s:s"     => \$serial,
137     "server:s"       => \$server,
138     "strip-rom"      => sub { $rom_prefix = ''; },
139     "stty=s"         => \$stty,
140     "target|t=s"     => sub { my ($opt_name, $opt_value) = @_;
141                               exists $CFG::targets{$opt_value} or die("Unknown target '$opt_value' (valid targets are: ".join(", ", sort keys(%CFG::targets)).")");
142                               GetOptionsFromString($CFG::targets{$opt_value}, %opt_spec); },
143     "uboot"          => \$uboot,
144     "uboot-init=s"   => \$uboot_init,
145     "h"              => \$help,
146     "help"           => \$man,
147     );
148 GetOptions %opt_spec;
149 pod2usage(1) if $help;
150 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
151
152 ### Dump sanitized configuration (if requested)
153
154 if ($dump_config) {
155     use Data::Dumper;
156     $Data::Dumper::Indent=1;
157     print "# This file is in perl syntax.\n";
158     foreach my $key(sort(keys(%CFG::))) { # See "Symbol Tables" in perlmod(1)
159         if (defined ${$CFG::{$key}}) { print Data::Dumper->Dump([${$CFG::{$key}}], ["*$key"]); }
160         if (        @{$CFG::{$key}}) { print Data::Dumper->Dump([\@{$CFG::{$key}}], ["*$key"]); }
161         if (        %{$CFG::{$key}}) { print Data::Dumper->Dump([\%{$CFG::{$key}}], ["*$key"]); }
162     }
163     print "1;\n";
164     exit;
165 }
166
167 ### Sanitize configuration
168
169 if (defined $config_name_opt && scalar(@ARGV) > 1) { die "You cannot use --name with multiple scripts"; }
170
171 # Default options
172 if (defined $serial) {
173     $serial ||= "/dev/ttyUSB0";
174     $ENV{NB_SERIAL} = $serial;
175 }
176 if (defined $grub_config) { $grub_config ||= "menu.lst"; }
177 if (defined $grub2_config) { $grub2_config ||= "grub.cfg"; }
178
179 ## Parse the novaboot script(s)
180 my @scripts;
181 my $file;
182 my $line;
183 my $EOF;
184 my $last_fn = '';
185 my ($modules, $variables, $generated, $continuation);
186 while (<>) {
187     if ($ARGV ne $last_fn) { # New script
188         die "Missing EOF in $last_fn" if $file;
189         die "Unfinished line in $last_fn" if $line;
190         $last_fn = $ARGV;
191         push @scripts, { 'filename' => $ARGV,
192                          'modules' => $modules = [],
193                          'variables' => $variables = {},
194                          'generated' => $generated = []};
195
196     }
197     chomp();
198     next if /^#/ || /^\s*$/;    # Skip comments and empty lines
199
200     foreach my $mod(@scriptmod) { eval $mod; }
201
202     print "$_\n" if $dump_opt;
203
204     if (/^([A-Z_]+)=(.*)$/) {   # Internal variable
205         $$variables{$1} = $2;
206         next;
207     }
208     if (/^([^ ]*)(.*?)[[:space:]]*<<([^ ]*)$/) { # Heredoc start
209         push @$modules, "$1$2";
210         $file = [];
211         push @$generated, {filename => $1, content => $file};
212         $EOF = $3;
213         next;
214     }
215     if ($file && $_ eq $EOF) {  # Heredoc end
216         undef $file;
217         next;
218     }
219     if ($file) {                # Heredoc content
220         push @{$file}, "$_\n";
221         next;
222     }
223     $_ =~ s/^[[:space:]]*// if ($continuation);
224     if (/\\$/) {                # Line continuation
225         $line .= substr($_, 0, length($_)-1);
226         $continuation = 1;
227         next;
228     }
229     $continuation = 0;
230     $line .= $_;
231     $line .= " $append" if ($append && scalar(@$modules) == 0);
232
233     if ($line =~ /^([^ ]*)(.*?)[[:space:]]*< ?(.*)$/) { # Command substitution
234         push @$modules, "$1$2";
235         push @$generated, {filename => $1, command => $3};
236         $line = '';
237         next;
238     }
239     push @$modules, $line;
240     $line = '';
241 }
242 #use Data::Dumper;
243 #print Dumper(\@scripts);
244
245 exit if $dump_opt;
246
247 ## Helper functions
248
249 sub generate_configs($$$) {
250     my ($base, $generated, $filename) = @_;
251     if ($base) { $base = "$base/"; };
252     foreach my $g(@$generated) {
253       if (exists $$g{content}) {
254         my $config = $$g{content};
255         my $fn = $$g{filename};
256         open(my $f, '>', $fn) || die("$fn: $!");
257         map { s|\brom://([^ ]*)|$rom_prefix$base$1|g; print $f "$_"; } @{$config};
258         close($f);
259         print "novaboot: Created $fn\n";
260       } elsif (exists $$g{command} && ! $no_file_gen) {
261         $ENV{SRCDIR} = dirname(File::Spec->rel2abs( $filename, $invocation_dir ));
262         system_verbose("( $$g{command} ) > $$g{filename}");
263       }
264     }
265 }
266
267 sub generate_grub_config($$$$;$)
268 {
269     my ($filename, $title, $base, $modules_ref, $preamble) = @_;
270     if ($base) { $base = "$base/"; };
271     open(my $fg, '>', $filename) or die "$filename: $!";
272     print $fg "$preamble\n" if $preamble;
273     print $fg "title $title\n" if $title;
274     #print $fg "root $base\n"; # root doesn't really work for (nd)
275     my $first = 1;
276     foreach (@$modules_ref) {
277         if ($first) {
278             $first = 0;
279             my ($kbin, $kcmd) = split(' ', $_, 2);
280             $kcmd = '' if !defined $kcmd;
281             print $fg "kernel ${base}$kbin $kcmd\n";
282         } else {
283             s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
284             print $fg "module $base$_\n";
285         }
286     }
287     close($fg);
288     print("novaboot: Created $builddir/$filename\n");
289     return $filename;
290 }
291
292 sub generate_grub2_config($$$$;$$)
293 {
294     my ($filename, $title, $base, $modules_ref, $preamble, $prolog) = @_;
295     if ($base && substr($base,-1,1) ne '/') { $base = "$base/"; };
296     open(my $fg, '>', $filename) or die "$filename: $!";
297     print $fg "$preamble\n" if $preamble;
298     $title ||= 'novaboot';
299     print $fg "menuentry $title {\n";
300     print $fg "$prolog\n" if $prolog;
301     my $first = 1;
302     foreach (@$modules_ref) {
303         if ($first) {
304             $first = 0;
305             my ($kbin, $kcmd) = split(' ', $_, 2);
306             $kcmd = '' if !defined $kcmd;
307             print $fg "  multiboot ${base}$kbin $kcmd\n";
308         } else {
309             my @args = split;
310             # GRUB2 doesn't pass filename in multiboot info so we have to duplicate it here
311             $_ = join(' ', ($args[0], @args));
312             s|\brom://|$rom_prefix|g; # We do not need to translate path for GRUB2
313             print $fg "  module $base$_\n";
314         }
315     }
316     print $fg "}\n";
317     close($fg);
318     print("novaboot: Created $builddir/$filename\n");
319     return $filename;
320 }
321
322 sub generate_pulsar_config($$)
323 {
324     my ($filename, $modules_ref) = @_;
325     open(my $fg, '>', $filename) or die "$filename: $!";
326     print $fg "root $pulsar_root\n" if defined $pulsar_root;
327     my $first = 1;
328     my ($kbin, $kcmd);
329     foreach (@$modules_ref) {
330         if ($first) {
331             $first = 0;
332             ($kbin, $kcmd) = split(' ', $_, 2);
333             $kcmd = '' if !defined $kcmd;
334         } else {
335             my @args = split;
336             s|\brom://|$rom_prefix|g;
337             print $fg "load $_\n";
338         }
339     }
340     # Put kernel as last - this is needed for booting Linux and has no influence on non-Linux OSes
341     print $fg "exec $kbin $kcmd\n";
342     close($fg);
343     print("novaboot: Created $builddir/$filename\n");
344     return $filename;
345 }
346
347 sub shell_cmd_string(@)
348 {
349     return join(' ', map((/^[-_=a-zA-Z0-9\/\.\+]+$/ ? "$_" : "'$_'"), @_));
350 }
351
352 sub exec_verbose(@)
353 {
354     print "novaboot: Running: ".shell_cmd_string(@_)."\n";
355     exec(@_);
356 }
357
358 sub system_verbose($)
359 {
360     my $cmd = shift;
361     print "novaboot: Running: $cmd\n";
362     my $ret = system($cmd);
363     if ($ret & 0x007f) { die("Command terminated by a signal"); }
364     if ($ret & 0xff00) {die("Command exit with non-zero exit code"); }
365     if ($ret) { die("Command failure $ret"); }
366 }
367
368 ## WvTest handline
369
370 if (exists $variables->{WVDESC}) {
371     print "Testing \"$variables->{WVDESC}\" in $last_fn:\n";
372 } elsif ($last_fn =~ /\.wv$/) {
373     print "Testing \"all\" in $last_fn:\n";
374 }
375
376 ## Connect to the target and check whether is not occupied
377
378 # We have to do this before file generation phase, because file
379 # generation is intermixed with file deployment phase and we want to
380 # check whether the target is not used by somebody else before
381 # deploying files. Otherwise, we may rewrite other user's files on a
382 # boot server.
383
384 my $exp; # Expect object to communicate with the target over serial line
385
386 my ($target_reset, $target_power_on, $target_power_off);
387
388 if (defined $iprelay) {
389     my $IPRELAY;
390     $iprelay =~ /([.0-9]+)(:([0-9]+))?/;
391     my $addr = $1;
392     my $port = $3 || 23;
393     my $paddr   = sockaddr_in($port, inet_aton($addr));
394     my $proto   = getprotobyname('tcp');
395     socket($IPRELAY, PF_INET, SOCK_STREAM, $proto)  || die "socket: $!";
396     print "novaboot: Connecting to IP relay... ";
397     connect($IPRELAY, $paddr)    || die "connect: $!";
398     print "done\n";
399     $exp = Expect->init(\*$IPRELAY);
400     $exp->log_stdout(1);
401
402     while (1) {
403         print $exp "\xFF\xF6";  # AYT
404         my $connected = $exp->expect(20, # Timeout in seconds
405                                      '<iprelayd: connected>',
406                                      '-re', '<WEB51 HW[^>]*>');
407         last if $connected;
408     }
409
410     sub relaycmd($$) {
411         my ($relay, $onoff) = @_;
412         die unless ($relay == 1 || $relay == 2);
413
414         my $cmd = ($relay == 1 ? 0x5 : 0x6) | ($onoff ? 0x20 : 0x10);
415         return "\xFF\xFA\x2C\x32".chr($cmd)."\xFF\xF0";
416     }
417
418     sub relayconf($$) {
419         my ($relay, $onoff) = @_;
420         die unless ($relay == 1 || $relay == 2);
421         my $cmd = ($relay == 1 ? 0xdf : 0xbf) | ($onoff ? 0x00 : 0xff);
422         return "\xFF\xFA\x2C\x97".chr($cmd)."\xFF\xF0";
423     }
424
425     sub relay($$;$) {
426         my ($relay, $onoff, $can_giveup) = @_;
427         my $confirmation = '';
428         $exp->log_stdout(0);
429         print $exp relaycmd($relay, $onoff);
430         my $confirmed = $exp->expect(20, # Timeout in seconds
431                                      relayconf($relay, $onoff));
432         if (!$confirmed) {
433             if ($can_giveup) {
434                 print("Relay confirmation timeout - ignoring\n");
435             } else {
436                 die "Relay confirmation timeout";
437             }
438         }
439         $exp->log_stdout(1);
440     }
441
442     $target_reset = sub {
443         relay(2, 1, 1); # Reset the machine
444         usleep(100000);
445         relay(2, 0);
446     };
447
448     $target_power_off = sub {
449         relay(1, 1);            # Press power button
450         usleep(6000000);        # Long press to switch off
451         relay(1, 0);
452     };
453
454     $target_power_on = sub {
455         relay(1, 1);            # Press power button
456         usleep(100000);         # Short press
457         relay(1, 0);
458     };
459 }
460 elsif ($serial) {
461     my $CONN;
462     system_verbose("stty -F $serial $stty");
463     open($CONN, "+<", $serial) || die "open $serial: $!";
464     $exp = Expect->init(\*$CONN);
465 } else {
466     $exp = new Expect(); # Make $exp ready for calling $exp->spawn() later
467
468     if ($remote_cmd) {
469         print "novaboot: Running: $remote_cmd\n";
470         $exp->spawn($remote_cmd);
471     }
472 }
473
474 if (defined $reset_cmd) {
475     $target_reset = sub {
476         system_verbose($reset_cmd);
477     };
478 }
479
480 if (defined $on_opt && defined $target_power_on) {
481     &$target_power_on();
482     exit;
483 }
484 if (defined $off_opt && defined $target_power_off) {
485     print "novaboot: Switching the target off...\n";
486     &$target_power_off();
487     exit;
488 }
489
490 $builddir ||= dirname(File::Spec->rel2abs( ${$scripts[0]}{filename})) if scalar @scripts;
491 if (defined $builddir) {
492     chdir($builddir) or die "Can't change directory to $builddir: $!";
493     print "novaboot: Entering directory `$builddir'\n";
494 }
495
496 ## File generation phase
497 my (%files_iso, $menu_iso, $filename);
498 my $config_name = '';
499
500 foreach my $script (@scripts) {
501     $filename = $$script{filename};
502     $modules = $$script{modules};
503     $generated = $$script{generated};
504     $variables = $$script{variables};
505
506     ($config_name = $filename) =~ s#.*/##;
507     $config_name = $config_name_opt if (defined $config_name_opt);
508
509     if (exists $variables->{BUILDDIR}) {
510         $builddir = File::Spec->rel2abs($variables->{BUILDDIR});
511         chdir($builddir) or die "Can't change directory to $builddir: $!";
512         print "novaboot: Entering directory `$builddir'\n";
513     }
514
515     my $kernel;
516     if (exists $variables->{KERNEL}) {
517         $kernel = $variables->{KERNEL};
518     } else {
519         if ($CFG::hypervisor) {
520             $kernel = $CFG::hypervisor . " ";
521             if (exists $variables->{HYPERVISOR_PARAMS}) {
522                 $kernel .= $variables->{HYPERVISOR_PARAMS};
523             } else {
524                 $kernel .= $CFG::hypervisor_params;
525             }
526         }
527     }
528     @$modules = ($kernel, @$modules) if $kernel;
529     @$modules = (@chainloaders, @$modules);
530     @$modules = ("bin/boot/bender", @$modules) if ($bender || defined $ENV{'NOVABOOT_BENDER'});
531
532     my $prefix;
533     ($prefix = $grub_prefix) =~ s/\$NAME/$config_name/ if defined $grub_prefix;
534     $prefix ||= $builddir;
535     # TODO: use $grub_prefix as first parameter if some switch is given
536     generate_configs('', $generated, $filename);
537
538 ### Generate bootloader configuration files
539     my @bootloader_configs;
540     push @bootloader_configs, generate_grub_config($grub_config, $config_name, $prefix, $modules, $grub_preamble) if (defined $grub_config);
541     push @bootloader_configs, generate_grub2_config($grub2_config, $config_name, $prefix, $modules, $grub_preamble, $grub2_prolog) if (defined $grub2_config);
542     push @bootloader_configs, generate_pulsar_config('config-'.($pulsar||'novaboot'), $modules) if (defined $pulsar);
543
544 ### Run scons or make
545     {
546         my @files = map({ ($file) = m/([^ ]*)/; $file; } @$modules);
547         # Filter-out generated files
548         my @to_build = grep({ my $file = $_; !scalar(grep($file eq $$_{filename}, @$generated)) } @files);
549
550         system_verbose($scons || $CFG::scons." ".join(" ", @to_build)) if (defined $scons);
551         system_verbose($make  || $CFG::make ." ".join(" ", @to_build)) if (defined $make);
552     }
553
554 ### Copy files (using rsync)
555     if (defined $server && !defined($gen_only)) {
556         (my $real_server = $server) =~ s/\$NAME/$config_name/;
557
558         my ($hostname, $path) = split(":", $real_server, 2);
559         if (! defined $path) {
560             $path = $hostname;
561             $hostname = "";
562         }
563         my $files = join(" ", map({ ($file) = m/([^ ]*)/; $file; } ( @$modules, @bootloader_configs)));
564         map({ my $file = (split)[0]; die "$file: $!" if ! -f $file; } @$modules);
565         my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb';
566         my $progress = $istty ? "--progress" : "";
567         system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
568         if ($server =~ m|/\$NAME$| && $concat) {
569             my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
570             system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
571         }
572     }
573
574 ### Prepare ISO image generation
575     if (defined $iso_image) {
576         generate_configs("(cd)", $generated, $filename);
577         my $menu;
578         generate_grub_config(\$menu, $config_name, "(cd)", $modules);
579         $menu_iso .= "$menu\n";
580         map { ($file,undef) = split; $files_iso{$file} = 1; } @$modules;
581     }
582 }
583
584 ## Generate ISO image
585 if (defined $iso_image) {
586     open(my $fh, ">menu-iso.lst");
587     print $fh "timeout 5\n\n$menu_iso";
588     close($fh);
589     my $files = "boot/grub/menu.lst=menu-iso.lst " . join(" ", map("$_=$_", keys(%files_iso)));
590     $iso_image ||= "$config_name.iso";
591     system_verbose("$CFG::genisoimage -R -b stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -hide-rr-moved -J -joliet-long -o $iso_image -graft-points bin/boot/grub/ $files");
592     print("ISO image created: $builddir/$iso_image\n");
593 }
594
595 exit(0) if defined $gen_only;
596
597 ## Boot the system using various methods and send serial output to stdout
598
599 if (scalar(@scripts) > 1 && ( defined $dhcp_tftp || defined $serial || defined $iprelay)) {
600     die "You cannot do this with multiple scripts simultaneously";
601 }
602
603 if ($variables->{WVTEST_TIMEOUT}) {
604     print "wvtest: timeout ", $variables->{WVTEST_TIMEOUT}, "\n";
605 }
606
607 sub trim($) {
608     my ($str) = @_;
609     $str =~ s/^\s+|\s+$//g;
610     return $str
611 }
612
613 ### Qemu
614
615 if (defined $qemu) {
616     # Qemu
617     $qemu ||= $variables->{QEMU} || $CFG::qemu;
618     my @qemu_flags = split(" ", $qemu);
619     $qemu = shift(@qemu_flags);
620
621     @qemu_flags = split(/ +/, trim($variables->{QEMU_FLAGS})) if exists $variables->{QEMU_FLAGS};
622     @qemu_flags = split(/ +/, trim($qemu_flags_cmd)) if $qemu_flags_cmd;
623     push(@qemu_flags, split(/ +/, trim($qemu_append || '')));
624
625     if (defined $iso_image) {
626         # Boot NOVA with grub (and test the iso image)
627         push(@qemu_flags, ('-cdrom', "$config_name.iso"));
628     } else {
629         # Boot NOVA without GRUB
630
631         # Non-patched qemu doesn't like commas, but NUL can live with pluses instead of commans
632         foreach (@$modules) {s/,/+/g;}
633         generate_configs("", $generated, $filename);
634
635         if (scalar @$modules) {
636             my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
637             $kcmd = '' if !defined $kcmd;
638             my $dtb;
639             @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
640             my $initrd = join ",", @$modules;
641
642             push(@qemu_flags, ('-kernel', $kbin, '-append', $kcmd));
643             push(@qemu_flags, ('-initrd', $initrd)) if $initrd;
644             push(@qemu_flags, ('-dtb', $dtb)) if $dtb;
645         }
646     }
647     push(@qemu_flags,  qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
648     unshift(@qemu_flags, ('-name', $config_name));
649     print "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
650     $exp->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
651 }
652
653 ### Local DHCPD and TFTPD
654
655 my ($dhcpd_pid, $tftpd_pid);
656
657 if (defined $dhcp_tftp)
658 {
659     generate_configs("(nd)", $generated, $filename);
660     system_verbose('mkdir -p tftpboot');
661     generate_grub_config("tftpboot/os-menu.lst", $config_name, "(nd)", \@$modules, "timeout 0");
662     open(my $fh, '>', 'dhcpd.conf');
663     my $mac = `cat /sys/class/net/eth0/address`;
664     chomp $mac;
665     print $fh "subnet 10.23.23.0 netmask 255.255.255.0 {
666                       range 10.23.23.10 10.23.23.100;
667                       filename \"bin/boot/grub/pxegrub.pxe\";
668                       next-server 10.23.23.1;
669 }
670 host server {
671         hardware ethernet $mac;
672         fixed-address 10.23.23.1;
673 }";
674     close($fh);
675     system_verbose("sudo ip a add 10.23.23.1/24 dev eth0;
676             sudo ip l set dev eth0 up;
677             sudo touch dhcpd.leases");
678
679     # We run servers by forking ourselves, because the servers end up
680     # in our process group and get killed by signals sent to the
681     # process group (e.g. Ctrl-C on terminal).
682     $dhcpd_pid = fork();
683     exec_verbose("sudo dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid") if ($dhcpd_pid == 0);
684     $tftpd_pid = fork();
685     exec_verbose("sudo in.tftpd --foreground --secure -v -v -v --pidfile tftpd.pid $builddir") if ($tftpd_pid == 0);
686
687     # Kill server when we die
688     $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid');
689                           system_verbose('sudo pkill --pidfile=tftpd.pid'); };
690 }
691
692 ### Serial line or IP relay
693
694 if (defined $target_reset) {
695     print "novaboot: Reseting the test box... ";
696     &$target_reset();
697     print "done\n";
698 }
699
700 if (defined $uboot) {
701     print "novaboot: Waiting for uBoot prompt...\n";
702     $exp->log_stdout(1);
703     $exp->expect(20,
704                  [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
705                  '=> ') || die "No uBoot prompt deteceted";
706     $exp->send("$uboot_init\n") if $uboot_init;
707     $exp->expect(10, '=> ') || die "uBoot prompt timeout";
708
709     my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
710     my $dtb;
711     @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
712     my $initrd = shift @$modules;
713
714     my $kern_addr = '800000';
715     my $initrd_addr = '-';
716     my $dtb_addr = '';
717
718     $exp->send("tftp $kern_addr $kbin\n");
719     $exp->expect(10,
720                  [qr/#/, sub { exp_continue; }],
721                  '=> ') || die "Kernel load failed";
722     if (defined $dtb) {
723         $dtb_addr = '7f0000';
724         $exp->send("tftp $dtb_addr $dtb\n");
725         $exp->expect(10,
726                      [qr/#/, sub { exp_continue; }],
727                      '=> ') || die "Device tree load failed";
728     }
729     if (defined $initrd) {
730         $initrd_addr = 'b00000';
731         $exp->send("tftp $initrd_addr $initrd\n");
732         $exp->expect(10,
733                      [qr/#/, sub { exp_continue; }],
734                      '=> ') || die "Initrd load failed";
735     }
736     $exp->send("set bootargs $kcmd\n");
737     $exp->expect(1, '=> ')  || die "uBoot prompt timeout";
738     $exp->send("bootm $kern_addr $initrd_addr $dtb_addr\n");
739     $exp->expect(1, "\n")  || die "uBoot command timeout";
740 }
741
742 if (defined $exp) {
743     # Serial line of the target is available
744     print "novaboot: Serial line interaction (press Ctrl-C to interrupt)...\n";
745     $exp->log_stdout(1);
746     my @inputs = ($exp);
747     if (-t STDIN) { # Set up bi-directional communication if we run on terminal
748         my $infile = new IO::File;
749         $infile->IO::File::fdopen(*STDIN,'r');
750         my $in_object = Expect->exp_init($infile);
751         $in_object->set_group($exp);
752         #$in_object->set_seq("\cC",undef);
753
754         # I'm not sure when to use raw mode and when not. With
755         # --dhcp-tftp, I want the output of daemons to be normally
756         # formated (no raw mode). On the other hand, I don't want
757         # input for qemu to be echoed. Need to think more about this.
758         $in_object->manual_stty(1);
759         push(@inputs, $in_object);
760     }
761     Expect::interconnect(@inputs);
762 }
763
764 ## Kill dhcpc or tftpd
765 if (defined $dhcp_tftp) {
766     die("novaboot: This should kill servers on background\n");
767 }
768
769 ## Documentation
770
771 =head1 NAME
772
773 novaboot - A tool for booting various operating systems on various hardware or in qemu
774
775 =head1 SYNOPSIS
776
777 B<novaboot> [ options ] [--] script...
778
779 B<./script> [ options ]
780
781 =head1 DESCRIPTION
782
783 This program makes it easier to boot NOVA or other operating system
784 (OS) on different targets (machines or emulators). It reads a so
785 called novaboot script, that specifies the boot configuration, and
786 setups the target to boot that configuration. Setting up the target
787 means to generate the bootloader configuration files, deploy the
788 binaries and other needed files to proper locations, perhaps on a
789 remote boot server and reset the target. Then, target's serial output
790 is redirected to standard output if that is possible.
791
792 A typical way of using novaboot is to make the novaboot script
793 executable and set its first line to I<#!/usr/bin/env novaboot>. Then,
794 booting a particular OS configuration becomes the same as executing a
795 local program - the novaboot script.
796
797 For example, with C<novaboot> you can:
798
799 =over 3
800
801 =item 1.
802
803 Run an OS in Qemu. This is the default action when no other action is
804 specified by command line switches. Thus running C<novaboot ./script>
805 (or C<./script> as described above) will run Qemu and make it boot the
806 configuration specified in the I<script>.
807
808 =item 2.
809
810 Create a bootloader configuration file (currently supported
811 bootloaders are GRUB, GRUB2, Pulsar and uBoot) and copy it with all
812 other files needed for booting to another, perhaps remote, location.
813
814  ./script --server --iprelay=192.168.1.2
815
816 This command copies files to a TFTP server specified in the
817 configuration file and uses TCP/IP-controlled relay to reset the test
818 box and receive its serial output.
819
820 =item 3.
821
822 Run DHCP and TFTP server on developer's machine to PXE-boot the OS
823 from it. E.g.
824
825  ./script --dhcp-tftp
826
827 When a PXE-bootable machine is connected via Ethernet to developer's
828 machine, it will boot the configuration described in I<script>.
829
830 =item 4.
831
832 Create bootable ISO images. E.g.
833
834  novaboot --iso -- script1 script2
835
836 The created ISO image will have GRUB bootloader installed on it and
837 the boot menu will allow selecting between I<script1> and I<script2>
838 configurations.
839
840 =back
841
842 =head1 PHASES AND OPTIONS
843
844 Novaboot performs its work in several phases. Each phase can be
845 influenced by several options, certain phases can be skipped. The list
846 of phases (in the execution order) and the corresponding options
847 follows.
848
849 =head2 Configuration reading phase
850
851 After starting, novaboot reads configuration files. By default, it
852 searches for files named F<.novaboot> starting from the directory of
853 the novaboot script (or working directory, see bellow) and continuing
854 upwards up to the root directory. The configuration files are read in
855 order from the root directory downwards with latter files overriding
856 settings from the former ones.
857
858 In certain cases, the location of the novaboot script cannot be
859 determined in this early phase. This happens either when the script is
860 read from the standard input or when novaboot is invoked explicitly
861 and options precede the script name, as in the example L</"4."> above.
862 In this case the current working directory is used as a starting point
863 for configuration file search.
864
865 =over 8
866
867 =item -c, --config=I<filename>
868
869 Use the specified configuration file instead of the default one(s).
870
871 =back
872
873 =head2 Command line processing phase
874
875 =over 8
876
877 =item --dump-config
878
879 Dump the current configuration to stdout end exits. Useful as an
880 initial template for a configuration file.
881
882 =item -h, --help
883
884 Print short (B<-h>) or long (B<--help>) help.
885
886 =item -t, --target=I<target>
887
888 This option serves as a user configurable shortcut for other novaboot
889 options. The effect of this option is the same as the options stored
890 in the C<%targets> configuration variable under key I<target>. See
891 also L</"CONFIGURATION FILE">.
892
893 =back
894
895 =head2 Script preprocessing phase
896
897 This phases allows to modify the parsed novaboot script before it is
898 used in the later phases.
899
900 =over 8
901
902 =item -a, --append=I<parameters>
903
904 Appends a string to the first "filename" line in the novaboot script.
905 This can be used to append parameters to the kernel's or root task's
906 command line.
907
908 =item -b, --bender
909
910 Use F<bender> chainloader. Bender scans the PCI bus for PCI serial
911 ports and stores the information about them in the BIOS data area for
912 use by the kernel.
913
914 =item --chainloader=I<chainloader>
915
916 Chainloader that is loaded before the kernel and other files specified
917 in the novaboot script. E.g. 'bin/boot/bender promisc'.
918
919 =item --dump
920
921 Prints the content of the novaboot script after removing comments and
922 evaluating all I<--scriptmod> expressions. Exit after reading (and
923 dumping) the script.
924
925 =item --scriptmod=I<perl expression>
926
927 When novaboot script is read, I<perl expression> is executed for every
928 line (in $_ variable). For example, C<novaboot
929 --scriptmod=s/sigma0/omega6/g> replaces every occurrence of I<sigma0>
930 in the script with I<omega6>.
931
932 When this option is present, it overrides I<$script_modifier> variable
933 from the configuration file, which has the same effect. If this option
934 is given multiple times all expressions are evaluated in the command
935 line order.
936
937 =item --strip-rom
938
939 Strip I<rom://> prefix from command lines and generated config files.
940 The I<rom://> prefix is used by NUL. For NRE, it has to be stripped.
941
942 =back
943
944 =head2 File generation phase
945
946 In this phase, files needed for booting are generated in a so called
947 I<build directory> (see TODO). In most cases configuration for a
948 bootloader is generated automatically by novaboot. It is also possible
949 to generate other files using I<heredoc> or I<"<"> syntax in novaboot
950 scripts. Finally, binaries can be generated in this phases by running
951 C<scons> or C<make>.
952
953 =over 8
954
955 =item --build-dir=I<directory>
956
957 Overrides the default build directory location.
958
959 The default build directory location is determined as follows: If the
960 configuration file defines the C<$builddir> variable, its value is
961 used. Otherwise, it is the directory that contains the first processed
962 novaboot script.
963
964 =item -g, --grub[=I<filename>]
965
966 Generates grub bootloader menu file. If the I<filename> is not
967 specified, F<menu.lst> is used. The I<filename> is relative to the
968 build directory (see B<--build-dir>).
969
970 =item --grub-preamble=I<prefix>
971
972 Specifies the I<preable> that is at the beginning of the generated
973 GRUB or GRUB2 config files. This is useful for specifying GRUB's
974 timeout.
975
976 =item --grub-prefix=I<prefix>
977
978 Specifies I<prefix> that is put in front of every file name in GRUB's
979 F<menu.lst>. The default value is the absolute path to the build directory.
980
981 If the I<prefix> contains string $NAME, it will be replaced with the
982 name of the novaboot script (see also B<--name>).
983
984 =item --grub2[=I<filename>]
985
986 Generate GRUB2 menuentry in I<filename>. If I<filename> is not
987 specified F<grub.cfg> is used. The content of the menuentry can be
988 customized with B<--grub-preable>, B<--grub2-prolog> or
989 B<--grub_prefix> options.
990
991 In order to use the the generated menuentry on your development
992 machine that uses GRUB2, append the following snippet to
993 F</etc/grub.d/40_custom> file and regenerate your grub configuration,
994 i.e. run update-grub on Debian/Ubuntu.
995
996   if [ -f /path/to/nul/build/grub.cfg ]; then
997     source /path/to/nul/build/grub.cfg
998   fi
999
1000 =item --grub2-prolog=I<prolog>
1001
1002 Specifies text I<preable> that is put at the begiging of the entry
1003 GRUB2 entry.
1004
1005 =item -m, --make[=make command]
1006
1007 Runs C<make> to build files that are not generated by novaboot itself.
1008
1009 =item --name=I<string>
1010
1011 Use the name I<string> instead of the name of the novaboot script.
1012 This name is used for things like a title of grub menu or for the
1013 server directory where the boot files are copied to.
1014
1015 =item --no-file-gen
1016
1017 Do not generate files on the fly (i.e. "<" syntax) except for the
1018 files generated via "<<WORD" syntax.
1019
1020 =item -p, --pulsar[=mac]
1021
1022 Generates pulsar bootloader configuration file named F<config-I<mac>>
1023 The I<mac> string is typically a MAC address and defaults to
1024 I<novaboot>.
1025
1026 =item --scons[=scons command]
1027
1028 Runs C<scons> to build files that are not generated by novaboot
1029 itself.
1030
1031 =item --gen-only
1032
1033 Exit novaboot after file generation phase.
1034
1035 =back
1036
1037 =head2 Target connection check
1038
1039 If supported by the target, the connection to it is made and it is
1040 checked whether the target is not occupied by another novaboot
1041 user/instance.
1042
1043 =over 8
1044
1045 =item --iprelay=I<addr[:port]>
1046
1047 Use TCP/IP relay and serial port to access the target's serial port
1048 and powercycle it. The IP address of the relay is given by I<addr>
1049 parameter. If I<port> is not specified, it default to 23.
1050
1051 Note: This option is supposed to work with HWG-ER02a IP relays.
1052
1053 =item -s, --serial[=device]
1054
1055 Target's serial line is connected to host's serial line (device). The
1056 default value for device is F</dev/ttyUSB0>.
1057
1058 The value of this option is exported in NB_NOVABOOT environment
1059 variable to all subprocesses run by C<novaboot>.
1060
1061 =item --stty=I<settings>
1062
1063 Specifies settings passed to C<stty> invoked on the serial line
1064 specified with B<--serial> option. If this option is not given,
1065 C<stty> is called with C<raw -crtscts -onlcr 115200> settings.
1066
1067 =item --remote-cmd=I<cmd>
1068
1069 Command that mediates connection to the target's serial line. For
1070 example C<ssh server 'cu -l /dev/ttyS0'>.
1071
1072 =back
1073
1074 =head2 File deployment phase
1075
1076 In some setups, it is necessary to copy the files needed for booting
1077 to a particular location, e.g. to a TFTP boot server or to the
1078 F</boot> partition.
1079
1080 =over 8
1081
1082 =item -d, --dhcp-tftp
1083
1084 Turns your workstation into a DHCP and TFTP server so that the OS can
1085 be booted via PXE BIOS (or similar mechanism) on the test machine
1086 directly connected by a plain Ethernet cable to your workstation.
1087
1088 The DHCP and TFTP servers require root privileges and C<novaboot>
1089 uses C<sudo> command to obtain those. You can put the following to
1090 I</etc/sudoers> to allow running the necessary commands without
1091 asking for password.
1092
1093  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 --foreground --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/touch dhcpd.leases, /usr/bin/pkill --pidfile=dhcpd.pid, /usr/bin/pkill --pidfile=tftpd.pid
1094  your_login ALL=NOPASSWD: NOVABOOT
1095
1096 =item -i, --iso[=filename]
1097
1098 Generates the ISO image that boots NOVA system via GRUB. If no filename
1099 is given, the image is stored under I<NAME>.iso, where I<NAME> is the name
1100 of the novaboot script (see also B<--name>).
1101
1102 =item --server[=[[user@]server:]path]
1103
1104 Copy all files needed for booting to another location (implies B<-g>
1105 unless B<--grub2> is given). The files will be copied (by B<rsync>
1106 tool) to the directory I<path>. If the I<path> contains string $NAME,
1107 it will be replaced with the name of the novaboot script (see also
1108 B<--name>).
1109
1110 =item --concat
1111
1112 If B<--server> is used and its value ends with $NAME, then after
1113 copying the files, a new bootloader configuration file (e.g. menu.lst)
1114 is created at I<path-wo-name>, i.e. the path specified by B<--server>
1115 with $NAME part removed. The content of the file is created by
1116 concatenating all files of the same name from all subdirectories of
1117 I<path-wo-name> found on the "server".
1118
1119 =item --rsync-flags=I<flags>
1120
1121 Specifies which I<flags> are appended to F<rsync> command line when
1122 copying files as a result of I<--server> option.
1123
1124 =back
1125
1126 =head2 Target power-on and reset phase
1127
1128 =over 8
1129
1130 =item --on, --off
1131
1132 Switch on/off the target machine. Currently works only with
1133 B<--iprelay>.
1134
1135 =item -Q, --qemu[=I<qemu-binary>]
1136
1137 Boot the configuration in qemu. Optionally, the name of qemu binary
1138 can be specified as a parameter.
1139
1140 =item --qemu-append=I<flags>
1141
1142 Append I<flags> to the default qemu flags (QEMU_FLAGS variable or
1143 C<-cpu coreduo -smp 2>).
1144
1145 =item -q, --qemu-flags=I<flags>
1146
1147 Replace the default qemu flags (QEMU_FLAGS variable or C<-cpu coreduo
1148 -smp 2>) with I<flags> specified here.
1149
1150 =item --reset-cmd=I<cmd>
1151
1152 Command that resets the target.
1153
1154 =back
1155
1156 =head2 Interaction with the bootloader on the target
1157
1158 =over 8
1159
1160 =item --uboot
1161
1162 Interact with uBoot bootloader to boot the thing described in the
1163 novaboot script. Implementation of this option is currently tied to a
1164 particular board that we use. It may be subject to changes in the
1165 future!
1166
1167 =item --uboot-init
1168
1169 Command(s) to send the uBoot bootloader before loading the images and
1170 botting them.
1171
1172 =back
1173
1174 =head2 Target interaction phase
1175
1176 In this phase, target's serial output is passed to C<novaboot> stdout.
1177 If C<novaboot>'s stdin is on TTY, the stdin is passed to the target
1178 allowing interactive work with the target.
1179
1180 This phase end when the target hangs up or when Ctrl-C is pressed.
1181
1182 =head1 NOVABOOT SCRIPT SYNTAX
1183
1184 The syntax tries to mimic POSIX shell syntax. The syntax is defined with the following rules.
1185
1186 Lines starting with "#" are ignored.
1187
1188 Lines that end with "\" are concatenated with the following line after
1189 removal of the final "\" and leading whitespace of the following line.
1190
1191 Lines in the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
1192 expression) assign values to internal variables. See VARIABLES
1193 section.
1194
1195 Otherwise, the first word on the line represents the filename
1196 (relative to the build directory (see B<--build-dir>) of the module to
1197 load and the remaining words are passed as the command line
1198 parameters.
1199
1200 When the line ends with "<<WORD" then the subsequent lines until the
1201 line containing only WORD are copied literally to the file named on
1202 that line.
1203
1204 When the line ends with "< CMD" the command CMD is executed with
1205 C</bin/sh> and its standard output is stored in the file named on that
1206 line. The SRCDIR variable in CMD's environment is set to the absolute
1207 path of the directory containing the interpreted novaboot script.
1208
1209 Example:
1210   #!/usr/bin/env novaboot
1211   WVDESC=Example program
1212   bin/apps/sigma0.nul S0_DEFAULT script_start:1,1 \
1213     verbose hostkeyb:0,0x60,1,12,2
1214   bin/apps/hello.nul
1215   hello.nulconfig <<EOF
1216   sigma0::mem:16 name::/s0/log name::/s0/timer name::/s0/fs/rom ||
1217   rom://bin/apps/hello.nul
1218   EOF
1219
1220 This example will load three modules: sigma0.nul, hello.nul and
1221 hello.nulconfig. sigma0 gets some command line parameters and
1222 hello.nulconfig file is generated on the fly from the lines between
1223 <<EOF and EOF.
1224
1225 =head2 VARIABLES
1226
1227 The following variables are interpreted in the novaboot script:
1228
1229 =over 8
1230
1231 =item BUILDDIR
1232
1233 Novaboot chdir()s to this directory before file generation phase. The
1234 directory name specified here is relative to the build directory
1235 specified by other means (see L</--build-dir>).
1236
1237 =item WVDESC
1238
1239 Description of the wvtest-compliant program.
1240
1241 =item WVTEST_TIMEOUT
1242
1243 The timeout in seconds for WvTest harness. If no complete line appears
1244 in the test output within the time specified here, the test fails. It
1245 is necessary to specify this for long running tests that produce no
1246 intermediate output.
1247
1248 =item QEMU
1249
1250 Use a specific qemu binary (can be overriden with B<-Q>) and flags
1251 when booting this script under qemu. If QEMU_FLAGS variable is also
1252 specified flags specified in QEMU variable are replaced by those in
1253 QEMU_FLAGS.
1254
1255 =item QEMU_FLAGS
1256
1257 Use specific qemu flags (can be overriden with B<-q>).
1258
1259 =item HYPERVISOR_PARAMS
1260
1261 Parameters passed to hypervisor. The default value is "serial", unless
1262 overriden in configuration file.
1263
1264 =item KERNEL
1265
1266 The kernel to use instead of NOVA hypervisor specified in the
1267 configuration file. The value should contain the name of the kernel
1268 image as well as its command line parameters. If this variable is
1269 defined and non-empty, the variable HYPERVISOR_PARAMS is not used.
1270
1271 =back
1272
1273 =head1 CONFIGURATION FILE
1274
1275 Novaboot can read its configuration from a file. Configuration file
1276 was necessary in early days of novaboot. Nowadays, an attempt is made
1277 to not use the configuration file because it makes certain novaboot
1278 scripts unusable on systems without (or with different) configuration
1279 file. The only recommended use of the configuration file is to specify
1280 custom_options (see bellow).
1281
1282 If you decide to use the configuration file, it is looked up, by
1283 default, in files named F<.novaboot> as described in L</Configuration
1284 reading phase>. Alternatively, its location can be specified with the
1285 B<-c> switch or with the NOVABOOT_CONFIG environment variable. The
1286 configuration file has perl syntax and should set values of certain
1287 Perl variables. The current configuration can be dumped with the
1288 B<--dump-config> switch. Some configuration variables can be overriden
1289 by environment variables (see below) or by command line switches.
1290
1291 Documentation of some configuration variables follows:
1292
1293 =over 8
1294
1295 =item $builddir
1296
1297 Build directory location relative to the location of the configuration
1298 file.
1299
1300 =item %targets
1301
1302 Hash of shortcuts to be used with the B<--target> option. If the hash
1303 contains, for instance, the following pair of values
1304
1305  'mybox' => '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
1306
1307 then the following two commands are equivalent:
1308
1309  ./script --server=boot:/tftproot --serial=/dev/ttyUSB0 --grub
1310  ./script -t mybox
1311
1312 =back
1313
1314 =head1 ENVIRONMENT VARIABLES
1315
1316 Some options can be specified not only via config file or command line
1317 but also through environment variables. Environment variables override
1318 the values from configuration file and command line parameters
1319 override the environment variables.
1320
1321 =over 8
1322
1323 =item NOVABOOT_CONFIG
1324
1325 Name of the novaboot configuration file to use instead of the default
1326 one(s).
1327
1328 =item NOVABOOT_BENDER
1329
1330 Defining this variable has the same meaning as B<--bender> option.
1331
1332 =back
1333
1334 =head1 AUTHORS
1335
1336 Michal Sojka <sojka@os.inf.tu-dresden.de>