Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 64 additions & 5 deletions notify/notify_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,26 @@

package notify

import "golang.org/x/sys/windows/svc"
import (
"log"
"strings"

"golang.org/x/sys/windows/svc"
)

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

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

// Ready notifies the SCM that the service is fully running and ready
// to accept stop or shutdown control requests.
func Ready() error {
if globalStatus != nil {
globalStatus <- svc.Status{
Expand All @@ -34,22 +44,71 @@ func Ready() error {
return nil
}

// Reloading notifies the SCM that the service is entering a transitional
// state.
func Reloading() error {
if globalStatus != nil {
globalStatus <- svc.Status{State: svc.StartPending}
}
return nil
}

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

// TODO: not implemented
func Status(_ string) error { return nil }
// Status sends an arbitrary service state to the SCM based on a string
// identifier of [svc.State].
// The unknown states will be logged.
func Status(name string) error {
if globalStatus == nil {
return nil
}

var state svc.State
var accepts svc.Accepted
accepts = 0

switch strings.ToLower(name) {
case "stopped":
state = svc.Stopped
case "start_pending":
state = svc.StartPending
case "stop_pending":
state = svc.StopPending
case "running":
state = svc.Running
accepts = svc.AcceptStop | svc.AcceptShutdown
case "continue_pending":
state = svc.ContinuePending
case "pause_pending":
state = svc.PausePending
case "paused":
state = svc.Paused
accepts = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
default:
log.Printf("unknown state: %s", name)
return nil
}

globalStatus <- svc.Status{State: state, Accepts: accepts}
return nil
}

// TODO: not implemented
func Error(_ error, _ int) error { return nil }
// Error notifies the SCM that the service is stopping due to a failure,
// including a service-specific exit code.
func Error(err error, code int) error {
if globalStatus != nil {
globalStatus <- svc.Status{
State: svc.StopPending,
ServiceSpecificExitCode: uint32(code),
}
}

return nil
}
Loading