{ stdenv, writeScript, vmTools, makeInitrd , samba, vde2, openssh, socat, netcat, coreutils, gnugrep, gzip }: { sshKey , qemuArgs ? [] , command ? "sync" , suspendTo ? null , resumeFrom ? null , installMode ? false }: with stdenv.lib; 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 /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 mknod /dev/urandom c 1 9 mknod /dev/tty c 5 0 ifconfig lo up ifconfig eth0 up 192.168.0.2 mount -t tmpfs none /fs mkdir -p /fs/nix/store /fs/xchg /fs/dev /fs/sys /fs/proc /fs/etc /fs/tmp mount -o bind /dev /fs/dev mount -t sysfs none /fs/sys mount -t proc none /fs/proc mount -t 9p \ -o trans=virtio,version=9p2000.L,msize=262144,cache=loose \ store /fs/nix/store mount -t 9p \ -o trans=virtio,version=9p2000.L,msize=262144,cache=loose \ xchg /fs/xchg echo root:x:0:0::/root:/bin/false > /fs/etc/passwd set +e chroot /fs $command $out echo $? > /fs/xchg/in-vm-exit poweroff -f ''; initrd = makeInitrd { contents = singleton { object = preInitScript; symlink = "/init"; }; }; shellEscape = x: "'${replaceChars ["'"] [("'\\'" + "'")] x}'"; loopForever = "while :; do ${coreutils}/bin/sleep 1; done"; 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 ${coreutils}/bin/touch /xchg/waiting_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 /var/log /var/run ${coreutils}/bin/cat > /etc/samba/smb.conf < 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" WINVM_PIDFILE="$(pwd)/winvm.pid" CTRLVM_PIDFILE="$(pwd)/ctrlvm.pid" ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" & echo 'alive?' | ${socat}/bin/socat - \ UNIX-CONNECT:$QEMU_VDE_SOCKET/ctl,retry=20 ''; vmExec = '' ${vmTools.qemuProg} ${controllerQemuArgs} & ${vmTools.qemuProg} ${cygwinQemuArgs} & echo -n "Waiting for VMs to start up..." timeout=60 while ! test -e "$WINVM_PIDFILE" -a -e "$CTRLVM_PIDFILE"; do timeout=$(($timeout - 1)) echo -n . if test $timeout -le 0; then echo " timed out." exit 1 fi ${coreutils}/bin/sleep 1 done echo " done." ''; checkDropOut = '' if ! test -e "$XCHG_DIR/waiting_done" && ! kill -0 $(< "$WINVM_PIDFILE"); then echo "Windows VM has dropped out early, bailing out!" >&2 exit 1 fi ''; toMonitor = "${socat}/bin/socat - UNIX-CONNECT:$MONITOR_SOCKET"; postVM = if suspendTo != null then '' while ! test -e "$XCHG_DIR/suspend_now"; do ${checkDropOut} ${coreutils}/bin/sleep 1 done ${toMonitor} < '${suspendTo}'" CMD echo -n "Waiting for memory dump to finish..." while ! echo info migrate | ${toMonitor} | \ ${gnugrep}/bin/grep -qi '^migration *status: *complete'; do ${coreutils}/bin/sleep 1 echo -n . done echo " done." echo quit | ${toMonitor} wait $(< "$WINVM_PIDFILE") eval "$postVM" exit 0 '' else if installMode then '' wait $(< "$WINVM_PIDFILE") eval "$postVM" exit 0 '' else '' while kill -0 $(< "$CTRLVM_PIDFILE"); do ${checkDropOut} done if ! test -e "$XCHG_DIR/in-vm-exit"; then echo "Virtual machine didn't produce an exit code." exit 1 fi eval "$postVM" exit $(< "$XCHG_DIR/in-vm-exit") ''; in writeScript "run-cygwin-vm.sh" '' #!${stdenv.shell} -e ${preVM} ${vmExec} ${postVM} ''