1414
1515package 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.
2126var 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.
2331func 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.
2737func 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.
3749func 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.
4458func 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