basesetup.ps1 7.58 KB
Newer Older
1 2 3 4 5 6
#
# Script for preparing a vanilla Windows 7 installation for Emulab
#

# First, grab script arguments - I really hate that this must come first
# in a powershell script (before any other executable lines).
Kirk Webb's avatar
Kirk Webb committed
7
param([string]$actionfile, [switch]$debug, [string]$logfile, [switch]$quiet)
8 9 10 11

#
# Constants
#
12
$MAXSLEEP = 1800
13
$DEFLOGFILE="C:\Windows\Temp\basesetup.log"
14 15 16
$FAIL = "fail"
$SUCCESS = "success"
$REG_TYPES = @("String", "Dword")
Kirk Webb's avatar
Kirk Webb committed
17 18
$BASH = "C:\Cygwin\bin\bash.exe"
$BASHARGS = "-l -c"
19
$CMDTMP = "C:\Windows\Temp\_tmpout-basesetup"
20

21 22 23 24
#
# Global Variables
#
$outlog = $DEFLOGFILE
25 26 27 28 29 30 31

#
# Utility functions
#

# Log to $LOGFILE
Function log($msg) {
32
	$time = Get-Date -format g
Kirk Webb's avatar
Kirk Webb committed
33 34 35 36 37
	$outmsg = $time + ": " + $msg
	$outmsg | Out-File -encoding "ASCII" -append $outlog
	if (!$quiet) {
		$outmsg | Out-Host
	}
38 39
}

40 41 42 43 44 45
Function debug($msg) {
	if ($debug) {
		log("DEBUG: $msg")
	}
}

46
Function lograw($msg) {
47
	$msg | Out-File -encoding "ASCII" -append $outlog
48 49
}

50 51 52 53
Function logfilecontents($fname) {
	Get-Content $fname | Out-File -encoding "ASCII" -append $outlog
}

54
Function isNumeric ($x) {
55 56 57
	$x2 = 0
	$isNum = [System.Int32]::TryParse($x, [ref]$x2)
	return $isNum
58 59 60 61 62 63
}

#
# Action execution functions
#

64 65 66 67 68 69 70 71
Function log_func($cmdarr) {
	foreach ($logline in $cmdarr) {
		log($logline)
	}

	return $SUCCESS
}

