diff --git a/cmds/portmaster-core/main.go b/cmds/portmaster-core/main.go index 01d71402..630f5827 100644 --- a/cmds/portmaster-core/main.go +++ b/cmds/portmaster-core/main.go @@ -55,10 +55,7 @@ func initialize() *service.Instance { // Create instance. var execCmdLine bool - instance, err := service.New(&service.ServiceConfig{ - IsRunningAsService: isRunningAsService(), - DefaultRestartCommand: defaultRestartCommand, - }) + instance, err := service.New(&service.ServiceConfig{}) switch { case err == nil: // Continue diff --git a/cmds/portmaster-core/main_linux.go b/cmds/portmaster-core/main_linux.go index e8268dd7..e0145822 100644 --- a/cmds/portmaster-core/main_linux.go +++ b/cmds/portmaster-core/main_linux.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "os/signal" + "strings" "syscall" "time" @@ -15,8 +16,6 @@ import ( "github.com/safing/portmaster/service" ) -var defaultRestartCommand = exec.Command("systemctl", "restart", "portmaster") - func run(instance *service.Instance) { // Set default log level. log.SetLogLevel(log.WarningLevel) @@ -101,9 +100,34 @@ func run(instance *service.Instance) { printStackTo(os.Stdout, "PRINTING STACK ON EXIT") } + // Check if restart was trigger and send start service command if true. + if isRunningAsService() && instance.ShouldRestart { + _ = runServiceRestart() + } + os.Exit(instance.ExitCode()) } +func runServiceRestart() error { + // Check if user defined custom command for restarting the service. + restartCommand, exists := os.LookupEnv("PORTMASTER_RESTART_COMMAND") + + // Run the service restart + if exists && restartCommand != "" { + log.Debugf(`instance: running custom restart command: "%s"`, restartCommand) + commandSplit := strings.Split(restartCommand, " ") + cmd := exec.Command(commandSplit[0], commandSplit[1:]...) + _ = cmd.Run() + } else { + cmd := exec.Command("systemctl", "restart", "portmaster") + if err := cmd.Start(); err != nil { + return fmt.Errorf("failed run restart command: %w", err) + } + + } + return nil +} + func isRunningAsService() bool { // Get the current process ID pid := os.Getpid() diff --git a/cmds/portmaster-core/main_windows.go b/cmds/portmaster-core/main_windows.go index cf47760e..7cd1c5bf 100644 --- a/cmds/portmaster-core/main_windows.go +++ b/cmds/portmaster-core/main_windows.go @@ -11,7 +11,6 @@ import ( "os" "os/exec" "os/signal" - "sync" "syscall" "time" @@ -21,14 +20,6 @@ import ( "golang.org/x/sys/windows/svc/debug" ) -var ( - // wait groups - runWg sync.WaitGroup - finishWg sync.WaitGroup - - defaultRestartCommand = exec.Command("sc.exe", "restart", "PortmasterCore") -) - const serviceName = "PortmasterCore" type windowsService struct { @@ -45,13 +36,15 @@ service: for { select { case <-ws.instance.Stopped(): - changes <- svc.Status{State: svc.StopPending} + log.Infof("instance stopped") break service case c := <-changeRequests: switch c.Cmd { case svc.Interrogate: changes <- c.CurrentStatus case svc.Stop, svc.Shutdown: + log.Debugf("received shutdown command") + changes <- svc.Status{State: svc.StopPending} ws.instance.Shutdown() default: log.Errorf("unexpected control request: #%d", c) @@ -59,9 +52,6 @@ service: } } - // wait until everything else is finished - // finishWg.Wait() - log.Shutdown() // send stopped status @@ -75,11 +65,13 @@ service: func run(instance *service.Instance) error { log.SetLogLevel(log.WarningLevel) _ = log.Start() + // check if we are running interactively isService, err := svc.IsWindowsService() if err != nil { return fmt.Errorf("could not determine if running interactively: %s", err) } + // select service run type svcRun := svc.Run if !isService { @@ -88,41 +80,20 @@ func run(instance *service.Instance) error { go registerSignalHandler(instance) } - runWg.Add(1) - // run service client - go func() { - sErr := svcRun(serviceName, &windowsService{ - instance: instance, - }) - if sErr != nil { - log.Infof("shuting down service with error: %s", sErr) - } else { - log.Infof("shuting down service") - } - runWg.Done() - }() + sErr := svcRun(serviceName, &windowsService{ + instance: instance, + }) + if sErr != nil { + fmt.Printf("shuting down service with error: %s", sErr) + } else { + fmt.Printf("shuting down service") + } - // finishWg.Add(1) - // run service - // go func() { - // // run slightly delayed - // time.Sleep(250 * time.Millisecond) - - // if err != nil { - // fmt.Printf("instance start failed: %s\n", err) - - // // Print stack on start failure, if enabled. - // if printStackOnExit { - // printStackTo(os.Stdout, "PRINTING STACK ON START FAILURE") - // } - - // } - // runWg.Done() - // finishWg.Done() - // }() - - runWg.Wait() + // Check if restart was trigger and send start service command if true. + if isRunningAsService() && instance.ShouldRestart { + _ = runServiceRestart() + } return err } @@ -179,3 +150,24 @@ func isRunningAsService() bool { } return isService } + +func runServiceRestart() error { + // Script that wait for portmaster service status to change to stop + // and then sends a start command for the same service. + command := ` +$serviceName = "PortmasterCore" +while ((Get-Service -Name $serviceName).Status -ne 'Stopped') { + Start-Sleep -Seconds 1 +} +sc.exe start $serviceName` + + // Create the command to execute the PowerShell script + cmd := exec.Command("powershell.exe", "-Command", command) + // Start the command. The script will continue even after the parent process exits. + err := cmd.Start() + if err != nil { + return err + } + + return nil +} diff --git a/service/config.go b/service/config.go index 8823ba5a..85a98603 100644 --- a/service/config.go +++ b/service/config.go @@ -1,8 +1,3 @@ package service -import "os/exec" - -type ServiceConfig struct { - IsRunningAsService bool - DefaultRestartCommand *exec.Cmd -} +type ServiceConfig struct{} diff --git a/service/instance.go b/service/instance.go index 7887b0ef..67682f1b 100644 --- a/service/instance.go +++ b/service/instance.go @@ -4,17 +4,14 @@ import ( "context" "fmt" "os" - "os/exec" "path/filepath" go_runtime "runtime" - "strings" "sync/atomic" "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" - "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/rng" @@ -104,9 +101,8 @@ type Instance struct { sluice *sluice.SluiceModule terminal *terminal.TerminalModule - CommandLineOperation func() error - isRunningAsService bool - defaultRestartCommand *exec.Cmd + CommandLineOperation func() error + ShouldRestart bool } func getCurrentBinaryFolder() (string, error) { @@ -183,8 +179,6 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx // Create instance to pass it to modules. instance := &Instance{} instance.ctx, instance.cancelCtx = context.WithCancel(context.Background()) - instance.isRunningAsService = svcCfg.IsRunningAsService - instance.defaultRestartCommand = svcCfg.DefaultRestartCommand var err error @@ -275,6 +269,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx if err != nil { return instance, fmt.Errorf("create customlist module: %w", err) } + instance.status, err = status.New(instance) if err != nil { return instance, fmt.Errorf("create status module: %w", err) @@ -678,7 +673,9 @@ func (i *Instance) Restart() { i.core.EventRestart.Submit(struct{}{}) time.Sleep(10 * time.Millisecond) - i.serviceRestart() + // Set the restart flag and shutdown. + i.ShouldRestart = true + i.shutdown(RestartExitCode) } // Shutdown asynchronously stops the instance. @@ -707,25 +704,6 @@ func (i *Instance) shutdown(exitCode int) { }) } -func (i *Instance) serviceRestart() { - if !i.isRunningAsService { - i.shutdown(RestartExitCode) - return - } - cmd := i.defaultRestartCommand - - // Check if user defined custom command for restarting the service. - restartCommand, exists := os.LookupEnv("PORTMASTER_RESTART_COMMAND") - - log.Debugf(`instance: running command "%s", %v`, restartCommand, exists) - if exists && restartCommand != "" { - commandSplit := strings.Split(restartCommand, " ") - cmd = exec.Command(commandSplit[0], commandSplit[1:]...) - } - log.Debugf("instance: running command %s", cmd.String()) - _ = cmd.Run() -} - // Stopping returns whether the instance is shutting down. func (i *Instance) Stopping() bool { return i.ctx.Err() != nil