From 276b72fb93d60ae0e59088ea0e0029da87e6f31c Mon Sep 17 00:00:00 2001 From: aszlig Date: Sat, 15 Feb 2014 03:47:57 +0100 Subject: [PATCH 01/23] vm: Introduce new Windows VM installer for Cygwin. After quite a lot of fighting against Windows and its various limitations, this new is the base architecture for installing and accessing the Windows VM and thus the Cygwin environment inside it: .------------. .---> | vde_switch | | `-[#]----[#]-' | | | ,' .' `---.___ ,' 192.168.0.1 `. | | 192.168.0.2 ,' _____[#]____ | ,' | | ______[#]______ | | Windows VM | | .--' | | |____________| | | | | | /|\ | .-| | | .---------. | | | | | | .-|-| manager |-' | | | | | | | `---------' | | | | | | | | | | | | | | .-------------. | | Samba | | | | BOOTSTRAP | | | | | | | | |-------------| | | | | .------| | `-| spawn VMs |-+--> | | `---| xchg | <-------. | |-------------| | | .---^------| | | | install |---. | `-| nixstore | <----. | | |-------------| | | `----------| | | |---| suspend VM | | | | | | | `------.------' | | Controller VM | | | | | | |_______________| | | | .--' | /|\ VirtIO | | __|__________:____________ | | | \|/ | | `. | | | | .------------. | | : | | | | | REAL BUILD | | | .-------^--------. | | | | |------------| | `-> | serial console | | | | `-| revive VM | | `----------------' | | | |------------| |------------. | | | | build |-->| /nix/store >>>-----------|-' | |------------| |------------| | | | collect |<--| xchg >>>-----------|----' `-----.------' |------------' | | | | \|/ | | | __ ___ | | | |--| | | (__ -|- | F I N I S H E D | | | |__| ___) | | |__________________________| This might look a bit overwhelming, but let me try to explain: We're starting at the base derivation ("BOOTSTRAP" above), where we actually install the Cygwin envirenment. Over there we basically fire up a vde_switch process and two virtual machines: One is the Windows machine, the other is a NixOS machine, which serves as some kind of proxy between the host and the Windows machine. The reason we're doing this, is because we don't have a lot of options for sharing files between a stock Windows machine and the host. In earlier experiments, I've tried to communicate with the Windows guest by using pipes and OpenSSH, but obviously this wasn't a big speed rush (or to say it bluntly: It was fucking slow). Using TCP/IP directly for accessing the guest would have been another option, but it could lead to possible errors when the port or a range of ports are in use at the Host system. Also, we would need to punch a hole into the sandbox of the Nix builder (as it doesn't allow networking), which in turn will possibly undermine deterministic builds/runs (well, at least as deterministic as it can be, we're running Windows, remember?). So, let's continue: The responsibility of the NixOS (controller) VM is to just wait until an SSH port becomes available on the Windows VM, whereas the Windows VM itself is installed using an unattended installation file provided via a virtual floppy image. With the installation of the basic Windows OS, we directly install Cygwin and start up an OpenSSH service. At this point the bootstrapping is almost finished and as soon as the port is available, the controller VM sets up Samba shares and makes it available as drive letters within Windows and as bind mounts (for example /nix/store) within Cygwin. Finally we're making a snapshot of the memory of the Windows VM in order to revive it within a few seconds when we want to build something. Now, the build process itself is fairly straightforward: Revive VM and build based on existing store derivations and collect the result _and_ the exit code from the xchg share/directory. Conclusion: This architecture may sound a bit complicated, but we're trying to achieve deterministic and reproducable builds and/or test runs. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 348 +++++++++++++++++++++ pkgs/build-support/vm/windows/mkclosure.py | 78 +++++ 2 files changed, 426 insertions(+) create mode 100644 pkgs/build-support/vm/windows/default.nix create mode 100644 pkgs/build-support/vm/windows/mkclosure.py diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix new file mode 100644 index 00000000000..03b66e9f504 --- /dev/null +++ b/pkgs/build-support/vm/windows/default.nix @@ -0,0 +1,348 @@ +with import {}; + +with import { + inherit system; + minimal = false; +}; + +let + winISO = /path/to/iso/XXX; + + bootstrapAfterLogin = runCommand "bootstrap.sh" {} '' + cat > "$out" < ~/.ssh/authorized_keys < "$out" + ''); + gen = { url, md5 }: { + source = fetchurl { + url = "${cygwinMirror}/${url}"; + inherit md5; + }; + target = url; + }; + in map gen expr; + + cygiso = import { + inherit (pkgs) stdenv perl cdrkit pathsFromGraph; + contents = [ + { source = bootstrapAfterLogin; + target = "bootstrap.sh"; + } + { source = fetchurl { + url = "http://cygwin.com/setup-x86_64.exe"; + sha256 = "1bjmq9h1p6mmiqp6f1kvmg94jbsdi1pxfa07a5l497zzv9dsfivm"; + }; + target = "setup.exe"; + } + { source = cygPkgList; + target = "setup.ini"; + } + ] ++ makeCygwinClosure { + packages = cygPkgList; + packageList = [ "openssh" ]; + }; + }; + + maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; + + cygwinQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [ + "-monitor unix:$MONITOR_SOCKET,server,nowait" + "-nographic" + "-boot order=c,once=d" + "-drive file=${floppyImg},readonly,index=0,if=floppy" + "-drive file=winvm.img,index=0,media=disk" + "-drive file=${winISO},index=1,media=cdrom" + "-drive file=${cygiso}/iso/cd.iso,index=2,media=cdrom" + "-net nic,vlan=0,macaddr=52:54:00:12:01:01" + "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" + "-rtc base=2010-01-01,clock=vm" + ]); + + modulesClosure = lib.overrideDerivation vmTools.modulesClosure (o: { + rootModules = o.rootModules ++ lib.singleton "virtio_net"; + }); + + snakeOilSSH = stdenv.mkDerivation { + name = "snakeoil-ssh-cygwin"; + buildCommand = '' + ensureDir "$out" + ${openssh}/bin/ssh-keygen -t ecdsa -f "$out/key" -N "" + ''; + }; + + controllerQemuArgs = cmd: let + preInitScript = writeScript "preinit.sh" '' + #!${vmTools.initrdUtils}/bin/ash -e + export PATH=${vmTools.initrdUtils}/bin + mount -t proc none /proc + mount -t sysfs none /sys + for arg in $(cat /proc/cmdline); do + if [ "x''${arg#command=}" != "x$arg" ]; then + command="''${arg#command=}" + fi + done + + for i in $(cat ${modulesClosure}/insmod-list); do + insmod $i + done + + mkdir -p /tmp /dev + mknod /dev/null c 1 3 + mknod /dev/zero c 1 5 + mknod /dev/random c 1 8 + mknod /dev/urandom c 1 9 + mknod /dev/tty c 5 0 + + ifconfig lo up + ifconfig eth0 up 192.168.0.2 + + mkdir -p /nix/store /etc /var/run /var/log + + cat > /etc/passwd < /etc/samba/smb.conf < '$out/state.gz'" + CMD + cp winvm.img "$out/disk.img" + ''; + }; + +in bootstrap diff --git a/pkgs/build-support/vm/windows/mkclosure.py b/pkgs/build-support/vm/windows/mkclosure.py new file mode 100644 index 00000000000..48d569a6bd3 --- /dev/null +++ b/pkgs/build-support/vm/windows/mkclosure.py @@ -0,0 +1,78 @@ +# Ugliest Python code I've ever written. -- aszlig +import sys + +def get_plist(path): + in_pack = False + in_str = False + current_key = None + buf = "" + packages = {} + package_name = None + package_attrs = {} + with open(path, 'r') as setup: + for line in setup: + if in_str and line.rstrip().endswith('"'): + package_attrs[current_key] = buf + line.rstrip()[:-1] + in_str = False + continue + elif in_str: + buf += line + continue + + if line.startswith('@'): + in_pack = True + package_name = line[1:].strip() + package_attrs = {} + elif in_pack and ':' in line: + key, value = line.split(':', 1) + if value.lstrip().startswith('"'): + if value.lstrip()[1:].rstrip().endswith('"'): + value = value.strip().strip('"') + else: + in_str = True + current_key = key.strip().lower() + buf = value.lstrip()[1:] + continue + package_attrs[key.strip().lower()] = value.strip() + elif in_pack: + in_pack = False + packages[package_name] = package_attrs + return packages + +def main(): + packages = get_plist(sys.argv[1]) + to_include = set() + + def traverse(package): + to_include.add(package) + attrs = packages.get(package, {}) + deps = attrs.get('requires', '').split() + for new_dep in set(deps) - to_include: + traverse(new_dep) + + map(traverse, sys.argv[2:]) + + sys.stdout.write('[\n') + for package, attrs in packages.iteritems(): + if package not in to_include: + cats = [c.lower() for c in attrs.get('category', '').split()] + if 'base' not in cats: + continue + + install_line = attrs.get('install') + if install_line is None: + continue + + url, size, md5 = install_line.split(' ', 2) + + pack = [ + ' {', + ' url = "{0}";'.format(url), + ' md5 = "{0}";'.format(md5), + ' }', + ]; + sys.stdout.write('\n'.join(pack) + '\n') + sys.stdout.write(']\n') + +if __name__ == '__main__': + main() From 4e21215d52025be5338e89020874b5ed7c903e19 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sat, 15 Feb 2014 18:17:21 +0100 Subject: [PATCH 02/23] vm/windows: Move the installer into install/. At least the largest portion of the installer, because in the end we don't want the installer to *actually* save the state but only prepare the base image. Signed-off-by: aszlig --- .../vm/windows/cygwin-iso/default.nix | 43 ++++ .../vm/windows/{ => cygwin-iso}/mkclosure.py | 0 pkgs/build-support/vm/windows/default.nix | 191 +----------------- .../vm/windows/install/default.nix | 39 ++++ .../vm/windows/install/unattended-image.nix | 123 +++++++++++ 5 files changed, 211 insertions(+), 185 deletions(-) create mode 100644 pkgs/build-support/vm/windows/cygwin-iso/default.nix rename pkgs/build-support/vm/windows/{ => cygwin-iso}/mkclosure.py (100%) create mode 100644 pkgs/build-support/vm/windows/install/default.nix create mode 100644 pkgs/build-support/vm/windows/install/unattended-image.nix diff --git a/pkgs/build-support/vm/windows/cygwin-iso/default.nix b/pkgs/build-support/vm/windows/cygwin-iso/default.nix new file mode 100644 index 00000000000..9df07bdb667 --- /dev/null +++ b/pkgs/build-support/vm/windows/cygwin-iso/default.nix @@ -0,0 +1,43 @@ +{ packages ? [] +, mirror ? "http://ftp.gwdg.de/pub/linux/sources.redhat.com/cygwin" +, extraContents ? [] +}: + +let + inherit (import {}) fetchurl runCommand python; + + cygPkgList = fetchurl { + url = "${mirror}/x86_64/setup.ini"; + sha256 = "0d54pli0gnm3010w9iq2bar3r2sc4syyblg62q75inc2cq341bi3"; + }; + + makeCygwinClosure = { packages, packageList }: let + expr = import (runCommand "cygwin.nix" { buildInputs = [ python ]; } '' + python ${./mkclosure.py} "${packages}" ${toString packageList} > "$out" + ''); + gen = { url, md5 }: { + source = fetchurl { + url = "${mirror}/${url}"; + inherit md5; + }; + target = url; + }; + in map gen expr; + +in import { + inherit (import {}) stdenv perl cdrkit pathsFromGraph; + contents = [ + { source = fetchurl { + url = "http://cygwin.com/setup-x86_64.exe"; + sha256 = "1bjmq9h1p6mmiqp6f1kvmg94jbsdi1pxfa07a5l497zzv9dsfivm"; + }; + target = "setup.exe"; + } + { source = cygPkgList; + target = "setup.ini"; + } + ] ++ makeCygwinClosure { + packages = cygPkgList; + packageList = packages; + } ++ extraContents; +} diff --git a/pkgs/build-support/vm/windows/mkclosure.py b/pkgs/build-support/vm/windows/cygwin-iso/mkclosure.py similarity index 100% rename from pkgs/build-support/vm/windows/mkclosure.py rename to pkgs/build-support/vm/windows/cygwin-iso/mkclosure.py diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 03b66e9f504..06bf6c9bb6d 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -8,189 +8,10 @@ with import { let winISO = /path/to/iso/XXX; - bootstrapAfterLogin = runCommand "bootstrap.sh" {} '' - cat > "$out" < ~/.ssh/authorized_keys < "$out" - ''); - gen = { url, md5 }: { - source = fetchurl { - url = "${cygwinMirror}/${url}"; - inherit md5; - }; - target = url; - }; - in map gen expr; - - cygiso = import { - inherit (pkgs) stdenv perl cdrkit pathsFromGraph; - contents = [ - { source = bootstrapAfterLogin; - target = "bootstrap.sh"; - } - { source = fetchurl { - url = "http://cygwin.com/setup-x86_64.exe"; - sha256 = "1bjmq9h1p6mmiqp6f1kvmg94jbsdi1pxfa07a5l497zzv9dsfivm"; - }; - target = "setup.exe"; - } - { source = cygPkgList; - target = "setup.ini"; - } - ] ++ makeCygwinClosure { - packages = cygPkgList; - packageList = [ "openssh" ]; - }; + base = import ./install { + isoFile = winISO; + productKey = "XXX"; + sshPublicKey = "${snakeOilSSH}/key.pub"; }; maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; @@ -199,10 +20,10 @@ let "-monitor unix:$MONITOR_SOCKET,server,nowait" "-nographic" "-boot order=c,once=d" - "-drive file=${floppyImg},readonly,index=0,if=floppy" + "-drive file=${base.floppy},readonly,index=0,if=floppy" "-drive file=winvm.img,index=0,media=disk" "-drive file=${winISO},index=1,media=cdrom" - "-drive file=${cygiso}/iso/cd.iso,index=2,media=cdrom" + "-drive file=${base.iso}/iso/cd.iso,index=2,media=cdrom" "-net nic,vlan=0,macaddr=52:54:00:12:01:01" "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" "-rtc base=2010-01-01,clock=vm" diff --git a/pkgs/build-support/vm/windows/install/default.nix b/pkgs/build-support/vm/windows/install/default.nix new file mode 100644 index 00000000000..fec7dac0714 --- /dev/null +++ b/pkgs/build-support/vm/windows/install/default.nix @@ -0,0 +1,39 @@ +{ isoFile +, productKey +, sshPublicKey +}: + +let + inherit (import {}) lib runCommand; + + bootstrapAfterLogin = runCommand "bootstrap.sh" {} '' + cat > "$out" < ~/.ssh/authorized_keys < {}) lib stdenv writeText dosfstools mtools; + + afterSetup = [ + cygwinSetup + "-L -n -q" + "-l ${cygwinRepository}" + "-R ${cygwinRoot}" + "-C base" + ] ++ map (p: "-P ${p}") cygwinPackages; + + winXpUnattended = writeText "winnt.sif" '' + [Data] + AutoPartition = 1 + AutomaticUpdates = 0 + MsDosInitiated = 0 + UnattendedInstall = Yes + + [Unattended] + DUDisable = Yes + DriverSigningPolicy = Ignore + Hibernation = No + OemPreinstall = No + OemSkipEula = Yes + Repartition = Yes + TargetPath = \WINDOWS + UnattendMode = FullUnattended + UnattendSwitch = Yes + WaitForReboot = No + + [GuiUnattended] + AdminPassword = "nopasswd" + AutoLogon = Yes + AutoLogonCount = 1 + OEMSkipRegional = 1 + OemSkipWelcome = 1 + ServerWelcome = No + TimeZone = 85 + + [UserData] + ComputerName = "cygwin" + FullName = "cygwin" + OrgName = "" + ProductKey = "${productKey}" + + [Networking] + InstallDefaultComponents = Yes + + [Identification] + JoinWorkgroup = cygwin + + [NetAdapters] + PrimaryAdapter = params.PrimaryAdapter + + [params.PrimaryAdapter] + InfID = * + + [params.MS_MSClient] + + [NetProtocols] + MS_TCPIP = params.MS_TCPIP + + [params.MS_TCPIP] + AdapterSections=params.MS_TCPIP.PrimaryAdapter + + [params.MS_TCPIP.PrimaryAdapter] + DHCP = No + IPAddress = 192.168.0.1 + SpecificTo = PrimaryAdapter + SubnetMask = 255.255.255.0 + WINS = No + + ; Turn off all components + [Components] + ${lib.concatMapStrings (comp: "${comp} = Off\n") [ + "AccessOpt" "Appsrv_console" "Aspnet" "BitsServerExtensionsISAPI" + "BitsServerExtensionsManager" "Calc" "Certsrv" "Certsrv_client" + "Certsrv_server" "Charmap" "Chat" "Clipbook" "Cluster" "Complusnetwork" + "Deskpaper" "Dialer" "Dtcnetwork" "Fax" "Fp_extensions" "Fp_vdir_deploy" + "Freecell" "Hearts" "Hypertrm" "IEAccess" "IEHardenAdmin" "IEHardenUser" + "Iis_asp" "Iis_common" "Iis_ftp" "Iis_inetmgr" "Iis_internetdataconnector" + "Iis_nntp" "Iis_serversideincludes" "Iis_smtp" "Iis_webdav" "Iis_www" + "Indexsrv_system" "Inetprint" "Licenseserver" "Media_clips" "Media_utopia" + "Minesweeper" "Mousepoint" "Msmq_ADIntegrated" "Msmq_Core" + "Msmq_HTTPSupport" "Msmq_LocalStorage" "Msmq_MQDSService" + "Msmq_RoutingSupport" "Msmq_TriggersService" "Msnexplr" "Mswordpad" + "Netcis" "Netoc" "OEAccess" "Objectpkg" "Paint" "Pinball" "Pop3Admin" + "Pop3Service" "Pop3Srv" "Rec" "Reminst" "Rootautoupdate" "Rstorage" "SCW" + "Sakit_web" "Solitaire" "Spider" "TSWebClient" "Templates" + "TerminalServer" "UDDIAdmin" "UDDIDatabase" "UDDIWeb" "Vol" "WMAccess" + "WMPOCM" "WbemMSI" "Wms" "Wms_admin_asp" "Wms_admin_mmc" "Wms_isapi" + "Wms_server" "Zonegames" + ]} + + [WindowsFirewall] + Profiles = WindowsFirewall.TurnOffFirewall + + [WindowsFirewall.TurnOffFirewall] + Mode = 0 + + [SetupParams] + UserExecute = "${lib.concatStringsSep " " afterSetup}" + + [GuiRunOnce] + Command0 = "${cygwinRoot}\bin\bash -l ${shExecAfterwards}" + ''; + +in stdenv.mkDerivation { + name = "unattended-floppy.img"; + buildCommand = '' + dd if=/dev/zero of="$out" count=1440 bs=1024 + ${dosfstools}/sbin/mkfs.msdos "$out" + ${mtools}/bin/mcopy -i "$out" "${winXpUnattended}" ::winnt.sif + ''; +} From 9b1862ca1fad149275e4d3e9241579b9aec9b952 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sat, 15 Feb 2014 18:23:43 +0100 Subject: [PATCH 03/23] vm/windows: Move creating SSH key into install/. This SSH key is specifically only for accessing the installed Cygwin within the Windows VM, so we only need to expose the private key. Yes, you heard right, the private key. It's not security-relevant because the machine is completely read-only, only exposed to the filesystem and networking is not available. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 11 +---------- pkgs/build-support/vm/windows/install/default.nix | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 06bf6c9bb6d..470fac0437d 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -11,7 +11,6 @@ let base = import ./install { isoFile = winISO; productKey = "XXX"; - sshPublicKey = "${snakeOilSSH}/key.pub"; }; maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; @@ -33,14 +32,6 @@ let rootModules = o.rootModules ++ lib.singleton "virtio_net"; }); - snakeOilSSH = stdenv.mkDerivation { - name = "snakeoil-ssh-cygwin"; - buildCommand = '' - ensureDir "$out" - ${openssh}/bin/ssh-keygen -t ecdsa -f "$out/key" -N "" - ''; - }; - controllerQemuArgs = cmd: let preInitScript = writeScript "preinit.sh" '' #!${vmTools.initrdUtils}/bin/ash -e @@ -108,7 +99,7 @@ let ${samba}/sbin/nmbd -D ${samba}/sbin/smbd -D - ${coreutils}/bin/cp -L "${snakeOilSSH}/key" /ssh.key + ${coreutils}/bin/cp -L "${base.sshKey}" /ssh.key ${coreutils}/bin/chmod 600 /ssh.key echo -n "Waiting for Windows VM to become ready" diff --git a/pkgs/build-support/vm/windows/install/default.nix b/pkgs/build-support/vm/windows/install/default.nix index fec7dac0714..0021bae87bc 100644 --- a/pkgs/build-support/vm/windows/install/default.nix +++ b/pkgs/build-support/vm/windows/install/default.nix @@ -1,16 +1,15 @@ { isoFile , productKey -, sshPublicKey }: let - inherit (import {}) lib runCommand; + inherit (import {}) lib stdenv runCommand openssh; bootstrapAfterLogin = runCommand "bootstrap.sh" {} '' cat > "$out" < ~/.ssh/authorized_keys < Date: Sat, 15 Feb 2014 19:46:01 +0100 Subject: [PATCH 04/23] vm/windows: Update sha256 of Cygwin's setup.ini. This is kinda stupid to do every little time the file is automatically regenerated upstream. But let's see how often that happens and whether it will become a major annoyance or not, and if yes, we might be forced to include it in our source tree. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/cygwin-iso/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/build-support/vm/windows/cygwin-iso/default.nix b/pkgs/build-support/vm/windows/cygwin-iso/default.nix index 9df07bdb667..17e4cff855b 100644 --- a/pkgs/build-support/vm/windows/cygwin-iso/default.nix +++ b/pkgs/build-support/vm/windows/cygwin-iso/default.nix @@ -8,7 +8,7 @@ let cygPkgList = fetchurl { url = "${mirror}/x86_64/setup.ini"; - sha256 = "0d54pli0gnm3010w9iq2bar3r2sc4syyblg62q75inc2cq341bi3"; + sha256 = "19vfm7zr8kcp1algmggk8vsilkccycx22mdf0ynfl6lcmp6dkfsz"; }; makeCygwinClosure = { packages, packageList }: let From c731467e2c83f3df0b59c44575616e6241a63c7d Mon Sep 17 00:00:00 2001 From: aszlig Date: Sat, 15 Feb 2014 23:23:47 +0100 Subject: [PATCH 05/23] vm/windows: Split install into several stages. These stages are in particular: * Install of the bare Windows VM with Cygwin and shut down. * Boot up the same VM again without the installation media and dump the VMs memory to state.gz. * Resume from state.gz and build whatever we want to build. Every single stage involves a new "controller", which is more like an abstraction on the Nix side that constructs the madness described in 276b72fb93d60ae0e59088ea0e0029da87e6f31c. Signed-off-by: aszlig --- .../vm/windows/controller/default.nix | 173 +++++++++++++++++ pkgs/build-support/vm/windows/default.nix | 177 ++++-------------- .../vm/windows/install/default.nix | 43 ++++- 3 files changed, 239 insertions(+), 154 deletions(-) create mode 100644 pkgs/build-support/vm/windows/controller/default.nix diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix new file mode 100644 index 00000000000..49d6815a3dc --- /dev/null +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -0,0 +1,173 @@ +{ sshKey +, qemuArgs ? [] +, command ? "sync" +, suspendTo ? null +, resumeFrom ? null +, installMode ? false +}: + +let + inherit (import {}) lib stdenv writeScript vmTools makeInitrd; + inherit (import {}) samba vde2 busybox openssh; + inherit (import {}) socat netcat coreutils gzip; + + preInitScript = writeScript "preinit.sh" '' + #!${vmTools.initrdUtils}/bin/ash -e + export PATH=${vmTools.initrdUtils}/bin + mount -t proc none /proc + mount -t sysfs none /sys + for arg in $(cat /proc/cmdline); do + if [ "x''${arg#command=}" != "x$arg" ]; then + command="''${arg#command=}" + fi + done + + for i in $(cat ${modulesClosure}/insmod-list); do + insmod $i + done + + mkdir -p /tmp /dev + mknod /dev/null c 1 3 + mknod /dev/zero c 1 5 + mknod /dev/random c 1 8 + mknod /dev/urandom c 1 9 + mknod /dev/tty c 5 0 + + ifconfig lo up + ifconfig eth0 up 192.168.0.2 + + mkdir -p /nix/store /etc /var/run /var/log + + cat > /etc/passwd < /etc/samba/smb.conf < '${suspendTo}'" + quit + CMD + wait %% + ''; + +in writeScript "run-cygwin-vm.sh" '' + #!${stdenv.shell} -e + ${preVM} + ${vmExec} +'' diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 470fac0437d..2ecadbae7cf 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -1,160 +1,49 @@ -with import {}; - -with import { - inherit system; - minimal = false; -}; - let + inherit (import {}) lib stdenv requireFile writeText qemu; + winISO = /path/to/iso/XXX; - base = import ./install { + installedVM = import ./install { isoFile = winISO; productKey = "XXX"; }; - maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; - - cygwinQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [ - "-monitor unix:$MONITOR_SOCKET,server,nowait" - "-nographic" - "-boot order=c,once=d" - "-drive file=${base.floppy},readonly,index=0,if=floppy" - "-drive file=winvm.img,index=0,media=disk" - "-drive file=${winISO},index=1,media=cdrom" - "-drive file=${base.iso}/iso/cd.iso,index=2,media=cdrom" - "-net nic,vlan=0,macaddr=52:54:00:12:01:01" - "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" - "-rtc base=2010-01-01,clock=vm" - ]); - - modulesClosure = lib.overrideDerivation vmTools.modulesClosure (o: { - rootModules = o.rootModules ++ lib.singleton "virtio_net"; + runInVM = img: attrs: import ./controller (attrs // { + inherit (installedVM) sshKey; + qemuArgs = attrs.qemuArgs or [] ++ [ + "-boot order=c" + "-drive file=${img},index=0,media=disk" + ]; }); - controllerQemuArgs = cmd: let - preInitScript = writeScript "preinit.sh" '' - #!${vmTools.initrdUtils}/bin/ash -e - export PATH=${vmTools.initrdUtils}/bin - mount -t proc none /proc - mount -t sysfs none /sys - for arg in $(cat /proc/cmdline); do - if [ "x''${arg#command=}" != "x$arg" ]; then - command="''${arg#command=}" - fi - done - - for i in $(cat ${modulesClosure}/insmod-list); do - insmod $i - done - - mkdir -p /tmp /dev - mknod /dev/null c 1 3 - mknod /dev/zero c 1 5 - mknod /dev/random c 1 8 - mknod /dev/urandom c 1 9 - mknod /dev/tty c 5 0 - - ifconfig lo up - ifconfig eth0 up 192.168.0.2 - - mkdir -p /nix/store /etc /var/run /var/log - - cat > /etc/passwd < /etc/samba/smb.conf < '$out/state.gz'" - CMD cp winvm.img "$out/disk.img" + cp state.gz "$out/state.gz" ''; }; -in bootstrap + resumeAndRun = command: runInVM "${suspendedVM}/disk.img" { + resumeFrom = "${suspendedVM}/state.gz"; + qemuArgs = lib.singleton "-snapshot"; + inherit command; + }; + + runFromSuspended = command: stdenv.mkDerivation { + name = "cygwin-vm-run"; + buildCommand = '' + ${resumeAndRun command} + ''; + }; + +in runFromSuspended "uname -a" diff --git a/pkgs/build-support/vm/windows/install/default.nix b/pkgs/build-support/vm/windows/install/default.nix index 0021bae87bc..d766cbcf8e3 100644 --- a/pkgs/build-support/vm/windows/install/default.nix +++ b/pkgs/build-support/vm/windows/install/default.nix @@ -3,7 +3,7 @@ }: let - inherit (import {}) lib stdenv runCommand openssh; + inherit (import {}) lib stdenv runCommand openssh qemu; bootstrapAfterLogin = runCommand "bootstrap.sh" {} '' cat > "$out" <> /etc/fstab + shutdown -s now EOF ''; @@ -28,10 +28,16 @@ let ''; }; - packages = [ "openssh" ]; + sshKey = "${cygwinSshKey}/key"; -in { - iso = import ../cygwin-iso { + packages = [ "openssh" "shutdown" ]; + + instfloppy = import ./unattended-image.nix { + cygwinPackages = packages; + inherit productKey; + }; + + cygiso = import ../cygwin-iso { inherit packages; extraContents = lib.singleton { source = bootstrapAfterLogin; @@ -39,10 +45,27 @@ in { }; }; - floppy = import ./unattended-image.nix { - cygwinPackages = packages; - inherit productKey; + installController = import ../controller { + inherit sshKey; + installMode = true; + qemuArgs = [ + "-boot order=c,once=d" + "-drive file=${instfloppy},readonly,index=0,if=floppy" + "-drive file=winvm.img,index=0,media=disk" + "-drive file=${isoFile},index=1,media=cdrom" + "-drive file=${cygiso}/iso/cd.iso,index=2,media=cdrom" + ]; }; - sshKey = "${cygwinSshKey}/key"; +in stdenv.mkDerivation { + name = "cygwin-base-vm"; + buildCommand = '' + ${qemu}/bin/qemu-img create -f qcow2 winvm.img 2G + ${installController} + ensureDir "$out" + cp winvm.img "$out/disk.img" + ''; + passthru = { + inherit sshKey; + }; } From 3e91192f079c2eac4bd15d564bb2fd4702506b36 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 00:27:43 +0100 Subject: [PATCH 06/23] vm/windows: Wait for VDE switch to startup. This could possibly cause flapping whenever qemu is too fast in starting up. As we are running with the shell's -e flag, the socat check also ensures that the VDE switch is properly started and causes the whole build to fail, should it not start up within 20 seconds. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/controller/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 49d6815a3dc..22451e0e394 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -148,6 +148,8 @@ let QEMU_VDE_SOCKET="$(pwd)/vde.ctl" MONITOR_SOCKET="$(pwd)/monitor" ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" & + echo 'alive?' | ${socat}/bin/socat - \ + UNIX-CONNECT:$QEMU_VDE_SOCKET/ctl,retry=20 ''; vmExec = if installMode then '' From cfa859d79222d034a9fe493192150b80faae4ee8 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 00:48:01 +0100 Subject: [PATCH 07/23] vm/windows: Don't init /nix/store on install. We're going to do this during the suspendedVM phase, so we're able to more easily change the shares without reinstalling the whole VM. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/install/default.nix | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkgs/build-support/vm/windows/install/default.nix b/pkgs/build-support/vm/windows/install/default.nix index d766cbcf8e3..64177ea1713 100644 --- a/pkgs/build-support/vm/windows/install/default.nix +++ b/pkgs/build-support/vm/windows/install/default.nix @@ -12,11 +12,8 @@ let $(cat "${cygwinSshKey}/key.pub") PUBKEY ssh-host-config -y -c 'binmode ntsec' -w dummy - - net use S: '\\192.168.0.2\nixstore' - mkdir -p /nix/store - echo "/cygdrives/s /nix/store none bind 0 0" >> /etc/fstab - shutdown -s now + cygrunsrv -S sshd + shutdown -s 5 EOF ''; From aa65a7057f3abba32726ed17853090bcb86992f9 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 01:42:10 +0100 Subject: [PATCH 08/23] vm/windows: Properly escape shell command. Security-wise it's not a big issue because we're still sandboxed, but I really don't want to write something like \\\\\\\\192.168.0.2\\\\share in order to set up network shares. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/controller/default.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 22451e0e394..6abf87f82c8 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -57,6 +57,8 @@ let }; }; + shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'"; + initScript = writeScript "init.sh" ('' #!${stdenv.shell} ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private /var/lib/samba @@ -104,7 +106,7 @@ let -o StrictHostKeyChecking=no \ -i /ssh.key \ -l Administrator \ - 192.168.0.1 -- "${command}" + 192.168.0.1 -- ${shellEscape command} ${busybox}/sbin/poweroff -f '')); From 0ce1fd07fe7e59115af60dd33a9e668cbb1ebd4c Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 01:46:28 +0100 Subject: [PATCH 09/23] vm/windows: Reintroduce setting up /nix/store. Now we're doing this at the point where we're saving the VM state. Unfortunately it's not quite right, because the controller VM is shut down _before_ we're saving the state, so the share gets disconnected despite autodisconnect being deactivated during setup. We can get around this issue by finally introducing the xchg share, which is the last item to be implemented before we can merge to master. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 2ecadbae7cf..b7c18f6d16c 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -17,6 +17,13 @@ let }); runAndSuspend = runInVM "winvm.img" { + command = lib.concatStringsSep "; " [ + "set -e" + "net config server /autodisconnect:-1" + "net use S: '\\\\192.168.0.2\\nixstore' /persistent:yes" + "mkdir -p /nix/store" + "mount -o bind /cygdrive/s /nix/store" + ]; suspendTo = "state.gz"; }; From fedf13e6cfb94bad00df71aba66f0b6fea233e0f Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 03:19:40 +0100 Subject: [PATCH 10/23] vm/windows: Implement and use "xchg" share. This now finally introduces our xchg share and also uses it for exchanging state while suspending a VM. However, accessing the _real_ Nix store still isn't possible because we're shadowing the directory in the initrd. Signed-off-by: aszlig --- .../vm/windows/controller/default.nix | 30 +++++++++++++++---- pkgs/build-support/vm/windows/default.nix | 3 ++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 6abf87f82c8..da3be51c102 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -36,13 +36,17 @@ let ifconfig lo up ifconfig eth0 up 192.168.0.2 - mkdir -p /nix/store /etc /var/run /var/log + mkdir -p /xchg /nix/store /etc /var/run /var/log cat > /etc/passwd < '${suspendTo}'" quit CMD - wait %% + wait %- ''; in writeScript "run-cygwin-vm.sh" '' diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index b7c18f6d16c..89acb9a2d68 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -21,8 +21,11 @@ let "set -e" "net config server /autodisconnect:-1" "net use S: '\\\\192.168.0.2\\nixstore' /persistent:yes" + "net use X: '\\\\192.168.0.2\\xchg' /persistent:yes" "mkdir -p /nix/store" "mount -o bind /cygdrive/s /nix/store" + "mkdir -p /tmp/xchg" + "mount -o bind /cygdrive/x /tmp/xchg" ]; suspendTo = "state.gz"; }; From a1d7974acd1a6c7d527dd0adc9fe711983990074 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 16:47:23 +0100 Subject: [PATCH 11/23] vm/windows: Remove Samba from installMode. This should trim down possible dependencies on the base installation and hereby reduce the need for reinstallation of the damn VM to only changes that affect the Windows installation and the base Cygwin + OpenSSH setup. Signed-off-by: aszlig --- .../vm/windows/controller/default.nix | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index da3be51c102..3d763468113 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -67,6 +67,19 @@ let initScript = writeScript "init.sh" ('' #!${stdenv.shell} + ${coreutils}/bin/cp -L "${sshKey}" /ssh.key + ${coreutils}/bin/chmod 600 /ssh.key + '' + (if installMode then '' + echo -n "Waiting for Windows installation to finish..." + while ! ${netcat}/bin/netcat -z 192.168.0.1 22; do + echo -n . + # Print a dot every 10 seconds only to shorten line length. + ${coreutils}/bin/sleep 10 + done + echo " success." + # Loop forever, because this VM is going to be killed. + ${loopForever} + '' else '' ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private /var/lib/samba ${coreutils}/bin/cat > /etc/samba/smb.conf < Date: Sun, 16 Feb 2014 16:53:02 +0100 Subject: [PATCH 12/23] vm/windows: Avoid shadowing the Nix store. This is done by putting the non-initrd /nix/store into a subdirectory, which we then chroot to and pass along the SSH command. Also, we now collect the exit code after the chroot command and power off the VM thereafter, because the store is no longer shadowed and we have still access to the busybox inside the initrd. Signed-off-by: aszlig --- .../vm/windows/controller/default.nix | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 3d763468113..60ec1d4783a 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -8,8 +8,7 @@ let inherit (import {}) lib stdenv writeScript vmTools makeInitrd; - inherit (import {}) samba vde2 busybox openssh; - inherit (import {}) socat netcat coreutils gzip; + inherit (import {}) samba vde2 openssh socat netcat coreutils gzip; preInitScript = writeScript "preinit.sh" '' #!${vmTools.initrdUtils}/bin/ash -e @@ -26,7 +25,9 @@ let insmod $i done - mkdir -p /tmp /dev + mkdir -p /dev /fs + + mount -t tmpfs none /dev mknod /dev/null c 1 3 mknod /dev/zero c 1 5 mknod /dev/random c 1 8 @@ -36,22 +37,31 @@ let ifconfig lo up ifconfig eth0 up 192.168.0.2 - mkdir -p /xchg /nix/store /etc /var/run /var/log + mount -t tmpfs none /fs + mkdir -p /fs/nix/store /fs/xchg /fs/dev /fs/sys /fs/proc /fs/etc /fs/tmp - cat > /etc/passwd < /fs/etc/passwd < /fs/xchg/in-vm-exit - mount -t 9p \ - -o trans=virtio,version=9p2000.L,msize=262144,cache=loose \ - store /nix/store - - exec "$command" + poweroff -f ''; initrd = makeInitrd { @@ -80,7 +90,8 @@ let # Loop forever, because this VM is going to be killed. ${loopForever} '' else '' - ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private /var/lib/samba + ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private \ + /var/lib/samba /var/log /var/run ${coreutils}/bin/cat > /etc/samba/smb.conf < Date: Sun, 16 Feb 2014 18:58:08 +0100 Subject: [PATCH 13/23] vm/windows: Fix perms of the Samba shares. We now map all guest accounts to the root user, because in the end the permissions of the current user boil down to the build user of the Nix builder of the host. That way it's not possible to gain more permissions at all and just makes the VM communication a lot easier. However, setting "writable" to yes instead of "read only" to no doesn't change anything here, I just found it to be clearer. Also, we now no longer need to have a "nobody" user. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/controller/default.nix | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 60ec1d4783a..fd6ad6354aa 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -52,10 +52,7 @@ let -o trans=virtio,version=9p2000.L,msize=262144,cache=loose \ xchg /fs/xchg - cat > /fs/etc/passwd < /fs/etc/passwd set +e chroot /fs $command $out @@ -96,6 +93,7 @@ let [global] security = user map to guest = Bad User + guest account = root workgroup = cygwin netbios name = controller server string = %h @@ -105,12 +103,12 @@ let [nixstore] path = /nix/store - read only = no + writable = yes guest ok = yes [xchg] path = /xchg - read only = no + writable = yes guest ok = yes CONFIG From d16dae8d3215c82070132694d82a1309d140f124 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 22:12:42 +0100 Subject: [PATCH 14/23] vm/windows: Use &&, not set -e in runAndSuspend. We now have proper quoting, so we no longer need the workaround using "set -e". Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 89acb9a2d68..36795fbb78e 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -17,8 +17,7 @@ let }); runAndSuspend = runInVM "winvm.img" { - command = lib.concatStringsSep "; " [ - "set -e" + command = lib.concatStringsSep " && " [ "net config server /autodisconnect:-1" "net use S: '\\\\192.168.0.2\\nixstore' /persistent:yes" "net use X: '\\\\192.168.0.2\\xchg' /persistent:yes" From 5258bbe4c97113dfa663a32408691fc93ee2dd9b Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 22:27:39 +0100 Subject: [PATCH 15/23] vm/windows: Create fstab entries in suspended VM. Cygwin initializes mounts on _every_ login via SSH and doesn't keep them consistently like on Unix systems, that's why we need to also add fstab entries for the bind mounts to the store and xchg shares. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 36795fbb78e..bb0833beec2 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -23,8 +23,10 @@ let "net use X: '\\\\192.168.0.2\\xchg' /persistent:yes" "mkdir -p /nix/store" "mount -o bind /cygdrive/s /nix/store" + "echo /cygdrive/s /nix/store none bind 0 0 >> /etc/fstab" "mkdir -p /tmp/xchg" "mount -o bind /cygdrive/x /tmp/xchg" + "echo /cygdrive/x /tmp/xchg none bind 0 0 >> /etc/fstab" ]; suspendTo = "state.gz"; }; From 707b7ad1bddf8b95ca1348260dea132b0514089d Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 22:30:40 +0100 Subject: [PATCH 16/23] vm/windows: Generate mounts from an attribute set. This is mainly to make it easier to quickly change mappings, without making room for errors such as typos. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 32 +++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index bb0833beec2..8ca31bc39d7 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -16,18 +16,28 @@ let ]; }); - runAndSuspend = runInVM "winvm.img" { - command = lib.concatStringsSep " && " [ - "net config server /autodisconnect:-1" - "net use S: '\\\\192.168.0.2\\nixstore' /persistent:yes" - "net use X: '\\\\192.168.0.2\\xchg' /persistent:yes" - "mkdir -p /nix/store" - "mount -o bind /cygdrive/s /nix/store" - "echo /cygdrive/s /nix/store none bind 0 0 >> /etc/fstab" - "mkdir -p /tmp/xchg" - "mount -o bind /cygdrive/x /tmp/xchg" - "echo /cygdrive/x /tmp/xchg none bind 0 0 >> /etc/fstab" + runAndSuspend = let + drives = { + s = { + source = "nixstore"; + target = "/nix/store"; + }; + x = { + source = "xchg"; + target = "/tmp/xchg"; + }; + }; + + genDriveCmds = letter: { source, target }: [ + "net use ${letter}: '\\\\192.168.0.2\\${source}' /persistent:yes" + "mkdir -p '${target}'" + "mount -o bind '/cygdrive/${letter}' '${target}'" + "echo '/cygdrive/${letter} ${target} none bind 0 0' >> /etc/fstab" ]; + in runInVM "winvm.img" { + command = lib.concatStringsSep " && " ([ + "net config server /autodisconnect:-1" + ] ++ lib.concatLists (lib.mapAttrsToList genDriveCmds drives)); suspendTo = "state.gz"; }; From dd8b0fcf527fa33e5dfe71b589ccb1044412bfad Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 22:34:45 +0100 Subject: [PATCH 17/23] vm/windows: Save envirenoment before running VM. Later, when we start the actual builder, we're going to restore those environment variables. We're using "(set; declare -p)", here, because the former is just printing _all_ environment variables, even those not supported, and the latter only lists specifically declared variables, which also encludes exports. The "declare -p" command also emits those variables in a format similar to the "export" command. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/controller/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index fd6ad6354aa..4a3e88a22c6 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -172,7 +172,9 @@ let }); preVM = '' + (set; declare -p) > saved-env XCHG_DIR="$(${coreutils}/bin/mktemp -d nix-vm.XXXXXXXXXX --tmpdir)" + ${coreutils}/bin/mv saved-env "$XCHG_DIR/" QEMU_VDE_SOCKET="$(pwd)/vde.ctl" MONITOR_SOCKET="$(pwd)/monitor" ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" & From b01c9624cf0c77837e1928a49778f74446ddfaa0 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 16 Feb 2014 22:42:10 +0100 Subject: [PATCH 18/23] vm/windows: Add new runInWindowsVM function. This function is quite similar to runInLinuxVM, but also ensures that the builder is run decoupled of the Nix store and using the userland inside the VM. We're now picking up the environment variables saved in the previous commit. The reason we suppress all errors from the source operation is that it would emit a ton of errors because we're trying to set read-only variables. Also, detecting whether the origBuilder is using the default builder from the stdenv is currently a bit of a workaround until we have a specialized pseudo-cross-stdenv someday in the future[TM]. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/default.nix | 42 +++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 8ca31bc39d7..9d870633ff1 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -60,11 +60,39 @@ let inherit command; }; - runFromSuspended = command: stdenv.mkDerivation { - name = "cygwin-vm-run"; - buildCommand = '' - ${resumeAndRun command} - ''; - }; + builder = '' + source /tmp/xchg/saved-env 2> /dev/null || true + export NIX_STORE=/nix/store + export NIX_BUILD_TOP=/tmp + export TMPDIR=/tmp + export PATH=/empty + cd "$NIX_BUILD_TOP" + exec $origBuilder $origArgs + ''; -in runFromSuspended "uname -a" +in { + runInWindowsVM = drv: let + newDrv = drv.override { + stdenv = drv.stdenv.override { + shell = "/bin/sh"; + }; + }; + in lib.overrideDerivation drv (attrs: { + requiredSystemFeatures = [ "kvm" ]; + buildur = "${stdenv.shell}"; + args = ["-e" (resumeAndRun builder)]; + origArgs = attrs.args; + origBuilder = if attrs.builder == attrs.stdenv.shell + then "/bin/sh" + else attrs.builder; + + postHook = '' + PATH=/usr/bin:/bin:/usr/sbin:/sbin + SHELL=/bin/sh + eval "$origPostHook" + ''; + + origPostHook = attrs.postHook or ""; + fixupPhase = ":"; + }); +} From e40f41e50562873fcdb78bec891a8f19141d4b42 Mon Sep 17 00:00:00 2001 From: aszlig Date: Mon, 17 Feb 2014 00:34:19 +0100 Subject: [PATCH 19/23] vm/windows: Collect exit code from xchg. This is the last item that was missing to get a fully working runInWindowsVM function. Apart from checking exit codes, we also now have preVM/postVM hooks which we can use to write arbitrary constructs around this architecture, without the need to worry about specific details. Signed-off-by: aszlig --- .../vm/windows/controller/default.nix | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 4a3e88a22c6..621729eaabb 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -128,12 +128,10 @@ let -i /ssh.key \ -l Administrator \ 192.168.0.1 -- ${shellEscape command} - - ${lib.optionalString (suspendTo != null) '' + '') + lib.optionalString (suspendTo != null) '' ${coreutils}/bin/touch /xchg/suspend_now ${loopForever} - ''} - '')); + ''); kernelAppend = lib.concatStringsSep " " [ "panic=1" @@ -175,6 +173,9 @@ let (set; declare -p) > saved-env XCHG_DIR="$(${coreutils}/bin/mktemp -d nix-vm.XXXXXXXXXX --tmpdir)" ${coreutils}/bin/mv saved-env "$XCHG_DIR/" + + eval "$preVM" + QEMU_VDE_SOCKET="$(pwd)/vde.ctl" MONITOR_SOCKET="$(pwd)/monitor" ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" & @@ -190,7 +191,9 @@ let '' else '' ${vmTools.qemuProg} ${cygwinQemuArgs} & ${vmTools.qemuProg} ${controllerQemuArgs}${bgBoth} - '' + lib.optionalString (suspendTo != null) '' + ''; + + postVM = if suspendTo != null then '' while ! test -e "$XCHG_DIR/suspend_now"; do sleep 1; done ${socat}/bin/socat - UNIX-CONNECT:$MONITOR_SOCKET < Date: Mon, 17 Feb 2014 02:10:00 +0100 Subject: [PATCH 20/23] vm/windows: Factor out bootstrapping process. This now isolates the vmTools integration from the bootstrap process and thus removes our fixed Windows ISO and product key. The latter can now be provided by an attribute "windowsImage" to runInWindowsVM. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/bootstrap.nix | 61 ++++++++++++++++++ pkgs/build-support/vm/windows/default.nix | 68 ++------------------- 2 files changed, 67 insertions(+), 62 deletions(-) create mode 100644 pkgs/build-support/vm/windows/bootstrap.nix diff --git a/pkgs/build-support/vm/windows/bootstrap.nix b/pkgs/build-support/vm/windows/bootstrap.nix new file mode 100644 index 00000000000..26aa6c7a686 --- /dev/null +++ b/pkgs/build-support/vm/windows/bootstrap.nix @@ -0,0 +1,61 @@ +{ isoFile, productKey }: + +let + inherit (import {}) lib stdenv qemu; +in rec { + installedVM = import ./install { + inherit isoFile productKey; + }; + + runInVM = img: attrs: import ./controller (attrs // { + inherit (installedVM) sshKey; + qemuArgs = attrs.qemuArgs or [] ++ [ + "-boot order=c" + "-drive file=${img},index=0,media=disk" + ]; + }); + + runAndSuspend = let + drives = { + s = { + source = "nixstore"; + target = "/nix/store"; + }; + x = { + source = "xchg"; + target = "/tmp/xchg"; + }; + }; + + genDriveCmds = letter: { source, target }: [ + "net use ${letter}: '\\\\192.168.0.2\\${source}' /persistent:yes" + "mkdir -p '${target}'" + "mount -o bind '/cygdrive/${letter}' '${target}'" + "echo '/cygdrive/${letter} ${target} none bind 0 0' >> /etc/fstab" + ]; + in runInVM "winvm.img" { + command = lib.concatStringsSep " && " ([ + "net config server /autodisconnect:-1" + ] ++ lib.concatLists (lib.mapAttrsToList genDriveCmds drives)); + suspendTo = "state.gz"; + }; + + suspendedVM = stdenv.mkDerivation { + name = "cygwin-suspended-vm"; + buildCommand = '' + ${qemu}/bin/qemu-img create \ + -b "${installedVM}/disk.img" \ + -f qcow2 winvm.img + ${runAndSuspend} + ensureDir "$out" + cp winvm.img "$out/disk.img" + cp state.gz "$out/state.gz" + ''; + }; + + resumeAndRun = command: runInVM "${suspendedVM}/disk.img" { + resumeFrom = "${suspendedVM}/state.gz"; + qemuArgs = lib.singleton "-snapshot"; + inherit command; + }; +} diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 9d870633ff1..8e8809f370f 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -1,64 +1,5 @@ let - inherit (import {}) lib stdenv requireFile writeText qemu; - - winISO = /path/to/iso/XXX; - - installedVM = import ./install { - isoFile = winISO; - productKey = "XXX"; - }; - - runInVM = img: attrs: import ./controller (attrs // { - inherit (installedVM) sshKey; - qemuArgs = attrs.qemuArgs or [] ++ [ - "-boot order=c" - "-drive file=${img},index=0,media=disk" - ]; - }); - - runAndSuspend = let - drives = { - s = { - source = "nixstore"; - target = "/nix/store"; - }; - x = { - source = "xchg"; - target = "/tmp/xchg"; - }; - }; - - genDriveCmds = letter: { source, target }: [ - "net use ${letter}: '\\\\192.168.0.2\\${source}' /persistent:yes" - "mkdir -p '${target}'" - "mount -o bind '/cygdrive/${letter}' '${target}'" - "echo '/cygdrive/${letter} ${target} none bind 0 0' >> /etc/fstab" - ]; - in runInVM "winvm.img" { - command = lib.concatStringsSep " && " ([ - "net config server /autodisconnect:-1" - ] ++ lib.concatLists (lib.mapAttrsToList genDriveCmds drives)); - suspendTo = "state.gz"; - }; - - suspendedVM = stdenv.mkDerivation { - name = "cygwin-suspended-vm"; - buildCommand = '' - ${qemu}/bin/qemu-img create \ - -b "${installedVM}/disk.img" \ - -f qcow2 winvm.img - ${runAndSuspend} - ensureDir "$out" - cp winvm.img "$out/disk.img" - cp state.gz "$out/state.gz" - ''; - }; - - resumeAndRun = command: runInVM "${suspendedVM}/disk.img" { - resumeFrom = "${suspendedVM}/state.gz"; - qemuArgs = lib.singleton "-snapshot"; - inherit command; - }; + inherit (import {}) lib stdenv; builder = '' source /tmp/xchg/saved-env 2> /dev/null || true @@ -77,10 +18,13 @@ in { shell = "/bin/sh"; }; }; - in lib.overrideDerivation drv (attrs: { + in lib.overrideDerivation drv (attrs: let + bootstrap = import ./bootstrap.nix attrs.windowsImage; + in { requiredSystemFeatures = [ "kvm" ]; buildur = "${stdenv.shell}"; - args = ["-e" (resumeAndRun builder)]; + args = ["-e" (bootstrap.resumeAndRun builder)]; + windowsImage = bootstrap.suspendedVM; origArgs = attrs.args; origBuilder = if attrs.builder == attrs.stdenv.shell then "/bin/sh" From 623f1940c0a9f8fcc150fb9c72ee08496025605a Mon Sep 17 00:00:00 2001 From: aszlig Date: Wed, 26 Feb 2014 05:43:34 +0100 Subject: [PATCH 21/23] vm/windows: Make list of dependencies explicit. So far, the VMs have always been using the native architecture, because it was reimporting several times. Now, we propagate a list of packages down to all sub-imports, which not only makes clearer which dependencies a part actually has, but also will make it easier in case we want to refactor those parts to use callPackage. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/bootstrap.nix | 31 +++++++++++++++---- .../vm/windows/controller/default.nix | 31 ++++++++++--------- .../vm/windows/cygwin-iso/default.nix | 4 +-- pkgs/build-support/vm/windows/default.nix | 14 ++++++--- .../vm/windows/install/default.nix | 18 +++++++---- .../vm/windows/install/unattended-image.nix | 8 ++--- 6 files changed, 70 insertions(+), 36 deletions(-) diff --git a/pkgs/build-support/vm/windows/bootstrap.nix b/pkgs/build-support/vm/windows/bootstrap.nix index 26aa6c7a686..b2febf19a89 100644 --- a/pkgs/build-support/vm/windows/bootstrap.nix +++ b/pkgs/build-support/vm/windows/bootstrap.nix @@ -1,13 +1,32 @@ +{ stdenv, fetchurl, vmTools, writeScript, writeText, runCommand, makeInitrd +, python, perl, coreutils, dosfstools, gzip, mtools, netcat, openssh, qemu +, samba, socat, vde2, cdrkit, pathsFromGraph +}: + { isoFile, productKey }: +with stdenv.lib; + let - inherit (import {}) lib stdenv qemu; + controller = import ./controller { + inherit stdenv writeScript vmTools makeInitrd; + inherit samba vde2 openssh socat netcat coreutils gzip; + }; + + mkCygwinImage = import ./cygwin-iso { + inherit stdenv fetchurl runCommand python perl cdrkit pathsFromGraph; + }; + + installer = import ./install { + inherit controller mkCygwinImage; + inherit stdenv runCommand openssh qemu writeText dosfstools mtools; + }; in rec { - installedVM = import ./install { + installedVM = installer { inherit isoFile productKey; }; - runInVM = img: attrs: import ./controller (attrs // { + runInVM = img: attrs: controller (attrs // { inherit (installedVM) sshKey; qemuArgs = attrs.qemuArgs or [] ++ [ "-boot order=c" @@ -34,9 +53,9 @@ in rec { "echo '/cygdrive/${letter} ${target} none bind 0 0' >> /etc/fstab" ]; in runInVM "winvm.img" { - command = lib.concatStringsSep " && " ([ + command = concatStringsSep " && " ([ "net config server /autodisconnect:-1" - ] ++ lib.concatLists (lib.mapAttrsToList genDriveCmds drives)); + ] ++ concatLists (mapAttrsToList genDriveCmds drives)); suspendTo = "state.gz"; }; @@ -55,7 +74,7 @@ in rec { resumeAndRun = command: runInVM "${suspendedVM}/disk.img" { resumeFrom = "${suspendedVM}/state.gz"; - qemuArgs = lib.singleton "-snapshot"; + qemuArgs = singleton "-snapshot"; inherit command; }; } diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix index 621729eaabb..fe4b5b7f6c2 100644 --- a/pkgs/build-support/vm/windows/controller/default.nix +++ b/pkgs/build-support/vm/windows/controller/default.nix @@ -1,3 +1,7 @@ +{ stdenv, writeScript, vmTools, makeInitrd +, samba, vde2, openssh, socat, netcat, coreutils, gzip +}: + { sshKey , qemuArgs ? [] , command ? "sync" @@ -6,10 +10,9 @@ , installMode ? false }: -let - inherit (import {}) lib stdenv writeScript vmTools makeInitrd; - inherit (import {}) samba vde2 openssh socat netcat coreutils gzip; +with stdenv.lib; +let preInitScript = writeScript "preinit.sh" '' #!${vmTools.initrdUtils}/bin/ash -e export PATH=${vmTools.initrdUtils}/bin @@ -62,13 +65,13 @@ let ''; initrd = makeInitrd { - contents = lib.singleton { + contents = singleton { object = preInitScript; symlink = "/init"; }; }; - shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'"; + shellEscape = x: "'${replaceChars ["'"] [("'\\'" + "'")] x}'"; loopForever = "while :; do ${coreutils}/bin/sleep 1; done"; @@ -128,12 +131,12 @@ let -i /ssh.key \ -l Administrator \ 192.168.0.1 -- ${shellEscape command} - '') + lib.optionalString (suspendTo != null) '' + '') + optionalString (suspendTo != null) '' ${coreutils}/bin/touch /xchg/suspend_now ${loopForever} ''); - kernelAppend = lib.concatStringsSep " " [ + kernelAppend = concatStringsSep " " [ "panic=1" "loglevel=4" "console=tty1" @@ -141,7 +144,7 @@ let "command=${initScript}" ]; - controllerQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [ + controllerQemuArgs = concatStringsSep " " (maybeKvm64 ++ [ "-nographic" "-no-reboot" "-virtfs local,path=/nix/store,security_model=none,mount_tag=store" @@ -153,20 +156,20 @@ let "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" ]); - maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; + maybeKvm64 = optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; - cygwinQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [ + cygwinQemuArgs = concatStringsSep " " (maybeKvm64 ++ [ "-monitor unix:$MONITOR_SOCKET,server,nowait" "-nographic" "-net nic,vlan=0,macaddr=52:54:00:12:01:01" "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" "-rtc base=2010-01-01,clock=vm" - ] ++ qemuArgs ++ lib.optionals (resumeFrom != null) [ + ] ++ qemuArgs ++ optionals (resumeFrom != null) [ "-incoming 'exec: ${gzip}/bin/gzip -c -d \"${resumeFrom}\"'" ]); - modulesClosure = lib.overrideDerivation vmTools.modulesClosure (o: { - rootModules = o.rootModules ++ lib.singleton "virtio_net"; + modulesClosure = overrideDerivation vmTools.modulesClosure (o: { + rootModules = o.rootModules ++ singleton "virtio_net"; }); preVM = '' @@ -183,7 +186,7 @@ let UNIX-CONNECT:$QEMU_VDE_SOCKET/ctl,retry=20 ''; - bgBoth = lib.optionalString (suspendTo != null) " &"; + bgBoth = optionalString (suspendTo != null) " &"; vmExec = if installMode then '' ${vmTools.qemuProg} ${controllerQemuArgs} & diff --git a/pkgs/build-support/vm/windows/cygwin-iso/default.nix b/pkgs/build-support/vm/windows/cygwin-iso/default.nix index 17e4cff855b..c7b1cc8bbcb 100644 --- a/pkgs/build-support/vm/windows/cygwin-iso/default.nix +++ b/pkgs/build-support/vm/windows/cygwin-iso/default.nix @@ -1,11 +1,11 @@ +{ stdenv, fetchurl, runCommand, python, perl, cdrkit, pathsFromGraph }: + { packages ? [] , mirror ? "http://ftp.gwdg.de/pub/linux/sources.redhat.com/cygwin" , extraContents ? [] }: let - inherit (import {}) fetchurl runCommand python; - cygPkgList = fetchurl { url = "${mirror}/x86_64/setup.ini"; sha256 = "19vfm7zr8kcp1algmggk8vsilkccycx22mdf0ynfl6lcmp6dkfsz"; diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix index 8e8809f370f..758120ed4f2 100644 --- a/pkgs/build-support/vm/windows/default.nix +++ b/pkgs/build-support/vm/windows/default.nix @@ -1,5 +1,11 @@ +pkgs: + let - inherit (import {}) lib stdenv; + bootstrapper = import ./bootstrap.nix { + inherit (pkgs) stdenv vmTools writeScript writeText runCommand makeInitrd; + inherit (pkgs) coreutils dosfstools gzip mtools netcat openssh qemu samba; + inherit (pkgs) socat vde2 fetchurl python perl cdrkit pathsFromGraph; + }; builder = '' source /tmp/xchg/saved-env 2> /dev/null || true @@ -18,11 +24,11 @@ in { shell = "/bin/sh"; }; }; - in lib.overrideDerivation drv (attrs: let - bootstrap = import ./bootstrap.nix attrs.windowsImage; + in pkgs.lib.overrideDerivation drv (attrs: let + bootstrap = bootstrapper attrs.windowsImage; in { requiredSystemFeatures = [ "kvm" ]; - buildur = "${stdenv.shell}"; + buildur = "${pkgs.stdenv.shell}"; args = ["-e" (bootstrap.resumeAndRun builder)]; windowsImage = bootstrap.suspendedVM; origArgs = attrs.args; diff --git a/pkgs/build-support/vm/windows/install/default.nix b/pkgs/build-support/vm/windows/install/default.nix index 64177ea1713..10690bf6b28 100644 --- a/pkgs/build-support/vm/windows/install/default.nix +++ b/pkgs/build-support/vm/windows/install/default.nix @@ -1,10 +1,12 @@ +{ stdenv, runCommand, openssh, qemu, controller, mkCygwinImage +, writeText, dosfstools, mtools +}: + { isoFile , productKey }: let - inherit (import {}) lib stdenv runCommand openssh qemu; - bootstrapAfterLogin = runCommand "bootstrap.sh" {} '' cat > "$out" < {}) lib stdenv writeText dosfstools mtools; - afterSetup = [ cygwinSetup "-L -n -q" @@ -80,7 +80,7 @@ let ; Turn off all components [Components] - ${lib.concatMapStrings (comp: "${comp} = Off\n") [ + ${stdenv.lib.concatMapStrings (comp: "${comp} = Off\n") [ "AccessOpt" "Appsrv_console" "Aspnet" "BitsServerExtensionsISAPI" "BitsServerExtensionsManager" "Calc" "Certsrv" "Certsrv_client" "Certsrv_server" "Charmap" "Chat" "Clipbook" "Cluster" "Complusnetwork" @@ -107,7 +107,7 @@ let Mode = 0 [SetupParams] - UserExecute = "${lib.concatStringsSep " " afterSetup}" + UserExecute = "${stdenv.lib.concatStringsSep " " afterSetup}" [GuiRunOnce] Command0 = "${cygwinRoot}\bin\bash -l ${shExecAfterwards}" From 895a683d399afca1b6e7ab8e02b9508dd81b281b Mon Sep 17 00:00:00 2001 From: aszlig Date: Wed, 26 Feb 2014 06:19:05 +0100 Subject: [PATCH 22/23] vm/windows: Add setup.ini for 32bit architecture. Also update 64bit setup.ini and check whether we have a 64 bit stdenv in order to choose the proper Cygwin version. Otherwise we now have the setup.ini for 32bit available as well. Signed-off-by: aszlig --- pkgs/build-support/vm/windows/cygwin-iso/default.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/build-support/vm/windows/cygwin-iso/default.nix b/pkgs/build-support/vm/windows/cygwin-iso/default.nix index c7b1cc8bbcb..a806ea9571f 100644 --- a/pkgs/build-support/vm/windows/cygwin-iso/default.nix +++ b/pkgs/build-support/vm/windows/cygwin-iso/default.nix @@ -6,9 +6,12 @@ }: let - cygPkgList = fetchurl { + cygPkgList = if stdenv.is64bit then fetchurl { url = "${mirror}/x86_64/setup.ini"; - sha256 = "19vfm7zr8kcp1algmggk8vsilkccycx22mdf0ynfl6lcmp6dkfsz"; + sha256 = "142f8zyfwgi6s2djxv3z5wn0ysl94pxwa79z8rjfqz4kvnpgz120"; + } else fetchurl { + url = "${mirror}/x86/setup.ini"; + sha256 = "1v596lln2iip5h7wxjnig5rflzvqa21zzd2iyhx07zs28q5h76i9"; }; makeCygwinClosure = { packages, packageList }: let From 88ea17284eea19315137638c8fe0c720ea5e3ad9 Mon Sep 17 00:00:00 2001 From: aszlig Date: Wed, 26 Feb 2014 06:21:48 +0100 Subject: [PATCH 23/23] vmTools: Hook in runInWindowsVM function. Signed-off-by: aszlig --- pkgs/build-support/vm/default.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/build-support/vm/default.nix b/pkgs/build-support/vm/default.nix index c6b774fc06c..3dcb8aa1705 100644 --- a/pkgs/build-support/vm/default.nix +++ b/pkgs/build-support/vm/default.nix @@ -1714,5 +1714,4 @@ rec { }; }; - -} +} // import ./windows pkgs