forked from xdissent/ievms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ievms.sh
executable file
·486 lines (403 loc) · 15.5 KB
/
ievms.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
#!/usr/bin/env bash
# Caution is a virtue.
set -o nounset
set -o errtrace
set -o errexit
set -o pipefail
# ## Global Variables
# The ievms version.
ievms_version="0.2.1"
# Options passed to each `curl` command.
curl_opts=${CURL_OPTS:-""}
# Reuse XP virtual machines for IE versions that are supported.
reuse_xp=${REUSE_XP:-"yes"}
# Reuse Win7 virtual machines for IE versions that are supported.
reuse_win7=${REUSE_WIN7:-"yes"}
# Timeout interval to wait between checks for various states.
sleep_wait="5"
# Store the original `cwd`.
orig_cwd=`pwd`
# The VM user to use for guest control.
guest_user="IEUser"
# The VM user password to use for guest control.
guest_pass="Passw0rd!"
# ## Utilities
# Print a message to the console.
log() { printf "$*\n" ; return $? ; }
# Print an error message to the console and bail out of the script.
fail() { log "\nERROR: $*\n" ; exit 1 ; }
check_md5() {
local md5
case $kernel in
Darwin) md5=`md5 "${1}" | rev | cut -c-32 | rev` ;;
Linux) md5=`md5sum "${1}" | cut -c-32` ;;
esac
if [ "${md5}" != "${2}" ]
then
log "MD5 check failed for ${1} (wanted ${2}, got ${md5})"
return 1
fi
log "MD5 check succeeded for ${1}"
}
# Download a URL to a local file. Accepts a name, URL and file.
download() { # name url path md5
local attempt=${5:-"0"}
local max=${6:-"3"}
let attempt+=1
if [[ -f "${3}" ]]
then
log "Found ${1} at ${3} - skipping download"
check_md5 "${3}" "${4}" && return 0
log "Check failed - redownloading ${1}"
rm -f "${3}"
fi
log "Downloading ${1} from ${2} to ${3} (attempt ${attempt} of ${max})"
curl ${curl_opts} -L "${2}" -o "${3}" || fail "Failed to download ${2} to ${ievms_home}/${3} using 'curl', error code ($?)"
check_md5 "${3}" "${4}" && return 0
if [ "${attempt}" == "${max}" ]
then
echo "Failed to download ${2} to ${ievms_home}/${3} (attempt ${attempt} of ${max})"
return 1
fi
log "Redownloading ${1}"
download "${1}" "${2}" "${3}" "${4}" "${attempt}" "${max}"
}
# ## General Setup
# Create the ievms home folder and `cd` into it. The `INSTALL_PATH` env variable
# is used to determine the full path. The home folder is then added to `PATH`.
create_home() {
local def_ievms_home="${HOME}/.ievms"
ievms_home=${INSTALL_PATH:-$def_ievms_home}
mkdir -p "${ievms_home}"
cd "${ievms_home}"
PATH="${PATH}:${ievms_home}"
# Move ovas and zips from a very old installation into place.
mv -f ./ova/IE*/IE*.{ova,zip} "${ievms_home}/" 2>/dev/null || true
}
# Check for a supported host system (Linux/OS X).
check_system() {
kernel=`uname -s`
case $kernel in
Darwin|Linux) ;;
*) fail "Sorry, $kernel is not supported." ;;
esac
}
# Ensure VirtualBox is installed and `VBoxManage` is on the `PATH`.
check_virtualbox() {
log "Checking for VirtualBox"
hash VBoxManage 2>&- || fail "VirtualBox command line utilities are not installed, please (re)install! (http://virtualbox.org)"
}
# Determine the VirtualBox version details, querying the download page to ensure
# validity.
check_version() {
local version=`VBoxManage -v`
major_minor_release="${version%%[-_r]*}"
local major_minor="${version%.*}"
local dl_page=`curl ${curl_opts} -L "http://download.virtualbox.org/virtualbox/" 2>/dev/null`
if [[ "$version" == *"kernel module is not loaded"* ]]; then
fail "$version"
fi
for (( release="${major_minor_release#*.*.}"; release >= 0; release-- ))
do
major_minor_release="${major_minor}.${release}"
if echo $dl_page | grep "${major_minor_release}/" &>/dev/null
then
log "Virtualbox version ${major_minor_release} found."
break
else
log "Virtualbox version ${major_minor_release} not found, skipping."
fi
done
}
# Check for the VirtualBox Extension Pack and install if not found.
check_ext_pack() {
log "Checking for Oracle VM VirtualBox Extension Pack"
if ! VBoxManage list extpacks | grep "Oracle VM VirtualBox Extension Pack"
then
check_version
local archive="Oracle_VM_VirtualBox_Extension_Pack-${major_minor_release}.vbox-extpack"
local url="http://download.virtualbox.org/virtualbox/${major_minor_release}/${archive}"
local md5s="https://www.virtualbox.org/download/hashes/${major_minor_release}/MD5SUMS"
local md5=`curl ${curl_opts} -L "${md5s}" | grep "${archive}" | cut -c-32`
download "Oracle VM VirtualBox Extension Pack" "${url}" "${archive}" "${md5}"
log "Installing Oracle VM VirtualBox Extension Pack from ${ievms_home}/${archive}"
VBoxManage extpack install "${archive}" || fail "Failed to install Oracle VM VirtualBox Extension Pack from ${ievms_home}/${archive}, error code ($?)"
fi
}
# Download and install `unar` from Google Code.
install_unar() {
local url="http://theunarchiver.googlecode.com/files/unar1.5.zip"
local archive=`basename "${url}"`
download "unar" "${url}" "${archive}" "fbf544d1332c481d7d0f4e3433fbe53b"
unzip "${archive}" || fail "Failed to extract ${ievms_home}/${archive} to ${ievms_home}/, unzip command returned error code $?"
hash unar 2>&- || fail "Could not find unar in ${ievms_home}"
}
# Check for the `unar` command, downloading and installing it if not found.
check_unar() {
if [ "${kernel}" == "Darwin" ]
then
hash unar 2>&- || install_unar
else
hash unar 2>&- || fail "Linux support requires unar (sudo apt-get install for Ubuntu/Debian)"
fi
}
# Pause execution until the virtual machine with a given name shuts down.
wait_for_shutdown() {
while true ; do
log "Waiting for ${1} to shutdown..."
sleep "${sleep_wait}"
VBoxManage showvminfo "${1}" | grep "State:" | grep -q "powered off" && return 0 || true
done
}
# Pause execution until guest control is available for a virtual machine.
wait_for_guestcontrol() {
while true ; do
log "Waiting for ${1} to be available for guestcontrol..."
sleep "${sleep_wait}"
VBoxManage showvminfo "${1}" | grep 'Additions run level:' | grep -q "3" && return 0 || true
done
}
# Find or download the ievms control ISO.
find_iso() {
local url="https://dl.dropboxusercontent.com/u/463624/ievms-control-${ievms_version}.iso"
local dev_iso="${orig_cwd}/ievms-control.iso" # Use local iso if in ievms dev root
if [[ -f "${dev_iso}" ]]
then
iso=$dev_iso
else
iso="${ievms_home}/ievms-control-${ievms_version}.iso"
download "ievms control ISO" "${url}" "${iso}" "6699cb421fc2f56e854fd3f5e143e84c"
fi
}
# Attach a dvd image to the virtual machine.
attach() {
log "Attaching ${3}"
VBoxManage storageattach "${1}" --storagectl "IDE Controller" --port 1 \
--device 0 --type dvddrive --medium "${2}"
}
# Eject the dvd image from the virtual machine.
eject() {
log "Ejecting ${2}"
VBoxManage modifyvm "${1}" --dvd none
}
# Boot the virtual machine with the control ISO in the dvd drive then wait for
# it to do its magic and shut down. For XP images, the "magic" is simply
# enabling guest control without a password. For other images, it installs
# a batch file that runs on first boot to install guest additions and activate
# the OS if possible.
boot_ievms() {
find_iso
attach "${1}" "${iso}" "ievms control ISO"
start_vm "${1}"
wait_for_shutdown "${1}"
eject "${1}" "ievms control ISO"
}
# Boot the virtual machine with guest additions in the dvd drive. After running
# `boot_ievms`, the next boot will attempt automatically install guest additions
# if present in the drive. It will shut itself down after installation.
boot_auto_ga() {
boot_ievms "${1}"
attach "${1}" "additions" "Guest Additions"
start_vm "${1}"
wait_for_shutdown "${1}"
eject "${1}" "Guest Additions"
}
# Start a virtual machine in headless mode.
start_vm() {
log "Starting VM ${1}"
VBoxManage startvm "${1}" --type headless
}
# Copy a file to the virtual machine. An optional password will be used
# if given.
copy_to_vm() {
log "Copying ${2} to ${3}"
VBoxManage guestcontrol "${1}" cp "${ievms_home}/${2}" "${3}" \
--username "${guest_user}" --password "${guest_pass}"
}
# Execute a command with arguments on a virtual machine.
guest_control_exec() {
local vm="${1}"
local image="${2}"
shift; shift
VBoxManage guestcontrol "${vm}" exec --image "${image}" \
--username "${guest_user}" --password "${guest_pass}" \
--wait-exit -- "$@"
}
# Start an XP virtual machine and set the password for the guest user.
set_xp_password() {
start_vm "${1}"
wait_for_guestcontrol "${1}"
log "Setting ${guest_user} password"
VBoxManage guestcontrol "${1}" exec --image "net.exe" --username \
Administrator --password "${guest_pass}" --wait-exit -- \
user "${guest_user}" "${guest_pass}"
log "Setting auto logon password"
VBoxManage guestcontrol "${1}" exec --image "reg.exe" --username \
Administrator --password "${guest_pass}" --wait-exit -- add \
"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" \
/f /v DefaultPassword /t REG_SZ /d "${guest_pass}"
log "Enabling auto admin logon"
VBoxManage guestcontrol "${1}" exec --image "reg.exe" --username \
Administrator --password "${guest_pass}" --wait-exit -- add \
"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" \
/f /v AutoAdminLogon /t REG_SZ /d 1
}
# Shutdown an XP virtual machine and wait for it to power off.
shutdown_xp() {
log "Shutting down ${1}"
guest_control_exec "${1}" "shutdown.exe" /s /f /t 0
wait_for_shutdown "${1}"
}
# Install an alternative version of IE in an XP virtual machine. Downloads the
# installer, copies it to the vm, then runs it before shutting down.
install_ie_xp() { # vm url md5
local src=`basename "${2}"`
local dest="/Documents and Settings/${guest_user}/Desktop/${src}"
download "${src}" "${2}" "${src}" "${3}"
copy_to_vm "${1}" "${src}" "${dest}"
log "Installing IE" # Always "fails"
guest_control_exec "${1}" "${dest}" /passive /norestart || true
shutdown_xp "${1}"
}
# Install an alternative version of IE in a Win7 virtual machine. Downloads the
# installer, copies it to the vm, then runs it before shutting down.
install_ie_win7() { # vm url md5
local src=`basename "${2}"`
local dest="/Users/${guest_user}/Desktop/${src}"
download "${src}" "${2}" "${src}" "${3}"
start_vm "${1}"
wait_for_guestcontrol "${1}"
copy_to_vm "${1}" "${src}" "${dest}"
log "Installing IE"
guest_control_exec "${1}" "cmd.exe" /c \
"echo ${dest} /passive /norestart >C:\\Users\\${guest_user}\\ievms.bat"
guest_control_exec "${1}" "cmd.exe" /c \
"echo shutdown.exe /s /f /t 0 >>C:\\Users\\${guest_user}\\ievms.bat"
guest_control_exec "${1}" "schtasks.exe" /run /tn ievms
wait_for_shutdown "${1}"
}
# Build an ievms virtual machine given the IE version desired.
build_ievm() {
unset archive
unset unit
case $1 in
6|7|8)
os="WinXP"
if [ "${reuse_xp}" != "yes" ]
then
if [ "$1" == "6" ]; then unit="10"; fi
if [ "$1" == "7" ]; then os="Vista"; fi
if [ "$1" == "8" ]; then os="Win7"; fi
else
archive="IE6_WinXP.zip"
unit="10"
fi
;;
9) os="Win7" ;;
10|11)
if [ "${reuse_win7}" != "yes" ]
then
if [ "$1" == "11" ]; then fail "IE11 is only available if REUSE_WIN7 is set"; fi
os="Win8"
else
os="Win7"
archive="IE9_Win7.zip"
fi
;;
*) fail "Invalid IE version: ${1}" ;;
esac
local vm="IE${1} - ${os}"
local def_archive="${vm/ - /_}.zip"
archive=${archive:-$def_archive}
unit=${unit:-"11"}
local ova=`basename "${archive/_/ - }" .zip`.ova
local url="http://virtualization.modern.ie/vhd/IEKitV1_Final/VirtualBox/OSX/${archive}"
local md5
case $archive in
IE6_WinXP.zip) md5="3d5b7d980296d048de008d28305ca224" ;;
IE7_Vista.zip) md5="d5269b2220f5c7fb9786dad513f2c05a" ;;
IE8_Win7.zip) md5="21b0aad3d66dac7f88635aa2318a3a55" ;;
IE9_Win7.zip) md5="58d201fe7dc7e890ad645412264f2a2c" ;;
IE10_Win8.zip) md5="cc4e2f4b195e1b1e24e2ce6c7a6f149c" ;;
esac
log "Checking for existing OVA at ${ievms_home}/${ova}"
if [[ ! -f "${ova}" ]]
then
download "OVA ZIP" "${url}" "${archive}" "${md5}"
log "Extracting OVA from ${ievms_home}/${archive}"
unar "${archive}" || fail "Failed to extract ${archive} to ${ievms_home}/${ova}, unar command returned error code $?"
fi
log "Checking for existing ${vm} VM"
if ! VBoxManage showvminfo "${vm}" >/dev/null 2>/dev/null
then
local disk_path="${ievms_home}/${vm}-disk1.vmdk"
log "Creating ${vm} VM (disk: ${disk_path})"
VBoxManage import "${ova}" --vsys 0 --vmname "${vm}" --unit "${unit}" --disk "${disk_path}"
log "Building ${vm} VM"
declare -F "build_ievm_ie${1}" && "build_ievm_ie${1}"
log "Tagging VM with ievms version"
VBoxManage setextradata "${vm}" "ievms" "{\"version\":\"${ievms_version}\"}"
log "Creating clean snapshot"
VBoxManage snapshot "${vm}" take clean --description "The initial VM state"
fi
}
# Build the IE6 virtual machine.
build_ievm_ie6() {
set_xp_password "IE6 - WinXP"
shutdown_xp "IE6 - WinXP"
}
# Build the IE7 virtual machine, reusing the XP VM if requested (the default).
build_ievm_ie7() {
if [ "${reuse_xp}" != "yes" ]
then
boot_auto_ga "IE7 - Vista"
else
set_xp_password "IE7 - WinXP"
install_ie_xp "IE7 - WinXP" "http://download.microsoft.com/download/3/8/8/38889dc1-848c-4bf2-8335-86c573ad86d9/IE7-WindowsXP-x86-enu.exe" "ea16789f6fc1d2523f704e8f9afbe906"
fi
}
# Build the IE8 virtual machine, reusing the XP VM if requested (the default).
build_ievm_ie8() {
if [ "${reuse_xp}" != "yes" ]
then
boot_auto_ga "IE8 - Win7"
else
set_xp_password "IE8 - WinXP"
install_ie_xp "IE8 - WinXP" "http://download.microsoft.com/download/C/C/0/CC0BD555-33DD-411E-936B-73AC6F95AE11/IE8-WindowsXP-x86-ENU.exe" "616c2e8b12aaa349cd3acb38bf581700"
fi
}
# Build the IE9 virtual machine.
build_ievm_ie9() {
boot_auto_ga "IE9 - Win7"
}
# Build the IE10 virtual machine, reusing the Win7 VM if requested (the default).
build_ievm_ie10() {
if [ "${reuse_win7}" != "yes" ]
then
boot_auto_ga "IE10 - Win8"
else
boot_auto_ga "IE10 - Win7"
install_ie_win7 "IE10 - Win7" "http://download.microsoft.com/download/8/A/C/8AC7C482-BC74-492E-B978-7ED04900CEDE/IE10-Windows6.1-x86-en-us.exe" "0f14b2de0b3cef611b9c1424049e996b"
fi
}
# Build the IE11 virtual machine, reusing the Win7 VM always.
build_ievm_ie11() {
boot_auto_ga "IE11 - Win7"
install_ie_win7 "IE11 - Win7" "http://download.microsoft.com/download/9/2/F/92FC119C-3BCD-476C-B425-038A39625558/IE11-Windows6.1-x86-en-us.exe" "7d3479b9007f3c0670940c1b10a3615f"
}
# ## Main Entry Point
# Run through all checks to get the host ready for installation.
check_system
create_home
check_virtualbox
check_ext_pack
check_unar
# Install each requested virtual machine sequentially.
all_versions="6 7 8 9 10 11"
for ver in ${IEVMS_VERSIONS:-$all_versions}
do
log "Building IE${ver} VM"
build_ievm $ver
done
# We made it!
log "Done!"