Browse code

Windows: Wait for OOBE to prevent crashing during host update

Signed-off-by: Darren Stahl <darst@microsoft.com>

Darren Stahl authored on 2017/02/02 03:52:16
Showing 6 changed files
... ...
@@ -260,6 +260,9 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) {
260 260
 		<-stopc // wait for daemonCli.start() to return
261 261
 	})
262 262
 
263
+	// Notify that the API is active, but before daemon is set up.
264
+	preNotifySystem()
265
+
263 266
 	d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote)
264 267
 	if err != nil {
265 268
 		return fmt.Errorf("Error starting daemon: %v", err)
... ...
@@ -1,5 +1,9 @@
1 1
 package main
2 2
 
3
+// preNotifySystem sends a message to the host when the API is active, but before the daemon is
4
+func preNotifySystem() {
5
+}
6
+
3 7
 // notifySystem sends a message to the host when the server is ready to be used
4 8
 func notifySystem() {
5 9
 }
... ...
@@ -4,6 +4,10 @@ package main
4 4
 
5 5
 import systemdDaemon "github.com/coreos/go-systemd/daemon"
6 6
 
7
+// preNotifySystem sends a message to the host when the API is active, but before the daemon is
8
+func preNotifySystem() {
9
+}
10
+
7 11
 // notifySystem sends a message to the host when the server is ready to be used
8 12
 func notifySystem() {
9 13
 	// Tell the init daemon we are accepting requests
... ...
@@ -46,6 +46,10 @@ func getDaemonConfDir(_ string) string {
46 46
 func (cli *DaemonCli) setupConfigReloadTrap() {
47 47
 }
48 48
 
49
+// preNotifySystem sends a message to the host when the API is active, but before the daemon is
50
+func preNotifySystem() {
51
+}
52
+
49 53
 // notifySystem sends a message to the host when the server is ready to be used
50 54
 func notifySystem() {
51 55
 }
... ...
@@ -29,8 +29,10 @@ func getDaemonConfDir(root string) string {
29 29
 	return filepath.Join(root, `\config`)
30 30
 }
31 31
 
32
-// notifySystem sends a message to the host when the server is ready to be used
33
-func notifySystem() {
32
+// preNotifySystem sends a message to the host when the API is active, but before the daemon is
33
+func preNotifySystem() {
34
+	// start the service now to prevent timeouts waiting for daemon to start
35
+	// but still (eventually) complete all requests that are sent after this
34 36
 	if service != nil {
35 37
 		err := service.started()
36 38
 		if err != nil {
... ...
@@ -39,6 +41,10 @@ func notifySystem() {
39 39
 	}
40 40
 }
41 41
 
42
+// notifySystem sends a message to the host when the server is ready to be used
43
+func notifySystem() {
44
+}
45
+
42 46
 // notifyShutdown is called after the daemon shuts down but before the process exits.
43 47
 func notifyShutdown(err error) {
44 48
 	if service != nil {
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"fmt"
5 5
 	"os"
6 6
 	"strings"
7
+	"syscall"
8
+	"unsafe"
7 9
 
8 10
 	"github.com/Microsoft/hcsshim"
9 11
 	"github.com/Sirupsen/logrus"
... ...
@@ -35,6 +37,8 @@ const (
35 35
 	windowsMinCPUPercent = 1
36 36
 	windowsMaxCPUPercent = 100
37 37
 	windowsMinCPUCount   = 1
38
+
39
+	errInvalidState = syscall.Errno(0x139F)
38 40
 )
39 41
 
40 42
 func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) {
... ...
@@ -229,7 +233,8 @@ func checkSystem() error {
229 229
 	if vmcompute.Load() != nil {
230 230
 		return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.")
231 231
 	}
232
-	return nil
232
+
233
+	return waitOOBEComplete()
233 234
 }
234 235
 
235 236
 // configureKernelSecuritySupport configures and validate security support for the kernel
... ...
@@ -601,3 +606,35 @@ func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
601 601
 func (daemon *Daemon) setupSeccompProfile() error {
602 602
 	return nil
603 603
 }
604
+
605
+func waitOOBEComplete() error {
606
+	kernel32 := windows.NewLazySystemDLL("kernel32.dll")
607
+	registerWaitUntilOOBECompleted := kernel32.NewProc("RegisterWaitUntilOOBECompleted")
608
+	unregisterWaitUntilOOBECompleted := kernel32.NewProc("UnregisterWaitUntilOOBECompleted")
609
+
610
+	callbackChan := make(chan struct{})
611
+	callbackFunc := func(uintptr) uintptr {
612
+		close(callbackChan)
613
+		return 0
614
+	}
615
+	callbackFuncPtr := syscall.NewCallback(callbackFunc)
616
+
617
+	var callbackHandle syscall.Handle
618
+	ret, _, err := registerWaitUntilOOBECompleted.Call(callbackFuncPtr, 0, uintptr(unsafe.Pointer(&callbackHandle)))
619
+	if ret == 0 {
620
+		if err == errInvalidState {
621
+			return nil
622
+		}
623
+		return fmt.Errorf("failed to register OOBEComplete callback. Error: %v", err)
624
+	}
625
+
626
+	// Wait for the callback when OOBE is finished
627
+	<-callbackChan
628
+
629
+	ret, _, err = unregisterWaitUntilOOBECompleted.Call(uintptr(callbackHandle))
630
+	if ret == 0 {
631
+		return fmt.Errorf("failed to unregister OOBEComplete callback. Error: %v", err)
632
+	}
633
+
634
+	return nil
635
+}