* To establish the connection to the root shell in the guest, let the

guest connect to a Unix domain socket on the host rather than the
  other way around.  The former is a QEMU feature (guestfwd to a
  socket) while the latter requires a patch (which we can now get rid
  of).

svn path=/nixos/branches/boot-order/; revision=22331
This commit is contained in:
Eelco Dolstra 2010-06-18 19:31:02 +00:00
parent 7eb7e8d732
commit 363806e89b
4 changed files with 26 additions and 39 deletions

View file

@ -2,7 +2,6 @@ package Machine;
use strict;
use threads;
use Thread::Queue;
use Socket;
use IO::Handle;
use POSIX qw(dup2);
@ -51,7 +50,6 @@ sub new {
booted => 0,
pid => 0,
connected => 0,
connectedQueue => Thread::Queue->new(),
socket => undef,
stateDir => "$tmpDir/$name",
monitor => undef,
@ -101,6 +99,14 @@ sub start {
bind($monitorS, sockaddr_un($monitorPath)) or die "cannot bind monitor socket: $!";
listen($monitorS, 1) or die;
# Create a Unix domain socket to which the root shell in the guest will connect.
my $shellPath = $self->{stateDir} . "/shell";
unlink $shellPath;
my $shellS;
socket($shellS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($shellS, sockaddr_un($shellPath)) or die "cannot bind shell socket: $!";
listen($shellS, 1) or die;
# Start the VM.
my $pid = fork();
die if $pid == -1;
@ -108,13 +114,15 @@ sub start {
if ($pid == 0) {
close $serialP;
close $monitorS;
close $shellS;
open NUL, "</dev/null" or die;
dup2(fileno(NUL), fileno(STDIN));
dup2(fileno($serialC), fileno(STDOUT));
dup2(fileno($serialC), fileno(STDERR));
$ENV{TMPDIR} = $self->{stateDir};
$ENV{USE_TMPDIR} = 1;
$ENV{QEMU_OPTS} = "-nographic -no-reboot -redir tcp:65535::514 -monitor unix:./monitor";
$ENV{QEMU_OPTS} = "-nographic -no-reboot -monitor unix:./monitor -chardev socket,id=shell,path=./shell";
$ENV{QEMU_NET_OPTS} = "guestfwd=tcp:10.0.2.6:23-chardev:shell";
$ENV{QEMU_KERNEL_PARAMS} = "hostTmpDir=$ENV{TMPDIR}";
chdir $self->{stateDir} or die;
exec $self->{startCommand};
@ -132,16 +140,20 @@ sub start {
chomp;
s/\r$//;
print STDERR $self->name, "# $_\n";
$self->{connectedQueue}->enqueue(1) if $_ eq "===UP===";
}
# If the child dies, wake up connect().
$self->{connectedQueue}->enqueue(1);
}
# Wait until QEMU connects to the monitor.
eval {
local $SIG{CHLD} = sub { die "QEMU died prematurely\n"; };
# Wait until QEMU connects to the monitor.
accept($self->{monitor}, $monitorS) or die;
# Wait until QEMU connects to the root shell socket. QEMU
# does so immediately; this doesn't mean that the root shell
# has connected yet inside the guest.
accept($self->{socket}, $shellS) or die;
$self->{socket}->autoflush(1);
};
die "$@" if $@;
@ -197,27 +209,9 @@ sub connect {
$self->start;
# Wait until the processQemuOutput thread signals that the machine
# is up.
retry sub {
return 1 if $self->{connectedQueue}->dequeue_nb();
};
retry sub {
$self->log("trying to connect");
my $socket = new IO::Handle;
$self->{socket} = $socket;
socket($socket, PF_UNIX, SOCK_STREAM, 0) or die;
connect($socket, sockaddr_un($self->{stateDir} . "/65535.socket")) or die;
$socket->autoflush(1);
print $socket "echo hello\n" or next;
flush $socket;
my $line = readline($socket);
chomp $line;
return 1 if $line eq "hello";
};
$self->log("connected");
my $line = readline $self->{socket} or die;
$self->log("connected to guest root shell");
$self->{connected} = 1;
}

View file

@ -74,6 +74,3 @@ END {
runTests;
print STDERR "DONE\n";

View file

@ -13,6 +13,7 @@ let
''
#! ${pkgs.perl}/bin/perl
$SIG{CHLD} = 'DEFAULT';
print "\n";
exec "/bin/sh";
'';
@ -25,12 +26,6 @@ in
jobs.backdoor =
{ startOn = "started network-interfaces";
preStart =
''
echo "guest running" > /dev/ttyS0
echo "===UP===" > dev/ttyS0
'';
script =
''
export USER=root
@ -39,7 +34,8 @@ in
export GCOV_PREFIX=/tmp/coverage-data
source /etc/profile
cd /tmp
exec ${pkgs.socat}/bin/socat tcp-listen:514,fork exec:${rootShell} 2> /dev/ttyS0
echo "connecting to host..." > /dev/ttyS0
exec ${pkgs.socat}/bin/socat tcp:10.0.2.6:23 exec:${rootShell} 2> /dev/ttyS0
'';
};

View file

@ -134,7 +134,7 @@ let
-m ${toString config.virtualisation.memorySize} \
-net nic,vlan=0,model=virtio \
-chardev socket,id=samba,path=./samba \
-net user,vlan=0,guestfwd=tcp:10.0.2.4:139-chardev:samba \
-net user,vlan=0,guestfwd=tcp:10.0.2.4:139-chardev:samba''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS} \
-drive file=$NIX_DISK_IMAGE,if=virtio,boot=on,cache=writeback,werror=report \
-kernel ${config.system.build.toplevel}/kernel \
-initrd ${config.system.build.toplevel}/initrd \