windows: add support for automatic delayed start in windows service

Change-Id: Iad33ea0f6627ac98c89dbaab0b41b3dd724c3163
GitHub-Last-Rev: 8764fdbd3200eb38d482cfffb5bd98f565f4395f
GitHub-Pull-Request: golang/sys#36
Reviewed-on: https://go-review.googlesource.com/c/sys/+/187198
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/windows/service.go b/windows/service.go
index 03383f1..847e00b 100644
--- a/windows/service.go
+++ b/windows/service.go
@@ -159,6 +159,10 @@
 	Description *uint16
 }
 
+type SERVICE_DELAYED_AUTO_START_INFO struct {
+	IsDelayedAutoStartUp uint32
+}
+
 type SERVICE_STATUS_PROCESS struct {
 	ServiceType             uint32
 	CurrentState            uint32
diff --git a/windows/svc/mgr/config.go b/windows/svc/mgr/config.go
index 61447a5..8431edb 100644
--- a/windows/svc/mgr/config.go
+++ b/windows/svc/mgr/config.go
@@ -43,6 +43,7 @@
 	Password         string
 	Description      string
 	SidType          uint32 // one of SERVICE_SID_TYPE, the type of sid to use for the service
+	DelayedAutoStart bool   // the service is started after other auto-start services are started plus a short delay
 }
 
 func toString(p *uint16) string {
@@ -95,6 +96,16 @@
 	}
 	p2 := (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0]))
 
+	b, err = s.queryServiceConfig2(windows.SERVICE_CONFIG_DELAYED_AUTO_START_INFO)
+	if err != nil {
+		return Config{}, err
+	}
+	p3 := (*windows.SERVICE_DELAYED_AUTO_START_INFO)(unsafe.Pointer(&b[0]))
+	delayedStart := false
+	if p3.IsDelayedAutoStartUp != 0 {
+		delayedStart = true
+	}
+
 	return Config{
 		ServiceType:      p.ServiceType,
 		StartType:        p.StartType,
@@ -106,6 +117,7 @@
 		ServiceStartName: toString(p.ServiceStartName),
 		DisplayName:      toString(p.DisplayName),
 		Description:      toString(p2.Description),
+		DelayedAutoStart: delayedStart,
 	}, nil
 }
 
@@ -119,6 +131,15 @@
 	return windows.ChangeServiceConfig2(handle, windows.SERVICE_CONFIG_SERVICE_SID_INFO, (*byte)(unsafe.Pointer(&sidType)))
 }
 
+func updateStartUp(handle windows.Handle, isDelayed bool) error {
+	var d windows.SERVICE_DELAYED_AUTO_START_INFO
+	if isDelayed {
+		d.IsDelayedAutoStartUp = 1
+	}
+	return windows.ChangeServiceConfig2(handle,
+		windows.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (*byte)(unsafe.Pointer(&d)))
+}
+
 // UpdateConfig updates service s configuration parameters.
 func (s *Service) UpdateConfig(c Config) error {
 	err := windows.ChangeServiceConfig(s.Handle, c.ServiceType, c.StartType,
@@ -132,6 +153,12 @@
 	if err != nil {
 		return err
 	}
+
+	err = updateStartUp(s.Handle, c.DelayedAutoStart)
+	if err != nil {
+		return err
+	}
+
 	return updateDescription(s.Handle, c.Description)
 }
 
diff --git a/windows/svc/mgr/mgr.go b/windows/svc/mgr/mgr.go
index ad4cd6b..8d1cfd8 100644
--- a/windows/svc/mgr/mgr.go
+++ b/windows/svc/mgr/mgr.go
@@ -149,6 +149,14 @@
 			return nil, err
 		}
 	}
+	if c.DelayedAutoStart {
+		err = updateStartUp(h, c.DelayedAutoStart)
+		if err != nil {
+			windows.DeleteService(h)
+			windows.CloseHandle(h)
+			return nil, err
+		}
+	}
 	return &Service{Name: name, Handle: h}, nil
 }
 
diff --git a/windows/svc/mgr/mgr_test.go b/windows/svc/mgr/mgr_test.go
index 9171f5b..750ffe8 100644
--- a/windows/svc/mgr/mgr_test.go
+++ b/windows/svc/mgr/mgr_test.go
@@ -80,6 +80,9 @@
 	if err != nil {
 		t.Fatalf("Config failed: %s", err)
 	}
+	if should.DelayedAutoStart != is.DelayedAutoStart {
+		t.Fatalf("config mismatch: DelayedAutoStart is %v, but should have %v", is.DelayedAutoStart, should.DelayedAutoStart)
+	}
 	if should.DisplayName != is.DisplayName {
 		t.Fatalf("config mismatch: DisplayName is %q, but should have %q", is.DisplayName, should.DisplayName)
 	}
@@ -257,6 +260,15 @@
 
 	testConfig(t, s, c)
 
+	c.StartType = mgr.StartAutomatic
+	c.DelayedAutoStart = true
+	err = s.UpdateConfig(c)
+	if err != nil {
+		t.Fatalf("UpdateConfig failed: %v", err)
+	}
+
+	testConfig(t, s, c)
+
 	svcnames, err := m.ListServices()
 	if err != nil {
 		t.Fatalf("ListServices failed: %v", err)