diff --git a/cmd/launcher/svc_windows.go b/cmd/launcher/svc_windows.go index 1fa4a6b96..0eaf5f1e3 100644 --- a/cmd/launcher/svc_windows.go +++ b/cmd/launcher/svc_windows.go @@ -5,6 +5,7 @@ package main import ( "context" + "errors" "fmt" "log/slog" "os" @@ -195,13 +196,14 @@ func (w *winSvc) Execute(args []string, r <-chan svc.ChangeRequest, changes chan ctx = ctxlog.NewContext(ctx, w.logger) + runLauncherErrs := make(chan error) + go func() { err := runLauncher(ctx, cancel, w.slogger, w.systemSlogger, w.opts) if err != nil { level.Info(w.logger).Log("msg", "runLauncher exited", "err", err) level.Debug(w.logger).Log("msg", "runLauncher exited", "err", err, "stack", fmt.Sprintf("%+v", err)) - changes <- svc.Status{State: svc.Stopped, Accepts: cmdsAccepted} - os.Exit(1) + runLauncherErrs <- fmt.Errorf("runLauncher exited with error: %w", err) } // If we get here, it means runLauncher returned nil. If we do @@ -209,8 +211,7 @@ func (w *winSvc) Execute(args []string, r <-chan svc.ChangeRequest, changes chan // functionality. Instead, signal that as a stop to the service // manager, and exit. We rely on the service manager to restart. level.Info(w.logger).Log("msg", "runLauncher exited cleanly") - changes <- svc.Status{State: svc.Stopped, Accepts: cmdsAccepted} - os.Exit(0) + runLauncherErrs <- errors.New("runLauncher exited cleanly") }() for { @@ -232,6 +233,13 @@ func (w *winSvc) Execute(args []string, r <-chan svc.ChangeRequest, changes chan default: level.Info(w.logger).Log("msg", "unexpected change request", "change_request", fmt.Sprintf("%+v", c)) } + case e := <-runLauncherErrs: + level.Info(w.logger).Log("msg", "received notice of runLauncher termination, marking service as stopped", "err", e) + changes <- svc.Status{State: svc.StopPending} + cancel() + time.Sleep(2 * time.Second) // give rungroups enough time to shut down + changes <- svc.Status{State: svc.Stopped, Accepts: cmdsAccepted} + return ssec, errno } } } diff --git a/pkg/execwrapper/exec_windows.go b/pkg/execwrapper/exec_windows.go index 09f37637c..d4a8849db 100644 --- a/pkg/execwrapper/exec_windows.go +++ b/pkg/execwrapper/exec_windows.go @@ -5,7 +5,6 @@ package execwrapper import ( "context" - "errors" "fmt" "os" "os/exec" @@ -32,24 +31,24 @@ func Exec(ctx context.Context, argv0 string, argv []string, envv []string) error ) // Now run it. This is faking exec, we need to distinguish - // between a failure to execute, and a failure in in the called program. + // between a failure to execute, and a failure in the called program. // I think https://github.com/golang/go/issues/26539 adds this functionality. err := cmd.Run() if cmd.ProcessState.ExitCode() == -1 { if err == nil { - return fmt.Errorf("Unknown error trying to exec %s (and nil err)", strings.Join(cmd.Args, " ")) + return fmt.Errorf("unknown error trying to exec %s (and nil err)", strings.Join(cmd.Args, " ")) } - return fmt.Errorf("Unknown error trying to exec %s: %w", strings.Join(cmd.Args, " "), err) + return fmt.Errorf("unknown error trying to exec %s: %w", strings.Join(cmd.Args, " "), err) } if err != nil { level.Info(logger).Log( "msg", "got error on exec", "err", err, + "exit_code", cmd.ProcessState.ExitCode(), ) } - os.Exit(cmd.ProcessState.ExitCode()) - return errors.New("Exec shouldn't have gotten here.") + return fmt.Errorf("error trying to exec %s: exit code %d: %w", strings.Join(cmd.Args, " "), cmd.ProcessState.ExitCode(), err) } diff --git a/pkg/osquery/runtime/runner.go b/pkg/osquery/runtime/runner.go index d999294e2..89f2dd497 100644 --- a/pkg/osquery/runtime/runner.go +++ b/pkg/osquery/runtime/runner.go @@ -122,10 +122,17 @@ func (r *Runner) Start() error { } if err := r.launchOsqueryInstance(); err != nil { level.Info(r.instance.logger).Log( - "msg", "fatal error restarting instance", + "msg", "fatal error restarting instance, shutting down", "err", err, ) - os.Exit(1) + r.instanceLock.Unlock() + if err := r.Shutdown(); err != nil { + level.Error(r.instance.logger).Log( + "msg", "could not perform shutdown", + "err", err, + ) + } + return } r.instanceLock.Unlock()