* 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:
parent
7eb7e8d732
commit
363806e89b
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,3 @@ END {
|
|||||||
|
|
||||||
|
|
||||||
runTests;
|
runTests;
|
||||||
|
|
||||||
|
|
||||||
print STDERR "DONE\n";
|
|
||||||
|
@ -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
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user