Skip to content

Commit e0b77ce

Browse files
committed
implement service status and error notifications
1 parent 374b7a6 commit e0b77ce

File tree

1 file changed

+60
-5
lines changed

1 file changed

+60
-5
lines changed

notify/notify_windows.go

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,26 @@
1414

1515
package notify
1616

17-
import "golang.org/x/sys/windows/svc"
17+
import (
18+
"fmt"
19+
"strings"
20+
21+
"golang.org/x/sys/windows/svc"
22+
)
1823

1924
// globalStatus store windows service status, it can be
2025
// use to notify caddy status.
2126
var globalStatus chan<- svc.Status
2227

28+
// SetGlobalStatus assigns the channel through which status updates
29+
// will be sent to the SCM. This is typically provided by the service
30+
// handler when the service starts.
2331
func SetGlobalStatus(status chan<- svc.Status) {
2432
globalStatus = status
2533
}
2634

35+
// Ready notifies the SCM that the service is fully running and ready
36+
// to accept stop or shutdown control requests.
2737
func Ready() error {
2838
if globalStatus != nil {
2939
globalStatus <- svc.Status{
@@ -34,22 +44,67 @@ func Ready() error {
3444
return nil
3545
}
3646

47+
// Reloading notifies the SCM that the service is entering a transitional
48+
// state.
3749
func Reloading() error {
3850
if globalStatus != nil {
3951
globalStatus <- svc.Status{State: svc.StartPending}
4052
}
4153
return nil
4254
}
4355

56+
// Stopping notifies the SCM that the service is in the process of stopping.
57+
// This allows Windows to track the shutdown transition properly.
4458
func Stopping() error {
4559
if globalStatus != nil {
4660
globalStatus <- svc.Status{State: svc.StopPending}
4761
}
4862
return nil
4963
}
5064

51-
// TODO: not implemented
52-
func Status(_ string) error { return nil }
65+
// Status sends an arbitrary service state to the SCM based on a string
66+
// identifier. This allows higher-level code to map custom states to
67+
// Windows service states when appropriate.
68+
func Status(name string) error {
69+
if globalStatus == nil {
70+
return nil
71+
}
72+
73+
var state svc.State
74+
75+
switch strings.ToLower(name) {
76+
case "stopped":
77+
state = svc.Stopped
78+
case "start_pending":
79+
state = svc.StartPending
80+
case "stop_pending":
81+
state = svc.StopPending
82+
case "running":
83+
state = svc.Running
84+
case "continue_pending":
85+
state = svc.ContinuePending
86+
case "pause_pending":
87+
state = svc.PausePending
88+
case "paused":
89+
state = svc.Paused
90+
default:
91+
return fmt.Errorf("unknown status: %s", name)
92+
}
93+
94+
globalStatus <- svc.Status{State: state}
95+
return nil
96+
}
97+
98+
// Error notifies the SCM that the service is stopping due to a failure,
99+
// including a service-specific exit code. The returned error allows the
100+
// caller to abort execution and let the service terminate.
101+
func Error(err error, code int) error {
102+
if globalStatus != nil {
103+
globalStatus <- svc.Status{
104+
State: svc.StopPending,
105+
ServiceSpecificExitCode: uint32(code),
106+
}
107+
}
53108

54-
// TODO: not implemented
55-
func Error(_ error, _ int) error { return nil }
109+
return fmt.Errorf("service failed (code=%d): %w", code, err)
110+
}

0 commit comments

Comments
 (0)