130 lines
3.0 KiB
Go
130 lines
3.0 KiB
Go
|
//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
|
||
|
}
|