diff --git a/lib/test-driver/Machine.pm b/lib/test-driver/Machine.pm index df1ace15453..85ef80f0d37 100644 --- a/lib/test-driver/Machine.pm +++ b/lib/test-driver/Machine.pm @@ -1,9 +1,12 @@ package Machine; use strict; +use threads; +use Thread::Queue; use Socket; use IO::Handle; use POSIX qw(dup2); +use FileHandle; # Stuff our PID in the multicast address/port to prevent collissions @@ -26,6 +29,7 @@ sub new { booted => 0, pid => 0, connected => 0, + connectedQueue => Thread::Queue->new(), socket => undef, stateDir => "$tmpDir/$name", }; @@ -62,14 +66,15 @@ sub start { $self->log("starting vm"); + my ($read, $write) = FileHandle::pipe; + my $pid = fork(); die if $pid == -1; if ($pid == 0) { - my $name = $self->{name}; - open LOG, "| sed --unbuffered 's|^|$name console: |'" or die; - dup2(fileno(LOG), fileno(STDOUT)); - dup2(fileno(LOG), fileno(STDERR)); + close $read; + dup2(fileno($write), fileno(STDOUT)); + dup2(fileno($write), fileno(STDERR)); open NUL, "{stateDir}; @@ -79,7 +84,22 @@ sub start { exec $self->{script}; die; } - + + close $write; + + threads->create(\&processQemuOutput)->detach; + + sub processQemuOutput { + $/ = "\r\n"; + while (<$read>) { + chomp; + print STDERR $self->name, "# $_\n"; + $self->{connectedQueue}->enqueue(1) if $_ eq "===UP==="; + } + # If the child dies, wake up connect(). + $self->{connectedQueue}->enqueue(1); + } + $self->log("vm running as pid $pid"); $self->{pid} = $pid; $self->{booted} = 1; @@ -92,12 +112,9 @@ sub connect { $self->start; - my $try = 0; - while (1) { - last if -e ($self->{stateDir} . "/running"); - sleep 1; - die ("VM " . $self->{name} . " timed out") if $try++ > 300; - } + # Wait until the processQemuOutput thread signals that the machine + # is up. + $self->{connectedQueue}->dequeue(); while (1) { $self->log("trying to connect"); diff --git a/modules/system/boot/stage-1-init.sh b/modules/system/boot/stage-1-init.sh index db712f48ee1..e0d7869f910 100644 --- a/modules/system/boot/stage-1-init.sh +++ b/modules/system/boot/stage-1-init.sh @@ -7,13 +7,15 @@ export PATH=@extraUtils@/bin:@klibc@/bin fail() { + if [ -n "$panicOnFail" ]; then exit 1; fi + # If starting stage 2 failed, allow the user to repair the problem # in an interactive shell. cat < /dev/ttyS0 - touch /hostfs/$hostTmpDir/running + echo "guest running" > /dev/ttyS0 + echo "===UP===" > dev/ttyS0 ''; exec = "${pkgs.socat}/bin/socat tcp-listen:514,fork exec:/bin/sh,stderr"; @@ -45,6 +44,10 @@ with pkgs.lib; # If the kernel has been built with coverage instrumentation, make # it available under /proc/gcov. boot.kernelModules = [ "gcov-proc" ]; + + # Panic if an error occurs in stage 1 (rather than waiting for + # user intervention). + boot.kernelParams = [ "stage1panic" ]; };