diff --git a/service/broadcasts/module.go b/service/broadcasts/module.go index 2d99115b..19c1641f 100644 --- a/service/broadcasts/module.go +++ b/service/broadcasts/module.go @@ -93,5 +93,5 @@ func New(instance instance) (*Broadcasts, error) { } type instance interface { - Updates() *updates.Updates + IntelUpdates() *updates.Updates } diff --git a/service/broadcasts/notify.go b/service/broadcasts/notify.go index 73b05f98..235a1bf0 100644 --- a/service/broadcasts/notify.go +++ b/service/broadcasts/notify.go @@ -67,7 +67,7 @@ type BroadcastNotification struct { func broadcastNotify(ctx *mgr.WorkerCtx) error { // Get broadcast notifications file, load it from disk and parse it. - broadcastsResource, err := module.instance.Updates().GetFile(broadcastsResourcePath) + broadcastsResource, err := module.instance.IntelUpdates().GetFile(broadcastsResourcePath) if err != nil { return fmt.Errorf("failed to get broadcast notifications update: %w", err) } diff --git a/service/instance.go b/service/instance.go index ad6e9dab..43c7562b 100644 --- a/service/instance.go +++ b/service/instance.go @@ -34,6 +34,7 @@ import ( "github.com/safing/portmaster/service/sync" "github.com/safing/portmaster/service/ui" "github.com/safing/portmaster/service/updates" + "github.com/safing/portmaster/service/updates/registry" "github.com/safing/portmaster/spn/access" "github.com/safing/portmaster/spn/cabin" "github.com/safing/portmaster/spn/captain" @@ -46,6 +47,23 @@ import ( "github.com/safing/portmaster/spn/terminal" ) +var binaryUpdateIndex = registry.UpdateIndex{ + Directory: "/usr/lib/portmaster", + DownloadDirectory: "/var/lib/portmaster/new_bin", + Ignore: []string{"databases", "intel", "config.json"}, + IndexURLs: []string{"http://localhost:8000/test-binary.json"}, + IndexFile: "bin-index.json", + AutoApply: false, +} + +var intelUpdateIndex = registry.UpdateIndex{ + Directory: "/var/lib/portmaster/intel", + DownloadDirectory: "/var/lib/portmaster/new_intel", + IndexURLs: []string{"http://localhost:8000/test-intel.json"}, + IndexFile: "intel-index.json", + AutoApply: true, +} + // Instance is an instance of a Portmaster service. type Instance struct { ctx context.Context @@ -63,25 +81,26 @@ type Instance struct { rng *rng.Rng base *base.Base - core *core.Core - updates *updates.Updates - geoip *geoip.GeoIP - netenv *netenv.NetEnv - ui *ui.UI - profile *profile.ProfileModule - network *network.Network - netquery *netquery.NetQuery - firewall *firewall.Firewall - filterLists *filterlists.FilterLists - interception *interception.Interception - customlist *customlists.CustomList - status *status.Status - broadcasts *broadcasts.Broadcasts - compat *compat.Compat - nameserver *nameserver.NameServer - process *process.ProcessModule - resolver *resolver.ResolverModule - sync *sync.Sync + core *core.Core + binaryUpdates *updates.Updates + intelUpdates *updates.Updates + geoip *geoip.GeoIP + netenv *netenv.NetEnv + ui *ui.UI + profile *profile.ProfileModule + network *network.Network + netquery *netquery.NetQuery + firewall *firewall.Firewall + filterLists *filterlists.FilterLists + interception *interception.Interception + customlist *customlists.CustomList + status *status.Status + broadcasts *broadcasts.Broadcasts + compat *compat.Compat + nameserver *nameserver.NameServer + process *process.ProcessModule + resolver *resolver.ResolverModule + sync *sync.Sync access *access.Access @@ -147,7 +166,11 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx if err != nil { return instance, fmt.Errorf("create core module: %w", err) } - instance.updates, err = updates.New(instance) + instance.binaryUpdates, err = updates.New(instance, "Binary Updater", binaryUpdateIndex) + if err != nil { + return instance, fmt.Errorf("create updates module: %w", err) + } + instance.intelUpdates, err = updates.New(instance, "Intel Updater", intelUpdateIndex) if err != nil { return instance, fmt.Errorf("create updates module: %w", err) } @@ -274,7 +297,8 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx instance.notifications, instance.core, - instance.updates, + instance.binaryUpdates, + instance.intelUpdates, instance.geoip, instance.netenv, @@ -373,9 +397,14 @@ func (i *Instance) Base() *base.Base { return i.base } -// Updates returns the updates module. -func (i *Instance) Updates() *updates.Updates { - return i.updates +// BinaryUpdates returns the updates module. +func (i *Instance) BinaryUpdates() *updates.Updates { + return i.binaryUpdates +} + +// IntelUpdates returns the updates module. +func (i *Instance) IntelUpdates() *updates.Updates { + return i.intelUpdates } // GeoIP returns the geoip module. diff --git a/service/intel/filterlists/index.go b/service/intel/filterlists/index.go index 842a96b2..ba5267c2 100644 --- a/service/intel/filterlists/index.go +++ b/service/intel/filterlists/index.go @@ -175,7 +175,7 @@ func updateListIndex() error { case listIndexUpdate == nil: // This is the first time this function is run, get updater file for index. var err error - listIndexUpdate, err = module.instance.Updates().GetFile(listIndexFilePath) + listIndexUpdate, err = module.instance.IntelUpdates().GetFile(listIndexFilePath) if err != nil { return err } diff --git a/service/intel/filterlists/module.go b/service/intel/filterlists/module.go index 92f6576e..ecded5dc 100644 --- a/service/intel/filterlists/module.go +++ b/service/intel/filterlists/module.go @@ -57,11 +57,12 @@ func init() { } func prep() error { - module.instance.Updates().EventResourcesUpdated.AddCallback("Check for blocklist updates", + module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("Check for blocklist updates", func(wc *mgr.WorkerCtx, s struct{}) (bool, error) { if ignoreUpdateEvents.IsSet() { return false, nil } + log.Debugf("performing filter list upadte") return false, tryListUpdate(wc.Ctx()) }) @@ -141,6 +142,6 @@ func New(instance instance) (*FilterLists, error) { } type instance interface { - Updates() *updates.Updates + IntelUpdates() *updates.Updates NetEnv() *netenv.NetEnv } diff --git a/service/intel/geoip/database.go b/service/intel/geoip/database.go index 72197cd8..99f02318 100644 --- a/service/intel/geoip/database.go +++ b/service/intel/geoip/database.go @@ -198,7 +198,7 @@ func getGeoIPDB(resource string) (*geoIPDB, error) { } func open(resource string) (*registry.File, error) { - f, err := module.instance.Updates().GetFile(resource) + f, err := module.instance.IntelUpdates().GetFile(resource) if err != nil { return nil, fmt.Errorf("getting file: %w", err) } diff --git a/service/intel/geoip/module.go b/service/intel/geoip/module.go index 6c2bb55e..2ebde990 100644 --- a/service/intel/geoip/module.go +++ b/service/intel/geoip/module.go @@ -19,7 +19,7 @@ func (g *GeoIP) Manager() *mgr.Manager { } func (g *GeoIP) Start() error { - module.instance.Updates().EventResourcesUpdated.AddCallback( + module.instance.IntelUpdates().EventResourcesUpdated.AddCallback( "Check for GeoIP database updates", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) { worker.triggerUpdate() @@ -66,5 +66,5 @@ func New(instance instance) (*GeoIP, error) { } type instance interface { - Updates() *updates.Updates + IntelUpdates() *updates.Updates } diff --git a/service/netenv/main.go b/service/netenv/main.go index 20426772..81cd540f 100644 --- a/service/netenv/main.go +++ b/service/netenv/main.go @@ -107,5 +107,5 @@ func New(instance instance) (*NetEnv, error) { } type instance interface { - Updates() *updates.Updates + IntelUpdates() *updates.Updates } diff --git a/service/netenv/online-status.go b/service/netenv/online-status.go index 137ce410..1d5feeaf 100644 --- a/service/netenv/online-status.go +++ b/service/netenv/online-status.go @@ -220,7 +220,7 @@ func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string) // Trigger update check when coming (semi) online. if Online() { - module.instance.Updates().EventResourcesUpdated.Submit(struct{}{}) + module.instance.IntelUpdates().EventResourcesUpdated.Submit(struct{}{}) } } } diff --git a/service/ui/module.go b/service/ui/module.go index e9a8b324..2e4f1d9a 100644 --- a/service/ui/module.go +++ b/service/ui/module.go @@ -82,5 +82,5 @@ func New(instance instance) (*UI, error) { type instance interface { API() *api.API - Updates() *updates.Updates + BinaryUpdates() *updates.Updates } diff --git a/service/ui/serve.go b/service/ui/serve.go index 1455c3f6..ad41b2d5 100644 --- a/service/ui/serve.go +++ b/service/ui/serve.go @@ -91,7 +91,7 @@ func (bs *archiveServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // get file from update system - zipFile, err := module.instance.Updates().GetFile(fmt.Sprintf("%s.zip", moduleName)) + zipFile, err := module.instance.BinaryUpdates().GetFile(fmt.Sprintf("%s.zip", moduleName)) if err != nil { if errors.Is(err, registry.ErrNotFound) { log.Tracef("ui: requested module %s does not exist", moduleName) diff --git a/service/updates/module.go b/service/updates/module.go index 31de626c..a4b6490c 100644 --- a/service/updates/module.go +++ b/service/updates/module.go @@ -1,10 +1,8 @@ package updates import ( - "errors" "flag" - "fmt" - "sync/atomic" + "time" "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" @@ -14,10 +12,10 @@ import ( "github.com/safing/portmaster/service/updates/registry" ) -var applyUpdates bool +var autoUpdate bool func init() { - flag.BoolVar(&applyUpdates, "update", false, "apply downloaded updates") + flag.BoolVar(&autoUpdate, "auto-update", false, "auto apply downloaded updates") } // Updates provides access to released artifacts. @@ -25,9 +23,8 @@ type Updates struct { m *mgr.Manager states *mgr.StateMgr - updateBinaryWorkerMgr *mgr.WorkerMgr - updateIntelWorkerMgr *mgr.WorkerMgr - restartWorkerMgr *mgr.WorkerMgr + updateCheckWorkerMgr *mgr.WorkerMgr + upgraderWorkerMgr *mgr.WorkerMgr EventResourcesUpdated *mgr.EventMgr[struct{}] EventVersionsUpdated *mgr.EventMgr[struct{}] @@ -37,15 +34,9 @@ type Updates struct { instance instance } -var shimLoaded atomic.Bool - -// New returns a new UI module. -func New(instance instance) (*Updates, error) { - if !shimLoaded.CompareAndSwap(false, true) { - return nil, errors.New("only one instance allowed") - } - - m := mgr.New("Updates") +// New returns a new Updates module. +func New(instance instance, name string, index registry.UpdateIndex) (*Updates, error) { + m := mgr.New(name) module := &Updates{ m: m, states: m.NewStateMgr(), @@ -57,63 +48,47 @@ func New(instance instance) (*Updates, error) { } // Events - module.updateBinaryWorkerMgr = m.NewWorkerMgr("binary updater", module.checkForBinaryUpdates, nil) - module.updateIntelWorkerMgr = m.NewWorkerMgr("intel updater", module.checkForIntelUpdates, nil) - module.restartWorkerMgr = m.NewWorkerMgr("automatic restart", automaticRestart, nil) + module.updateCheckWorkerMgr = m.NewWorkerMgr("update checker", module.checkForUpdates, nil) + module.updateCheckWorkerMgr.Repeat(30 * time.Second) + module.upgraderWorkerMgr = m.NewWorkerMgr("upgrader", func(w *mgr.WorkerCtx) error { + err := module.registry.ApplyUpdates() + if err != nil { + // TODO(vladimir): Send notification to UI + log.Errorf("updates: failed to apply updates: %s", err) + } else { + module.instance.Restart() + } + return nil + }, nil) - binIndex := registry.UpdateIndex{ - Directory: "/usr/lib/portmaster", - DownloadDirectory: "/var/lib/portmaster/new_bin", - Ignore: []string{"databases", "intel", "config.json"}, - IndexURLs: []string{"http://localhost:8000/test-binary.json"}, - IndexFile: "bin-index.json", - AutoApply: false, - } - - intelIndex := registry.UpdateIndex{ - Directory: "/var/lib/portmaster/intel", - DownloadDirectory: "/var/lib/portmaster/new_intel", - IndexURLs: []string{"http://localhost:8000/test-intel.json"}, - IndexFile: "intel-index.json", - AutoApply: true, - } - module.registry = registry.New(binIndex, intelIndex) + module.registry = registry.New(index) + _ = module.registry.Initialize() return module, nil } -func (u *Updates) checkForBinaryUpdates(_ *mgr.WorkerCtx) error { - hasUpdates, err := u.registry.CheckForBinaryUpdates() +func (u *Updates) checkForUpdates(_ *mgr.WorkerCtx) error { + hasUpdates, err := u.registry.CheckForUpdates() if err != nil { - log.Errorf("updates: failed to check for binary updates: %s", err) + log.Errorf("updates: failed to check for updates: %s", err) } if hasUpdates { - log.Infof("updates: there is updates available in the binary bundle") - err = u.registry.DownloadBinaryUpdates() + log.Infof("updates: there is updates available") + err = u.registry.DownloadUpdates() if err != nil { log.Errorf("updates: failed to download bundle: %s", err) + } else if autoUpdate { + u.ApplyUpdates() } } else { - log.Infof("updates: no new binary updates") + log.Infof("updates: no new updates") + u.EventResourcesUpdated.Submit(struct{}{}) } return nil } -func (u *Updates) checkForIntelUpdates(_ *mgr.WorkerCtx) error { - hasUpdates, err := u.registry.CheckForIntelUpdates() - if err != nil { - log.Errorf("updates: failed to check for intel updates: %s", err) - } - if hasUpdates { - log.Infof("updates: there is updates available in the intel bundle") - err = u.registry.DownloadIntelUpdates() - if err != nil { - log.Errorf("updates: failed to download bundle: %s", err) - } - } else { - log.Infof("updates: no new intel data updates") - } - return nil +func (u *Updates) ApplyUpdates() { + u.upgraderWorkerMgr.Go() } // States returns the state manager. @@ -128,29 +103,7 @@ func (u *Updates) Manager() *mgr.Manager { // Start starts the module. func (u *Updates) Start() error { - // initConfig() - - if applyUpdates { - err := u.registry.ApplyBinaryUpdates() - if err != nil { - log.Errorf("updates: failed to apply binary updates: %s", err) - } - err = u.registry.ApplyIntelUpdates() - if err != nil { - log.Errorf("updates: failed to apply intel updates: %s", err) - } - u.instance.Restart() - return nil - } - - err := u.registry.Initialize() - if err != nil { - // TODO(vladimir): Find a better way to handle this error. The service will stop if parsing of the bundle files fails. - return fmt.Errorf("failed to initialize registry: %w", err) - } - - u.updateBinaryWorkerMgr.Go() - u.updateIntelWorkerMgr.Go() + u.updateCheckWorkerMgr.Go() return nil } diff --git a/service/updates/registry/bundle.go b/service/updates/registry/bundle.go index 3034438a..24953966 100644 --- a/service/updates/registry/bundle.go +++ b/service/updates/registry/bundle.go @@ -17,12 +17,6 @@ import ( "github.com/safing/portmaster/base/log" ) -const ( - defaultFileMode = os.FileMode(0o0644) - executableFileMode = os.FileMode(0o0744) - defaultDirMode = os.FileMode(0o0755) -) - const MaxUnpackSize = 1 << 30 // 2^30 == 1GB type Artifact struct { @@ -35,18 +29,17 @@ type Artifact struct { } type Bundle struct { - dir string Name string `json:"Bundle"` Version string `json:"Version"` Published time.Time `json:"Published"` Artifacts []Artifact `json:"Artifacts"` } -func (bundle Bundle) downloadAndVerify() { +func (bundle Bundle) downloadAndVerify(dir string) { client := http.Client{} for _, artifact := range bundle.Artifacts { - filePath := fmt.Sprintf("%s/%s", bundle.dir, artifact.Filename) + filePath := fmt.Sprintf("%s/%s", dir, artifact.Filename) // TODO(vladimir): is this needed? _ = os.MkdirAll(filepath.Dir(filePath), defaultDirMode) @@ -66,9 +59,9 @@ func (bundle Bundle) downloadAndVerify() { } // Verify checks if the files are present int the dataDir and have the correct hash. -func (bundle Bundle) Verify() error { +func (bundle Bundle) Verify(dir string) error { for _, artifact := range bundle.Artifacts { - artifactPath := fmt.Sprintf("%s/%s", bundle.dir, artifact.Filename) + artifactPath := fmt.Sprintf("%s/%s", dir, artifact.Filename) file, err := os.Open(artifactPath) if err != nil { return fmt.Errorf("failed to open file %s: %w", artifactPath, err) diff --git a/service/updates/registry/index.go b/service/updates/registry/index.go index f5900b3f..cc237163 100644 --- a/service/updates/registry/index.go +++ b/service/updates/registry/index.go @@ -12,6 +12,7 @@ import ( type UpdateIndex struct { Directory string DownloadDirectory string + PurgeDirectory string Ignore []string IndexURLs []string IndexFile string @@ -23,7 +24,7 @@ func (ui *UpdateIndex) downloadIndexFile() (err error) { for _, url := range ui.IndexURLs { err = ui.downloadIndexFileFromURL(url) if err != nil { - log.Warningf("updates: %s", err) + log.Warningf("updates: failed while downloading index file %s", err) continue } // Downloading was successful. @@ -37,7 +38,7 @@ func (ui *UpdateIndex) downloadIndexFileFromURL(url string) error { client := http.Client{} resp, err := client.Get(url) if err != nil { - return fmt.Errorf("failed a get request to %s: %w", url, err) + return fmt.Errorf("failed GET request to %s: %w", url, err) } defer func() { _ = resp.Body.Close() }() filePath := fmt.Sprintf("%s/%s", ui.DownloadDirectory, ui.IndexFile) diff --git a/service/updates/registry/registry.go b/service/updates/registry/registry.go index 223b90de..f0e77adb 100644 --- a/service/updates/registry/registry.go +++ b/service/updates/registry/registry.go @@ -14,6 +14,12 @@ import ( var ErrNotFound error = errors.New("file not found") +const ( + defaultFileMode = os.FileMode(0o0644) + executableFileMode = os.FileMode(0o0744) + defaultDirMode = os.FileMode(0o0755) +) + type File struct { id string path string @@ -32,24 +38,19 @@ func (f *File) Version() string { } type Registry struct { - binaryUpdateIndex UpdateIndex - intelUpdateIndex UpdateIndex + updateIndex UpdateIndex - binaryBundle *Bundle - intelBundle *Bundle - - binaryUpdateBundle *Bundle - intelUpdateBundle *Bundle + bundle *Bundle + updateBundle *Bundle files map[string]File } // New create new Registry. -func New(binIndex UpdateIndex, intelIndex UpdateIndex) Registry { +func New(index UpdateIndex) Registry { return Registry{ - binaryUpdateIndex: binIndex, - intelUpdateIndex: intelIndex, - files: make(map[string]File), + updateIndex: index, + files: make(map[string]File), } } @@ -58,26 +59,20 @@ func (reg *Registry) Initialize() error { var err error // Parse current installed binary bundle. - reg.binaryBundle, err = parseBundle(reg.binaryUpdateIndex.Directory, reg.binaryUpdateIndex.IndexFile) + reg.bundle, err = parseBundle(reg.updateIndex.Directory, reg.updateIndex.IndexFile) if err != nil { return fmt.Errorf("failed to parse binary bundle: %w", err) } - // Parse current installed intel bundle. - reg.intelBundle, err = parseBundle(reg.intelUpdateIndex.Directory, reg.intelUpdateIndex.IndexFile) - if err != nil { - return fmt.Errorf("failed to parse intel bundle: %w", err) - } // Add bundle artifacts to registry. - reg.processBundle(reg.binaryBundle) - reg.processBundle(reg.intelBundle) + reg.processBundle(reg.bundle) return nil } func (reg *Registry) processBundle(bundle *Bundle) { for _, artifact := range bundle.Artifacts { - artifactPath := fmt.Sprintf("%s/%s", bundle.dir, artifact.Filename) + artifactPath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, artifact.Filename) reg.files[artifact.Filename] = File{id: artifact.Filename, path: artifactPath} } } @@ -89,112 +84,84 @@ func (reg *Registry) GetFile(id string) (*File, error) { return &file, nil } else { log.Errorf("updates: requested file id not found: %s", id) + for _, file := range reg.files { + log.Debugf("File: %s", file) + } return nil, ErrNotFound } } -// CheckForBinaryUpdates checks if there is a new binary bundle updates. -func (reg *Registry) CheckForBinaryUpdates() (bool, error) { - err := reg.binaryUpdateIndex.downloadIndexFile() +// CheckForUpdates checks if there is a new binary bundle updates. +func (reg *Registry) CheckForUpdates() (bool, error) { + err := reg.updateIndex.downloadIndexFile() if err != nil { return false, err } - reg.binaryUpdateBundle, err = parseBundle(reg.binaryUpdateIndex.DownloadDirectory, reg.binaryUpdateIndex.IndexFile) + reg.updateBundle, err = parseBundle(reg.updateIndex.DownloadDirectory, reg.updateIndex.IndexFile) if err != nil { - return false, fmt.Errorf("failed to parse bundle file: %w", err) + return false, err } // TODO(vladimir): Make a better check. - if reg.binaryBundle.Version != reg.binaryUpdateBundle.Version { + if reg.bundle.Version != reg.updateBundle.Version { return true, nil } return false, nil } -// DownloadBinaryUpdates downloads available binary updates. -func (reg *Registry) DownloadBinaryUpdates() error { - if reg.binaryUpdateBundle == nil { +// DownloadUpdates downloads available binary updates. +func (reg *Registry) DownloadUpdates() error { + if reg.updateBundle == nil { // CheckForBinaryUpdates needs to be called before this. return fmt.Errorf("no valid update bundle found") } - _ = deleteUnfinishedDownloads(reg.binaryBundle.dir) - reg.binaryUpdateBundle.downloadAndVerify() + _ = deleteUnfinishedDownloads(reg.updateIndex.DownloadDirectory) + reg.updateBundle.downloadAndVerify(reg.updateIndex.DownloadDirectory) return nil } -// CheckForIntelUpdates checks if there is a new intel data bundle updates. -func (reg *Registry) CheckForIntelUpdates() (bool, error) { - err := reg.intelUpdateIndex.downloadIndexFile() +// ApplyUpdates removes the current binary folder and replaces it with the downloaded one. +func (reg *Registry) ApplyUpdates() error { + // Create purge dir. + err := os.MkdirAll(filepath.Dir(reg.updateIndex.PurgeDirectory), defaultDirMode) if err != nil { - return false, err + return fmt.Errorf("failed to create directory: %w", err) } - reg.intelUpdateBundle, err = parseBundle(reg.intelUpdateIndex.DownloadDirectory, reg.intelUpdateIndex.IndexFile) + // Read all files in the current version folder. + files, err := os.ReadDir(reg.updateIndex.Directory) if err != nil { - return false, fmt.Errorf("failed to parse bundle file: %w", err) + return err } - // TODO(vladimir): Make a better check. - if reg.intelBundle.Version != reg.intelUpdateBundle.Version { - return true, nil + // Move current version files into purge folder. + for _, file := range files { + filepath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, file.Name()) + purgePath := fmt.Sprintf("%s/%s", reg.updateIndex.PurgeDirectory, file.Name()) + err := os.Rename(filepath, purgePath) + if err != nil { + return fmt.Errorf("failed to move file %s: %w", filepath, err) + } } - return false, nil -} - -// DownloadIntelUpdates downloads available intel data updates. -func (reg *Registry) DownloadIntelUpdates() error { - if reg.intelUpdateBundle == nil { - // CheckForIntelUpdates needs to be called before this. - return fmt.Errorf("no valid update bundle found") - } - _ = deleteUnfinishedDownloads(reg.intelBundle.dir) - reg.intelUpdateBundle.downloadAndVerify() - return nil -} - -// ApplyBinaryUpdates removes the current binary folder and replaces it with the downloaded one. -func (reg *Registry) ApplyBinaryUpdates() error { - bundle, err := parseBundle(reg.binaryUpdateIndex.DownloadDirectory, reg.binaryUpdateIndex.IndexFile) + // Move the new index file + indexFile := fmt.Sprintf("%s/%s", reg.updateIndex.DownloadDirectory, reg.updateIndex.IndexFile) + newIndexFile := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, reg.updateIndex.IndexFile) + err = os.Rename(indexFile, newIndexFile) if err != nil { - return fmt.Errorf("failed to parse index file: %w", err) - } - err = bundle.Verify() - if err != nil { - return fmt.Errorf("binary bundle is not valid: %w", err) + return fmt.Errorf("failed to move index file %s: %w", indexFile, err) } - err = os.RemoveAll(reg.binaryUpdateIndex.Directory) - if err != nil { - return fmt.Errorf("failed to remove dir: %w", err) - } - err = os.Rename(reg.binaryUpdateIndex.DownloadDirectory, reg.binaryUpdateIndex.Directory) - if err != nil { - return fmt.Errorf("failed to move dir: %w", err) - } - return nil -} - -// ApplyIntelUpdates removes the current intel folder and replaces it with the downloaded one. -func (reg *Registry) ApplyIntelUpdates() error { - bundle, err := parseBundle(reg.intelUpdateIndex.DownloadDirectory, reg.intelUpdateIndex.IndexFile) - if err != nil { - return fmt.Errorf("failed to parse index file: %w", err) - } - err = bundle.Verify() - if err != nil { - return fmt.Errorf("binary bundle is not valid: %w", err) - } - - err = os.RemoveAll(reg.intelUpdateIndex.Directory) - if err != nil { - return fmt.Errorf("failed to remove dir: %w", err) - } - err = os.Rename(reg.intelUpdateIndex.DownloadDirectory, reg.intelUpdateIndex.Directory) - if err != nil { - return fmt.Errorf("failed to move dir: %w", err) + // Move downloaded files to the current version folder. + for _, artifact := range reg.bundle.Artifacts { + fromFilepath := fmt.Sprintf("%s/%s", reg.updateIndex.DownloadDirectory, artifact.Filename) + toFilepath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, artifact.Filename) + err = os.Rename(fromFilepath, toFilepath) + if err != nil { + return fmt.Errorf("failed to move file %s: %w", fromFilepath, err) + } } return nil } @@ -220,8 +187,6 @@ func parseBundle(dir string, indexFile string) (*Bundle, error) { if err != nil { return nil, err } - bundle.dir = dir - return &bundle, nil } diff --git a/service/updates/restart.go b/service/updates/restart.go index 30fc9289..66ee82d8 100644 --- a/service/updates/restart.go +++ b/service/updates/restart.go @@ -1,15 +1,12 @@ package updates import ( - "os/exec" - "runtime" "sync" "time" "github.com/tevino/abool" "github.com/safing/portmaster/base/log" - "github.com/safing/portmaster/service/mgr" ) var ( @@ -86,50 +83,3 @@ func RestartNow() { restartPending.Set() // module.restartWorkerMgr.Go() } - -func automaticRestart(w *mgr.WorkerCtx) 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 { - // module.instance.Restart() - // } else { - // module.instance.Shutdown() - // } - } - - 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 -} diff --git a/spn/captain/intel.go b/spn/captain/intel.go index 6411f4c6..df04c016 100644 --- a/spn/captain/intel.go +++ b/spn/captain/intel.go @@ -22,7 +22,7 @@ var ( ) func registerIntelUpdateHook() error { - module.instance.Updates().EventResourcesUpdated.AddCallback("update SPN intel", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { + module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("update SPN intel", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) { return false, updateSPNIntel(wc.Ctx(), nil) }) @@ -49,7 +49,7 @@ func updateSPNIntel(_ context.Context, _ interface{}) (err error) { } // Get intel file and load it from disk. - intelResource, err = module.instance.Updates().GetFile(intelResourcePath) + intelResource, err = module.instance.IntelUpdates().GetFile(intelResourcePath) if err != nil { return fmt.Errorf("failed to get SPN intel update: %w", err) } diff --git a/spn/captain/module.go b/spn/captain/module.go index 1f82d763..2cd16719 100644 --- a/spn/captain/module.go +++ b/spn/captain/module.go @@ -249,6 +249,6 @@ type instance interface { NetEnv() *netenv.NetEnv Patrol() *patrol.Patrol Config() *config.Config - Updates() *updates.Updates + IntelUpdates() *updates.Updates SPNGroup() *mgr.ExtendedGroup }