//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 }