]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - novaboot
Set $builddir to invocation dir if it is not specified by other means
[novaboot.git] / novaboot
index 38ba5bdacd090068df85ab4121acbf88553a36fc..0a88f693b055aed3e3a824100f476df81ff58e6d 100755 (executable)
--- a/novaboot
+++ b/novaboot
@@ -16,7 +16,7 @@
 use strict;
 use warnings;
 use warnings (exists $ENV{NOVABOOT_TEST} ? (FATAL => 'all') : ());
-use Getopt::Long qw(GetOptionsFromString);
+use Getopt::Long qw(GetOptionsFromString GetOptionsFromArray);
 use Pod::Usage;
 use File::Basename;
 use File::Spec;
@@ -32,7 +32,7 @@ use Expect;
 # always flush
 $| = 1;
 
-my $invocation_dir = getcwd();
+my $invocation_dir = $ENV{PWD} || getcwd();
 
 ## Configuration file handling
 
@@ -40,17 +40,20 @@ my $invocation_dir = getcwd();
 $CFG::hypervisor = "";
 $CFG::hypervisor_params = "serial";
 $CFG::genisoimage = "genisoimage";
-$CFG::qemu = 'qemu -cpu coreduo -smp 2';
+$CFG::qemu = 'qemu-system-i386 -cpu coreduo -smp 2';
+$CFG::default_target = 'qemu';
 %CFG::targets = (
+    'qemu' => '--qemu',
     "tud" => '--server=erwin.inf.tu-dresden.de:~sojka/boot/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --grub --grub-prefix=(nd)/tftpboot/sojka/novaboot --grub-preamble="timeout 0" --concat --iprelay=141.76.48.80:2324 --scriptmod=s/\\\\bhostserial\\\\b/hostserialpci/g',
     "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',
     "localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog="  set root=\'(hd0,msdos1)\'"',
-    "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\""',
-    "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"',
+    "ryuglab" => '--server=pc-sojkam.felk.cvut.cz:/srv/tftp --uboot --uboot-init="mw f0000b00 \${psc_cfg}; sleep 1" --remote-cmd="ssh -tt pc-sojkam.felk.cvut.cz \"sterm -d -s 115200 /dev/ttyUSB0\""',
+    "ryulocal" => '--dhcp-tftp --serial --uboot --uboot-init="dhcp; mw f0000b00 \${psc_cfg}; sleep 1" --reset-cmd="if which dtrrts; then dtrrts $NB_SERIAL 0 1; sleep 0.1; dtrrts $NB_SERIAL 1 1; fi"',
 
     );
-my $nproc = `nproc`;
+chomp(my $nproc = `nproc`);
 $CFG::scons = "scons -j$nproc";
+$CFG::make = "make -j$nproc";
 
 my $builddir;
 
@@ -80,28 +83,58 @@ my @cfgs;
     # when a script is run as "novaboot <options> <script>" then $ARGV[0]
     # contains the first option. Hence the -f check.
     my $dir = File::Spec->rel2abs($ARGV[0] && -f $ARGV[0] ? dirname($ARGV[0]) : '', $invocation_dir);
