Browse code

Merge pull request #31054 from darrenstahlmsft/WaitOOBE

Windows: Wait for OOBE to prevent crashing during host update

Brian Goff authored on 2017/02/17 01:18:25
Showing 6 changed files
... ...
@@ -261,6 +261,9 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) {
261 261
 		<-stopc // wait for daemonCli.start() to return
262 262
 	})
263 263
 
264
+	// Notify that the API is active, but before daemon is set up.
265
+	preNotifySystem()
266
+
264 267
 	d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote)
265 268
 	if err != nil {
266 269
 		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 {
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"os"
6 6
 	"path/filepath"
7 7
 	"strings"
8
+	"syscall"
9
+	"unsafe"
8 10
 
9 11
 	"github.com/Microsoft/hcsshim"
10 12
 	"github.com/Sirupsen/logrus"
... ...
@@ -37,6 +39,8 @@ const (
37 37
 	windowsMinCPUPercent = 1
38 38
 	windowsMaxCPUPercent = 100
39 39
 	windowsMinCPUCount   = 1
40
+
41
+	errInvalidState = syscall.Errno(0x139F)
40 42
 )
41 43
 
42 44
 // Windows has no concept of an execution state directory. So use config.Root here.
... ...
@@ -236,7 +240,8 @@ func checkSystem() error {
236 236
 	if vmcompute.Load() != nil {
237 237
 		return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.")
238 238
 	}
239
-	return nil
239
+
240
+	return waitOOBEComplete()
240 241
 }
241 242
 
242 243
 // configureKernelSecuritySupport configures and validate security support for the kernel
... ...
@@ -608,3 +613,35 @@ func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
608 608
 func (daemon *Daemon) setupSeccompProfile() error {
609 609
 	return nil
610 610
 }
611
+
612
+func waitOOBEComplete() error {
613
+	kernel32 := windows.NewLazySystemDLL("kernel32.dll")
614
+	registerWaitUntilOOBECompleted := kernel32.NewProc("RegisterWaitUntilOOBECompleted")
615
+	unregisterWaitUntilOOBECompleted := kernel32.NewProc("UnregisterWaitUntilOOBECompleted")
616
+
617
+	callbackChan := make(chan struct{})
618
+	callbackFunc := func(uintptr) uintptr {
619
+		close(callbackChan)
620
+		return 0
621
+	}
622
+	callbackFuncPtr := syscall.NewCallback(callbackFunc)
623
+
624
+	var callbackHandle syscall.Handle
625
+	ret, _, err := registerWaitUntilOOBECompleted.Call(callbackFuncPtr, 0, uintptr(unsafe.Pointer(&callbackHandle)))
626
+	if ret == 0 {
627
+		if err == errInvalidState {
628
+			return nil
629
+		}
630
+		return fmt.Errorf("failed to register OOBEComplete callback. Error: %v", err)
631
+	}
632
+
633
+	// Wait for the callback when OOBE is finished
634
+	<-callbackChan
635
+
636
+	ret, _, err = unregisterWaitUntilOOBECompleted.Call(uintptr(callbackHandle))
637
+	if ret == 0 {
638
+		return fmt.Errorf("failed to unregister OOBEComplete callback. Error: %v", err)
639
+	}
640
+
641
+	return nil
642
+}