mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
[WIP] Add restart service command for windows
This commit is contained in:
parent
61babe2822
commit
f0272766c1
5 changed files with 72 additions and 86 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
package service
|
||||
|
||||
import "os/exec"
|
||||
|
||||
type ServiceConfig struct {
|
||||
IsRunningAsService bool
|
||||
DefaultRestartCommand *exec.Cmd
|
||||
}
|
||||
type ServiceConfig struct{}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue