at 17.09-beta 262 lines 6.9 kB view raw
1{ stdenv, writeScript, vmTools, makeInitrd 2, samba, vde2, openssh, socat, netcat-gnu, coreutils, gnugrep, gzip 3}: 4 5{ sshKey 6, qemuArgs ? [] 7, command ? "sync" 8, suspendTo ? null 9, resumeFrom ? null 10, installMode ? false 11}: 12 13with stdenv.lib; 14 15let 16 preInitScript = writeScript "preinit.sh" '' 17 #!${vmTools.initrdUtils}/bin/ash -e 18 export PATH=${vmTools.initrdUtils}/bin 19 mount -t proc none /proc 20 mount -t sysfs none /sys 21 for arg in $(cat /proc/cmdline); do 22 if [ "x''${arg#command=}" != "x$arg" ]; then 23 command="''${arg#command=}" 24 fi 25 done 26 27 for i in $(cat ${modulesClosure}/insmod-list); do 28 insmod $i 29 done 30 31 mkdir -p /dev /fs 32 33 mount -t tmpfs none /dev 34 mknod /dev/null c 1 3 35 mknod /dev/zero c 1 5 36 mknod /dev/random c 1 8 37 mknod /dev/urandom c 1 9 38 mknod /dev/tty c 5 0 39 40 ifconfig lo up 41 ifconfig eth0 up 192.168.0.2 42 43 mount -t tmpfs none /fs 44 mkdir -p /fs/nix/store /fs/xchg /fs/dev /fs/sys /fs/proc /fs/etc /fs/tmp 45 46 mount -o bind /dev /fs/dev 47 mount -t sysfs none /fs/sys 48 mount -t proc none /fs/proc 49 50 mount -t 9p \ 51 -o trans=virtio,version=9p2000.L,cache=loose \ 52 store /fs/nix/store 53 54 mount -t 9p \ 55 -o trans=virtio,version=9p2000.L,cache=loose \ 56 xchg /fs/xchg 57 58 echo root:x:0:0::/root:/bin/false > /fs/etc/passwd 59 60 set +e 61 chroot /fs $command $out 62 echo $? > /fs/xchg/in-vm-exit 63 64 poweroff -f 65 ''; 66 67 initrd = makeInitrd { 68 contents = singleton { 69 object = preInitScript; 70 symlink = "/init"; 71 }; 72 }; 73 74 loopForever = "while :; do ${coreutils}/bin/sleep 1; done"; 75 76 initScript = writeScript "init.sh" ('' 77 #!${stdenv.shell} 78 ${coreutils}/bin/cp -L "${sshKey}" /ssh.key 79 ${coreutils}/bin/chmod 600 /ssh.key 80 '' + (if installMode then '' 81 echo -n "Waiting for Windows installation to finish..." 82 while ! ${netcat-gnu}/bin/netcat -z 192.168.0.1 22; do 83 echo -n . 84 # Print a dot every 10 seconds only to shorten line length. 85 ${coreutils}/bin/sleep 10 86 done 87 ${coreutils}/bin/touch /xchg/waiting_done 88 echo " success." 89 # Loop forever, because this VM is going to be killed. 90 ${loopForever} 91 '' else '' 92 ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private \ 93 /var/lib/samba /var/log /var/run 94 ${coreutils}/bin/cat > /etc/samba/smb.conf <<CONFIG 95 [global] 96 security = user 97 map to guest = Bad User 98 guest account = root 99 workgroup = cygwin 100 netbios name = controller 101 server string = %h 102 log level = 1 103 max log size = 1000 104 log file = /var/log/samba.log 105 106 [nixstore] 107 path = /nix/store 108 writable = yes 109 guest ok = yes 110 111 [xchg] 112 path = /xchg 113 writable = yes 114 guest ok = yes 115 CONFIG 116 117 ${samba}/sbin/nmbd -D 118 ${samba}/sbin/smbd -D 119 120 echo -n "Waiting for Windows VM to become available..." 121 while ! ${netcat-gnu}/bin/netcat -z 192.168.0.1 22; do 122 echo -n . 123 ${coreutils}/bin/sleep 1 124 done 125 ${coreutils}/bin/touch /xchg/waiting_done 126 echo " success." 127 128 ${openssh}/bin/ssh \ 129 -o UserKnownHostsFile=/dev/null \ 130 -o StrictHostKeyChecking=no \ 131 -i /ssh.key \ 132 -l Administrator \ 133 192.168.0.1 -- ${lib.escapeShellArg command} 134 '') + optionalString (suspendTo != null) '' 135 ${coreutils}/bin/touch /xchg/suspend_now 136 ${loopForever} 137 ''); 138 139 kernelAppend = concatStringsSep " " [ 140 "panic=1" 141 "loglevel=4" 142 "console=tty1" 143 "console=ttyS0" 144 "command=${initScript}" 145 ]; 146 147 controllerQemuArgs = concatStringsSep " " (maybeKvm64 ++ [ 148 "-pidfile $CTRLVM_PIDFILE" 149 "-nographic" 150 "-no-reboot" 151 "-virtfs local,path=/nix/store,security_model=none,mount_tag=store" 152 "-virtfs local,path=$XCHG_DIR,security_model=none,mount_tag=xchg" 153 "-kernel ${modulesClosure.kernel}/bzImage" 154 "-initrd ${initrd}/initrd" 155 "-append \"${kernelAppend}\"" 156 "-net nic,vlan=0,macaddr=52:54:00:12:01:02,model=virtio" 157 "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" 158 ]); 159 160 maybeKvm64 = optional (stdenv.system == "x86_64-linux") "-cpu kvm64"; 161 162 cygwinQemuArgs = concatStringsSep " " (maybeKvm64 ++ [ 163 "-monitor unix:$MONITOR_SOCKET,server,nowait" 164 "-pidfile $WINVM_PIDFILE" 165 "-nographic" 166 "-net nic,vlan=0,macaddr=52:54:00:12:01:01" 167 "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET" 168 "-rtc base=2010-01-01,clock=vm" 169 ] ++ qemuArgs ++ optionals (resumeFrom != null) [ 170 "-incoming 'exec: ${gzip}/bin/gzip -c -d \"${resumeFrom}\"'" 171 ]); 172 173 modulesClosure = overrideDerivation vmTools.modulesClosure (o: { 174 rootModules = o.rootModules ++ singleton "virtio_net"; 175 }); 176 177 preVM = '' 178 (set; declare -p) > saved-env 179 XCHG_DIR="$(${coreutils}/bin/mktemp -d nix-vm.XXXXXXXXXX --tmpdir)" 180 ${coreutils}/bin/mv saved-env "$XCHG_DIR/" 181 182 eval "$preVM" 183 184 QEMU_VDE_SOCKET="$(pwd)/vde.ctl" 185 MONITOR_SOCKET="$(pwd)/monitor" 186 WINVM_PIDFILE="$(pwd)/winvm.pid" 187 CTRLVM_PIDFILE="$(pwd)/ctrlvm.pid" 188 ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" --dirmode 0700 & 189 echo 'alive?' | ${socat}/bin/socat - \ 190 UNIX-CONNECT:$QEMU_VDE_SOCKET/ctl,retry=20 191 ''; 192 193 vmExec = '' 194 ${vmTools.qemuProg} ${controllerQemuArgs} & 195 ${vmTools.qemuProg} ${cygwinQemuArgs} & 196 echo -n "Waiting for VMs to start up..." 197 timeout=60 198 while ! test -e "$WINVM_PIDFILE" -a -e "$CTRLVM_PIDFILE"; do 199 timeout=$(($timeout - 1)) 200 echo -n . 201 if test $timeout -le 0; then 202 echo " timed out." 203 exit 1 204 fi 205 ${coreutils}/bin/sleep 1 206 done 207 echo " done." 208 ''; 209 210 checkDropOut = '' 211 if ! test -e "$XCHG_DIR/waiting_done" && 212 ! kill -0 $(< "$WINVM_PIDFILE"); then 213 echo "Windows VM has dropped out early, bailing out!" >&2 214 exit 1 215 fi 216 ''; 217 218 toMonitor = "${socat}/bin/socat - UNIX-CONNECT:$MONITOR_SOCKET"; 219 220 postVM = if suspendTo != null then '' 221 while ! test -e "$XCHG_DIR/suspend_now"; do 222 ${checkDropOut} 223 ${coreutils}/bin/sleep 1 224 done 225 ${toMonitor} <<CMD 226 stop 227 migrate_set_speed 4095m 228 migrate "exec:${gzip}/bin/gzip -c > '${suspendTo}'" 229 CMD 230 echo -n "Waiting for memory dump to finish..." 231 while ! echo info migrate | ${toMonitor} | \ 232 ${gnugrep}/bin/grep -qi '^migration *status: *complete'; do 233 ${coreutils}/bin/sleep 1 234 echo -n . 235 done 236 echo " done." 237 echo quit | ${toMonitor} 238 wait $(< "$WINVM_PIDFILE") 239 eval "$postVM" 240 exit 0 241 '' else if installMode then '' 242 wait $(< "$WINVM_PIDFILE") 243 eval "$postVM" 244 exit 0 245 '' else '' 246 while kill -0 $(< "$CTRLVM_PIDFILE"); do 247 ${checkDropOut} 248 done 249 if ! test -e "$XCHG_DIR/in-vm-exit"; then 250 echo "Virtual machine didn't produce an exit code." 251 exit 1 252 fi 253 eval "$postVM" 254 exit $(< "$XCHG_DIR/in-vm-exit") 255 ''; 256 257in writeScript "run-cygwin-vm.sh" '' 258 #!${stdenv.shell} -e 259 ${preVM} 260 ${vmExec} 261 ${postVM} 262''