diff --git a/updates/config.go b/updates/config.go index c0070d6f..79ed7c9a 100644 --- a/updates/config.go +++ b/updates/config.go @@ -5,14 +5,17 @@ import ( "fmt" "github.com/safing/portbase/config" + "github.com/safing/portbase/log" ) var ( releaseChannel config.StringOption devMode config.BoolOption + disableUpdates config.BoolOption - previousReleaseChannel string - previousDevMode bool + previousReleaseChannel string + updatesCurrentlyDisabled bool + previousDevMode bool ) func registerConfig() error { @@ -32,16 +35,35 @@ func registerConfig() error { return err } - return module.RegisterEventHook("config", "config change", "update registry config", updateRegistryConfig) + err = config.Register(&config.Option{ + Name: "Disable Updates", + Key: disableUpdatesKey, + Description: "Disable automatic updates.", + OptType: config.OptTypeBool, + ExpertiseLevel: config.ExpertiseLevelExpert, + ReleaseLevel: config.ReleaseLevelStable, + RequiresRestart: false, + DefaultValue: false, + ExternalOptType: "disable updates", + }) + if err != nil { + return err + } + + return nil } func initConfig() { releaseChannel = config.GetAsString(releaseChannelKey, releaseChannelStable) + disableUpdates = config.GetAsBool(disableUpdatesKey, false) + devMode = config.GetAsBool("core/devMode", false) } func updateRegistryConfig(_ context.Context, _ interface{}) error { changed := false + forceUpdate := false + if releaseChannel() != previousReleaseChannel { registry.SetBeta(releaseChannel() == releaseChannelBeta) previousReleaseChannel = releaseChannel() @@ -54,9 +76,24 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error { changed = true } + if disableUpdates() != updatesCurrentlyDisabled { + updatesCurrentlyDisabled = disableUpdates() + changed = true + forceUpdate = !updatesCurrentlyDisabled + } + if changed { registry.SelectVersions() module.TriggerEvent(VersionUpdateEvent, nil) + + if forceUpdate { + module.Resolve(updateFailed) + TriggerUpdate() + log.Infof("Automatic updates enabled again.") + } else { + module.Warning(updateFailed, "Updates are disabled!") + log.Warningf("Automatic updates are now disabled.") + } } return nil diff --git a/updates/main.go b/updates/main.go index 3a7fef99..c1040d42 100644 --- a/updates/main.go +++ b/updates/main.go @@ -19,6 +19,8 @@ const ( releaseChannelStable = "stable" releaseChannelBeta = "beta" + disableUpdatesKey = "core/disableUpdates" + // ModuleName is the name of the update module // and can be used when declaring module dependencies. ModuleName = "updates" @@ -36,6 +38,10 @@ const ( // to check if new versions of their resources are // available by checking File.UpgradeAvailable(). ResourceUpdateEvent = "resource update" + + // TriggerUpdateEvent is the event that can be emitted + // by the updates module to trigger an update. + TriggerUpdateEvent = "trigger update" ) var ( @@ -46,15 +52,51 @@ var ( disableTaskSchedule bool ) +const ( + updateInProgress = "update-in-progress" + updateInProcessDescr = "Portmaster is currently checking and downloading updates." + updateFailed = "update-failed" +) + func init() { - module = modules.Register(ModuleName, registerConfig, start, stop, "base") + module = modules.Register(ModuleName, prep, start, stop, "base") module.RegisterEvent(VersionUpdateEvent) module.RegisterEvent(ResourceUpdateEvent) } +func prep() error { + if err := registerConfig(); err != nil { + return err + } + + module.RegisterEvent(TriggerUpdateEvent) + + return nil +} + func start() error { initConfig() + if err := module.RegisterEventHook( + "config", + "config change", + "update registry config", + updateRegistryConfig); err != nil { + return err + } + + if err := module.RegisterEventHook( + module.Name, + TriggerUpdateEvent, + "Check for and download available updates", + func(context.Context, interface{}) error { + TriggerUpdate() + return nil + }, + ); err != nil { + return err + } + var mandatoryUpdates []string if onWindows { mandatoryUpdates = []string{ @@ -115,8 +157,8 @@ func start() error { if !disableTaskSchedule { updateTask. - Repeat(24 * time.Hour). - MaxDelay(1 * time.Hour). + Repeat(1 * time.Hour). + MaxDelay(30 * time.Minute). Schedule(time.Now().Add(10 * time.Second)) } @@ -138,6 +180,7 @@ func TriggerUpdate() error { updateASAP = true } else { updateTask.StartASAP() + log.Debugf("updates: triggering update to run as soon as possible") } return nil @@ -156,14 +199,33 @@ func DisableUpdateSchedule() error { return nil } -func checkForUpdates(ctx context.Context) error { - if err := registry.UpdateIndexes(); err != nil { - return fmt.Errorf("updates: failed to update indexes: %w", err) +func checkForUpdates(ctx context.Context) (err error) { + if updatesCurrentlyDisabled { + log.Debugf("updates: automatic updates are disabled") + return nil + } + defer log.Debugf("updates: finished checking for updates") + + module.Hint(updateInProgress, updateInProcessDescr) + + defer func() { + if err == nil { + module.Resolve(updateInProgress) + module.Resolve(updateFailed) + } else { + module.Warning(updateFailed, "Failed to check for updates: "+err.Error()) + } + }() + + if err = registry.UpdateIndexes(); err != nil { + err = fmt.Errorf("failed to update indexes: %w", err) + return } - err := registry.DownloadUpdates(ctx) + err = registry.DownloadUpdates(ctx) if err != nil { - return fmt.Errorf("updates: failed to update: %w", err) + err = fmt.Errorf("failed to update: %w", err) + return } registry.SelectVersions()