Skip to content

Commit 409a072

Browse files
authored
notify: implement windows service status and error notifications (#7389)
* implement service status and error notifications * adjust return of Error function * configure accepts on status * align windows with linux semantics
1 parent 6a4296b commit 409a072

File tree

1 file changed

+64
-5
lines changed

1 file changed

+64
-5
lines changed

notify/notify_windows.go

Lines changed: 64 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+
"log"
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,71 @@ 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 of [svc.State].
67+
// The unknown states will be logged.
68+
func Status(name string) error {
69+
if globalStatus == nil {
70+
return nil
71+
}
72+
73+
var state svc.State
74+
var accepts svc.Accepted
75+
accepts = 0
76+
77+
switch strings.ToLower(name) {
78+
case "stopped":
79+
state = svc.Stopped
80+
case "start_pending":
81+
state = svc.StartPending
82+
case "stop_pending":
83+
state = svc.StopPending
84+
case "running":
85+
state = svc.Running
86+
accepts = svc.AcceptStop | svc.AcceptShutdown
87+
case "continue_pending":
88+
state = svc.ContinuePending
89+
case "pause_pending":
90+
state = svc.PausePending
91+
case "paused":
92+
state = svc.Paused
93+
accepts = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
94+
default:
95+
log.Printf("unknown state: %s", name)
96+
return nil
97+
}
98+
99+
globalStatus <- svc.Status{State: state, Accepts: accepts}
100+
return nil
101+
}
53102

54-
// TODO: not implemented
55-
func Error(_ error, _ int) error { return nil }
103+
// Error notifies the SCM that the service is stopping due to a failure,
104+
// including a service-specific exit code.
105+
func Error(err error, code int) error {
106+
if globalStatus != nil {
107+
globalStatus <- svc.Status{
108+
State: svc.StopPending,
109+
ServiceSpecificExitCode: uint32(code),
110+
}
111+
}
112+
113+
return nil
114+
}

0 commit comments

Comments
 (0)