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