@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 10, 23:45 UTC

master
go 160 lines 4.42 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package notify
16
17 import (
18 "log"
19 "strings"
20 "sync"
21
22 "golang.org/x/sys/windows/svc"
23 )
24
25 // statusMu guards the service status below. It is held while sending
26 // to globalStatus so that every update reaches the SCM in order. The
27 // receiver is the dispatcher loop in golang.org/x/sys/windows/svc,
28 // which always accepts status updates and never takes this lock.
29 var statusMu sync.Mutex
30
31 var (
32 // globalStatus store windows service status, it can be
33 // use to notify caddy status.
34 globalStatus chan<- svc.Status
35
36 // currentStatus is the authoritative service status. It is kept
37 // while no channel is attached, so a status reported before the
38 // service handler starts is not lost.
39 currentStatus = svc.Status{State: svc.StartPending}
40
41 // stopping is set once shutdown begins; after that, only stop
42 // states are accepted.
43 stopping bool
44 )
45
46 // SetGlobalStatus assigns the channel through which status updates
47 // will be sent to the SCM and publishes the current status to it.
48 // Passing nil detaches the channel and resets the status, which the
49 // service handler does before it returns.
50 func SetGlobalStatus(status chan<- svc.Status) {
51 statusMu.Lock()
52 defer statusMu.Unlock()
53 globalStatus = status
54 if status == nil {
55 currentStatus = svc.Status{State: svc.StartPending}
56 stopping = false
57 return
58 }
59 status <- currentStatus
60 }
61
62 // Interrogate republishes the current status, in response to the
63 // SCM's Interrogate control request.
64 func Interrogate() {
65 statusMu.Lock()
66 defer statusMu.Unlock()
67 if globalStatus != nil {
68 globalStatus <- currentStatus
69 }
70 }
71
72 // setStatus makes s the current status and publishes it if a channel
73 // is attached. Once shutdown has begun, statuses other than StopPending
74 // and Stopped are ignored, so a late Ready cannot undo it.
75 func setStatus(s svc.Status) {
76 statusMu.Lock()
77 defer statusMu.Unlock()
78 setStatusLocked(s)
79 }
80
81 func setStatusLocked(s svc.Status) {
82 if stopping && s.State != svc.StopPending && s.State != svc.Stopped {
83 return
84 }
85 currentStatus = s
86 if globalStatus != nil {
87 globalStatus <- s
88 }
89 }
90
91 // Ready notifies the SCM that the service is fully running and ready
92 // to accept stop or shutdown control requests.
93 func Ready() error {
94 setStatus(svc.Status{
95 State: svc.Running,
96 Accepts: svc.AcceptStop | svc.AcceptShutdown,
97 })
98 return nil
99 }
100
101 // Reloading notifies the SCM that the service is entering a transitional
102 // state.
103 func Reloading() error {
104 setStatus(svc.Status{State: svc.StartPending})
105 return nil
106 }
107
108 // Stopping notifies the SCM that the service is in the process of stopping.
109 // This allows Windows to track the shutdown transition properly.
110 func Stopping() error {
111 statusMu.Lock()
112 defer statusMu.Unlock()
113 stopping = true
114 setStatusLocked(svc.Status{State: svc.StopPending})
115 return nil
116 }
117
118 // Status sends an arbitrary service state to the SCM based on a string
119 // identifier of [svc.State].
120 // The unknown states will be logged.
121 func Status(name string) error {
122 var state svc.State
123 var accepts svc.Accepted
124 accepts = 0
125
126 switch strings.ToLower(name) {
127 case "stopped":
128 state = svc.Stopped
129 case "start_pending":
130 state = svc.StartPending
131 case "stop_pending":
132 state = svc.StopPending
133 case "running":
134 state = svc.Running
135 accepts = svc.AcceptStop | svc.AcceptShutdown
136 case "continue_pending":
137 state = svc.ContinuePending
138 case "pause_pending":
139 state = svc.PausePending
140 case "paused":
141 state = svc.Paused
142 accepts = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
143 default:
144 log.Printf("unknown state: %s", name)
145 return nil
146 }
147
148 setStatus(svc.Status{State: state, Accepts: accepts})
149 return nil
150 }
151
152 // Error notifies the SCM that the service is stopping due to a failure,
153 // including a service-specific exit code.
154 func Error(err error, code int) error {
155 setStatus(svc.Status{
156 State: svc.StopPending,
157 ServiceSpecificExitCode: uint32(code),
158 })
159 return nil
160 }