72 73 74
# Create or set an existing registry value.  Create entire key path as required.
# XXX: Update to return powershell errors
Function addreg_func($cmdarr) {
75
	debug("addreg called with: $cmdarr")
76 77 78 79 80 81 82

	# set / check args
	if (!$cmdarr -or $cmdarr.count -ne 4) {
		log("addreg called with improper argument list")
		return $FAIL
	}
	$path, $vname, $type, $value = $cmdarr
83
	$regpath = "Registry::$path"
84 85 86 87
	if ($REG_TYPES -notcontains $type) {
		log("ERROR: Unknown registry value type specified: $type")
		return $FAIL
	}
88 89
	if (!(Test-Path -IsValid -Path $regpath)) {
		log("Invalid registry key specified: '$path'")
90 91 92 93
		return $FAIL
	}
	
	# Set the value, creating the full key path if necessary
94 95 96
	if (!(Test-Path -Path $regpath)) {
		if (!(New-Item -Path $regpath -Force)) {
			log("Couldn't create registry key path: '$path'")
97 98 99
			return $FAIL
		}
	}
100
	if (!(New-ItemProperty -Path $regpath -Name $vname `
101
	      -PropertyType $type -Value $value -Force)) {
102
		    log("ERROR: Could not set registry value: '$vname' to '$value'")
103 104 105 106 107 108 109
		    return $FAIL
	    }

	return $SUCCESS
}

Function reboot_func($cmdarr) {
110 111
	debug("reboot called with: $cmdarr")

112
	if ($cmdarr) {
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
		$force = $cmdarr
	}

	# Reboot ...
	if ($force) {
		"force reboot..." | Out-Host
		#Retart-Computer -Force
	} else {
		"reboot..." | Out-Host
		#Restart-Computer
	}

	return $SUCCESS
}

Function sleep_func($cmdarr) {
129 130
	debug("sleep called with: $cmdarr")

131 132 133
	if ($cmdarr.count -lt 1) {
		log("ERROR: Must supply a time to sleep!")
		return $FAIL
134 135
	}

136 137 138
	$wtime = $cmdarr[0]
	if (!(isNumeric($wtime)) -or `
	    (0 -gt $wtime) -or `
139
	    ($MAXSLEEP -lt $wtime))
140 141 142 143
	{
		log("ERROR: Invalid sleep time: $wtime")
		return $FAIL
	}
144

145 146 147
	# Sleep...
	Start-Sleep -s $wtime
	
148 149 150 151
	return $SUCCESS
}

Function runcmd_func($cmdarr) {
152 153
	debug("runcmd called with: $cmdarr")

154 155 156 157 158
	if ($cmdarr.count -lt 1) {
		log("No command given to run.")
		return $FAIL
	}

159 160 161
	$cmd, $cmdargs, $expret = $cmdarr
	
	# XXX: Implement timeout?
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	$procargs = @{
		FilePath = $cmd
		ArgumentList = $cmdargs
		RedirectStandardOutput = $CMDTMP
		NoNewWindow = $true
		PassThru = $true
		Wait = $true
	}
	$proc = $null
	try {
		$proc = Start-Process @procargs
	} catch {
		log("ERROR: failed to execute command: $cmd: $_")
		Remove-Item -Path $CMDTMP
		return $FAIL
	}
178
	
179 180
	if ($debug) {
		debug("Command output:")
181
		logfilecontents($CMDTMP)
182
	}
183 184 185
	
	Remove-Item -Path $CMDTMP

186
	# $null is a special varibale in PS - always null!
187
	if ($expret -ne $null -and $proc.ExitCode -ne $expret) {
Kirk Webb's avatar
Kirk Webb committed
188
		log("Command returned unexpected code: " + $proc.ExitCode)
189 190 191
		return $FAIL
	}

192 193 194
	return $SUCCESS
}

195 196 197 198 199 200 201 202 203 204 205 206 207
Function runcyg_func($cmdarr) {
	debug("runcyg called with: $cmdarr")

	if ($cmdarr.count -lt 1) {
		log("No command given to run.")
		return $FAIL
	}

	if (!(Test-Path $BASH)) {
		log("Bash not present - Is Cygwin installed?")
		return $FAIL
	}

208
	# Push the bash args, command + args into place as 
209 210
	# the new argument string and insert bash as the command to run.  
	# Pass this to runcmd_func.
211
	$cmdarr[1] = $BASHARGS + ' "' + $cmdarr[0] + '"'
212 213
	$cmdarr[0] = $BASH
	return runcmd_func($cmdarr)
214 215
}

Kirk Webb's avatar
Kirk Webb committed
216
Function getfile_func($cmdarr) {
217 218 219
	debug("getfile called with: $cmdarr")
	$retcode = $FAIL

Kirk Webb's avatar
Kirk Webb committed
220 221 222 223 224
	if ($cmdarr.count -lt 2) {
		log("URL and local file must be provided.")
		return $FAIL
	}

225
	$url, $filename = $cmdarr
Kirk Webb's avatar
Kirk Webb committed
226 227 228
	if (Test-Path -Path $filename) {
		log("WARNING: Overwriting existing file: $filename")
	}
229
	
230 231 232 233 234 235 236 237 238 239
	try {
		$webclient = New-Object System.Net.WebClient
		$webclient.DownloadFile($url,$filename)
		$retcode = $SUCCESS
	} catch {
		log("Error Trying to download file: $filename: $_")
		$retcode = $FAIL
	}

	return $retcode
Kirk Webb's avatar
Kirk Webb committed
240 241
}

242 243 244 245 246 247 248 249 250
Function mkdir_func($cmdarr) {
	debug("mkdir called with: $cmdarr")
	if ($cmdarr.count -ne 1) {
		log("Must specify directory to create and nothing else!")
		return $FAIL
	}

	$dir = $cmdarr[0]
	if (Test-Path -Path $dir) {
Kirk Webb's avatar
Kirk Webb committed
251
		if (Test-Path -PathType Leaf -Path $dir) {
252 253 254 255 256
			log("ERROR: Path already exists, but is not a directory!")
			return $FAIL
		} else {
			log("WARNING: Path already exists: $dir")
		}
Kirk Webb's avatar
Kirk Webb committed
257
	} elseif (!(Test-Path -IsValid -Path $dir)) {
258 259 260 261 262 263 264 265 266 267 268 269 270 271
		log("ERROR: Invalid path specified: $dir")
		return $FAIL
	} else {
		try {
			New-Item -ItemType Directory -Path $dir
		} catch {
			log("Error creating new directory: $dir: $_")
			return $FAIL
		}
	}

	return $SUCCESS
}

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
Function waitproc_func($cmdarr) {
	debug("waitproc called with: $cmdarr")

	if ($cmdarr.count -lt 2) {
		log("Must specify process name and timeout.")
		return $FAIL
	}

	$procname, $timeout, $excode = $cmdarr

	$proc = $null
	try {
		$proc = get-process -name $procname
	} catch {
		log("WARNING: Process not found: $procname")
	} 

	if ($proc) {
		if (!($proc.WaitForExit(1000 * $timeout))) {
			log("ERROR: timeout waiting for process: $procname")
			return $FAIL
		}

		if ($excode -and $proc.ExitCode -ne $excode) {
			log("ERROR: process exited with unexpected code: $proc.ExCode")
			return $FAIL
		}
	}
	
	return $SUCCESS

}

305
# Main starts here
306 307 308 309 310 311 312 313 314
if ($logfile) {
	if (Test-Path -IsValid -Path $logfile) {
		$outlog = $logfile
	} else {
		Write-Host "ERROR: Can't use logfile specified: $logfile"
		exit 1
	}
}

315 316 317 318 319 320 321 322 323
if ($actionfile -and !(Test-Path -pathtype leaf $actionfile)) {
	log("Specified action sequence file does not exist: $actionfile")
	exit 1;
} else {
	log("Executing action sequence: $actionfile")
}

# Parse and run through the actions in the input sequence
foreach ($cmdline in (Get-Content -Path $actionfile)) {
324
	if (!$cmdline -or ($cmdline.startswith("#"))) {
325 326 327 328 329 330
		continue
	}
	$cmd, $argtoks = $cmdline.split()
	$cmdarr = @()
	if ($argtoks) {
		$cmdargs = [string]::join(" ", $argtoks)
331
		#$cmdargs = [regex]::replace($cmdargs,',','`,')
332 333 334
		$cmdarr = [regex]::split($cmdargs, '\s*;;\s*')
	}
	$result = $FAIL
335
	# XXX: Maybe refactor all of this with OOP at some point.
336
	switch($cmd) {
337 338 339
		"log" {
			$result = log_func($cmdarr)
		}
340 341 342 343 344 345
		"addreg" {
			$result = addreg_func($cmdarr)
		}
		"runcmd" {
			$result = runcmd_func($cmdarr)
		}
346 347 348
		"runcyg" {
			$result = runcyg_func($cmdarr)
		}
349 350 351
		"reboot" {
			$result = reboot_func($cmdarr)
		}
352 353 354
		"sleep" {
			$result = sleep_func($cmdarr)
		}
355 356 357
		"getfile" {
			$result = getfile_func($cmdarr)
		}
358 359 360
		"mkdir" {
			$result = mkdir_func($cmdarr)
		}
361 362 363
		"waitproc" {
			$result = waitproc_func($cmdarr)
		}
364 365 366 367 368 369
		default {
			log("WARNING: Skipping unknown action: $cmd")
			$result = $SUCCESS
		}
	}
	if ($result -eq $FAIL) {
Kirk Webb's avatar
Kirk Webb committed
370
		log("ERROR: Action failed: $cmdline")
371 372 373 374
		log("Exiting!")
		exit 1
	}
}
375

Kirk Webb's avatar
Kirk Webb committed
376
log("Action sequence finished successfully." )
377
exit 0