* 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 strict;
use threads; use threads;
use Thread::Queue;
use Socket; use Socket;
use IO::Handle; use IO::Handle;
use POSIX qw(dup2); use POSIX qw(dup2);
@ -51,7 +50,6 @@ sub new {
booted => 0, booted => 0,
pid => 0, pid => 0,
connected => 0, connected => 0,
connectedQueue => Thread::Queue->new(),
socket => undef, socket => undef,
stateDir => "$tmpDir/$name", stateDir => "$tmpDir/$name",
monitor => undef, monitor => undef,
@ -101,6 +99,14 @@ sub start {
bind($monitorS, sockaddr_un($monitorPath)) or die "cannot bind monitor socket: $!"; bind($monitorS, sockaddr_un($monitorPath)) or die "cannot bind monitor socket: $!";
listen($monitorS, 1) or die; 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. # Start the VM.
my $pid = fork(); my $pid = fork();
die if $pid == -1; die if $pid == -1;
@ -108,13 +114,15 @@ sub start {
if ($pid == 0) { if ($pid == 0) {
close $serialP; close $serialP;
close $monitorS; close $monitorS;
close $shellS;
open NUL, "</dev/null" or die; open NUL, "</dev/null" or die;
dup2(fileno(NUL), fileno(STDIN)); dup2(fileno(NUL), fileno(STDIN));
dup2(fileno($serialC), fileno(STDOUT)); dup2(fileno($serialC), fileno(STDOUT));
dup2(fileno($serialC), fileno(STDERR)); dup2(fileno($serialC), fileno(STDERR));
$ENV{TMPDIR} = $self->{stateDir}; $ENV{TMPDIR} = $self->{stateDir};
$ENV{USE_TMPDIR} = 1; $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}"; $ENV{QEMU_KERNEL_PARAMS} = "hostTmpDir=$ENV{TMPDIR}";
chdir $self->{stateDir} or die; chdir $self->{stateDir} or die;
exec $self->{startCommand}; exec $self->{startCommand};
@ -132,16 +140,20 @@ sub start {
chomp; chomp;
s/\r$//; s/\r$//;
print STDERR $self->name, "# $_\n"; 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 { eval {
local $SIG{CHLD} = sub { die "QEMU died prematurely\n"; }; local $SIG{CHLD} = sub { die "QEMU died prematurely\n"; };
# Wait until QEMU connects to the monitor.
accept($self->{monitor}, $monitorS) or die; 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 $@; die "$@" if $@;
@ -197,27 +209,9 @@ sub connect {
$self->start; $self->start;
# Wait until the processQemuOutput thread signals that the machine my $line = readline $self->{socket} or die;
# is up. $self->log("connected to guest root shell");
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");
$self->{connected} = 1; $self->{connected} = 1;
} }

View File

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

View File

@ -13,6 +13,7 @@ let
'' ''
#! ${pkgs.perl}/bin/perl #! ${pkgs.perl}/bin/perl
$SIG{CHLD} = 'DEFAULT'; $SIG{CHLD} = 'DEFAULT';
print "\n";
exec "/bin/sh"; exec "/bin/sh";
''; '';
@ -25,12 +26,6 @@ in
jobs.backdoor = jobs.backdoor =
{ startOn = "started network-interfaces"; { startOn = "started network-interfaces";
preStart =
''
echo "guest running" > /dev/ttyS0
echo "===UP===" > dev/ttyS0
'';
script = script =
'' ''
export USER=root export USER=root
@ -39,7 +34,8 @@ in
export GCOV_PREFIX=/tmp/coverage-data export GCOV_PREFIX=/tmp/coverage-data
source /etc/profile source /etc/profile
cd /tmp 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} \ -m ${toString config.virtualisation.memorySize} \
-net nic,vlan=0,model=virtio \ -net nic,vlan=0,model=virtio \
-chardev socket,id=samba,path=./samba \ -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 \ -drive file=$NIX_DISK_IMAGE,if=virtio,boot=on,cache=writeback,werror=report \
-kernel ${config.system.build.toplevel}/kernel \ -kernel ${config.system.build.toplevel}/kernel \
-initrd ${config.system.build.toplevel}/initrd \ -initrd ${config.system.build.toplevel}/initrd \