From e128a65685dafd640e32118eb1903581489f7e31 Mon Sep 17 00:00:00 2001 From: Darren Stahl Date: Wed, 1 Feb 2017 10:52:16 -0800 Subject: [PATCH] Windows: Wait for OOBE to prevent crashing during host update Signed-off-by: Darren Stahl --- cmd/dockerd/daemon.go | 3 +++ cmd/dockerd/daemon_freebsd.go | 4 ++++ cmd/dockerd/daemon_linux.go | 4 ++++ cmd/dockerd/daemon_solaris.go | 4 ++++ cmd/dockerd/daemon_windows.go | 10 +++++++-- daemon/daemon_windows.go | 39 ++++++++++++++++++++++++++++++++++- 6 files changed, 61 insertions(+), 3 deletions(-) diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index ddc160d704..eda9814120 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -260,6 +260,9 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) { <-stopc // wait for daemonCli.start() to return }) + // Notify that the API is active, but before daemon is set up. + preNotifySystem() + d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) if err != nil { return fmt.Errorf("Error starting daemon: %v", err) diff --git a/cmd/dockerd/daemon_freebsd.go b/cmd/dockerd/daemon_freebsd.go index 623aaf4b09..6d013b8103 100644 --- a/cmd/dockerd/daemon_freebsd.go +++ b/cmd/dockerd/daemon_freebsd.go @@ -1,5 +1,9 @@ package main +// preNotifySystem sends a message to the host when the API is active, but before the daemon is +func preNotifySystem() { +} + // notifySystem sends a message to the host when the server is ready to be used func notifySystem() { } diff --git a/cmd/dockerd/daemon_linux.go b/cmd/dockerd/daemon_linux.go index a556daa187..a909ee4fbd 100644 --- a/cmd/dockerd/daemon_linux.go +++ b/cmd/dockerd/daemon_linux.go @@ -4,6 +4,10 @@ package main import systemdDaemon "github.com/coreos/go-systemd/daemon" +// preNotifySystem sends a message to the host when the API is active, but before the daemon is +func preNotifySystem() { +} + // notifySystem sends a message to the host when the server is ready to be used func notifySystem() { // Tell the init daemon we are accepting requests diff --git a/cmd/dockerd/daemon_solaris.go b/cmd/dockerd/daemon_solaris.go index 974ba16345..e12da525a5 100644 --- a/cmd/dockerd/daemon_solaris.go +++ b/cmd/dockerd/daemon_solaris.go @@ -46,6 +46,10 @@ func getDaemonConfDir(_ string) string { func (cli *DaemonCli) setupConfigReloadTrap() { } +// preNotifySystem sends a message to the host when the API is active, but before the daemon is +func preNotifySystem() { +} + // notifySystem sends a message to the host when the server is ready to be used func notifySystem() { } diff --git a/cmd/dockerd/daemon_windows.go b/cmd/dockerd/daemon_windows.go index 4cccd32688..6682cc8cda 100644 --- a/cmd/dockerd/daemon_windows.go +++ b/cmd/dockerd/daemon_windows.go @@ -29,8 +29,10 @@ func getDaemonConfDir(root string) string { return filepath.Join(root, `\config`) } -// notifySystem sends a message to the host when the server is ready to be used -func notifySystem() { +// preNotifySystem sends a message to the host when the API is active, but before the daemon is +func preNotifySystem() { + // start the service now to prevent timeouts waiting for daemon to start + // but still (eventually) complete all requests that are sent after this if service != nil { err := service.started() if err != nil { @@ -39,6 +41,10 @@ func notifySystem() { } } +// notifySystem sends a message to the host when the server is ready to be used +func notifySystem() { +} + // notifyShutdown is called after the daemon shuts down but before the process exits. func notifyShutdown(err error) { if service != nil { diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 5ea9a4cb46..bd39d03737 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "strings" + "syscall" + "unsafe" "github.com/Microsoft/hcsshim" "github.com/Sirupsen/logrus" @@ -35,6 +37,8 @@ const ( windowsMinCPUPercent = 1 windowsMaxCPUPercent = 100 windowsMinCPUCount = 1 + + errInvalidState = syscall.Errno(0x139F) ) func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) { @@ -229,7 +233,8 @@ func checkSystem() error { if vmcompute.Load() != nil { return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.") } - return nil + + return waitOOBEComplete() } // configureKernelSecuritySupport configures and validate security support for the kernel @@ -601,3 +606,35 @@ func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { func (daemon *Daemon) setupSeccompProfile() error { return nil } + +func waitOOBEComplete() error { + kernel32 := windows.NewLazySystemDLL("kernel32.dll") + registerWaitUntilOOBECompleted := kernel32.NewProc("RegisterWaitUntilOOBECompleted") + unregisterWaitUntilOOBECompleted := kernel32.NewProc("UnregisterWaitUntilOOBECompleted") + + callbackChan := make(chan struct{}) + callbackFunc := func(uintptr) uintptr { + close(callbackChan) + return 0 + } + callbackFuncPtr := syscall.NewCallback(callbackFunc) + + var callbackHandle syscall.Handle + ret, _, err := registerWaitUntilOOBECompleted.Call(callbackFuncPtr, 0, uintptr(unsafe.Pointer(&callbackHandle))) + if ret == 0 { + if err == errInvalidState { + return nil + } + return fmt.Errorf("failed to register OOBEComplete callback. Error: %v", err) + } + + // Wait for the callback when OOBE is finished + <-callbackChan + + ret, _, err = unregisterWaitUntilOOBECompleted.Call(uintptr(callbackHandle)) + if ret == 0 { + return fmt.Errorf("failed to unregister OOBEComplete callback. Error: %v", err) + } + + return nil +}