servicemain

package
v0.52.0-devel Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2024 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Rendered for windows/amd64

Overview

Package servicemain provides Windows Service application helpers

Index

Constants

View Source
const (
	// DefaultHardStopTimeout is a default value. See Service.HardStopTimeout for details.
	DefaultHardStopTimeout = 15 * time.Second
	// EnvHardStopTimeoutOverride is an environment variable that a user can set
	// to override DefaultHardStopTimeout.
	EnvHardStopTimeoutOverride = "DD_WINDOWS_SERVICE_STOP_TIMEOUT_SECONDS"
)

Variables

View Source
var ErrCleanStopAfterInit = errors.New("the service did not start but requested a clean exit")

ErrCleanStopAfterInit should be returned from Service.Init() to report SERVICE_RUNNING and then exit without error after a delay. See runTimeExitGate for more information on why the delay is necessary.

Example use case, the service detects that it is not configured and wishes to stop running, but does not want an error reported, as failing to start may cause commands like `Restart-Service -Force datadogagent` to fail if run after modifying the configuration to disable the service.

If your service detects this state in Service.Run() instead then you do not need to do anything, it is handled automatically.

We may be able to remove this and runTimeExitGate if we re-work our current model of change config -> `Restart-Service -Force datadogagent`, which we currently expect to trigger all services to restart. Perhaps we can use an agent command instead of PowerShell and it can check for a special exit code from each of the services. However we wouldn't be able to use configuration management tools' built-in Windows Service commands.

Functions

func Run

func Run(service Service)

Run fullfills the contract required by programs running as Windows Services. https://learn.microsoft.com/en-us/windows/win32/services/service-programs

Run should be called as early as possible in the process initialization. If called too late you may encounter service start timeout errors from SCM. If the process exits without calling this function then SCM will erroneously report a timeout error, regardless of how fast the process exits.

SCM only gives services 30 seconds (by default) to respond after the process is created. Specifically, this timeout refers to calling StartServiceCtrlDispatcher, which is called by golang's svc.Run. This timeout is adjustable at the host level with the ServicesPipeTimeout registry value. https://learn.microsoft.com/en-us/troubleshoot/windows-server/system-management-components/service-not-start-events-7000-7011-time-out-error

Golang initializes all packages before giving control to main(). This means that if the package initialization takes longer than the SCM timeout then SCM will kill our process before main() is even called. One observed source of extended process initialization times is dependency packages that call golang's user.Current() function to get the current user. If this becomes a recurring issue we may want to consider calling StartServiceCtrlDispatcher before the go runtime is initialized, for example via a C constructor.

func RunningAsWindowsService

func RunningAsWindowsService() bool

RunningAsWindowsService returns true if the current process is running as a Windows Service and the application should call Run().

Types

type DefaultSettings

type DefaultSettings struct{}

DefaultSettings provides default values to Service implementations when embedded

func (*DefaultSettings) HardStopTimeout

func (s *DefaultSettings) HardStopTimeout() time.Duration

HardStopTimeout provides a default hard stop timeout for Service implementations, and allows a user to override the default by setting the DD_WINDOWS_SERVICE_STOP_TIMEOUT_SECONDS environment variable.

type Service

type Service interface {
	// Name() returns the string to be used as the source for event log records.
	Name() string

	// Init() implements application initialization and is run when the service status is SERVICE_START_PENDING.
	// The service status is set to SERVICE_RUNNING when Init() returns successfully.
	// See ErrCleanStopAfterInit if you need to exit without calling Service.Run() or throwing an error.
	//
	// This function will block service tools like PowerShell's `Start-Service` until it returns.
	Init() error

	// Run() implements all application logic and is run when the service status is SERVICE_RUNNING.
	//
	// The provided context is cancellable. Run() must monitor ctx.Done() and return as soon as possible
	// when it is set. If Run() does not return after the context is set the process will exit after
	// the duration returned by HardStopTimeout()
	//
	// The service will exit when Run() returns. Run() must return for the service status to be be updated to SERVICE_STOPPED.
	// If the process exits without setting SERVICE_STOPPED, Service Control Manager (SCM) will treat
	// this as an unexpected exit and enter failure/recovery, regardless of the process exit code.
	// https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_failure_actionsa
	Run(ctx context.Context) error

	// Most platforms send SIGKILL if the service does not respond to the initial SIGTERM
	// within a set amount of time. The set amount of time differs between platforms but
	// is usually also configurable
	// - upstart: 5 seconds
	// - systemd: 90 seconds
	// However, on Windows most service manager tools let the process stay running if it does not stop,
	// with different timeouts, and there is no standard way to configure a timeout.
	// - PowerShell Restart-Service: (no kill) error after 30 seconds for dependent services
	//                               (no kill) no timeout for a single service
	// - net stop: (no kill) error after 20 seconds
	// - sc stop: does not wait/block
	// - Services.msc: (no kill) error after 125 seconds
	// - Host/OS shutdown: shutdown after 20 seconds
	// https://learn.microsoft.com/en-us/windows/win32/services/service-control-handler-function
	//
	// This means that on Windows if our service hangs on stop then it may need to be force killed
	// by the user, and may cause uninstall to fail. CM tools like our Ansible plugin use Restart-Service,
	// when the configuration changes so it is troublesome for the user if Restart-Service fails.
	//
	// It seems useful to keep this timeout under the commonly used Windows tools error timeouts, so
	// we set it that way for now. However our primary requirement is it be less than the Agent
	// installer timeout (3 minutes).
	HardStopTimeout() time.Duration
}

Service defines the interface that applications should implement to run as Windows Services

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL