watch-spring/cmd/windows/main.go

130 lines
3.0 KiB
Go
Raw Normal View History

2024-08-23 14:11:38 +02:00
//go:build windows
// +build windows
package cmd
import (
"context"
"errors"
"flag"
"fmt"
"log"
"time"
"gitea.urkob.com/mcr-swiss/watch-spring/internal"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows/svc"
)
type WinService struct {
logger *logrus.Logger
dirToWatch string
scriptToExecutePath string
args []string
}
func NewWinService(logger *logrus.Logger, fileToWatchPath, scriptToExecutePath string, args ...string) *WinService {
return &WinService{
logger: logger,
dirToWatch: fileToWatchPath,
scriptToExecutePath: scriptToExecutePath,
args: args,
}
}
func (winsvc *WinService) Execute(args []string, req <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
status <- svc.Status{State: svc.StartPending}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
notif := internal.NewNotifier()
w, err := internal.NewWatcher(winsvc.logger, notif, internal.Execute)
if err != nil {
panic(err)
}
defer func() {
if err := w.Close(); err != nil {
log.Fatalf("watcherIface.Close: %s\n", err)
}
}()
if err := w.Monitor(winsvc.dirToWatch); err != nil {
log.Fatalf("watcherIface.Monitor: %s\n", err)
}
errChan := make(chan error)
go w.Listen(ctx, errChan, winsvc.scriptToExecutePath)
loop:
for {
select {
case r := <-req:
switch r.Cmd {
case svc.Stop, svc.Shutdown:
cancel()
break loop
default:
winsvc.logger.Debugf("Received control request: %v", r.Cmd)
}
case err, ok := <-errChan:
if !ok {
cancel()
winsvc.logger.Error(fmt.Errorf("channel has been closed"))
break loop
}
if err != nil {
winsvc.logger.Error(err)
continue
}
default:
time.Sleep(2 * time.Second)
}
}
status <- svc.Status{State: svc.StopPending}
return false, 0
}
func main() {
// Define command-line flags
dirFlag := flag.String("dir", "", "Directory to monitor")
scriptFlag := flag.String("script", "", "Script to execute")
nameFlag := flag.String("name", "", "Name")
logLevelFlag := flag.Uint("log-level", uint(logrus.InfoLevel), "Log Leven")
flag.Parse()
if *dirFlag == "" || *scriptFlag == "" || *nameFlag == "" {
log.Fatal("Both --name --dir and --script flags are required")
}
logger := logrus.New()
logger.SetLevel(logrus.Level(*logLevelFlag))
if err := runService(logger, fmt.Sprintf("WatchSpring-%s", *nameFlag), NewWinService(
logger,
*dirFlag,
*scriptFlag,
)); err != nil {
panic(err)
}
}
func runService(logger *logrus.Logger, name string, svcHandler *WinService) error {
isWindowsService, err := svc.IsWindowsService()
if err != nil {
return fmt.Errorf("failed to determine if we are running in an interactive session: %w", err)
}
if !isWindowsService {
return errors.New("Running in interactive mode.")
}
logger.Debug("Running as a Windows service.")
if err := svc.Run(name, svcHandler); err != nil {
return fmt.Errorf("failed to run service: %w", err)
}
return nil
}