123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- // +build windows
- /*
- Copyright 2018 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package service
- import (
- "os"
- "time"
- "k8s.io/apiserver/pkg/server"
- "k8s.io/klog"
- "golang.org/x/sys/windows"
- "golang.org/x/sys/windows/svc"
- )
- var (
- service *handler
- )
- type handler struct {
- tosvc chan bool
- fromsvc chan error
- }
- // InitService is the entry point for running the daemon as a Windows
- // service. It returns an indication of whether it is running as a service;
- // and an error.
- func InitService(serviceName string) error {
- h := &handler{
- tosvc: make(chan bool),
- fromsvc: make(chan error),
- }
- service = h
- var err error
- go func() {
- err = svc.Run(serviceName, h)
- h.fromsvc <- err
- }()
- // Wait for the first signal from the service handler.
- err = <-h.fromsvc
- if err != nil {
- return err
- }
- klog.Infof("Running %s as a Windows service!", serviceName)
- return nil
- }
- func (h *handler) Execute(_ []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
- s <- svc.Status{State: svc.StartPending, Accepts: 0}
- // Unblock initService()
- h.fromsvc <- nil
- s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)}
- klog.Infof("Service running")
- Loop:
- for {
- select {
- case <-h.tosvc:
- break Loop
- case c := <-r:
- switch c.Cmd {
- case svc.Cmd(windows.SERVICE_CONTROL_PARAMCHANGE):
- s <- c.CurrentStatus
- case svc.Interrogate:
- s <- c.CurrentStatus
- case svc.Stop, svc.Shutdown:
- klog.Infof("Service stopping")
- // We need to translate this request into a signal that can be handled by the signal handler
- // handling shutdowns normally (currently apiserver/pkg/server/signal.go).
- // If we do not do this, our main threads won't be notified of the upcoming shutdown.
- // Since Windows services do not use any console, we cannot simply generate a CTRL_BREAK_EVENT
- // but need a dedicated notification mechanism.
- graceful := server.RequestShutdown()
- // Free up the control handler and let us terminate as gracefully as possible.
- // If that takes too long, the service controller will kill the remaining threads.
- // As per https://docs.microsoft.com/en-us/windows/desktop/services/service-control-handler-function
- s <- svc.Status{State: svc.StopPending}
- // If we cannot exit gracefully, we really only can exit our process, so atleast the
- // service manager will think that we gracefully exited. At the time of writing this comment this is
- // needed for applications that do not use signals (e.g. kube-proxy)
- if !graceful {
- go func() {
- // Ensure the SCM was notified (The operation above (send to s) was received and communicated to the
- // service control manager - so it doesn't look like the service crashes)
- time.Sleep(1 * time.Second)
- os.Exit(0)
- }()
- }
- break Loop
- }
- }
- }
- return false, 0
- }
|