wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
143
service/updates/restart.go
Normal file
143
service/updates/restart.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
|
||||
const (
|
||||
// RestartExitCode will instruct portmaster-start to restart the process immediately, potentially with a new version.
|
||||
RestartExitCode = 23
|
||||
)
|
||||
|
||||
var (
|
||||
// RebootOnRestart defines whether the whole system, not just the service,
|
||||
// should be restarted automatically when triggering a restart internally.
|
||||
RebootOnRestart bool
|
||||
|
||||
restartTask *modules.Task
|
||||
restartPending = abool.New()
|
||||
restartTriggered = abool.New()
|
||||
|
||||
restartTime time.Time
|
||||
restartTimeLock sync.Mutex
|
||||
)
|
||||
|
||||
// IsRestarting returns whether a restart has been triggered.
|
||||
func IsRestarting() bool {
|
||||
return restartTriggered.IsSet()
|
||||
}
|
||||
|
||||
// RestartIsPending returns whether a restart is pending.
|
||||
func RestartIsPending() (pending bool, restartAt time.Time) {
|
||||
if restartPending.IsNotSet() {
|
||||
return false, time.Time{}
|
||||
}
|
||||
|
||||
restartTimeLock.Lock()
|
||||
defer restartTimeLock.Unlock()
|
||||
|
||||
return true, restartTime
|
||||
}
|
||||
|
||||
// DelayedRestart triggers a restart of the application by shutting down the
|
||||
// module system gracefully and returning with RestartExitCode. The restart
|
||||
// may be further delayed by up to 10 minutes by the internal task scheduling
|
||||
// system. This only works if the process is managed by portmaster-start.
|
||||
func DelayedRestart(delay time.Duration) {
|
||||
// Check if restart is already pending.
|
||||
if !restartPending.SetToIf(false, true) {
|
||||
return
|
||||
}
|
||||
|
||||
// Schedule the restart task.
|
||||
log.Warningf("updates: restart triggered, will execute in %s", delay)
|
||||
restartAt := time.Now().Add(delay)
|
||||
restartTask.Schedule(restartAt)
|
||||
|
||||
// Set restartTime.
|
||||
restartTimeLock.Lock()
|
||||
defer restartTimeLock.Unlock()
|
||||
restartTime = restartAt
|
||||
}
|
||||
|
||||
// AbortRestart aborts a (delayed) restart.
|
||||
func AbortRestart() {
|
||||
if restartPending.SetToIf(true, false) {
|
||||
log.Warningf("updates: restart aborted")
|
||||
|
||||
// Cancel schedule.
|
||||
restartTask.Schedule(time.Time{})
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerRestartIfPending triggers an automatic restart, if one is pending.
|
||||
// This can be used to prepone a scheduled restart if the conditions are preferable.
|
||||
func TriggerRestartIfPending() {
|
||||
if restartPending.IsSet() {
|
||||
restartTask.StartASAP()
|
||||
}
|
||||
}
|
||||
|
||||
// RestartNow immediately executes a restart.
|
||||
// This only works if the process is managed by portmaster-start.
|
||||
func RestartNow() {
|
||||
restartPending.Set()
|
||||
restartTask.StartASAP()
|
||||
}
|
||||
|
||||
func automaticRestart(_ context.Context, _ *modules.Task) error {
|
||||
// Check if the restart is still scheduled.
|
||||
if restartPending.IsNotSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Trigger restart.
|
||||
if restartTriggered.SetToIf(false, true) {
|
||||
log.Warning("updates: initiating (automatic) restart")
|
||||
|
||||
// Check if we should reboot instead.
|
||||
var rebooting bool
|
||||
if RebootOnRestart {
|
||||
// Trigger system reboot and record success.
|
||||
rebooting = triggerSystemReboot()
|
||||
if !rebooting {
|
||||
log.Warningf("updates: rebooting failed, only restarting service instead")
|
||||
}
|
||||
}
|
||||
|
||||
// Set restart exit code.
|
||||
if !rebooting {
|
||||
modules.SetExitStatusCode(RestartExitCode)
|
||||
}
|
||||
|
||||
// Do not use a worker, as this would block itself here.
|
||||
go modules.Shutdown() //nolint:errcheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func triggerSystemReboot() (success bool) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err := exec.Command("systemctl", "reboot").Run()
|
||||
if err != nil {
|
||||
log.Errorf("updates: triggering reboot with systemctl failed: %s", err)
|
||||
return false
|
||||
}
|
||||
default:
|
||||
log.Warningf("updates: rebooting is not support on %s", runtime.GOOS)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user