2019-09-19 12:00:26 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
// Implements support for running the storagenode-updater as a Windows Service.
|
|
|
|
//
|
|
|
|
// The Windows Service can be created with sc.exe, e.g.
|
|
|
|
//
|
|
|
|
// sc.exe create storagenode-updater binpath= "C:\Users\MyUser\storagenode-updater.exe run ..."
|
|
|
|
|
2022-03-21 14:48:03 +00:00
|
|
|
//go:build windows
|
2020-04-01 12:59:34 +01:00
|
|
|
// +build windows
|
2019-09-19 12:00:26 +01:00
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2019-09-24 20:17:22 +01:00
|
|
|
"os"
|
|
|
|
"time"
|
2019-09-19 12:00:26 +01:00
|
|
|
|
2019-12-03 13:56:49 +00:00
|
|
|
"go.uber.org/zap"
|
2019-11-22 13:57:37 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2019-09-19 12:00:26 +01:00
|
|
|
"golang.org/x/sys/windows/svc"
|
2019-11-22 13:57:37 +00:00
|
|
|
|
2020-03-23 19:18:20 +00:00
|
|
|
"storj.io/private/process"
|
2019-09-19 12:00:26 +01:00
|
|
|
)
|
|
|
|
|
2020-04-01 12:59:34 +01:00
|
|
|
func isRunCmd() bool {
|
|
|
|
return len(os.Args) > 1 && os.Args[1] == "run"
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2021-01-13 14:08:38 +00:00
|
|
|
isService, err := svc.IsWindowsService()
|
2019-09-19 12:00:26 +01:00
|
|
|
if err != nil {
|
2020-04-13 10:31:17 +01:00
|
|
|
zap.L().Fatal("Failed to determine if session is interactive.", zap.Error(err))
|
2019-09-19 12:00:26 +01:00
|
|
|
}
|
|
|
|
|
2021-01-13 14:08:38 +00:00
|
|
|
if !isService || !isRunCmd() {
|
2020-04-01 12:59:34 +01:00
|
|
|
process.Exec(rootCmd)
|
2019-11-04 09:22:08 +00:00
|
|
|
return
|
|
|
|
}
|
2019-09-19 12:00:26 +01:00
|
|
|
|
|
|
|
err = svc.Run("storagenode-updater", &service{})
|
|
|
|
if err != nil {
|
2020-04-13 10:31:17 +01:00
|
|
|
zap.L().Fatal("Service failed.", zap.Error(err))
|
2019-09-19 12:00:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type service struct{}
|
|
|
|
|
|
|
|
func (m *service) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
|
|
|
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
|
|
|
|
|
|
|
changes <- svc.Status{State: svc.StartPending}
|
|
|
|
|
2019-11-22 13:57:37 +00:00
|
|
|
var group errgroup.Group
|
|
|
|
group.Go(func() error {
|
|
|
|
process.Exec(rootCmd)
|
|
|
|
return nil
|
|
|
|
})
|
2019-09-19 12:00:26 +01:00
|
|
|
|
|
|
|
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
|
|
|
|
2019-11-14 08:31:30 +00:00
|
|
|
for c := range r {
|
|
|
|
switch c.Cmd {
|
|
|
|
case svc.Interrogate:
|
2020-04-13 10:31:17 +01:00
|
|
|
zap.L().Info("Interrogate request received.")
|
2019-11-14 08:31:30 +00:00
|
|
|
changes <- c.CurrentStatus
|
|
|
|
// Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
changes <- c.CurrentStatus
|
|
|
|
case svc.Stop, svc.Shutdown:
|
2020-04-13 10:31:17 +01:00
|
|
|
zap.L().Info("Stop/Shutdown request received.")
|
2019-11-14 08:31:30 +00:00
|
|
|
changes <- svc.Status{State: svc.StopPending}
|
2019-11-22 13:57:37 +00:00
|
|
|
// Cancel the command's root context to cleanup resources
|
|
|
|
_, cancel := process.Ctx(runCmd)
|
2019-11-14 08:31:30 +00:00
|
|
|
cancel()
|
2019-11-22 13:57:37 +00:00
|
|
|
_ = group.Wait() // process.Exec does not return an error
|
2019-11-14 08:31:30 +00:00
|
|
|
// After returning the Windows Service is stopped and the process terminates
|
2020-06-05 11:53:36 +01:00
|
|
|
return false, 0
|
2019-11-14 08:31:30 +00:00
|
|
|
default:
|
2020-04-13 10:31:17 +01:00
|
|
|
zap.L().Info("Unexpected control request.", zap.Uint32("Event Type", c.EventType))
|
2019-09-19 12:00:26 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-01 12:59:34 +01:00
|
|
|
|
2020-06-05 11:53:36 +01:00
|
|
|
return false, 0
|
2019-09-25 17:35:56 +01:00
|
|
|
}
|