-    while (-d $dir && $dir ne "/") {
+    while ((-d $dir || -l $dir ) && $dir ne "/") {
        push @cfgs, "$dir/.novaboot" if -r "$dir/.novaboot";
-       $dir = abs_path($dir."/..");
+       my @dirs = File::Spec->splitdir($dir);
+       $dir = File::Spec->catdir(@dirs[0..$#dirs-1]);
+    }
+    @cfgs = reverse @cfgs;
+    $dir = $ENV{'NOVABOOT_CONFIG_DIR'} || '/etc/novaboot.d';
+    if (opendir(my $dh, $dir)) {
+       my @etccfg = map { "$dir/$_" } grep { /^[-_a-zA-Z0-9]+$/ && -f "$dir/$_" } readdir($dh);
+       closedir $dh;
+       @etccfg = sort(@etccfg);
+       @cfgs = ( @etccfg, @cfgs );
     }
 }
 my $cfg = $ENV{'NOVABOOT_CONFIG'};
 Getopt::Long::Configure(qw/no_ignore_case pass_through/);
 GetOptions ("config|c=s" => \$cfg);
-read_config($_) foreach $cfg or reverse @cfgs;
+read_config($_) foreach $cfg or @cfgs;
 
 ## Command line handling
 
-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, $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);
+my $explicit_target;
+GetOptions ("target|t=s" => \$explicit_target);
+
+my ($amt, @append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, @exiton, @expect_raw, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $iprelay, $iso_image, $interactive, $kernel_opt, $make, $man, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, $remote_cmd, $remote_expect, $reset_cmd, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, @uboot_init);
 
 $rsync_flags = '';
 $rom_prefix = 'rom://';
 $stty = 'raw -crtscts -onlcr 115200';
 
-Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
+my @expect_seen = ();
+sub handle_expect
+{
+    my ($n, $v) = @_;
+    push(@expect_seen, '-re') if $n eq "expect-re";
+    push(@expect_seen, $v);
+}
+
+sub handle_send
+{
+    my ($n, $v) = @_;
+    unless (@expect_seen) { die("No --expect before --send"); }
+    my $ret = ($n eq "sendcont") ? exp_continue : 0;
+    unshift(@expect_raw, sub { shift->send(eval("\"$v\"")); $ret; });
+    unshift(@expect_raw, @expect_seen);
+    @expect_seen = ();
+}
+
 my %opt_spec;
 %opt_spec = (
-    "append|a=s"     => \$append,
+    "amt=s"         => \$amt,
+    "append|a=s"     => \@append,
     "bender|b"       => \$bender,
     "build-dir=s"    => sub { my ($n, $v) = @_; $builddir = File::Spec->rel2abs($v); },
     "concat"        => \$concat,
@@ -109,41 +142,72 @@ my %opt_spec;
     "dhcp-tftp|d"    => \$dhcp_tftp,
     "dump"          => \$dump_opt,
     "dump-config"    => \$dump_config,
+    "exiton=s"       => \@exiton,
+    "expect=s"      => \&handle_expect,
+    "expect-re=s"    => \&handle_expect,
+    "expect-raw=s"   => sub { my ($n, $v) = @_; unshift(@expect_raw, eval($v)); },
     "gen-only"      => \$gen_only,
     "grub|g:s"              => \$grub_config,
     "grub-preamble=s"=> \$grub_preamble,
     "grub-prefix=s"  => \$grub_prefix,
     "grub2:s"       => \$grub2_config,
     "grub2-prolog=s" => \$grub2_prolog,
+    "ider"           => \$ider,
     "iprelay=s"             => \$iprelay,
-    "iso|i:s"       => \$iso_image,
+    "iso:s"         => \$iso_image,
+    "kernel|k=s"     => \$kernel_opt,
+    "interactive|i"  => \$interactive,
     "name=s"        => \$config_name_opt,
+    "make|m:s"      => \$make,
     "no-file-gen"    => \$no_file_gen,
     "off"           => \$off_opt,
     "on"            => \$on_opt,
     "pulsar|p:s"     => \$pulsar,
     "pulsar-root=s"  => \$pulsar_root,
-    "qemu|Q=s"              => \$qemu,
+    "qemu|Q:s"              => \$qemu,
     "qemu-append=s"  => \$qemu_append,
     "qemu-flags|q=s" => \$qemu_flags_cmd,
     "remote-cmd=s"   => \$remote_cmd,
+    "remote-expect=s"=> \$remote_expect,
     "reset-cmd=s"    => \$reset_cmd,
     "rsync-flags=s"  => \$rsync_flags,
     "scons:s"       => \$scons,
     "scriptmod=s"    => \@scriptmod,
+    "send=s"        => \&handle_send,
+    "sendcont=s"     => \&handle_send,
     "serial|s:s"     => \$serial,
     "server:s"              => \$server,
     "strip-rom"             => sub { $rom_prefix = ''; },
     "stty=s"        => \$stty,
-    "target|t=s"     => sub { my ($opt_name, $opt_value) = @_;
-                             exists $CFG::targets{$opt_value} or die("Unknown target '$opt_value' (valid targets are: ".join(", ", sort keys(%CFG::targets)).")");
-                             GetOptionsFromString($CFG::targets{$opt_value}, %opt_spec); },
-    "uboot"         => \$uboot,
-    "uboot-init=s"   => \$uboot_init,
+    "tftp"          => \$tftp,
+    "tftp-port=i"    => \$tftp_port,
+    "uboot:s"       => \$uboot,
+    "uboot-init=s"   => \@uboot_init,
     "h"             => \$help,
     "help"          => \$man,
     );
-GetOptions %opt_spec or pod2usage(2);
+
+# First process target options
+{
+    my $t = defined($explicit_target) ? $explicit_target : $CFG::default_target;
+    my @target_expanded;
+    Getopt::Long::Configure(qw/no_ignore_case pass_through/);
+    while ($t) {
+       exists $CFG::targets{$t} or die("Unknown target '$t' (valid targets are: ".join(", ", sort keys(%CFG::targets)).")");
+
+       undef $explicit_target;
+       my ($ret, $remaining_args) = GetOptionsFromString ($CFG::targets{$t}, ("target|t=s" => \$explicit_target));
+       if (!$ret) { die "Error parsing target $t option"; }
+       push(@target_expanded, @$remaining_args);
+       $t = $explicit_target;
+    }
+    Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
+    GetOptionsFromArray(\@target_expanded, %opt_spec) or die ("Error in target definition");
+}
+
+# Then process other command line options - some of them may override
+# what was specified by the target
+GetOptions %opt_spec or die("Error in command line arguments");
 pod2usage(1) if $help;
 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
 
@@ -164,8 +228,17 @@ if ($dump_config) {
 
 ### Sanitize configuration
 
+if ($interactive && !-t STDIN) {
+    die("novaboot: Interactive mode not supported when not on terminal");
+}
+
 if (defined $config_name_opt && scalar(@ARGV) > 1) { die "You cannot use --name with multiple scripts"; }
 
+if ($ider) {
+    $iso_image //= ''; # IDE-R needs an ISO image
+    if (!defined $amt) { die "Error: --ider requires --amt"; }
+}
+
 # Default options
 if (defined $serial) {
     $serial ||= "/dev/ttyUSB0";
@@ -177,14 +250,14 @@ if (defined $grub2_config) { $grub2_config ||= "grub.cfg"; }
 ## Parse the novaboot script(s)
 my @scripts;
 my $file;
-my $line;
 my $EOF;
 my $last_fn = '';
 my ($modules, $variables, $generated, $continuation);
-while (<>) {
+my $skip_reading = defined($on_opt) || defined($off_opt);
+while (!$skip_reading && ($_ = <>)) {
     if ($ARGV ne $last_fn) { # New script
        die "Missing EOF in $last_fn" if $file;
-       die "Unfinished line in $last_fn" if $line;
+       die "Unfinished line in $last_fn" if $continuation;
        $last_fn = $ARGV;
        push @scripts, { 'filename' => $ARGV,
                         'modules' => $modules = [],
@@ -195,21 +268,20 @@ while (<>) {
     chomp();
     next if /^#/ || /^\s*$/;   # Skip comments and empty lines
 
-    foreach my $mod(@scriptmod) { eval $mod; }
-
-    print "$_\n" if $dump_opt;
+    $_ =~ s/^[[:space:]]*// if ($continuation);
 
-    if (/^([A-Z_]+)=(.*)$/) {  # Internal variable
-       $$variables{$1} = $2;
+    if (/\\$/) {               # Line continuation
+       $continuation .= substr($_, 0, length($_)-1);
        next;
     }
-    if (/^([^ ]*)(.*?)[[:space:]]*<<([^ ]*)$/) { # Heredoc start
-       push @$modules, "$1$2";
-       $file = [];
-       push @$generated, {filename => $1, content => $file};
-       $EOF = $3;
-       next;
+
+    if ($continuation) {       # Last continuation line
+       $_ = $continuation . $_;
+       $continuation = '';
     }
+
+    foreach my $mod(@scriptmod) { eval $mod; }
+
     if ($file && $_ eq $EOF) { # Heredoc end
        undef $file;
        next;
@@ -218,29 +290,67 @@ while (<>) {
        push @{$file}, "$_\n";
        next;
     }
-    $_ =~ s/^[[:space:]]*// if ($continuation);
-    if (/\\$/) {               # Line continuation
-       $line .= substr($_, 0, length($_)-1);
-       $continuation = 1;
+    if (/^([A-Z_]+)=(.*)$/) {  # Internal variable
+       $$variables{$1} = $2;
+       push(@exiton, $2) if ($1 eq "EXITON");
+       next;
+    }
+    if (s/^load *//) {         # Load line
+       die("novaboot: '$last_fn' line $.: Missing file name\n") unless /^[^ <]+/;
+       if (/^([^ ]*)(.*?)[[:space:]]*<<([^ ]*)$/) { # Heredoc start
+           push @$modules, "$1$2";
+           $file = [];
+           push @$generated, {filename => $1, content => $file};
+           $EOF = $3;
+           next;
+       }
+       if (/^([^ ]*)(.*?)[[:space:]]*< ?(.*)$/) { # Command substitution
+           push @$modules, "$1$2";
+           push @$generated, {filename => $1, command => $3};
+           next;
+       }
+       push @$modules, $_;
        next;
     }
-    $continuation = 0;
-    $line .= $_;
-    $line .= " $append" if ($append && scalar(@$modules) == 0);
-
-    if ($line =~ /^([^ ]*)(.*?)[[:space:]]*< ?(.*)$/) { # Command substitution
-       push @$modules, "$1$2";
-       push @$generated, {filename => $1, command => $3};
-       $line = '';
+    if (/^run (.*)/) {         # run line
+       push @$generated, {command => $1};
        next;
     }
-    push @$modules, $line;
-    $line = '';
+
+    die("novaboot: Cannot parse script '$last_fn' line $.. Didn't you forget 'load' keyword?\n");
 }
-#use Data::Dumper;
-#print Dumper(\@scripts);
+# use Data::Dumper;
+# print Dumper(\@scripts);
 
-exit if $dump_opt;
+foreach my $script (@scripts) {
+    $modules = $$script{modules};
+    @$modules[0] =~ s/^[^ ]*/$kernel_opt/ if $kernel_opt;
+    @$modules[0] .= ' ' . join(' ', @append) if @append;
+
+    my $kernel;
+    if (exists $variables->{KERNEL}) {
+       $kernel = $variables->{KERNEL};
+    } else {
+       if ($CFG::hypervisor) {
+           $kernel = $CFG::hypervisor . " ";
+           if (exists $variables->{HYPERVISOR_PARAMS}) {
+               $kernel .= $variables->{HYPERVISOR_PARAMS};
+           } else {
+               $kernel .= $CFG::hypervisor_params;
+           }
+       }
+    }
+    @$modules = ($kernel, @$modules) if $kernel;
+    @$modules = (@chainloaders, @$modules);
+    @$modules = ("bin/boot/bender", @$modules) if ($bender || defined $ENV{'NOVABOOT_BENDER'});
+}
+
+if ($dump_opt) {
+    foreach my $script (@scripts) {
+       print join("\n", @{$$script{modules}})."\n";
+    }
+    exit(0);
+}
 
 ## Helper functions
 
@@ -257,7 +367,11 @@ sub generate_configs($$$) {
        print "novaboot: Created $fn\n";
       } elsif (exists $$g{command} && ! $no_file_gen) {
        $ENV{SRCDIR} = dirname(File::Spec->rel2abs( $filename, $invocation_dir ));
-       system_verbose("( $$g{command} ) > $$g{filename}");
+       if (exists $$g{filename}) {
+           system_verbose("( $$g{command} ) > $$g{filename}");
+       } else {
+           system_verbose($$g{command});
+       }
       }
     }
 }
@@ -287,6 +401,39 @@ sub generate_grub_config($$$$;$)
     return $filename;
 }
 
+sub generate_syslinux_config($$$$)
+{
+    my ($filename, $title, $base, $modules_ref) = @_;
+    if ($base && $base !~ /\/$/) { $base = "$base/"; };
+    open(my $fg, '>', $filename) or die "$filename: $!";
+    print $fg "LABEL $title\n";
+    #TODO print $fg "MENU LABEL $human_readable_title\n";
+
+    my ($kbin, $kcmd) = split(' ', @$modules_ref[0], 2);
+
+    if (system("file $kbin|grep 'Linux kernel'") == 0) {
+       my $initrd = @$modules_ref[1];
+       die('To many "load" lines for Linux kernel') if (scalar @$modules_ref > 2);
+       print $fg "LINUX $base$kbin\n";
+       print $fg "APPEND $kcmd\n";
+       print $fg "INITRD $base$initrd\n";
+    } else {
+       print $fg "KERNEL mboot.c32\n";
+       my @append;
+       foreach (@$modules_ref) {
+           s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
+           push @append, "$base$_";
+           print $fg "APPEND ".join(' --- ', @append)."\n";
+       }
+    }
+    #TODO print $fg "TEXT HELP\n";
+    #TODO print $fg "some help here\n";
+    #TODO print $fg "ENDTEXT\n";
+    close($fg);
+    print("novaboot: Created $builddir/$filename\n");
+    return $filename;
+}
+
 sub generate_grub2_config($$$$;$$)
 {
     my ($filename, $title, $base, $modules_ref, $preamble, $prolog) = @_;
@@ -351,6 +498,7 @@ sub exec_verbose(@)
 {
     print "novaboot: Running: ".shell_cmd_string(@_)."\n";
     exec(@_);
+    exit(1); # should not be reached
 }
 
 sub system_verbose($)
@@ -371,7 +519,7 @@ if (exists $variables->{WVDESC}) {
     print "Testing \"all\" in $last_fn:\n";
 }
 
-## Connect to the target and check whether is not occupied
+## Connect to the target and check whether it is not occupied
 
 # We have to do this before file generation phase, because file
 # generation is intermixed with file deployment phase and we want to
@@ -382,6 +530,7 @@ if (exists $variables->{WVDESC}) {
 my $exp; # Expect object to communicate with the target over serial line
 
 my ($target_reset, $target_power_on, $target_power_off);
+my ($amt_user, $amt_password, $amt_host, $amt_port);
 
 if (defined $iprelay) {
     my $IPRELAY;
@@ -460,13 +609,115 @@ elsif ($serial) {
     system_verbose("stty -F $serial $stty");
     open($CONN, "+<", $serial) || die "open $serial: $!";
     $exp = Expect->init(\*$CONN);
-} else {
-    $exp = new Expect(); # Make $exp ready for calling $exp->spawn() later
+}
+elsif ($remote_cmd) {
+    print "novaboot: Running: $remote_cmd\n";
+    $exp = Expect->spawn($remote_cmd);
+}
+elsif (defined $amt) {
+    require LWP::UserAgent;
+    require LWP::Authen::Digest;
+
+    sub genXML {
+        my ($host, $username, $password, $schema, $className, $pstate) = @_;
+       #AMT numbers for PowerStateChange (MNI => bluescreen on windows;-)
+       my %pstates = ("on"        => 2,
+                      "standby"   => 4,
+                      "hibernate" => 7,
+                      "off"       => 8,
+                      "reset"     => 10,
+                      "MNI"       => 11);
+        return <<END;
+               <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">
+               <s:Header><a:To>http://$host:16992/wsman</a:To>
+               <w:ResourceURI s:mustUnderstand="true">$schema</w:ResourceURI>
+               <a:ReplyTo><a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>
+               <a:Action s:mustUnderstand="true">$schema$className</a:Action>
+               <w:MaxEnvelopeSize s:mustUnderstand="true">153600</w:MaxEnvelopeSize>
+               <a:MessageID>uuid:709072C9-609C-4B43-B301-075004043C7C</a:MessageID>
+               <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
+               <w:OperationTimeout>PT60.000S</w:OperationTimeout>
+               <w:SelectorSet><w:Selector Name="Name">Intel(r) AMT Power Management Service</w:Selector></w:SelectorSet>
+               </s:Header><s:Body>
+               <p:RequestPowerStateChange_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService">
+               <p:PowerState>$pstates{$pstate}</p:PowerState>
+               <p:ManagedElement><a:Address>http://$host:16992/wsman</a:Address>
+               <a:ReferenceParameters><w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</w:ResourceURI>
+               <w:SelectorSet><w:Selector Name="Name">ManagedSystem</w:Selector></w:SelectorSet>
+               </a:ReferenceParameters></p:ManagedElement>
+               </p:RequestPowerStateChange_INPUT>
+               </s:Body></s:Envelope>
+END
+    }
+
+    sub sendPOST {
+        my ($host, $username, $password, $content) = @_;
+
+        my $ua = LWP::UserAgent->new();
+        $ua->agent("novaboot");
+
+       my $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
+        my $res = $ua->request($req);
+        die ("Unexpected AMT response: " . $res->status_line) unless $res->code == 401;
 
-    if ($remote_cmd) {
-       print "novaboot: Running: $remote_cmd\n";
-       $exp->spawn($remote_cmd);
+        my ($realm) = $res->header("WWW-Authenticate") =~ /Digest realm="(.*?)"/;
+        $ua->credentials("$host:16992", $realm, $username => $password);
+
+        # Create a request
+        $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
+        $req->content_type('application/x-www-form-urlencoded');
+        $req->content($content);
+        $res = $ua->request($req);
+        die ("AMT power change request failed: " . $res->status_line) unless $res->is_success;
+        $res->content() =~ /<g:ReturnValue>(\d+)<\/g:ReturnValue>/;
+        return $1;
+    }
+
+    sub powerChange  {
+        my ($host, $username, $password, $pstate)=@_;
+        my $schema="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService";
+        my $className="/RequestPowerStateChange";
+        my $content = genXML($host, $username, $password ,$schema, $className, $pstate);
+        return sendPOST($host, $username, $password, $content);
     }
+
+    ($amt_user,$amt_password,$amt_host,$amt_port) = ($amt =~ /(?:(.*?)(?::(.*))?@)?([^:]*)(?::([0-9]*))?/);;
+    $amt_user ||= "admin";
+    $amt_password ||= $ENV{'AMT_PASSWORD'} || die "AMT password not specified";
+    $amt_host || die "AMT host not specified";
+    $amt_port ||= 16994;
+
+
+    $target_power_off = sub {
+       $exp->close();
+       my $result = powerChange($amt_host,$amt_user,$amt_password, "off");
+       die "AMT power off failed (ReturnValue $result)" if $result != 0;
+    };
+
+    $target_power_on = sub {
+       my $result = powerChange($amt_host,$amt_user,$amt_password, "on");
+       die "AMT power on failed (ReturnValue $result)" if $result != 0;
+    };
+
+    $target_reset = sub {
+       my $result = powerChange($amt_host,$amt_user,$amt_password, "reset");
+       if ($result != 0) {
+           print STDERR "Warning: Cannot reset $amt_host, trying power on. ";
+           $result = powerChange($amt_host,$amt_user,$amt_password, "on");
+       }
+       die "AMT reset failed (ReturnValue $result)" if $result != 0;
+    };
+
+    my $cmd = "amtterm -u $amt_user -p $amt_password $amt_host $amt_port";
+    print "novaboot: Running: $cmd\n" =~ s/\Q$amt_password\E/???/r;
+    $exp = Expect->spawn($cmd);
+    $exp->expect(10, "RUN_SOL") || die "Expect for 'RUN_SOL' timed out";
+
+}
+
+
+if ($remote_expect) {
+    $exp->expect(10, $remote_expect) || die "Expect for '$remote_expect' timed out";
 }
 
 if (defined $reset_cmd) {
@@ -489,6 +740,8 @@ $builddir ||= dirname(File::Spec->rel2abs( ${$scripts[0]}{filename})) if scalar
 if (defined $builddir) {
     chdir($builddir) or die "Can't change directory to $builddir: $!";
     print "novaboot: Entering directory `$builddir'\n";
+} else {
+    $builddir = $invocation_dir;
 }
 
 ## File generation phase
@@ -510,23 +763,6 @@ foreach my $script (@scripts) {
        print "novaboot: Entering directory `$builddir'\n";
     }
 
-    my $kernel;
-    if (exists $variables->{KERNEL}) {
-       $kernel = $variables->{KERNEL};
-    } else {
-       if ($CFG::hypervisor) {
-           $kernel = $CFG::hypervisor . " ";
-           if (exists $variables->{HYPERVISOR_PARAMS}) {
-               $kernel .= $variables->{HYPERVISOR_PARAMS};
-           } else {
-               $kernel .= $CFG::hypervisor_params;
-           }
-       }
-    }
-    @$modules = ($kernel, @$modules) if $kernel;
-    @$modules = (@chainloaders, @$modules);
-    @$modules = ("bin/boot/bender", @$modules) if ($bender || defined $ENV{'NOVABOOT_BENDER'});
-
     my $prefix;
     ($prefix = $grub_prefix) =~ s/\$NAME/$config_name/ if defined $grub_prefix;
     $prefix ||= $builddir;
@@ -539,12 +775,14 @@ foreach my $script (@scripts) {
     push @bootloader_configs, generate_grub2_config($grub2_config, $config_name, $prefix, $modules, $grub_preamble, $grub2_prolog) if (defined $grub2_config);
     push @bootloader_configs, generate_pulsar_config('config-'.($pulsar||'novaboot'), $modules) if (defined $pulsar);
 
-### Run scons
-    if (defined $scons) {
+### Run scons or make
+    {
        my @files = map({ ($file) = m/([^ ]*)/; $file; } @$modules);
        # Filter-out generated files
-       my @to_build = grep({ my $file = $_; !scalar(grep($file eq $$_{filename}, @$generated)) } @files);
-       system_verbose($scons || $CFG::scons." ".join(" ", @to_build));
+       my @to_build = grep({ my $file = $_; !scalar(grep($file eq ($$_{filename} || ''), @$generated)) } @files);
+
+       system_verbose($scons || $CFG::scons." ".join(" ", @to_build)) if (defined $scons);
+       system_verbose($make  || $CFG::make ." ".join(" ", @to_build)) if (defined $make);
     }
 
 ### Copy files (using rsync)
@@ -571,7 +809,7 @@ foreach my $script (@scripts) {
     if (defined $iso_image) {
        generate_configs("(cd)", $generated, $filename);
        my $menu;
-       generate_grub_config(\$menu, $config_name, "(cd)", $modules);
+       generate_syslinux_config(\$menu, $config_name, "/", $modules);
        $menu_iso .= "$menu\n";
        map { ($file,undef) = split; $files_iso{$file} = 1; } @$modules;
     }
@@ -579,12 +817,35 @@ foreach my $script (@scripts) {
 
 ## Generate ISO image
 if (defined $iso_image) {
-    open(my $fh, ">menu-iso.lst");
-    print $fh "timeout 5\n\n$menu_iso";
+    system_verbose("mkdir -p isolinux");
+
+    my @files;
+    if (-f '/usr/lib/ISOLINUX/isolinux.bin') {
+       # Newer ISOLINUX version
+       @files = qw(/usr/lib/ISOLINUX/isolinux.bin /usr/lib/syslinux/modules/bios/mboot.c32 /usr/lib/syslinux/modules/bios/libcom32.c32 /usr/lib/syslinux/modules/bios/menu.c32 /usr/lib/syslinux/modules/bios/ldlinux.c32);
+    } else {
+       # Older ISOLINUX version
+       @files = qw(/usr/lib/syslinux/isolinux.bin /usr/lib/syslinux/mboot.c32 /usr/lib/syslinux/menu.c32);
+    }
+    system_verbose("cp @files isolinux");
+    open(my $fh, ">isolinux/isolinux.cfg");
+    if ($#scripts) {
+       print $fh "TIMEOUT 50\n";
+       print $fh "DEFAULT menu\n";
+    } else {
+       print $fh "DEFAULT $config_name\n";
+    }
+    print $fh "$menu_iso";
     close($fh);
-    my $files = "boot/grub/menu.lst=menu-iso.lst " . join(" ", map("$_=$_", keys(%files_iso)));
+
+    my $files = join(" ", map("$_=$_", (keys(%files_iso), 'isolinux/isolinux.cfg', map(s|.*/|isolinux/|r, @files))));
     $iso_image ||= "$config_name.iso";
-    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");
+
+    # Note: We use -U flag below to "Allow 'untranslated' filenames,
+    # completely violating the ISO9660 standards". Without this
+    # option, isolinux is not able to read files names for example
+    # bzImage-3.0.
+    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");
     print("ISO image created: $builddir/$iso_image\n");
 }
 
@@ -606,9 +867,9 @@ sub trim($) {
     return $str
 }
 
-### Qemu
+### Start in Qemu
 
-if (!(defined $dhcp_tftp || defined $serial || defined $iprelay || defined $server || defined $iso_image)) {
+if (defined $qemu) {
     # Qemu
     $qemu ||= $variables->{QEMU} || $CFG::qemu;
     my @qemu_flags = split(" ", $qemu);
@@ -620,7 +881,7 @@ if (!(defined $dhcp_tftp || defined $serial || defined $iprelay || defined $serv
 
     if (defined $iso_image) {
        # Boot NOVA with grub (and test the iso image)
-       push(@qemu_flags, ('-cdrom', "$config_name.iso"));
+       push(@qemu_flags, ('-cdrom', $iso_image));
     } else {
        # Boot NOVA without GRUB
 
@@ -643,13 +904,15 @@ if (!(defined $dhcp_tftp || defined $serial || defined $iprelay || defined $serv
     push(@qemu_flags,  qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
     unshift(@qemu_flags, ('-name', $config_name));
     print "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
-    $exp->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
+    $exp = Expect->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
 }
 
 ### Local DHCPD and TFTPD
 
 my ($dhcpd_pid, $tftpd_pid);
 
+$tftp=1 if $tftp_port;
+
 if (defined $dhcp_tftp)
 {
     generate_configs("(nd)", $generated, $filename);
@@ -677,15 +940,37 @@ host server {
     # process group (e.g. Ctrl-C on terminal).
     $dhcpd_pid = fork();
     exec_verbose("sudo dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid") if ($dhcpd_pid == 0);
-    $tftpd_pid = fork();
-    exec_verbose("sudo in.tftpd --foreground --secure -v -v -v --pidfile tftpd.pid $builddir") if ($tftpd_pid == 0);
+}
+
+if (defined $dhcp_tftp || defined $tftp) {
+    $tftp_port ||= 69;
+    # Unfortunately, tftpd requires root privileges even with
+    # non-privileged (>1023) port due to initgroups().
+    system_verbose("sudo in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid  --address :$tftp_port $builddir");
 
     # Kill server when we die
-    $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid');
+    $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid') if (defined $dhcp_tftp);
                          system_verbose('sudo pkill --pidfile=tftpd.pid'); };
+
+    # We have to kill tftpd explicitely, because it is not in our process group
+    $SIG{INT} = sub { system_verbose('sudo pkill --pidfile=tftpd.pid'); exit(0); };
 }
 
-### Serial line or IP relay
+### AMT IDE-R
+if (defined $ider) {
+    my $ider_cmd= "amtider -c $iso_image -u $amt_user -p $amt_password $amt_host $amt_port"  ;
+    print "novaboot: Running: $ider_cmd\n" =~ s/\Q$amt_password\E/???/r;
+    my $ider_pid = fork();
+    if ($ider_pid == 0) {
+       exec($ider_cmd);
+       die "IDE redirection failed";
+    }
+    # FIXME: This collides with --tftp option. Hopefully, nobody needs
+    # to use both simultaneously.
+    $SIG{__DIE__} = sub { system_verbose('kill $ider_pid'); };
+}
+
+### Reset target (IP relay, AMT, ...)
 
 if (defined $target_reset) {
     print "novaboot: Reseting the test box... ";
@@ -693,72 +978,94 @@ if (defined $target_reset) {
     print "done\n";
 }
 
+### U-boot conversation
 if (defined $uboot) {
+    my $uboot_prompt = $uboot || '=> ';
     print "novaboot: Waiting for uBoot prompt...\n";
     $exp->log_stdout(1);
+    #$exp->exp_internal(1);
     $exp->expect(20,
                 [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
-                '=> ') || die "No uBoot prompt deteceted";
-    $exp->send("$uboot_init\n") if $uboot_init;
-    $exp->expect(10, '=> ') || die "uBoot prompt timeout";
-
-    my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
-    my $dtb;
-    @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
-    my $initrd = shift @$modules;
-
-    my $kern_addr = '800000';
-    my $initrd_addr = '-';
-    my $dtb_addr = '';
-
-    $exp->send("tftp $kern_addr $kbin\n");
-    $exp->expect(10,
-                [qr/#/, sub { exp_continue; }],
-                '=> ') || die "Kernel load failed";
-    if (defined $dtb) {
-       $dtb_addr = '7f0000';
-       $exp->send("tftp $dtb_addr $dtb\n");
-       $exp->expect(10,
-                    [qr/#/, sub { exp_continue; }],
-                    '=> ') || die "Device tree load failed";
+                $uboot_prompt) || die "No uBoot prompt deteceted";
+    foreach my $cmd (@uboot_init) {
+       chomp($cmd);
+       $exp->send("$cmd\n");
+       $exp->expect(10, $uboot_prompt) || die "uBoot prompt timeout";
     }
-    if (defined $initrd) {
-       $initrd_addr = 'b00000';
-       $exp->send("tftp $initrd_addr $initrd\n");
+
+    # Boot the system if there are some load lines in the script
+    if (scalar(@$modules) > 0) {
+       my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
+       my $dtb;
+       @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
+       my $initrd = shift @$modules;
+
+       my $kern_addr = '800000';
+       my $initrd_addr = '-';
+       my $dtb_addr = '';
+
+       $exp->send("tftp $kern_addr $kbin\n");
        $exp->expect(10,
                     [qr/#/, sub { exp_continue; }],
-                    '=> ') || die "Initrd load failed";
+                    $uboot_prompt) || die "Kernel load failed";
+       if (defined $dtb) {
+           $dtb_addr = '7f0000';
+           $exp->send("tftp $dtb_addr $dtb\n");
+           $exp->expect(10,
+                        [qr/#/, sub { exp_continue; }],
+                        $uboot_prompt) || die "Device tree load failed";
+       }
+       if (defined $initrd) {
+           $initrd_addr = 'b00000';
+           $exp->send("tftp $initrd_addr $initrd\n");
+           $exp->expect(10,
+                        [qr/#/, sub { exp_continue; }],
+                        $uboot_prompt) || die "Initrd load failed";
+       }
+       $exp->send("set bootargs '$kcmd'\n");
+       $exp->expect(5, $uboot_prompt)  || die "uBoot prompt timeout";
+       $exp->send("bootm $kern_addr $initrd_addr $dtb_addr\n");
+       $exp->expect(5, "\n")  || die "uBoot command timeout";
     }
-    $exp->send("set bootargs $kcmd\n");
-    $exp->expect(1, '=> ')  || die "uBoot prompt timeout";
-    $exp->send("bootm $kern_addr $initrd_addr $dtb_addr\n");
-    $exp->expect(1, "\n")  || die "uBoot command timeout";
 }
 
+### Serial line interaction
 if (defined $exp) {
     # Serial line of the target is available
-    print "novaboot: Serial line interaction (press Ctrl-C to interrupt)...\n";
+    my $interrupt = 'Ctrl-C';
+    if ($interactive && !@exiton) {
+       $interrupt = '"~~."';
+    }
+    my $note = (-t STDIN) ? '' : '- only target->host ';
+    print "novaboot: Serial line interaction $note(press $interrupt to interrupt)...\n";
     $exp->log_stdout(1);
-    my @inputs = ($exp);
-    if (-t STDIN) { # Set up bi-directional communication if we run on terminal
-       my $infile = new IO::File;
-       $infile->IO::File::fdopen(*STDIN,'r');
-       my $in_object = Expect->exp_init($infile);
-       $in_object->set_group($exp);
-       #$in_object->set_seq("\cC",undef);
-
-       # I'm not sure when to use raw mode and when not. With
-       # --dhcp-tftp, I want the output of daemons to be normally
-       # formated (no raw mode). On the other hand, I don't want
-       # input for qemu to be echoed. Need to think more about this.
-       $in_object->manual_stty(1);
-       push(@inputs, $in_object);
+    if (@exiton) {
+       $exp->expect(undef, @expect_raw, @exiton);
+    } else {
+       my @inputs = ($exp);
+       if (-t STDIN) { # Set up bi-directional communication if we run on terminal
+           my $infile = new IO::File;
+           $infile->IO::File::fdopen(*STDIN,'r');
+           my $in_object = Expect->exp_init($infile);
+           $in_object->set_group($exp);
+
+           if ($interactive) {
+               $in_object->set_seq('~~\.', sub { print "novaboot: Escape sequence detected\r\n"; undef; });
+               $in_object->manual_stty(0);       # Use raw terminal mode
+           } else {
+               $in_object->manual_stty(1);       # Do not modify terminal settings
+           }
+           push(@inputs, $in_object);
+       }
+       #use Data::Dumper;
+       #print Dumper(\@expect_raw);
+       $exp->expect(undef, @expect_raw) if @expect_raw;
+       Expect::interconnect(@inputs) unless defined($exp->exitstatus);
     }
-    Expect::interconnect(@inputs);
 }
 
 ## Kill dhcpc or tftpd
-if (defined $dhcp_tftp) {
+if (defined $dhcp_tftp || defined $tftp) {
     die("novaboot: This should kill servers on background\n");
 }
 
@@ -770,22 +1077,28 @@ novaboot - A tool for booting various operating systems on various hardware or i
 
 =head1 SYNOPSIS
 
-B<novaboot> [ options ] [--] script...
+B<novaboot> --help
 
-B<./script> [ options ]
+B<novaboot> [option]... [--] script...
 
-=head1 DESCRIPTION
+B<./script> [option]...
 
-This program makes it easier to boot NOVA or other operating system
-(OS) on different targets (machines or emulators). It reads a so
-called novaboot script, that specifies the boot configuration, and
-setups the target to boot that configuration. Setting up the target
-means to generate the bootloader configuration files, deploy the
-binaries and other needed files to proper locations, perhaps on a
-remote boot server and reset the target. Then, target's serial output
-is redirected to standard output if that is possible.
+=head1 DESCRIPTION
 
-A typical way of using novaboot is to make the novaboot script
+This program makes booting of an operating system (e.g. NOVA or Linux)
+as simple as running a local program. It facilitates booting on local
+or remote hosts or in emulators such as qemu. Novaboot operation is
+controlled by command line options and by a so called novaboot script,
+which can be thought as a generalization of bootloader configuration
+files (see L</"NOVABOOT SCRIPT SYNTAX">). Based on this input,
+novaboot setups everything for the target host to boot the desired
+configuration, i.e. it generates the bootloader configuration file in
+the proper format, deploy the binaries and other needed files to
+required locations, perhaps on a remote boot server and reset the
+target host. Finally, target host's serial output is redirected to
+standard output if that is possible.
+
+Typical way of using novaboot is to make the novaboot script
 executable and set its first line to I<#!/usr/bin/env novaboot>. Then,
 booting a particular OS configuration becomes the same as executing a
 local program - the novaboot script.
@@ -799,24 +1112,24 @@ For example, with C<novaboot> you can:
 Run an OS in Qemu. This is the default action when no other action is
 specified by command line switches. Thus running C<novaboot ./script>
 (or C<./script> as described above) will run Qemu and make it boot the
-configuration specified in the I<script>.
+configuration specified in the F<script>.
 
 =item 2.
 
 Create a bootloader configuration file (currently supported
-bootloaders are GRUB, GRUB2, Pulsar and uBoot) and copy it with all
-other files needed for booting to another, perhaps remote, location.
+bootloaders are GRUB, GRUB2, Pulsar and U-Boot) and copy it with all
+other files needed for booting to a remote boot server.
 
- ./script --server --iprelay=192.168.1.2
+ ./script --server=192.168.1.1:/tftp --iprelay=192.168.1.2
 
-This command copies files to a TFTP server specified in the
-configuration file and uses TCP/IP-controlled relay to reset the test
-box and receive its serial output.
+This command copies files to the TFTP server and uses
+TCP/IP-controlled relay to reset the target host and receive its
+serial output.
 
 =item 3.
 
-Run DHCP and TFTP server on developer's machine to PXE-boot the OS
-from it. E.g.
+Run DHCP and TFTP server on developer's machine to PXE-boot the target
+host from it. E.g.
 
  ./script --dhcp-tftp
 
@@ -835,28 +1148,45 @@ configurations.
 
 =back
 
+Note that the options needed for a specific target can be stored in a
+L</"CONFIGURATION FILE">. Then it is sufficient to use only the B<-t>
+option to specify the name of the target.
+
 =head1 PHASES AND OPTIONS
 
 Novaboot performs its work in several phases. Each phase can be
 influenced by several options, certain phases can be skipped. The list
 of phases (in the execution order) and the corresponding options
-follows.
+follow.
 
 =head2 Configuration reading phase
 
-After starting, novaboot reads configuration files. By default, it
-searches for files named F<.novaboot> starting from the directory of
-the novaboot script (or working directory, see bellow) and continuing
-upwards up to the root directory. The configuration files are read in
-order from the root directory downwards with latter files overriding
-settings from the former ones.
+After starting, novaboot reads configuration files. Their content is
+described in L</"CONFIGURATION FILE">. By default, configuration is
+read from two locations. First from the configuration directory and
+second from F<.novaboot> files along the path to the current
+directory. The later read files override settings from the former
+ones.
+
+Configuration directory is determined by the content of
+NOVABOOT_CONFIG_DIR environment variable defaulting to
+F</etc/novaboot.d>. Files in this directory with names consisting
+solely from English letters, numbers, dashes '-' and underscores '_'
+(note that dot '.' is not included) are read in alphabetical order.
+
+Then novaboot searches for files named F<.novaboot> starting
+from the directory of the novaboot script (or working directory, see
+bellow) and continuing upwards up to the root directory. The found
+configuration files are then read in order from the root directory
+downwards.
 
 In certain cases, the location of the novaboot script cannot be
 determined in this early phase. This happens either when the script is
 read from the standard input or when novaboot is invoked explicitly
 and options precede the script name, as in the example L</"4."> above.
 In this case the current working directory is used as a starting point
-for configuration file search.
+for configuration file search instead of the novaboot script
+directory.
 
 =over 8
 
@@ -897,9 +1227,9 @@ used in the later phases.
 
 =item -a, --append=I<parameters>
 
-Appends a string to the first "filename" line in the novaboot script.
-This can be used to append parameters to the kernel's or root task's
-command line.
+Append a string to the first C<load> line in the novaboot script. This
+can be used to append parameters to the kernel's or root task's
+command line. Can appear multiple times.
 
 =item -b, --bender
 
@@ -914,9 +1244,14 @@ in the novaboot script. E.g. 'bin/boot/bender promisc'.
 
 =item --dump
 
-Prints the content of the novaboot script after removing comments and
-evaluating all I<--scriptmod> expressions. Exit after reading (and
-dumping) the script.
+Print the modules to boot and their parameters after this phase
+finishes. Then exit. This is useful for seeing the effect of other
+options in this section.
+
+=item -k, --kernel=F<file>
+
+Replace the first word on the first C<load> line in the novaboot
+script with F<file>.
 
 =item --scriptmod=I<perl expression>
 
@@ -930,21 +1265,16 @@ from the configuration file, which has the same effect. If this option
 is given multiple times all expressions are evaluated in the command
 line order.
 
-=item --strip-rom
-
-Strip I<rom://> prefix from command lines and generated config files.
-The I<rom://> prefix is used by NUL. For NRE, it has to be stripped.
-
 =back
 
 =head2 File generation phase
 
 In this phase, files needed for booting are generated in a so called
-I<build directory> (see TODO). In most cases configuration for a
-bootloader is generated automatically by novaboot. It is also possible
-to generate other files using I<heredoc> or I<"<"> syntax in novaboot
-scripts. Finally, binaries can be generated in this phases by running
-C<scons> or C<make>.
+I<build directory> (see L</--build-dir>). In most cases configuration
+for a bootloader is generated automatically by novaboot. It is also
+possible to generate other files using I<heredoc> or I<"<"> syntax in
+novaboot scripts. Finally, binaries can be generated in this phases by
+running C<scons> or C<make>.
 
 =over 8
 
@@ -957,6 +1287,8 @@ configuration file defines the C<$builddir> variable, its value is
 used. Otherwise, it is the directory that contains the first processed
 novaboot script.
 
+See also L</BUILDDIR> variable.
+
 =item -g, --grub[=I<filename>]
 
 Generates grub bootloader menu file. If the I<filename> is not
@@ -995,9 +1327,13 @@ i.e. run update-grub on Debian/Ubuntu.
 
 =item --grub2-prolog=I<prolog>
 
-Specifies text I<preable> that is put at the begiging of the entry
+Specifies text I<preable> that is put at the beginning of the entry
 GRUB2 entry.
 
+=item -m, --make[=make command]
+
+Runs C<make> to build files that are not generated by novaboot itself.
+
 =item --name=I<string>
 
 Use the name I<string> instead of the name of the novaboot script.
@@ -1006,8 +1342,9 @@ server directory where the boot files are copied to.
 
 =item --no-file-gen
 
-Do not generate files on the fly (i.e. "<" syntax) except for the
-files generated via "<<WORD" syntax.
+Do not run external commands to generate files (i.e. "<" syntax and
+C<run> keyword). This switch does not influence generation of files
+specified with "<<WORD" syntax.
 
 =item -p, --pulsar[=mac]
 
@@ -1017,9 +1354,14 @@ I<novaboot>.
 
 =item --scons[=scons command]
 
-Runs I<scons> to build files that are not generated by novaboot
+Runs C<scons> to build files that are not generated by novaboot
 itself.
 
+=item --strip-rom
+
+Strip I<rom://> prefix from command lines and generated config files.
+The I<rom://> prefix is used by NUL. For NRE, it has to be stripped.
+
 =item --gen-only
 
 Exit novaboot after file generation phase.
@@ -1034,6 +1376,23 @@ user/instance.
 
 =over 8
 
+=item --amt=I<"[user[:password]@]host[:port]>
+
+Use Intel AMT technology to control the target machine. WS management
+is used to powercycle it and Serial-Over-Lan (SOL) for input/output.
+The hostname or (IP address) is given by the I<host> parameter. If
+I<password> is not specified, environment variable AMT_PASSWORD is
+used. The I<port> specifies a TCP port for SOL. If not specified, the
+default is 16992. Default I<user> is admin.
+
+=item --ider
+
+Use Intel AMT technology for IDE redirection. This allows the target
+machine to boot from nonvaboot created ISO image.
+
+The experimental I<amtider> utility needed by this option can be
+obtained from https://github.com/wentasah/amtterm.
+
 =item --iprelay=I<addr[:port]>
 
 Use TCP/IP relay and serial port to access the target's serial port
@@ -1061,6 +1420,12 @@ C<stty> is called with C<raw -crtscts -onlcr 115200> settings.
 Command that mediates connection to the target's serial line. For
 example C<ssh server 'cu -l /dev/ttyS0'>.
 
+=item --remote-expect=I<string>
+
+Wait for reception of I<string> after establishing the the remote
+connection before continuing.
+
+
 =back
 
 =head2 File deployment phase
@@ -1077,15 +1442,31 @@ Turns your workstation into a DHCP and TFTP server so that the OS can
 be booted via PXE BIOS (or similar mechanism) on the test machine
 directly connected by a plain Ethernet cable to your workstation.
 
-The DHCP and TFTP servers require root privileges and C<novaboot>
+The DHCP and TFTP servers requires root privileges and C<novaboot>
 uses C<sudo> command to obtain those. You can put the following to
-I</etc/sudoers> to allow running the necessary commands without
-asking for password.
+I</etc/sudoers> to allow running the necessary commands without asking
+for password.
 
- 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
+ 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
  your_login ALL=NOPASSWD: NOVABOOT
 
-=item -i, --iso[=filename]
+=item --tftp
+
+Starts a TFTP server on your workstation. This is similar to
+B<--dhcp-tftp> except that DHCP server is not started.
+
+The TFTP server require root privileges and C<novaboot> uses C<sudo>
+command to obtain those. You can put the following to I</etc/sudoers>
+to allow running the necessary commands without asking for password.
+
+ Cmnd_Alias NOVABOOT =  /usr/sbin/in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/pkill --pidfile=tftpd.pid
+ your_login ALL=NOPASSWD: NOVABOOT
+
+=item --tftp-port=I<port>
+
+Port to run the TFTP server on. Implies B<--tftp>.
+
+=item --iso[=filename]
 
 Generates the ISO image that boots NOVA system via GRUB. If no filename
 is given, the image is stored under I<NAME>.iso, where I<NAME> is the name
@@ -1121,12 +1502,14 @@ copying files as a result of I<--server> option.
 
 =item --on, --off
 
-Switch on/off the target machine. Currently works only with
-B<--iprelay>.
+Switch on/off the target machine and exit. The script (if any) is
+completely ignored. Currently it works only with B<--iprelay> or
+B<--amt>.
 
-=item -Q, --qemu=I<qemu-binary>
+=item -Q, --qemu[=I<qemu-binary>]
 
-The name of qemu binary to use. The default is 'qemu'.
+Boot the configuration in qemu. Optionally, the name of qemu binary
+can be specified as a parameter.
 
 =item --qemu-append=I<flags>
 
@@ -1148,70 +1531,141 @@ Command that resets the target.
 
 =over 8
 
-=item --uboot
+=item --uboot[=I<prompt>]
 
 Interact with uBoot bootloader to boot the thing described in the
-novaboot script. Implementation of this option is currently tied to a
-particular board that we use. It may be subject to changes in the
-future!
+novaboot script. I<prompt> specifies the U-Boot's prompt (default is
+"=> ", other common prompts are "U-Boot> " or "U-Boot# ").
+Implementation of this option is currently tied to a particular board
+that we use. It may be subject to changes in the future!
 
 =item --uboot-init
 
-Command(s) to send the uBoot bootloader before loading the images and
-botting them.
+Command(s) to send the U-Boot bootloader before loading the images and
+booting them. This option can be given multiple times. After sending
+commands from each option novaboot waits for U-Boot I<prompt>.
 
 =back
 
 =head2 Target interaction phase
 
-In this phase, target's serial output is passed to C<novaboot> stdout.
-If C<novaboot>'s stdin is on TTY, the stdin is passed to the target
-allowing interactive work with the target.
+In this phase, target's serial output is redirected to stdout and if
+stdin is a TTY, it is redirected to the target's serial input allowing
+interactive work with the target.
+
+=over 8
+
+=item --exiton=I<string>
+
+When I<string> is sent by the target, novaboot exits. This option can
+be specified multiple times.
+
+If I<string> is C<-re>, then the next B<--exiton>'s I<string> is
+treated as regular expression. For example:
+
+    --exiton -re --exiton 'error:.*failed'
+
+=item -i, --interactive
+
+Setup things for interactive use of target. Your terminal will be
+switched to raw mode. In raw mode, your system does not process input
+in any way (no echoing of entered characters, no interpretation
+special characters). This, among others, means that Ctrl-C is passed
+to the target and does no longer interrupt novaboot. Use "~~."
+sequence to exit novaboot.
+
+=item --expect=I<string>
+
+When I<string> is received from the target, send the string specified
+with the subsequent B<--send*> option to the target.
+
+=item --expect-re=I<regex>
 
-This phase end when the target hangs up or when Ctrl-C is pressed.
+When target's output matches regular expression I<regex>, send the
+string specified with the subsequent B<--send*> option to the target.
+
+=item --expect-raw=I<perl-code>
+
+Provides direct control over Perl's Expect module.
+
+=item --send=I<string>
+
+Send I<string> to the target after the previously specified
+B<--expect*> was matched in the target's output. The I<string> may
+contain escape sequences such as "\n".
+
+Note that I<string> is actually interpreted by Perl, so it can contain
+much more that escape sequences. This behavior may change in the
+future.
+
+Example: C<--expect='login: ' --send='root\n'>
+
+=item --sendcont=I<string>
+
+Similar to B<--send> but continue expecting more input.
+
+Example: C<--expect='Continue?' --sendcont='yes\n'>
+
+=back
 
 =head1 NOVABOOT SCRIPT SYNTAX
 
-The syntax tries to mimic POSIX shell syntax. The syntax is defined with the following rules.
+The syntax tries to mimic POSIX shell syntax. The syntax is defined
+with the following rules.
 
-Lines starting with "#" are ignored.
+Lines starting with "#" and empty lines are ignored.
 
 Lines that end with "\" are concatenated with the following line after
 removal of the final "\" and leading whitespace of the following line.
 
-Lines in the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
-expression) assign values to internal variables. See VARIABLES
+Lines of the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
+expression) assign values to internal variables. See L</VARIABLES>
 section.
 
-Otherwise, the first word on the line represents the filename
-(relative to the build directory (see B<--build-dir>) of the module to
-load and the remaining words are passed as the command line
-parameters.
+Lines starting with C<load> keyword represent modules to boot. The
+word after C<load> is a file name (relative to the build directory
+(see B<--build-dir>) of the module to load and the remaining words are
+passed to it as the command line parameters.
+
+When the C<load> line ends with "<<WORD" then the subsequent lines
+until the line containing solely WORD are copied literally to the file
+named on that line. This is similar to shell's heredoc feature.
+
+When the C<load> line ends with "< CMD" then command CMD is executed
+with F</bin/sh> and its standard output is stored in the file named on
+that line. The SRCDIR variable in CMD's environment is set to the
+absolute path of the directory containing the interpreted novaboot
+script.
+
+Lines starting with C<run> keyword contain shell commands that are run
+during file generation phase. This is the same as the "< CMD" syntax
+for C<load> keyboard except that the command's output is not
+redirected to a file. The ordering of commands is the same as they
+appear in the novaboot script.
 
-When the line ends with "<<WORD" then the subsequent lines until the
-line containing only WORD are copied literally to the file named on
-that line.
+Example (Linux):
 
-When the line ends with "< CMD" the command CMD is executed with
-C</bin/sh> and its standard output is stored in the file named on that
-line. The SRCDIR variable in CMD's environment is set to the absolute
-path of the directory containing the interpreted novaboot script.
+  #!/usr/bin/env novaboot
+  load bzImage console=ttyS0,115200
+  run  make -C buildroot
+  load rootfs.cpio < gen_cpio buildroot/images/rootfs.cpio "myapp->/etc/init.d/S99myapp"
+
+Example (NOVA User Land - NUL):
 
-Example:
   #!/usr/bin/env novaboot
   WVDESC=Example program
-  bin/apps/sigma0.nul S0_DEFAULT script_start:1,1 \
-    verbose hostkeyb:0,0x60,1,12,2
-  bin/apps/hello.nul
-  hello.nulconfig <<EOF
+  load bin/apps/sigma0.nul S0_DEFAULT script_start:1,1 \
+                           verbose hostkeyb:0,0x60,1,12,2
+  load bin/apps/hello.nul
+  load hello.nulconfig <<EOF
   sigma0::mem:16 name::/s0/log name::/s0/timer name::/s0/fs/rom ||
   rom://bin/apps/hello.nul
   EOF
 
-This example will load three modules: sigma0.nul, hello.nul and
-hello.nulconfig. sigma0 gets some command line parameters and
-hello.nulconfig file is generated on the fly from the lines between
-<<EOF and EOF.
+This example will load three modules: F<sigma0.nul>, F<hello.nul> and
+F<hello.nulconfig>. sigma0 receives some command line parameters and
+F<hello.nulconfig> file is generated on the fly from the lines between
+C<<<EOF> and C<EOF>.
 
 =head2 VARIABLES
 
@@ -1225,61 +1679,61 @@ Novaboot chdir()s to this directory before file generation phase. The
 directory name specified here is relative to the build directory
 specified by other means (see L</--build-dir>).
 
-=item WVDESC
+=item EXITON
 
-Description of the wvtest-compliant program.
+Assigning this variable has the same effect as specifying L</--exiton>
+option.
 
-=item WVTEST_TIMEOUT
+=item HYPERVISOR_PARAMS
 
-The timeout in seconds for WvTest harness. If no complete line appears
-in the test output within the time specified here, the test fails. It
-is necessary to specify this for long running tests that produce no
-intermediate output.
+Parameters passed to hypervisor. The default value is "serial", unless
+overridden in configuration file.
+
+=item KERNEL
+
+The kernel to use instead of the hypervisor specified in the
+configuration file with the C<$hypervisor> variable. The value should
+contain the name of the kernel image as well as its command line
+parameters. If this variable is defined and non-empty, the variable
+HYPERVISOR_PARAMS is not used.
 
 =item QEMU
 
-Use a specific qemu binary (can be overriden with B<-Q>) and flags
+Use a specific qemu binary (can be overridden with B<-Q>) and flags
 when booting this script under qemu. If QEMU_FLAGS variable is also
 specified flags specified in QEMU variable are replaced by those in
 QEMU_FLAGS.
 
 =item QEMU_FLAGS
 
-Use specific qemu flags (can be overriden with B<-q>).
+Use specific qemu flags (can be overridden with B<-q>).
 
-=item HYPERVISOR_PARAMS
+=item WVDESC
 
-Parameters passed to hypervisor. The default value is "serial", unless
-overriden in configuration file.
+Description of the wvtest-compliant program.
 
-=item KERNEL
+=item WVTEST_TIMEOUT
 
-The kernel to use instead of NOVA hypervisor specified in the
-configuration file. The value should contain the name of the kernel
-image as well as its command line parameters. If this variable is
-defined and non-empty, the variable HYPERVISOR_PARAMS is not used.
+The timeout in seconds for WvTest harness. If no complete line appears
+in the test output within the time specified here, the test fails. It
+is necessary to specify this for long running tests that produce no
+intermediate output.
 
 =back
 
 =head1 CONFIGURATION FILE
 
-Novaboot can read its configuration from a file. Configuration file
-was necessary in early days of novaboot. Nowadays, an attempt is made
-to not use the configuration file because it makes certain novaboot
-scripts unusable on systems without (or with different) configuration
-file. The only recommended use of the configuration file is to specify
-custom_options (see bellow).
+Novaboot can read its configuration from one or more files. By
+default, novaboot looks for files named F<.novaboot> as described in
+L</Configuration reading phase>. Alternatively, configuration file
+location can be specified with the B<-c> switch or with the
+NOVABOOT_CONFIG environment variable. The configuration file has perl
+syntax and should set values of certain Perl variables. The current
+configuration can be dumped with the B<--dump-config> switch. Some
+configuration variables can be overridden by environment variables
+(see below) or by command line switches.
 
-If you decide to use the configuration file, it is looked up, by
-default, in files named F<.novaboot> as described in L</Configuration
-reading phase>. Alternatively, its location can be specified with the
-B<-c> switch or with the NOVABOOT_CONFIG environment variable. The
-configuration file has perl syntax and should set values of certain
-Perl variables. The current configuration can be dumped with the
-B<--dump-config> switch. Some configuration variables can be overriden
-by environment variables (see below) or by command line switches.
-
-Documentation of some configuration variables follows:
+Supported configuration variables include:
 
 =over 8
 
@@ -1288,12 +1742,18 @@ Documentation of some configuration variables follows:
 Build directory location relative to the location of the configuration
 file.
 
+=item $default_target
+
+Default target (see below) to use when no target is explicitly
+specified on command line with the B<--target> option.
+
 =item %targets
 
-Hash of shortcuts to be used with the B<--target> option. If the hash
-contains, for instance, the following pair of values
+Hash of target definitions to be used with the B<--target> option. The
+key is the identifier of the target, the value is the string with
+command line options. For instance, if the configuration file contains:
 
'mybox' => '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
$targets{'mybox'} = '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
 
 then the following two commands are equivalent:
 
@@ -1316,6 +1776,11 @@ override the environment variables.
 Name of the novaboot configuration file to use instead of the default
 one(s).
 
+=item NOVABOOT_CONFIG_DIR
+
+Name of the novaboot configuration directory. When not specified
+F</etc/novaboot.d> is used.
+
 =item NOVABOOT_BENDER
 
 Defining this variable has the same meaning as B<--bender> option.