Merge pull request #331 from safing/fix/index-update-error

Improve release channels
This commit is contained in:
Daniel
2021-06-07 12:02:17 +02:00
committed by GitHub
20 changed files with 393 additions and 421 deletions

View File

@@ -14,7 +14,6 @@ import (
)
func checkAndCreateInstanceLock(path, name string) (pid int32, err error) {
lockFilePath := filepath.Join(dataRoot.Path, path, fmt.Sprintf("%s-lock.pid", name))
// read current pid file
@@ -86,7 +85,8 @@ func createInstanceLock(lockFilePath string) error {
}
// create lock file
err = ioutil.WriteFile(lockFilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0666)
// TODO: Investigate required permissions.
err = ioutil.WriteFile(lockFilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0666) //nolint:gosec
if err != nil {
return err
}

View File

@@ -96,7 +96,7 @@ func getPmStartLogFile(ext string) *os.File {
}, info.Version(), ext)
}
//nolint:deadcode,unused // false positive on linux, currently used by windows only
//nolint:unused // false positive on linux, currently used by windows only. TODO: move to a _windows file.
func logControlError(cErr error) {
// check if error present
if cErr == nil {
@@ -112,7 +112,7 @@ func logControlError(cErr error) {
fmt.Fprintln(errorFile, cErr.Error())
}
//nolint:deadcode,unused // false positive on linux, currently used by windows only
//nolint:deadcode,unused // false positive on linux, currently used by windows only. TODO: move to a _windows file.
func runAndLogControlError(wrappedFunc func(cmd *cobra.Command, args []string) error) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
err := wrappedFunc(cmd, args)

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
@@ -11,6 +12,10 @@ import (
"strings"
"syscall"
"github.com/safing/portmaster/updates/helper"
"github.com/tidwall/gjson"
"github.com/safing/portbase/dataroot"
"github.com/safing/portbase/info"
portlog "github.com/safing/portbase/log"
@@ -33,7 +38,6 @@ var (
UpdateURLs: []string{
"https://updates.safing.io",
},
Beta: false,
DevMode: false,
Online: true, // is disabled later based on command
}
@@ -65,7 +69,7 @@ func init() {
{
flags.StringVar(&dataDir, "data", "", "Configures the data directory. Alternatively, this can also be set via the environment variable PORTMASTER_DATA.")
flags.StringVar(&registry.UserAgent, "update-agent", "Start", "Sets the user agent for requests to the update server")
flags.BoolVar(&staging, "staging", false, "Use staging update channel (for testing only)")
flags.BoolVar(&staging, "staging", false, "Deprecated, configure in settings instead.")
flags.IntVar(&maxRetries, "max-retries", 5, "Maximum number of retries when starting a Portmaster component")
flags.BoolVar(&stdinSignals, "input-signals", false, "Emulate signals using stdin.")
_ = rootCmd.MarkPersistentFlagDirname("data")
@@ -164,32 +168,6 @@ func configureRegistry(mustLoadIndex bool) error {
return err
}
registry.AddIndex(updater.Index{
Path: "stable.json",
Stable: true,
Beta: false,
})
// TODO: enable loading beta versions
// registry.AddIndex(updater.Index{
// Path: "beta.json",
// Stable: false,
// Beta: true,
// })
if stagingActive() {
// Set flag no matter how staging was activated.
staging = true
log.Println("WARNING: staging environment is active.")
registry.AddIndex(updater.Index{
Path: "staging.json",
Stable: true,
Beta: true,
})
}
return updateRegistryIndex(mustLoadIndex)
}
@@ -210,6 +188,16 @@ func configureLogging() error {
}
func updateRegistryIndex(mustLoadIndex bool) error {
// Get release channel from config.
releaseChannel := getReleaseChannel(dataRoot)
// Set indexes based on the release channel.
helper.SetIndexes(registry, releaseChannel)
// Update registry to use pre-releases or not.
registry.SetUsePreReleases(releaseChannel != helper.ReleaseChannelStable)
// Load indexes from disk or network, if needed and desired.
err := registry.LoadIndexes(context.Background())
if err != nil {
log.Printf("WARNING: error loading indexes: %s\n", err)
@@ -218,6 +206,7 @@ func updateRegistryIndex(mustLoadIndex bool) error {
}
}
// Load versions from disk to know which others we have and which are available.
err = registry.ScanStorage("")
if err != nil {
log.Printf("WARNING: error during storage scan: %s\n", err)
@@ -247,13 +236,23 @@ func detectInstallationDir() string {
return parent
}
func stagingActive() bool {
// Check flag and env variable.
if staging || os.Getenv("PORTMASTER_STAGING") == "enabled" {
return true
func getReleaseChannel(dataRoot *utils.DirStructure) string {
configData, err := ioutil.ReadFile(filepath.Join(dataRoot.Path, "config.json"))
if err != nil {
if !os.IsNotExist(err) {
log.Printf("WARNING: failed to read config.json to get release channel: %s", err)
}
return helper.ReleaseChannelStable
}
// Check if staging index is present and acessible.
_, err := os.Stat(filepath.Join(registry.StorageDir().Path, "staging.json"))
return err == nil
// Get release channel from config, validate and return it.
channel := gjson.GetBytes(configData, helper.ReleaseChannelJSONKey).String()
switch channel {
case helper.ReleaseChannelStable,
helper.ReleaseChannelBeta,
helper.ReleaseChannelStaging:
return channel
default:
return helper.ReleaseChannelStable
}
}

View File

@@ -19,8 +19,8 @@ var recoverIPTablesCmd = &cobra.Command{
// we don't get the errno of the actual error and need to parse the
// output instead. Make sure it's always english by setting LC_ALL=C
currentLocale := os.Getenv("LC_ALL")
os.Setenv("LC_ALL", "C") // nolint:errcheck - we tried at least ...
defer os.Setenv("LC_ALL", currentLocale) // nolint:errcheck
os.Setenv("LC_ALL", "C")
defer os.Setenv("LC_ALL", currentLocale)
err := interception.DeactivateNfqueueFirewall()
if err == nil {

View File

@@ -33,17 +33,17 @@ var (
childIsRunning = abool.NewBool(false)
)
// Options for starting component
// Options for starting component.
type Options struct {
Name string
Identifier string // component identifier
ShortIdentifier string // populated automatically
LockPathPrefix string
PIDFile bool
ShortIdentifier string // populated automatically
SuppressArgs bool // do not use any args
AllowDownload bool // allow download of component if it is not yet available
AllowHidingWindow bool // allow hiding the window of the subprocess
NoOutput bool // do not use stdout/err if logging to file is available (did not fail to open log file)
SuppressArgs bool // do not use any args
AllowDownload bool // allow download of component if it is not yet available
AllowHidingWindow bool // allow hiding the window of the subprocess
NoOutput bool // do not use stdout/err if logging to file is available (did not fail to open log file)
}
func init() {
@@ -313,7 +313,7 @@ func execute(opts *Options, args []string) (cont bool, err error) {
log.Printf("starting %s %s\n", binPath, strings.Join(args, " "))
// create command
exc := exec.Command(binPath, args...) //nolint:gosec // everything is okay
exc := exec.Command(binPath, args...)
if !runningInConsole && opts.AllowHidingWindow {
// Windows only:

View File

@@ -7,7 +7,7 @@ import (
var (
startupComplete = make(chan struct{}) // signal that the start procedure completed (is never closed, just signaled once)
shuttingDown = make(chan struct{}) // signal that we are shutting down (will be closed, may not be closed directly, use initiateShutdown)
//nolint:deadcode,unused // false positive on linux, currently used by windows only
//nolint:unused // false positive on linux, currently used by windows only
shutdownError error // protected by shutdownLock
shutdownLock sync.Mutex
)

View File

@@ -40,8 +40,7 @@ var (
func indexRequired(cmd *cobra.Command) bool {
switch cmd {
case updateCmd,
purgeCmd:
case updateCmd, purgeCmd:
return true
default:
return false
@@ -49,35 +48,9 @@ func indexRequired(cmd *cobra.Command) bool {
}
func downloadUpdates() error {
// mark required updates
if onWindows {
registry.MandatoryUpdates = []string{
helper.PlatformIdentifier("core/portmaster-core.exe"),
helper.PlatformIdentifier("kext/portmaster-kext.dll"),
helper.PlatformIdentifier("kext/portmaster-kext.sys"),
helper.PlatformIdentifier("start/portmaster-start.exe"),
helper.PlatformIdentifier("notifier/portmaster-notifier.exe"),
helper.PlatformIdentifier("notifier/portmaster-snoretoast.exe"),
}
} else {
registry.MandatoryUpdates = []string{
helper.PlatformIdentifier("core/portmaster-core"),
helper.PlatformIdentifier("start/portmaster-start"),
helper.PlatformIdentifier("notifier/portmaster-notifier"),
}
}
// add updates that we require on all platforms.
registry.MandatoryUpdates = append(
registry.MandatoryUpdates,
helper.PlatformIdentifier("app/portmaster-app.zip"),
"all/ui/modules/portmaster.zip",
)
// Add assets that need unpacking.
registry.AutoUnpack = []string{
helper.PlatformIdentifier("app/portmaster-app.zip"),
}
// Set required updates.
registry.MandatoryUpdates = helper.MandatoryUpdates()
registry.AutoUnpack = helper.AutoUnpackUpdates()
// logging is configured as a persistent pre-run method inherited from
// the root command but since we don't use run.Run() we need to start
@@ -100,8 +73,8 @@ func downloadUpdates() error {
return fmt.Errorf("failed to create update dir: %w", err)
}
// Reset registry state.
registry.Reset()
// Reset registry resources.
registry.ResetResources()
}
// Update all indexes.

View File

@@ -12,62 +12,64 @@ import (
"github.com/spf13/cobra"
)
var showShortVersion bool
var showAllVersions bool
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display various portmaster versions",
Args: cobra.NoArgs,
PersistentPreRunE: func(*cobra.Command, []string) error {
if showAllVersions {
// If we are going to show all component versions,
// we need the registry to be configured.
if err := configureRegistry(false); err != nil {
return err
}
}
return nil
},
RunE: func(*cobra.Command, []string) error {
if !showAllVersions {
if showShortVersion {
fmt.Println(info.Version())
return nil
}
fmt.Println(info.FullVersion())
return nil
}
fmt.Printf("portmaster-start %s\n\n", info.Version())
fmt.Printf("Assets:\n")
all := registry.Export()
keys := make([]string, 0, len(all))
for identifier := range all {
keys = append(keys, identifier)
}
sort.Strings(keys)
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
for _, identifier := range keys {
res := all[identifier]
if showShortVersion {
// in "short" mode, skip all resources that are irrelevant on that platform
if !strings.HasPrefix(identifier, "all") && !strings.HasPrefix(identifier, runtime.GOOS) {
continue
var (
showShortVersion bool
showAllVersions bool
versionCmd = &cobra.Command{
Use: "version",
Short: "Display various portmaster versions",
Args: cobra.NoArgs,
PersistentPreRunE: func(*cobra.Command, []string) error {
if showAllVersions {
// If we are going to show all component versions,
// we need the registry to be configured.
if err := configureRegistry(false); err != nil {
return err
}
}
fmt.Fprintf(tw, " %s\t%s\n", identifier, res.SelectedVersion.VersionNumber)
}
tw.Flush()
return nil
},
RunE: func(*cobra.Command, []string) error {
if !showAllVersions {
if showShortVersion {
fmt.Println(info.Version())
return nil
}
return nil
},
}
fmt.Println(info.FullVersion())
return nil
}
fmt.Printf("portmaster-start %s\n\n", info.Version())
fmt.Printf("Assets:\n")
all := registry.Export()
keys := make([]string, 0, len(all))
for identifier := range all {
keys = append(keys, identifier)
}
sort.Strings(keys)
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
for _, identifier := range keys {
res := all[identifier]
if showShortVersion {
// in "short" mode, skip all resources that are irrelevant on that platform
if !strings.HasPrefix(identifier, "all") && !strings.HasPrefix(identifier, runtime.GOOS) {
continue
}
}
fmt.Fprintf(tw, " %s\t%s\n", identifier, res.SelectedVersion.VersionNumber)
}
tw.Flush()
return nil
},
}
)
func init() {
flags := versionCmd.Flags()

View File

@@ -1,7 +1,6 @@
package main
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -31,24 +30,7 @@ var rootCmd = &cobra.Command{
}
registry = &updater.ResourceRegistry{}
err = registry.Initialize(utils.NewDirStructure(absDistPath, 0o755))
if err != nil {
return err
}
registry.AddIndex(updater.Index{
Path: "stable.json",
Stable: true,
Beta: false,
})
registry.AddIndex(updater.Index{
Path: "beta.json",
Stable: false,
Beta: true,
})
err = registry.LoadIndexes(context.TODO())
err = registry.Initialize(utils.NewDirStructure(absDistPath, 0755))
if err != nil {
return err
}

View File

@@ -4,83 +4,140 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"github.com/spf13/cobra"
)
var (
releaseCmd = &cobra.Command{
Use: "release",
Short: "Release scans the distribution directory and creates registry indexes and the symlink structure",
RunE: release,
}
preReleaseCmd = &cobra.Command{
Use: "prerelease",
Short: "Stage scans the specified directory and loads the indexes - it then creates a staging index with all files newer than the stable and beta indexes",
Args: cobra.ExactArgs(1),
RunE: prerelease,
}
resetPreReleases bool
includeUnreleased bool
)
func init() {
rootCmd.AddCommand(releaseCmd)
}
var releaseCmd = &cobra.Command{
Use: "release",
Short: "Release scans the distribution directory and creates registry indexes and the symlink structure",
RunE: release,
rootCmd.AddCommand(preReleaseCmd)
preReleaseCmd.Flags().BoolVar(&resetPreReleases, "reset", false, "Reset pre-release assets")
}
func release(cmd *cobra.Command, args []string) error {
// Set stable and beta to latest version.
updateToLatestVersion(true, true)
return writeIndex(
"stable",
getChannelVersions("", false),
)
}
// Export versions.
betaData, err := json.MarshalIndent(exportSelected(true), "", " ")
if err != nil {
return err
func prerelease(cmd *cobra.Command, args []string) error {
channel := args[0]
// Check if we want to reset instead.
if resetPreReleases {
return removeFilesFromIndex(getChannelVersions(channel, true))
}
stableData, err := json.MarshalIndent(exportSelected(false), "", " ")
return writeIndex(
channel,
getChannelVersions(channel, false),
)
}
func writeIndex(channel string, versions map[string]string) error {
// Export versions and format them.
versionData, err := json.MarshalIndent(versions, "", " ")
if err != nil {
return err
}
// Build destination paths.
betaIndexFilePath := filepath.Join(registry.StorageDir().Path, "beta.json")
stableIndexFilePath := filepath.Join(registry.StorageDir().Path, "stable.json")
// Build destination path.
indexFilePath := filepath.Join(registry.StorageDir().Path, channel+".json")
// Print previews.
fmt.Printf("beta (%s):\n", betaIndexFilePath)
fmt.Println(string(betaData))
fmt.Printf("\nstable: (%s)\n", stableIndexFilePath)
fmt.Println(string(stableData))
// Print preview.
fmt.Printf("%s (%s):\n", channel, indexFilePath)
fmt.Println(string(versionData))
// Ask for confirmation.
if !confirm("\nDo you want to write these new indexes (and update latest symlinks)?") {
if !confirm("\nDo you want to write this index?") {
fmt.Println("aborted...")
return nil
}
// Write indexes.
err = ioutil.WriteFile(betaIndexFilePath, betaData, 0o644) //nolint:gosec // 0644 is intended
// Write new index to disk.
err = ioutil.WriteFile(indexFilePath, versionData, 0644) //nolint:gosec // 0644 is intended
if err != nil {
return err
}
fmt.Printf("written %s\n", betaIndexFilePath)
err = ioutil.WriteFile(stableIndexFilePath, stableData, 0o644) //nolint:gosec // 0644 is intended
if err != nil {
return err
}
fmt.Printf("written %s\n", stableIndexFilePath)
// Create symlinks to latest stable versions.
symlinksDir := registry.StorageDir().ChildDir("latest", 0o755)
err = registry.CreateSymlinks(symlinksDir)
if err != nil {
return err
}
fmt.Printf("updated stable symlinks in %s\n", symlinksDir.Path)
fmt.Printf("written %s\n", indexFilePath)
return nil
}
func updateToLatestVersion(stable, beta bool) error {
for _, resource := range registry.Export() {
sort.Sort(resource)
err := resource.AddVersion(resource.Versions[0].VersionNumber, false, stable, beta)
func removeFilesFromIndex(versions map[string]string) error {
// Print preview.
fmt.Println("To be deleted:")
for _, filePath := range versions {
fmt.Println(filePath)
}
// Ask for confirmation.
if !confirm("\nDo you want to delete these files?") {
fmt.Println("aborted...")
return nil
}
// Delete files.
for _, filePath := range versions {
err := os.Remove(filePath)
if err != nil {
return err
}
}
fmt.Println("deleted")
return nil
}
func getChannelVersions(channel string, storagePath bool) map[string]string {
// Sort all versions.
registry.SelectVersions()
export := registry.Export()
// Go through all versions and save the highest version, if not stable or beta.
versions := make(map[string]string)
for _, rv := range export {
for _, v := range rv.Versions {
// Ignore versions that don't match the release channel.
if v.SemVer().Prerelease() != channel {
// Stop at the first stable version, nothing should ever be selected
// beyond that.
if v.SemVer().Prerelease() == "" {
break
}
continue
}
// Add highest version of matching release channel.
if storagePath {
versions[rv.Identifier] = rv.GetFile().Path()
} else {
versions[rv.Identifier] = v.VersionNumber
}
break
}
}
return versions
}

View File

@@ -19,7 +19,7 @@ var scanCmd = &cobra.Command{
func scan(cmd *cobra.Command, args []string) error {
// Reset and rescan.
registry.Reset()
registry.ResetResources()
err := registry.ScanStorage("")
if err != nil {
return err
@@ -36,8 +36,8 @@ func scan(cmd *cobra.Command, args []string) error {
return nil
}
func exportSelected(beta bool) map[string]string {
registry.SetBeta(beta)
func exportSelected(preReleases bool) map[string]string {
registry.SetUsePreReleases(preReleases)
registry.SelectVersions()
export := registry.Export()

View File

@@ -1,96 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/spf13/cobra"
)
var (
stageReset bool
)
func init() {
rootCmd.AddCommand(stageCmd)
stageCmd.Flags().BoolVar(&stageReset, "reset", false, "Reset staging assets")
}
var stageCmd = &cobra.Command{
Use: "stage",
Short: "Stage scans the specified directory and loads the indexes - it then creates a staging index with all files newer than the stable and beta indexes",
RunE: stage,
}
func stage(cmd *cobra.Command, args []string) error {
// Check if we want to reset staging instead.
if stageReset {
for _, stagedPath := range exportStaging(true) {
err := os.Remove(stagedPath)
if err != nil {
return err
}
}
return nil
}
// Export all staged versions and format them.
stagingData, err := json.MarshalIndent(exportStaging(false), "", " ")
if err != nil {
return err
}
// Build destination path.
stagingIndexFilePath := filepath.Join(registry.StorageDir().Path, "staging.json")
// Print preview.
fmt.Printf("staging (%s):\n", stagingIndexFilePath)
fmt.Println(string(stagingData))
// Ask for confirmation.
if !confirm("\nDo you want to write this index?") {
fmt.Println("aborted...")
return nil
}
// Write new index to disk.
err = ioutil.WriteFile(stagingIndexFilePath, stagingData, 0o644) //nolint:gosec // 0644 is intended
if err != nil {
return err
}
fmt.Printf("written %s\n", stagingIndexFilePath)
return nil
}
func exportStaging(storagePath bool) map[string]string {
// Sort all versions.
registry.SetBeta(false)
registry.SelectVersions()
export := registry.Export()
// Go through all versions and save the highest version, if not stable or beta.
versions := make(map[string]string)
for _, rv := range export {
// Get highest version.
v := rv.Versions[0]
// Do not take stable or beta releases into account.
if v.StableRelease || v.BetaRelease {
continue
}
// Add highest version to staging
if storagePath {
rv.SelectedVersion = v
versions[rv.Identifier] = rv.GetFile().Path()
} else {
versions[rv.Identifier] = v.VersionNumber
}
}
return versions
}

2
go.mod
View File

@@ -30,7 +30,7 @@ require (
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tevino/abool v1.2.0
github.com/tidwall/gjson v1.7.5 // indirect
github.com/tidwall/gjson v1.7.5
github.com/tidwall/sjson v1.1.6 // indirect
github.com/tjfoc/gmsm v1.4.0 // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/safing/portbase/notifications"
"github.com/safing/portmaster/updates/helper"
"github.com/tevino/abool"
"github.com/safing/portbase/config"
@@ -20,6 +21,7 @@ var (
devMode config.BoolOption
enableUpdates config.BoolOption
initialReleaseChannel string
previousReleaseChannel string
updatesCurrentlyEnabled bool
previousDevMode bool
@@ -29,21 +31,33 @@ var (
func registerConfig() error {
err := config.Register(&config.Option{
Name: "Release Channel",
Key: releaseChannelKey,
Key: helper.ReleaseChannelKey,
Description: "Switch release channel.",
OptType: config.OptTypeString,
ExpertiseLevel: config.ExpertiseLevelDeveloper,
ReleaseLevel: config.ReleaseLevelExperimental,
RequiresRestart: false,
DefaultValue: releaseChannelStable,
RequiresRestart: true,
DefaultValue: helper.ReleaseChannelStable,
PossibleValues: []config.PossibleValue{
{
Name: "Stable",
Value: releaseChannelStable,
Name: "Stable",
Description: "Production releases.",
Value: helper.ReleaseChannelStable,
},
{
Name: "Beta",
Value: releaseChannelBeta,
Name: "Beta",
Description: "Production releases for testing new features that may break and cause interruption.",
Value: helper.ReleaseChannelBeta,
},
{
Name: "Special",
Description: "Special releases or version changes for troubleshooting. Only use temporarily and when instructed.",
Value: helper.ReleaseChannelSpecial,
},
{
Name: "Staging",
Description: "Dangerous development releases for testing random things and experimenting. Only use temporarily and when instructed.",
Value: helper.ReleaseChannelStaging,
},
},
Annotations: config.Annotations{
@@ -78,7 +92,8 @@ func registerConfig() error {
}
func initConfig() {
releaseChannel = config.GetAsString(releaseChannelKey, releaseChannelStable)
releaseChannel = config.GetAsString(helper.ReleaseChannelKey, helper.ReleaseChannelStable)
initialReleaseChannel = releaseChannel()
previousReleaseChannel = releaseChannel()
enableUpdates = config.GetAsBool(enableUpdatesKey, true)
@@ -107,7 +122,7 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error {
changed := false
if releaseChannel() != previousReleaseChannel {
registry.SetBeta(releaseChannel() == releaseChannelBeta)
registry.SetUsePreReleases(releaseChannel() != helper.ReleaseChannelStable)
previousReleaseChannel = releaseChannel()
changed = true
}

View File

@@ -11,14 +11,14 @@ import (
"github.com/safing/portbase/info"
"github.com/safing/portbase/log"
"github.com/safing/portbase/updater"
"github.com/safing/portmaster/updates/helper"
)
// database key for update information
// Database key for update information.
const (
versionsDBKey = "core:status/versions"
)
// working vars
var (
versionExport *versions
versionExportDB = database.NewInterface(&database.Options{
@@ -35,6 +35,7 @@ type versions struct {
Core *info.Info
Resources map[string]*updater.Resource
Channel string
Beta bool
Staging bool
@@ -45,8 +46,9 @@ func initVersionExport() (err error) {
// init export struct
versionExport = &versions{
internalSave: true,
Beta: registry.Beta,
Staging: staging,
Channel: initialReleaseChannel,
Beta: initialReleaseChannel == helper.ReleaseChannelBeta,
Staging: initialReleaseChannel == helper.ReleaseChannelStaging,
}
versionExport.SetKey(versionsDBKey)
@@ -68,7 +70,7 @@ func stopVersionExport() error {
return versionExportHook.Cancel()
}
// export is an event hook
// export is an event hook.
func export(_ context.Context, _ interface{}) error {
// populate
versionExport.lock.Lock()

View File

@@ -1,7 +1,6 @@
package helper
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -54,14 +53,6 @@ func EnsureChromeSandboxPermissions(reg *updater.ResourceRegistry) error {
return nil
}
// PlatformIdentifier converts identifier for the current platform.
func PlatformIdentifier(identifier string) string {
// From https://golang.org/pkg/runtime/#GOARCH
// GOOS is the running program's operating system target: one of darwin, freebsd, linux, and so on.
// GOARCH is the running program's architecture target: one of 386, amd64, arm, s390x, and so on.
return fmt.Sprintf("%s_%s/%s", runtime.GOOS, runtime.GOARCH, identifier)
}
func checkSysctl(setting string, value byte) bool {
c, err := sysctl(setting)
if err != nil {

58
updates/helper/indexes.go Normal file
View File

@@ -0,0 +1,58 @@
package helper
import (
"github.com/safing/portbase/updater"
)
const (
ReleaseChannelKey = "core/releaseChannel"
ReleaseChannelJSONKey = "core.releaseChannel"
ReleaseChannelStable = "stable"
ReleaseChannelBeta = "beta"
ReleaseChannelStaging = "staging"
ReleaseChannelSpecial = "special"
)
func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string) {
// Be reminded that the order is important, as indexes added later will
// override the current release from earlier indexes.
// Reset indexes before adding them (again).
registry.ResetIndexes()
// Always add the stable index as a base.
registry.AddIndex(updater.Index{
Path: ReleaseChannelStable + ".json",
})
// Add beta index if in beta or staging channel.
if releaseChannel == ReleaseChannelBeta ||
releaseChannel == ReleaseChannelStaging {
registry.AddIndex(updater.Index{
Path: ReleaseChannelBeta + ".json",
PreRelease: true,
})
}
// Add staging index if in staging channel.
if releaseChannel == ReleaseChannelStaging {
registry.AddIndex(updater.Index{
Path: ReleaseChannelStaging + ".json",
PreRelease: true,
})
}
// Add special index if in special channel.
if releaseChannel == ReleaseChannelSpecial {
registry.AddIndex(updater.Index{
Path: ReleaseChannelSpecial + ".json",
})
}
// Add the intel index last, as it updates the fastest and should not be
// crippled by other faulty indexes. It can only specify versions for its
// scope anyway.
registry.AddIndex(updater.Index{
Path: "all/intel/intel.json",
})
}

68
updates/helper/updates.go Normal file
View File

@@ -0,0 +1,68 @@
package helper
import (
"fmt"
"runtime"
)
const (
onWindows = runtime.GOOS == "windows"
)
// PlatformIdentifier converts identifier for the current platform.
func PlatformIdentifier(identifier string) string {
// From https://golang.org/pkg/runtime/#GOARCH
// GOOS is the running program's operating system target: one of darwin, freebsd, linux, and so on.
// GOARCH is the running program's architecture target: one of 386, amd64, arm, s390x, and so on.
return fmt.Sprintf("%s_%s/%s", runtime.GOOS, runtime.GOARCH, identifier)
}
// MandatoryUpdates returns mandatory updates that should be loaded on install
// or reset.
func MandatoryUpdates() (identifiers []string) {
// Binaries
if onWindows {
identifiers = []string{
PlatformIdentifier("core/portmaster-core.exe"),
PlatformIdentifier("kext/portmaster-kext.dll"),
PlatformIdentifier("kext/portmaster-kext.sys"),
PlatformIdentifier("start/portmaster-start.exe"),
PlatformIdentifier("notifier/portmaster-notifier.exe"),
PlatformIdentifier("notifier/portmaster-snoretoast.exe"),
}
} else {
identifiers = []string{
PlatformIdentifier("core/portmaster-core"),
PlatformIdentifier("start/portmaster-start"),
PlatformIdentifier("notifier/portmaster-notifier"),
}
}
// Components, Assets and Data
identifiers = append(
identifiers,
// User interface components
PlatformIdentifier("app/portmaster-app.zip"),
"all/ui/modules/portmaster.zip",
"all/ui/modules/assets.zip",
// Filter lists data
"all/intel/lists/base.dsdl",
"all/intel/lists/intermediate.dsdl",
"all/intel/lists/urgent.dsdl",
// Geo IP data
"all/intel/geoip/geoipv4.mmdb.gz",
"all/intel/geoip/geoipv6.mmdb.gz",
)
return identifiers
}
// AutoUnpackUpdates returns assets that need unpacking.
func AutoUnpackUpdates() []string {
return []string{
PlatformIdentifier("app/portmaster-app.zip"),
}
}

View File

@@ -4,11 +4,11 @@ import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
"runtime"
"time"
"github.com/safing/portmaster/updates/helper"
"github.com/safing/portbase/dataroot"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
@@ -19,10 +19,6 @@ import (
const (
onWindows = runtime.GOOS == "windows"
releaseChannelKey = "core/releaseChannel"
releaseChannelStable = "stable"
releaseChannelBeta = "beta"
enableUpdatesKey = "core/automaticUpdates"
// ModuleName is the name of the update module
@@ -48,16 +44,11 @@ var (
module *modules.Module
registry *updater.ResourceRegistry
userAgentFromFlag string
staging bool
updateTask *modules.Task
updateASAP bool
disableTaskSchedule bool
// MandatoryUpdates is a list of full identifiers that
// should always be kept up to date.
MandatoryUpdates []string
// UserAgent is an HTTP User-Agent that is used to add
// more context to requests made by the registry when
// fetching resources from the update server.
@@ -65,9 +56,8 @@ var (
)
const (
updateInProgress = "updates:in-progress"
updateFailed = "updates:failed"
updateSuccess = "updates:success"
updateFailed = "updates:failed"
updateSuccess = "updates:success"
)
func init() {
@@ -76,29 +66,9 @@ func init() {
module.RegisterEvent(ResourceUpdateEvent, true)
flag.StringVar(&userAgentFromFlag, "update-agent", "", "set the user agent for requests to the update server")
flag.BoolVar(&staging, "staging", false, "use staging update channel; for testing only")
// initialize mandatory updates
if onWindows {
MandatoryUpdates = []string{
platform("core/portmaster-core.exe"),
platform("start/portmaster-start.exe"),
platform("notifier/portmaster-notifier.exe"),
platform("notifier/portmaster-snoretoast.exe"),
}
} else {
MandatoryUpdates = []string{
platform("core/portmaster-core"),
platform("start/portmaster-start"),
platform("notifier/portmaster-notifier"),
}
}
MandatoryUpdates = append(
MandatoryUpdates,
platform("app/portmaster-app.zip"),
"all/ui/modules/portmaster.zip",
)
var dummy bool
flag.BoolVar(&dummy, "staging", false, "deprecated, configure in settings instead")
}
func prep() error {
@@ -107,8 +77,6 @@ func prep() error {
}
return registerAPIEndpoints()
return nil
}
func start() error {
@@ -131,13 +99,11 @@ func start() error {
"https://updates.safing.io",
},
UserAgent: UserAgent,
MandatoryUpdates: MandatoryUpdates,
AutoUnpack: []string{
platform("app/portmaster-app.zip"),
},
Beta: releaseChannel() == releaseChannelBeta,
DevMode: devMode(),
Online: true,
MandatoryUpdates: helper.MandatoryUpdates(),
AutoUnpack: helper.AutoUnpackUpdates(),
UsePreReleases: initialReleaseChannel != helper.ReleaseChannelStable,
DevMode: devMode(),
Online: true,
}
if userAgentFromFlag != "" {
// override with flag value
@@ -149,38 +115,8 @@ func start() error {
return err
}
registry.AddIndex(updater.Index{
Path: "stable.json",
Stable: true,
Beta: false,
})
if registry.Beta {
registry.AddIndex(updater.Index{
Path: "beta.json",
Stable: false,
Beta: true,
})
}
registry.AddIndex(updater.Index{
Path: "all/intel/intel.json",
Stable: true,
Beta: true,
})
if stagingActive() {
// Set flag no matter how staging was activated.
staging = true
log.Warning("updates: staging environment is active")
registry.AddIndex(updater.Index{
Path: "staging.json",
Stable: true,
Beta: true,
})
}
// Set indexes based on the release channel.
helper.SetIndexes(registry, initialReleaseChannel)
err = registry.LoadIndexes(module.Ctx)
if err != nil {
@@ -293,7 +229,7 @@ func checkForUpdates(ctx context.Context) (err error) {
notifications.NotifyWarn(
updateFailed,
"Update Check Failed",
"The Portmaster failed to check for updates. This might be a temporary issue of your device, your network or the update servers. The Portmaster will automatically try again later.",
"The Portmaster failed to check for updates. This might be a temporary issue of your device, your network or the update servers. The Portmaster will automatically try again later. If you just installed the Portmaster, please try disabling potentially conflicting software, such as other firewalls or VPNs.",
notifications.Action{
ID: "retry",
Text: "Try Again Now",
@@ -308,12 +244,13 @@ func checkForUpdates(ctx context.Context) (err error) {
}()
if err = registry.UpdateIndexes(ctx); err != nil {
log.Warningf("updates: failed to update indexes: %s", err)
err = fmt.Errorf("failed to update indexes: %s", err)
return
}
err = registry.DownloadUpdates(ctx)
if err != nil {
err = fmt.Errorf("failed to update: %w", err)
err = fmt.Errorf("failed to download updates: %w", err)
return
}
@@ -322,7 +259,7 @@ func checkForUpdates(ctx context.Context) (err error) {
// Unpack selected resources.
err = registry.UnpackResources()
if err != nil {
err = fmt.Errorf("failed to update: %w", err)
err = fmt.Errorf("failed to unpack updates: %w", err)
return
}
@@ -341,21 +278,6 @@ func stop() error {
return stopVersionExport()
}
func platform(identifier string) string {
return fmt.Sprintf("%s_%s/%s", runtime.GOOS, runtime.GOARCH, identifier)
}
func stagingActive() bool {
// Check flag and env variable.
if staging || os.Getenv("PORTMASTER_STAGING") == "enabled" {
return true
}
// Check if staging index is present and acessible.
_, err := os.Stat(filepath.Join(registry.StorageDir().Path, "staging.json"))
return err == nil
}
// RootPath returns the root path used for storing updates.
func RootPath() string {
if !module.Online() {

View File

@@ -351,10 +351,9 @@ func upgradeFile(fileToUpgrade string, file *updater.File) error {
}
// CopyFile atomically copies a file using the update registry's tmp dir.
func CopyFile(srcPath, dstPath string) (err error) {
func CopyFile(srcPath, dstPath string) error {
// check tmp dir
err = registry.TmpDir().Ensure()
err := registry.TmpDir().Ensure()
if err != nil {
return fmt.Errorf("could not prepare tmp directory for copying file: %w", err)
}
@@ -369,14 +368,14 @@ func CopyFile(srcPath, dstPath string) (err error) {
// open source
srcFile, err := os.Open(srcPath)
if err != nil {
return
return err
}
defer srcFile.Close()
// copy data
_, err = io.Copy(atomicDstFile, srcFile)
if err != nil {
return
return err
}
// finalize file