safing-portmaster/updates/updater.go
2019-07-30 13:07:33 +02:00

162 lines
4.1 KiB
Go

package updates
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
"time"
"github.com/safing/portbase/log"
"github.com/safing/portbase/utils"
)
func updater() {
time.Sleep(10 * time.Second)
for {
err := UpdateIndexes()
if err != nil {
log.Warningf("updates: updating index failed: %s", err)
}
err = DownloadUpdates()
if err != nil {
log.Warningf("updates: downloading updates failed: %s", err)
}
err = runFileUpgrades()
if err != nil {
log.Warningf("updates: failed to upgrade portmaster-control: %s", err)
}
err = cleanOldUpgradedFiles()
if err != nil {
log.Warningf("updates: failed to clean old upgraded files: %s", err)
}
time.Sleep(1 * time.Hour)
}
}
func markFileForDownload(identifier string) {
// get file
_, ok := localUpdates[identifier]
// only mark if it does not yet exist
if !ok {
localUpdates[identifier] = "loading..."
}
}
func markPlatformFileForDownload(identifier string) {
// add platform prefix
identifier = path.Join(fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH), identifier)
// mark file
markFileForDownload(identifier)
}
// UpdateIndexes downloads the current update indexes.
func UpdateIndexes() (err error) {
// download new indexes
var data []byte
for tries := 0; tries < 3; tries++ {
data, err = fetchData("stable.json", tries)
if err == nil {
break
}
}
if err != nil {
return fmt.Errorf("failed to download: %s", err)
}
newStableUpdates := make(map[string]string)
err = json.Unmarshal(data, &newStableUpdates)
if err != nil {
return fmt.Errorf("failed to parse: %s", err)
}
if len(newStableUpdates) == 0 {
return errors.New("index is empty")
}
// update stable index
updatesLock.Lock()
stableUpdates = newStableUpdates
updatesLock.Unlock()
// check dir
err = utils.EnsureDirectory(updateStoragePath, 0755)
if err != nil {
return err
}
// save stable index
err = ioutil.WriteFile(filepath.Join(updateStoragePath, "stable.json"), data, 0644)
if err != nil {
log.Warningf("updates: failed to save new version of stable.json: %s", err)
}
// update version status
updatesLock.RLock()
updateStatus(versionClassStable, stableUpdates)
updatesLock.RUnlock()
// FIXME IN STABLE: correct log line
log.Infof("updates: updated index stable.json (alpha/beta until we actually reach stable)")
return nil
}
// DownloadUpdates checks if updates are available and downloads updates of used components.
func DownloadUpdates() (err error) {
// ensure important components are always updated
updatesLock.Lock()
if runtime.GOOS == "windows" {
markPlatformFileForDownload("core/portmaster-core.exe")
markPlatformFileForDownload("control/portmaster-control.exe")
markPlatformFileForDownload("app/portmaster-app.exe")
markPlatformFileForDownload("notifier/portmaster-notifier.exe")
markPlatformFileForDownload("notifier/portmaster-snoretoast.exe")
} else {
markPlatformFileForDownload("core/portmaster-core")
markPlatformFileForDownload("control/portmaster-control")
markPlatformFileForDownload("app/portmaster-app")
markPlatformFileForDownload("notifier/portmaster-notifier")
}
updatesLock.Unlock()
// RLock for the remaining function
updatesLock.RLock()
defer updatesLock.RUnlock()
// update existing files
log.Tracef("updates: updating existing files")
for identifier, newVersion := range stableUpdates {
oldVersion, ok := localUpdates[identifier]
if ok && newVersion != oldVersion {
log.Tracef("updates: updating %s to %s", identifier, newVersion)
filePath := GetVersionedPath(identifier, newVersion)
realFilePath := filepath.Join(updateStoragePath, filePath)
for tries := 0; tries < 3; tries++ {
err = fetchFile(realFilePath, filePath, tries)
if err == nil {
break
}
}
if err != nil {
log.Warningf("updates: failed to update %s to %s: %s", identifier, newVersion, err)
}
}
}
log.Tracef("updates: finished updating existing files")
// remove tmp folder after we are finished
err = os.RemoveAll(downloadTmpPath)
if err != nil {
log.Tracef("updates: failed to remove tmp dir %s after downloading updates: %s", updateStoragePath, err)
}
return nil
}