Skip to content

Commit 2b751dd

Browse files
craig65535alexbrainman
authored andcommitted
windows/svc/mgr: add Service.RecoveryActionsOnNonCrashFailures
Fixes golang/go#59016 Change-Id: I5e16f61ea2fc384052565342c6517687562260a1 GitHub-Last-Rev: 3626b01 GitHub-Pull-Request: #157 Reviewed-on: https://go-review.googlesource.com/c/sys/+/484896 Run-TryBot: Bryan Mills <[email protected]> Reviewed-by: Alex Brainman <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent e0c3b6e commit 2b751dd

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

windows/service.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ type SERVICE_FAILURE_ACTIONS struct {
218218
Actions *SC_ACTION
219219
}
220220

221+
type SERVICE_FAILURE_ACTIONS_FLAG struct {
222+
FailureActionsOnNonCrashFailures int32
223+
}
224+
221225
type SC_ACTION struct {
222226
Type uint32
223227
Delay uint32

windows/svc/mgr/mgr_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,57 @@ func testRecoveryCommand(t *testing.T, s *mgr.Service, should string) {
209209
}
210210
}
211211

212+
func testRecoveryActionsOnNonCrashFailures(t *testing.T, s *mgr.Service, should bool) {
213+
err := s.SetRecoveryActionsOnNonCrashFailures(should)
214+
if err != nil {
215+
t.Fatalf("SetRecoveryActionsOnNonCrashFailures failed: %v", err)
216+
}
217+
is, err := s.RecoveryActionsOnNonCrashFailures()
218+
if err != nil {
219+
t.Fatalf("RecoveryActionsOnNonCrashFailures failed: %v", err)
220+
}
221+
if should != is {
222+
t.Errorf("RecoveryActionsOnNonCrashFailures mismatch: flag is %v, but should have %v", is, should)
223+
}
224+
}
225+
226+
func testMultipleRecoverySettings(t *testing.T, s *mgr.Service, rebootMsgShould, recoveryCmdShould string, actionsFlagShould bool) {
227+
err := s.SetRebootMessage(rebootMsgShould)
228+
if err != nil {
229+
t.Fatalf("SetRebootMessage failed: %v", err)
230+
}
231+
err = s.SetRecoveryActionsOnNonCrashFailures(actionsFlagShould)
232+
if err != nil {
233+
t.Fatalf("SetRecoveryActionsOnNonCrashFailures failed: %v", err)
234+
}
235+
err = s.SetRecoveryCommand(recoveryCmdShould)
236+
if err != nil {
237+
t.Fatalf("SetRecoveryCommand failed: %v", err)
238+
}
239+
240+
rebootMsgIs, err := s.RebootMessage()
241+
if err != nil {
242+
t.Fatalf("RebootMessage failed: %v", err)
243+
}
244+
if rebootMsgShould != rebootMsgIs {
245+
t.Errorf("reboot message mismatch: message is %q, but should have %q", rebootMsgIs, rebootMsgShould)
246+
}
247+
recoveryCommandIs, err := s.RecoveryCommand()
248+
if err != nil {
249+
t.Fatalf("RecoveryCommand failed: %v", err)
250+
}
251+
if recoveryCmdShould != recoveryCommandIs {
252+
t.Errorf("recovery command mismatch: command is %q, but should have %q", recoveryCommandIs, recoveryCmdShould)
253+
}
254+
actionsFlagIs, err := s.RecoveryActionsOnNonCrashFailures()
255+
if err != nil {
256+
t.Fatalf("RecoveryActionsOnNonCrashFailures failed: %v", err)
257+
}
258+
if actionsFlagShould != actionsFlagIs {
259+
t.Errorf("RecoveryActionsOnNonCrashFailures mismatch: flag is %v, but should have %v", actionsFlagIs, actionsFlagShould)
260+
}
261+
}
262+
212263
func testControl(t *testing.T, s *mgr.Service, c svc.Cmd, expectedErr error, expectedStatus svc.Status) {
213264
status, err := s.Control(c)
214265
if err != expectedErr {
@@ -305,6 +356,9 @@ func TestMyService(t *testing.T) {
305356
testRebootMessage(t, s, "") // delete reboot message
306357
testRecoveryCommand(t, s, fmt.Sprintf("sc query %s", name))
307358
testRecoveryCommand(t, s, "") // delete recovery command
359+
testRecoveryActionsOnNonCrashFailures(t, s, true)
360+
testRecoveryActionsOnNonCrashFailures(t, s, false)
361+
testMultipleRecoverySettings(t, s, fmt.Sprintf("%s failed", name), fmt.Sprintf("sc query %s", name), true)
308362

309363
expectedStatus := svc.Status{
310364
State: svc.Stopped,

windows/svc/mgr/recovery.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,30 @@ func (s *Service) RecoveryCommand() (string, error) {
140140
p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0]))
141141
return windows.UTF16PtrToString(p.Command), nil
142142
}
143+
144+
// SetRecoveryActionsOnNonCrashFailures sets the failure actions flag. If the
145+
// flag is set to false, recovery actions will only be performed if the service
146+
// terminates without reporting a status of SERVICE_STOPPED. If the flag is set
147+
// to true, recovery actions are also perfomed if the service stops with a
148+
// nonzero exit code.
149+
func (s *Service) SetRecoveryActionsOnNonCrashFailures(flag bool) error {
150+
var setting windows.SERVICE_FAILURE_ACTIONS_FLAG
151+
if flag {
152+
setting.FailureActionsOnNonCrashFailures = 1
153+
}
154+
return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, (*byte)(unsafe.Pointer(&setting)))
155+
}
156+
157+
// RecoveryActionsOnNonCrashFailures returns the current value of the failure
158+
// actions flag. If the flag is set to false, recovery actions will only be
159+
// performed if the service terminates without reporting a status of
160+
// SERVICE_STOPPED. If the flag is set to true, recovery actions are also
161+
// perfomed if the service stops with a nonzero exit code.
162+
func (s *Service) RecoveryActionsOnNonCrashFailures() (bool, error) {
163+
b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG)
164+
if err != nil {
165+
return false, err
166+
}
167+
p := (*windows.SERVICE_FAILURE_ACTIONS_FLAG)(unsafe.Pointer(&b[0]))
168+
return p.FailureActionsOnNonCrashFailures != 0, nil
169+
}

0 commit comments

Comments
 (0)