mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
[WIP] Error and state handleing improvments, better logs
This commit is contained in:
parent
072d7e6971
commit
b3ff6f14f1
24 changed files with 146 additions and 1981 deletions
|
@ -107,27 +107,29 @@ func registerAPIEndpoints() error {
|
|||
}
|
||||
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "updates/check",
|
||||
Read: api.PermitUser,
|
||||
Path: "updates/check",
|
||||
WriteMethod: "POST",
|
||||
Write: api.PermitUser,
|
||||
ActionFunc: func(ar *api.Request) (string, error) {
|
||||
module.instance.BinaryUpdates().TriggerUpdateCheck()
|
||||
module.instance.IntelUpdates().TriggerUpdateCheck()
|
||||
return "update check triggered", nil
|
||||
},
|
||||
Name: "Get the ID of the calling profile",
|
||||
Name: "Trigger updates check event",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "updates/apply",
|
||||
Read: api.PermitUser,
|
||||
Path: "updates/apply",
|
||||
WriteMethod: "POST",
|
||||
Write: api.PermitUser,
|
||||
ActionFunc: func(ar *api.Request) (string, error) {
|
||||
module.instance.BinaryUpdates().TriggerApplyUpdates()
|
||||
module.instance.IntelUpdates().TriggerApplyUpdates()
|
||||
return "upgrade triggered", nil
|
||||
},
|
||||
Name: "Get the ID of the calling profile",
|
||||
Name: "Trigger updates apply event",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -133,7 +132,8 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bo
|
|||
var originalPid int
|
||||
|
||||
// Get authenticated path.
|
||||
authenticatedPath := updates.RootPath()
|
||||
// FIXME(vladimir): provide a better check for detecting filepath. Note there is exception on linux with portmaster ui.
|
||||
authenticatedPath := "" // updates.RootPath()
|
||||
if authenticatedPath == "" {
|
||||
return false, fmt.Errorf(deniedMsgMisconfigured, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user
|
||||
}
|
||||
|
|
|
@ -133,13 +133,14 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
return nil, err
|
||||
}
|
||||
binaryUpdateIndex = updates.UpdateIndex{
|
||||
Directory: binaryFolder, // Default: C:/Program Files/Portmaster/binary
|
||||
Directory: binaryFolder, // Default: C:/Program Files/Portmaster
|
||||
DownloadDirectory: os.ExpandEnv("%ProgramData%/Portmaster/new_binary"),
|
||||
PurgeDirectory: os.ExpandEnv("%ProgramData%/Portmaster/old_binary"),
|
||||
Ignore: []string{"databases", "intel", "config.json"},
|
||||
IndexURLs: []string{"http://192.168.88.11:8000/test-binary.json"},
|
||||
IndexFile: "bin-index.json",
|
||||
AutoApply: false,
|
||||
NeedsRestart: true,
|
||||
}
|
||||
|
||||
intelUpdateIndex = updates.UpdateIndex{
|
||||
|
@ -149,6 +150,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
IndexURLs: []string{"http://192.168.88.11:8000/test-intel.json"},
|
||||
IndexFile: "intel-index.json",
|
||||
AutoApply: true,
|
||||
NeedsRestart: false,
|
||||
}
|
||||
} else if go_runtime.GOOS == "linux" {
|
||||
binaryUpdateIndex = updates.UpdateIndex{
|
||||
|
@ -159,6 +161,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
IndexURLs: []string{"http://localhost:8000/test-binary.json"},
|
||||
IndexFile: "bin-index.json",
|
||||
AutoApply: false,
|
||||
NeedsRestart: true,
|
||||
}
|
||||
|
||||
intelUpdateIndex = updates.UpdateIndex{
|
||||
|
@ -168,6 +171,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
IndexURLs: []string{"http://localhost:8000/test-intel.json"},
|
||||
IndexFile: "intel-index.json",
|
||||
AutoApply: true,
|
||||
NeedsRestart: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,9 @@ package process
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
)
|
||||
|
||||
type ProcessModule struct {
|
||||
|
@ -19,10 +17,6 @@ func (pm *ProcessModule) Manager() *mgr.Manager {
|
|||
}
|
||||
|
||||
func (pm *ProcessModule) Start() error {
|
||||
updatesPath = updates.RootPath()
|
||||
if updatesPath != "" {
|
||||
updatesPath += string(os.PathSeparator)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -30,8 +24,6 @@ func (pm *ProcessModule) Stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var updatesPath string
|
||||
|
||||
func prep() error {
|
||||
if err := registerConfiguration(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -72,19 +72,20 @@ func (p *Process) getSpecialProfileID() (specialProfileID string) {
|
|||
specialProfileID = profile.PortmasterProfileID
|
||||
default:
|
||||
// Check if this is another Portmaster component.
|
||||
if updatesPath != "" && strings.HasPrefix(p.Path, updatesPath) {
|
||||
switch {
|
||||
case strings.Contains(p.Path, "portmaster-app"):
|
||||
specialProfileID = profile.PortmasterAppProfileID
|
||||
case strings.Contains(p.Path, "portmaster-notifier"):
|
||||
specialProfileID = profile.PortmasterNotifierProfileID
|
||||
default:
|
||||
// Unexpected binary from within the Portmaster updates directpry.
|
||||
log.Warningf("process: unexpected binary in the updates directory: %s", p.Path)
|
||||
// TODO: Assign a fully restricted profile in the future when we are
|
||||
// sure that we won't kill any of our own things.
|
||||
}
|
||||
}
|
||||
// FIXME(vladimir): provide a better check for detecting filepath. Note there is exception on linux with portmaster ui.
|
||||
// if updatesPath != "" && strings.HasPrefix(p.Path, updatesPath) {
|
||||
// switch {
|
||||
// case strings.Contains(p.Path, "portmaster-app"):
|
||||
// specialProfileID = profile.PortmasterAppProfileID
|
||||
// case strings.Contains(p.Path, "portmaster-notifier"):
|
||||
// specialProfileID = profile.PortmasterNotifierProfileID
|
||||
// default:
|
||||
// // Unexpected binary from within the Portmaster updates directpry.
|
||||
// log.Warningf("process: unexpected binary in the updates directory: %s", p.Path)
|
||||
// // TODO: Assign a fully restricted profile in the future when we are
|
||||
// // sure that we won't kill any of our own things.
|
||||
// }
|
||||
// }
|
||||
// Check if this is the system resolver.
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
|
|
|
@ -3,7 +3,6 @@ package profile
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portmaster/base/config"
|
||||
|
@ -14,13 +13,9 @@ import (
|
|||
_ "github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/profile/binmeta"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
)
|
||||
|
||||
var (
|
||||
migrations = migration.New("core:migrations/profile")
|
||||
updatesPath string
|
||||
)
|
||||
var migrations = migration.New("core:migrations/profile")
|
||||
|
||||
// Events.
|
||||
const (
|
||||
|
@ -80,11 +75,6 @@ func prep() error {
|
|||
}
|
||||
|
||||
func start() error {
|
||||
updatesPath = updates.RootPath()
|
||||
if updatesPath != "" {
|
||||
updatesPath += string(os.PathSeparator)
|
||||
}
|
||||
|
||||
if err := loadProfilesMetadata(); err != nil {
|
||||
if !errors.Is(err, database.ErrNotFound) {
|
||||
log.Warningf("profile: failed to load profiles metadata, falling back to empty state: %s", err)
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
// "bytes"
|
||||
// "io"
|
||||
// "net/http"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
|
||||
// "github.com/ghodss/yaml"
|
||||
|
||||
// "github.com/safing/portmaster/base/api"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
apiPathCheckForUpdates = "updates/check"
|
||||
)
|
||||
|
||||
// func registerAPIEndpoints() error {
|
||||
// if err := api.RegisterEndpoint(api.Endpoint{
|
||||
// Name: "Check for Updates",
|
||||
// Description: "Checks if new versions are available. If automatic updates are enabled, they are also downloaded and applied.",
|
||||
// Parameters: []api.Parameter{{
|
||||
// Method: http.MethodPost,
|
||||
// Field: "download",
|
||||
// Value: "",
|
||||
// Description: "Force downloading and applying of all updates, regardless of auto-update settings.",
|
||||
// }},
|
||||
// Path: apiPathCheckForUpdates,
|
||||
// Write: api.PermitUser,
|
||||
// ActionFunc: func(r *api.Request) (msg string, err error) {
|
||||
// // Check if we should also download regardless of settings.
|
||||
// downloadAll := r.URL.Query().Has("download")
|
||||
|
||||
// // Trigger update task.
|
||||
// err = TriggerUpdate(true, downloadAll)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
// // Report how we triggered.
|
||||
// if downloadAll {
|
||||
// return "downloading all updates...", nil
|
||||
// }
|
||||
// return "checking for updates...", nil
|
||||
// },
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if err := api.RegisterEndpoint(api.Endpoint{
|
||||
// Name: "Get Resource",
|
||||
// Description: "Returns the requested resource from the udpate system",
|
||||
// Path: `updates/get/{identifier:[A-Za-z0-9/\.\-_]{1,255}}`,
|
||||
// Read: api.PermitUser,
|
||||
// ReadMethod: http.MethodGet,
|
||||
// HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
||||
// // Get identifier from URL.
|
||||
// var identifier string
|
||||
// if ar := api.GetAPIRequest(r); ar != nil {
|
||||
// identifier = ar.URLVars["identifier"]
|
||||
// }
|
||||
// if identifier == "" {
|
||||
// http.Error(w, "no resource speicified", http.StatusBadRequest)
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Get resource.
|
||||
// resource, err := registry.GetFile(identifier)
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusNotFound)
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Open file for reading.
|
||||
// file, err := os.Open(resource.Path())
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// defer file.Close() //nolint:errcheck,gosec
|
||||
|
||||
// // Assign file to reader
|
||||
// var reader io.Reader = file
|
||||
|
||||
// // Add version to header.
|
||||
// w.Header().Set("Resource-Version", resource.Version())
|
||||
|
||||
// // Set Content-Type.
|
||||
// contentType, _ := utils.MimeTypeByExtension(filepath.Ext(resource.Path()))
|
||||
// w.Header().Set("Content-Type", contentType)
|
||||
|
||||
// // Check if the content type may be returned.
|
||||
// accept := r.Header.Get("Accept")
|
||||
// if accept != "" {
|
||||
// mimeTypes := strings.Split(accept, ",")
|
||||
// // First, clean mime types.
|
||||
// for i, mimeType := range mimeTypes {
|
||||
// mimeType = strings.TrimSpace(mimeType)
|
||||
// mimeType, _, _ = strings.Cut(mimeType, ";")
|
||||
// mimeTypes[i] = mimeType
|
||||
// }
|
||||
// // Second, check if we may return anything.
|
||||
// var acceptsAny bool
|
||||
// for _, mimeType := range mimeTypes {
|
||||
// switch mimeType {
|
||||
// case "*", "*/*":
|
||||
// acceptsAny = true
|
||||
// }
|
||||
// }
|
||||
// // Third, check if we can convert.
|
||||
// if !acceptsAny {
|
||||
// var converted bool
|
||||
// sourceType, _, _ := strings.Cut(contentType, ";")
|
||||
// findConvertiblePair:
|
||||
// for _, mimeType := range mimeTypes {
|
||||
// switch {
|
||||
// case sourceType == "application/yaml" && mimeType == "application/json":
|
||||
// yamlData, err := io.ReadAll(reader)
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// jsonData, err := yaml.YAMLToJSON(yamlData)
|
||||
// if err != nil {
|
||||
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// reader = bytes.NewReader(jsonData)
|
||||
// converted = true
|
||||
// break findConvertiblePair
|
||||
// }
|
||||
// }
|
||||
|
||||
// // If we could not convert to acceptable format, return an error.
|
||||
// if !converted {
|
||||
// http.Error(w, "conversion to requested format not supported", http.StatusNotAcceptable)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Write file.
|
||||
// w.WriteHeader(http.StatusOK)
|
||||
// if r.Method != http.MethodHead {
|
||||
// _, err = io.Copy(w, reader)
|
||||
// if err != nil {
|
||||
// log.Errorf("updates: failed to serve resource file: %s", err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
|
@ -4,6 +4,7 @@ import (
|
|||
"archive/zip"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
@ -106,7 +107,7 @@ func (bundle Bundle) CopyMatchingFilesFromCurrent(current Bundle, currentDir, ne
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to write to file %s: %w", destFilePath, err)
|
||||
}
|
||||
|
||||
log.Debugf("updates: file copied from current version: %s", newArtifact.Filename)
|
||||
}
|
||||
break new
|
||||
}
|
||||
|
@ -115,7 +116,7 @@ func (bundle Bundle) CopyMatchingFilesFromCurrent(current Bundle, currentDir, ne
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bundle Bundle) DownloadAndVerify(client *http.Client, dir string) {
|
||||
func (bundle Bundle) DownloadAndVerify(ctx context.Context, client *http.Client, dir string) {
|
||||
// Make sure dir exists
|
||||
_ = os.MkdirAll(dir, defaultDirMode)
|
||||
|
||||
|
@ -130,7 +131,7 @@ func (bundle Bundle) DownloadAndVerify(client *http.Client, dir string) {
|
|||
}
|
||||
|
||||
// Download artifact
|
||||
err := processArtifact(client, artifact, filePath)
|
||||
err := processArtifact(ctx, client, artifact, filePath)
|
||||
if err != nil {
|
||||
log.Errorf("updates: %s", err)
|
||||
}
|
||||
|
@ -179,14 +180,15 @@ func checkIfFileIsValid(filename string, artifact Artifact) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func processArtifact(client *http.Client, artifact Artifact, filePath string) error {
|
||||
func processArtifact(ctx context.Context, client *http.Client, artifact Artifact, filePath string) error {
|
||||
providedHash, err := hex.DecodeString(artifact.SHA256)
|
||||
if err != nil || len(providedHash) != sha256.Size {
|
||||
return fmt.Errorf("invalid provided hash %s: %w", artifact.SHA256, err)
|
||||
}
|
||||
|
||||
// Download
|
||||
content, err := downloadFile(client, artifact.URLs)
|
||||
log.Debugf("updates: downloading file: %s", artifact.Filename)
|
||||
content, err := downloadFile(ctx, client, artifact.URLs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download artifact: %w", err)
|
||||
}
|
||||
|
@ -222,13 +224,23 @@ func processArtifact(client *http.Client, artifact Artifact, filePath string) er
|
|||
return fmt.Errorf("failed to rename file: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("updates: file downloaded and verified: %s", artifact.Filename)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadFile(client *http.Client, urls []string) ([]byte, error) {
|
||||
func downloadFile(ctx context.Context, client *http.Client, urls []string) ([]byte, error) {
|
||||
for _, url := range urls {
|
||||
// Try to make the request
|
||||
resp, err := client.Get(url)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, http.NoBody)
|
||||
if err != nil {
|
||||
log.Warningf("failed to create GET request to %s: %s", url, err)
|
||||
continue
|
||||
}
|
||||
if UserAgent != "" {
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Warningf("failed a get file request to: %s", err)
|
||||
continue
|
||||
|
|
|
@ -1,178 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portmaster/base/config"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/service/mgr"
|
||||
// "github.com/safing/portmaster/service/updates/helper"
|
||||
)
|
||||
|
||||
const cfgDevModeKey = "core/devMode"
|
||||
|
||||
var (
|
||||
releaseChannel config.StringOption
|
||||
devMode config.BoolOption
|
||||
enableSoftwareUpdates config.BoolOption
|
||||
enableIntelUpdates config.BoolOption
|
||||
|
||||
initialReleaseChannel string
|
||||
previousReleaseChannel string
|
||||
|
||||
softwareUpdatesCurrentlyEnabled bool
|
||||
intelUpdatesCurrentlyEnabled bool
|
||||
previousDevMode bool
|
||||
forceCheck = abool.New()
|
||||
forceDownload = abool.New()
|
||||
)
|
||||
|
||||
// func registerConfig() error {
|
||||
// err := config.Register(&config.Option{
|
||||
// Name: "Release Channel",
|
||||
// Key: helper.ReleaseChannelKey,
|
||||
// Description: `Use "Stable" for the best experience. The "Beta" channel will have the newest features and fixes, but may also break and cause interruption. Use others only temporarily and when instructed.`,
|
||||
// OptType: config.OptTypeString,
|
||||
// ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
// ReleaseLevel: config.ReleaseLevelStable,
|
||||
// RequiresRestart: true,
|
||||
// DefaultValue: helper.ReleaseChannelStable,
|
||||
// PossibleValues: []config.PossibleValue{
|
||||
// {
|
||||
// Name: "Stable",
|
||||
// Description: "Production releases.",
|
||||
// Value: helper.ReleaseChannelStable,
|
||||
// },
|
||||
// {
|
||||
// Name: "Beta",
|
||||
// Description: "Production releases for testing new features that may break and cause interruption.",
|
||||
// Value: helper.ReleaseChannelBeta,
|
||||
// },
|
||||
// {
|
||||
// Name: "Support",
|
||||
// Description: "Support releases or version changes for troubleshooting. Only use temporarily and when instructed.",
|
||||
// Value: helper.ReleaseChannelSupport,
|
||||
// },
|
||||
// {
|
||||
// Name: "Staging",
|
||||
// Description: "Dangerous development releases for testing random things and experimenting. Only use temporarily and when instructed.",
|
||||
// Value: helper.ReleaseChannelStaging,
|
||||
// },
|
||||
// },
|
||||
// Annotations: config.Annotations{
|
||||
// config.DisplayOrderAnnotation: -4,
|
||||
// config.DisplayHintAnnotation: config.DisplayHintOneOf,
|
||||
// config.CategoryAnnotation: "Updates",
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// err = config.Register(&config.Option{
|
||||
// Name: "Automatic Software Updates",
|
||||
// Key: enableSoftwareUpdatesKey,
|
||||
// Description: "Automatically check for and download software updates. This does not include intelligence data updates.",
|
||||
// OptType: config.OptTypeBool,
|
||||
// ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
// ReleaseLevel: config.ReleaseLevelStable,
|
||||
// RequiresRestart: false,
|
||||
// DefaultValue: true,
|
||||
// Annotations: config.Annotations{
|
||||
// config.DisplayOrderAnnotation: -12,
|
||||
// config.CategoryAnnotation: "Updates",
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// err = config.Register(&config.Option{
|
||||
// Name: "Automatic Intelligence Data Updates",
|
||||
// Key: enableIntelUpdatesKey,
|
||||
// Description: "Automatically check for and download intelligence data updates. This includes filter lists, geo-ip data, and more. Does not include software updates.",
|
||||
// OptType: config.OptTypeBool,
|
||||
// ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
// ReleaseLevel: config.ReleaseLevelStable,
|
||||
// RequiresRestart: false,
|
||||
// DefaultValue: true,
|
||||
// Annotations: config.Annotations{
|
||||
// config.DisplayOrderAnnotation: -11,
|
||||
// config.CategoryAnnotation: "Updates",
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func initConfig() {
|
||||
// releaseChannel = config.Concurrent.GetAsString(helper.ReleaseChannelKey, helper.ReleaseChannelStable)
|
||||
// initialReleaseChannel = releaseChannel()
|
||||
// previousReleaseChannel = releaseChannel()
|
||||
|
||||
// enableSoftwareUpdates = config.Concurrent.GetAsBool(enableSoftwareUpdatesKey, true)
|
||||
// enableIntelUpdates = config.Concurrent.GetAsBool(enableIntelUpdatesKey, true)
|
||||
// softwareUpdatesCurrentlyEnabled = enableSoftwareUpdates()
|
||||
// intelUpdatesCurrentlyEnabled = enableIntelUpdates()
|
||||
|
||||
// devMode = config.Concurrent.GetAsBool(cfgDevModeKey, false)
|
||||
// previousDevMode = devMode()
|
||||
// }
|
||||
|
||||
// func updateRegistryConfig(_ *mgr.WorkerCtx, _ struct{}) (cancel bool, err error) {
|
||||
// changed := false
|
||||
|
||||
// if enableSoftwareUpdates() != softwareUpdatesCurrentlyEnabled {
|
||||
// softwareUpdatesCurrentlyEnabled = enableSoftwareUpdates()
|
||||
// changed = true
|
||||
// }
|
||||
|
||||
// if enableIntelUpdates() != intelUpdatesCurrentlyEnabled {
|
||||
// intelUpdatesCurrentlyEnabled = enableIntelUpdates()
|
||||
// changed = true
|
||||
// }
|
||||
|
||||
// if devMode() != previousDevMode {
|
||||
// registry.SetDevMode(devMode())
|
||||
// previousDevMode = devMode()
|
||||
// changed = true
|
||||
// }
|
||||
|
||||
// if releaseChannel() != previousReleaseChannel {
|
||||
// previousReleaseChannel = releaseChannel()
|
||||
// changed = true
|
||||
// }
|
||||
|
||||
// if changed {
|
||||
// // Update indexes based on new settings.
|
||||
// warning := helper.SetIndexes(
|
||||
// registry,
|
||||
// releaseChannel(),
|
||||
// true,
|
||||
// softwareUpdatesCurrentlyEnabled,
|
||||
// intelUpdatesCurrentlyEnabled,
|
||||
// )
|
||||
// if warning != nil {
|
||||
// log.Warningf("updates: %s", warning)
|
||||
// }
|
||||
|
||||
// // Select versions depending on new indexes and modes.
|
||||
// registry.SelectVersions()
|
||||
// module.EventVersionsUpdated.Submit(struct{}{})
|
||||
|
||||
// if softwareUpdatesCurrentlyEnabled || intelUpdatesCurrentlyEnabled {
|
||||
// module.states.Clear()
|
||||
// if err := TriggerUpdate(true, false); err != nil {
|
||||
// log.Warningf("updates: failed to trigger update: %s", err)
|
||||
// }
|
||||
// log.Infof("updates: automatic updates are now enabled")
|
||||
// } else {
|
||||
// log.Warningf("updates: automatic updates are now completely disabled")
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false, nil
|
||||
// }
|
|
@ -1,237 +0,0 @@
|
|||
package updates
|
||||
|
||||
// import (
|
||||
// "fmt"
|
||||
// "sort"
|
||||
// "sync"
|
||||
|
||||
// "github.com/safing/portmaster/base/database/record"
|
||||
// "github.com/safing/portmaster/base/info"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/updater"
|
||||
// "github.com/safing/portmaster/base/utils/debug"
|
||||
// "github.com/safing/portmaster/service/mgr"
|
||||
// "github.com/safing/portmaster/service/updates/helper"
|
||||
// )
|
||||
|
||||
// const (
|
||||
// // versionsDBKey is the database key for update version information.
|
||||
// versionsDBKey = "core:status/versions"
|
||||
|
||||
// // versionsDBKey is the database key for simple update version information.
|
||||
// simpleVersionsDBKey = "core:status/simple-versions"
|
||||
|
||||
// // updateStatusDBKey is the database key for update status information.
|
||||
// updateStatusDBKey = "core:status/updates"
|
||||
// )
|
||||
|
||||
// // Versions holds update versions and status information.
|
||||
// type Versions struct {
|
||||
// record.Base
|
||||
// sync.Mutex
|
||||
|
||||
// Core *info.Info
|
||||
// Resources map[string]*updater.Resource
|
||||
// Channel string
|
||||
// Beta bool
|
||||
// Staging bool
|
||||
// }
|
||||
|
||||
// // SimpleVersions holds simplified update versions and status information.
|
||||
// type SimpleVersions struct {
|
||||
// record.Base
|
||||
// sync.Mutex
|
||||
|
||||
// Build *info.Info
|
||||
// Resources map[string]*SimplifiedResourceVersion
|
||||
// Channel string
|
||||
// }
|
||||
|
||||
// // SimplifiedResourceVersion holds version information about one resource.
|
||||
// type SimplifiedResourceVersion struct {
|
||||
// Version string
|
||||
// }
|
||||
|
||||
// // UpdateStateExport is a wrapper to export the updates state.
|
||||
// type UpdateStateExport struct {
|
||||
// record.Base
|
||||
// sync.Mutex
|
||||
|
||||
// *updater.UpdateState
|
||||
// }
|
||||
|
||||
// // GetVersions returns the update versions and status information.
|
||||
// // Resources must be locked when accessed.
|
||||
// func GetVersions() *Versions {
|
||||
// return &Versions{
|
||||
// Core: info.GetInfo(),
|
||||
// Resources: nil,
|
||||
// Channel: initialReleaseChannel,
|
||||
// Beta: initialReleaseChannel == helper.ReleaseChannelBeta,
|
||||
// Staging: initialReleaseChannel == helper.ReleaseChannelStaging,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // GetSimpleVersions returns the simplified update versions and status information.
|
||||
// func GetSimpleVersions() *SimpleVersions {
|
||||
// // Fill base info.
|
||||
// v := &SimpleVersions{
|
||||
// Build: info.GetInfo(),
|
||||
// Resources: make(map[string]*SimplifiedResourceVersion),
|
||||
// Channel: initialReleaseChannel,
|
||||
// }
|
||||
|
||||
// // Iterate through all versions and add version info.
|
||||
// // for id, resource := range registry.Export() {
|
||||
// // func() {
|
||||
// // resource.Lock()
|
||||
// // defer resource.Unlock()
|
||||
|
||||
// // // Get current in-used or selected version.
|
||||
// // var rv *updater.ResourceVersion
|
||||
// // switch {
|
||||
// // case resource.ActiveVersion != nil:
|
||||
// // rv = resource.ActiveVersion
|
||||
// // case resource.SelectedVersion != nil:
|
||||
// // rv = resource.SelectedVersion
|
||||
// // }
|
||||
|
||||
// // // Get information from resource.
|
||||
// // if rv != nil {
|
||||
// // v.Resources[id] = &SimplifiedResourceVersion{
|
||||
// // Version: rv.VersionNumber,
|
||||
// // }
|
||||
// // }
|
||||
// // }()
|
||||
// // }
|
||||
|
||||
// return v
|
||||
// }
|
||||
|
||||
// // GetStateExport gets the update state from the registry and returns it in an
|
||||
// // exportable struct.
|
||||
// func GetStateExport() *UpdateStateExport {
|
||||
// // export := registry.GetState()
|
||||
// return &UpdateStateExport{
|
||||
// // UpdateState: &export.Updates,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // LoadStateExport loads the exported update state from the database.
|
||||
// func LoadStateExport() (*UpdateStateExport, error) {
|
||||
// r, err := db.Get(updateStatusDBKey)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// // unwrap
|
||||
// if r.IsWrapped() {
|
||||
// // only allocate a new struct, if we need it
|
||||
// newRecord := &UpdateStateExport{}
|
||||
// err = record.Unwrap(r, newRecord)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return newRecord, nil
|
||||
// }
|
||||
|
||||
// // or adjust type
|
||||
// newRecord, ok := r.(*UpdateStateExport)
|
||||
// if !ok {
|
||||
// return nil, fmt.Errorf("record not of type *UpdateStateExport, but %T", r)
|
||||
// }
|
||||
// return newRecord, nil
|
||||
// }
|
||||
|
||||
// func initVersionExport() (err error) {
|
||||
// if err := GetVersions().save(); err != nil {
|
||||
// log.Warningf("updates: failed to export version information: %s", err)
|
||||
// }
|
||||
// if err := GetSimpleVersions().save(); err != nil {
|
||||
// log.Warningf("updates: failed to export version information: %s", err)
|
||||
// }
|
||||
|
||||
// // module.EventVersionsUpdated.AddCallback("export version status", export)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (v *Versions) save() error {
|
||||
// if !v.KeyIsSet() {
|
||||
// v.SetKey(versionsDBKey)
|
||||
// }
|
||||
// return db.Put(v)
|
||||
// }
|
||||
|
||||
// func (v *SimpleVersions) save() error {
|
||||
// if !v.KeyIsSet() {
|
||||
// v.SetKey(simpleVersionsDBKey)
|
||||
// }
|
||||
// return db.Put(v)
|
||||
// }
|
||||
|
||||
// func (s *UpdateStateExport) save() error {
|
||||
// if !s.KeyIsSet() {
|
||||
// s.SetKey(updateStatusDBKey)
|
||||
// }
|
||||
// return db.Put(s)
|
||||
// }
|
||||
|
||||
// // export is an event hook.
|
||||
// func export(_ *mgr.WorkerCtx, _ struct{}) (cancel bool, err error) {
|
||||
// // Export versions.
|
||||
// if err := GetVersions().save(); err != nil {
|
||||
// return false, err
|
||||
// }
|
||||
// if err := GetSimpleVersions().save(); err != nil {
|
||||
// return false, err
|
||||
// }
|
||||
// // Export udpate state.
|
||||
// if err := GetStateExport().save(); err != nil {
|
||||
// return false, err
|
||||
// }
|
||||
|
||||
// return false, nil
|
||||
// }
|
||||
|
||||
// // AddToDebugInfo adds the update system status to the given debug.Info.
|
||||
// func AddToDebugInfo(di *debug.Info) {
|
||||
// // Get resources from registry.
|
||||
// // resources := registry.Export()
|
||||
// // platformPrefix := helper.PlatformIdentifier("")
|
||||
|
||||
// // Collect data for debug info.
|
||||
// var active, selected []string
|
||||
// var activeCnt, totalCnt int
|
||||
// // for id, r := range resources {
|
||||
// // // Ignore resources for other platforms.
|
||||
// // if !strings.HasPrefix(id, "all/") && !strings.HasPrefix(id, platformPrefix) {
|
||||
// // continue
|
||||
// // }
|
||||
|
||||
// // totalCnt++
|
||||
// // if r.ActiveVersion != nil {
|
||||
// // activeCnt++
|
||||
// // active = append(active, fmt.Sprintf("%s: %s", id, r.ActiveVersion.VersionNumber))
|
||||
// // }
|
||||
// // if r.SelectedVersion != nil {
|
||||
// // selected = append(selected, fmt.Sprintf("%s: %s", id, r.SelectedVersion.VersionNumber))
|
||||
// // }
|
||||
// // }
|
||||
// sort.Strings(active)
|
||||
// sort.Strings(selected)
|
||||
|
||||
// // Compile to one list.
|
||||
// lines := make([]string, 0, len(active)+len(selected)+3)
|
||||
// lines = append(lines, "Active:")
|
||||
// lines = append(lines, active...)
|
||||
// lines = append(lines, "")
|
||||
// lines = append(lines, "Selected:")
|
||||
// lines = append(lines, selected...)
|
||||
|
||||
// // Add section.
|
||||
// di.AddSection(
|
||||
// fmt.Sprintf("Updates: %s (%d/%d)", initialReleaseChannel, activeCnt, totalCnt),
|
||||
// debug.UseCodeSection|debug.AddContentLineBreaks,
|
||||
// lines...,
|
||||
// )
|
||||
// }
|
|
@ -1,65 +0,0 @@
|
|||
package updates
|
||||
|
||||
// GetPlatformFile returns the latest platform specific file identified by the given identifier.
|
||||
// func GetPlatformFile(identifier string) (*updater.File, error) {
|
||||
// identifier = helper.PlatformIdentifier(identifier)
|
||||
|
||||
// file, err := registry.GetFile(identifier)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// module.EventVersionsUpdated.Submit(struct{}{})
|
||||
// return file, nil
|
||||
// }
|
||||
|
||||
// GetFile returns the latest generic file identified by the given identifier.
|
||||
// func GetFile(identifier string) (*updater.File, error) {
|
||||
// identifier = path.Join("all", identifier)
|
||||
|
||||
// file, err := registry.GetFile(identifier)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// module.EventVersionsUpdated.Submit(struct{}{})
|
||||
// return file, nil
|
||||
// }
|
||||
|
||||
// GetPlatformVersion returns the selected platform specific version of the
|
||||
// given identifier.
|
||||
// The returned resource version may not be modified.
|
||||
// func GetPlatformVersion(identifier string) (*updater.ResourceVersion, error) {
|
||||
// identifier = helper.PlatformIdentifier(identifier)
|
||||
|
||||
// rv, err := registry.GetVersion(identifier)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// return rv, nil
|
||||
// }
|
||||
|
||||
// GetVersion returns the selected generic version of the given identifier.
|
||||
// The returned resource version may not be modified.
|
||||
// func GetVersion(identifier string) (*updater.ResourceVersion, error) {
|
||||
// identifier = path.Join("all", identifier)
|
||||
|
||||
// rv, err := registry.GetVersion(identifier)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// return rv, nil
|
||||
// }
|
||||
|
||||
// GetVersionWithFullID returns the selected generic version of the given full identifier.
|
||||
// The returned resource version may not be modified.
|
||||
// func GetVersionWithFullID(identifier string) (*updater.ResourceVersion, error) {
|
||||
// rv, err := registry.GetVersion(identifier)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// return rv, nil
|
||||
// }
|
|
@ -1,58 +0,0 @@
|
|||
package helper
|
||||
|
||||
// import (
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "runtime"
|
||||
// "strings"
|
||||
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/updater"
|
||||
// "github.com/safing/portmaster/service/updates/registry"
|
||||
// )
|
||||
|
||||
// var pmElectronUpdate *registry.File
|
||||
|
||||
// const suidBitWarning = `Failed to set SUID permissions for chrome-sandbox. This is required for Linux kernel versions that do not have unprivileged user namespaces (CONFIG_USER_NS_UNPRIVILEGED) enabled. If you're running and up-to-date distribution kernel you can likely ignore this warning. If you encounter issue starting the user interface please either update your kernel or set the SUID bit (mode 0%0o) on %s`
|
||||
|
||||
// // EnsureChromeSandboxPermissions makes sure the chrome-sandbox distributed
|
||||
// // by our app-electron package has the SUID bit set on systems that do not
|
||||
// // allow unprivileged CLONE_NEWUSER (clone(3)).
|
||||
// // On non-linux systems or systems that have kernel.unprivileged_userns_clone
|
||||
// // set to 1 EnsureChromeSandboPermissions is a NO-OP.
|
||||
// func EnsureChromeSandboxPermissions(reg *updater.ResourceRegistry) error {
|
||||
// if runtime.GOOS != "linux" {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// if pmElectronUpdate != nil && !pmElectronUpdate.UpgradeAvailable() {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// identifier := PlatformIdentifier("app/portmaster-app.zip")
|
||||
|
||||
// var err error
|
||||
// pmElectronUpdate, err = reg.GetFile(identifier)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, updater.ErrNotAvailableLocally) {
|
||||
// return nil
|
||||
// }
|
||||
// return fmt.Errorf("failed to get file: %w", err)
|
||||
// }
|
||||
|
||||
// unpackedPath := strings.TrimSuffix(
|
||||
// pmElectronUpdate.Path(),
|
||||
// filepath.Ext(pmElectronUpdate.Path()),
|
||||
// )
|
||||
// sandboxFile := filepath.Join(unpackedPath, "chrome-sandbox")
|
||||
// if err := os.Chmod(sandboxFile, 0o0755|os.ModeSetuid); err != nil {
|
||||
// log.Errorf(suidBitWarning, 0o0755|os.ModeSetuid, sandboxFile)
|
||||
|
||||
// return fmt.Errorf("failed to chmod: %w", err)
|
||||
// }
|
||||
// log.Debugf("updates: fixed SUID permission for chrome-sandbox")
|
||||
|
||||
// return nil
|
||||
// }
|
|
@ -1,136 +0,0 @@
|
|||
package helper
|
||||
|
||||
// import (
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "io/fs"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
|
||||
// "github.com/safing/jess/filesig"
|
||||
// "github.com/safing/portmaster/base/updater"
|
||||
// )
|
||||
|
||||
// // Release Channel Configuration Keys.
|
||||
// const (
|
||||
// ReleaseChannelKey = "core/releaseChannel"
|
||||
// ReleaseChannelJSONKey = "core.releaseChannel"
|
||||
// )
|
||||
|
||||
// // Release Channels.
|
||||
// const (
|
||||
// ReleaseChannelStable = "stable"
|
||||
// ReleaseChannelBeta = "beta"
|
||||
// ReleaseChannelStaging = "staging"
|
||||
// ReleaseChannelSupport = "support"
|
||||
// )
|
||||
|
||||
// const jsonSuffix = ".json"
|
||||
|
||||
// // SetIndexes sets the update registry indexes and also configures the registry
|
||||
// // to use pre-releases based on the channel.
|
||||
// func SetIndexes(
|
||||
// registry *updater.ResourceRegistry,
|
||||
// releaseChannel string,
|
||||
// deleteUnusedIndexes bool,
|
||||
// autoDownload bool,
|
||||
// autoDownloadIntel bool,
|
||||
// ) (warning error) {
|
||||
// usePreReleases := false
|
||||
|
||||
// // 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()
|
||||
|
||||
// // Add the intel index first, in order to be able to override it with the
|
||||
// // other indexes when needed.
|
||||
// registry.AddIndex(updater.Index{
|
||||
// Path: "all/intel/intel.json",
|
||||
// AutoDownload: autoDownloadIntel,
|
||||
// })
|
||||
|
||||
// // Always add the stable index as a base.
|
||||
// registry.AddIndex(updater.Index{
|
||||
// Path: ReleaseChannelStable + jsonSuffix,
|
||||
// AutoDownload: autoDownload,
|
||||
// })
|
||||
|
||||
// // Add beta index if in beta or staging channel.
|
||||
// indexPath := ReleaseChannelBeta + jsonSuffix
|
||||
// if releaseChannel == ReleaseChannelBeta ||
|
||||
// releaseChannel == ReleaseChannelStaging ||
|
||||
// (releaseChannel == "" && indexExists(registry, indexPath)) {
|
||||
// registry.AddIndex(updater.Index{
|
||||
// Path: indexPath,
|
||||
// PreRelease: true,
|
||||
// AutoDownload: autoDownload,
|
||||
// })
|
||||
// usePreReleases = true
|
||||
// } else if deleteUnusedIndexes {
|
||||
// err := deleteIndex(registry, indexPath)
|
||||
// if err != nil {
|
||||
// warning = fmt.Errorf("failed to delete unused index %s: %w", indexPath, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Add staging index if in staging channel.
|
||||
// indexPath = ReleaseChannelStaging + jsonSuffix
|
||||
// if releaseChannel == ReleaseChannelStaging ||
|
||||
// (releaseChannel == "" && indexExists(registry, indexPath)) {
|
||||
// registry.AddIndex(updater.Index{
|
||||
// Path: indexPath,
|
||||
// PreRelease: true,
|
||||
// AutoDownload: autoDownload,
|
||||
// })
|
||||
// usePreReleases = true
|
||||
// } else if deleteUnusedIndexes {
|
||||
// err := deleteIndex(registry, indexPath)
|
||||
// if err != nil {
|
||||
// warning = fmt.Errorf("failed to delete unused index %s: %w", indexPath, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Add support index if in support channel.
|
||||
// indexPath = ReleaseChannelSupport + jsonSuffix
|
||||
// if releaseChannel == ReleaseChannelSupport ||
|
||||
// (releaseChannel == "" && indexExists(registry, indexPath)) {
|
||||
// registry.AddIndex(updater.Index{
|
||||
// Path: indexPath,
|
||||
// AutoDownload: autoDownload,
|
||||
// })
|
||||
// usePreReleases = true
|
||||
// } else if deleteUnusedIndexes {
|
||||
// err := deleteIndex(registry, indexPath)
|
||||
// if err != nil {
|
||||
// warning = fmt.Errorf("failed to delete unused index %s: %w", indexPath, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Set pre-release usage.
|
||||
// registry.SetUsePreReleases(usePreReleases)
|
||||
|
||||
// return warning
|
||||
// }
|
||||
|
||||
// func indexExists(registry *updater.ResourceRegistry, indexPath string) bool {
|
||||
// _, err := os.Stat(filepath.Join(registry.StorageDir().Path, indexPath))
|
||||
// return err == nil
|
||||
// }
|
||||
|
||||
// func deleteIndex(registry *updater.ResourceRegistry, indexPath string) error {
|
||||
// // Remove index itself.
|
||||
// err := os.Remove(filepath.Join(registry.StorageDir().Path, indexPath))
|
||||
// if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// // Remove any accompanying signature.
|
||||
// err = os.Remove(filepath.Join(registry.StorageDir().Path, indexPath+filesig.Extension))
|
||||
// if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
|
@ -1,42 +0,0 @@
|
|||
package helper
|
||||
|
||||
// import (
|
||||
// "github.com/safing/jess"
|
||||
// "github.com/safing/portmaster/base/updater"
|
||||
// )
|
||||
|
||||
// var (
|
||||
// // VerificationConfig holds the complete verification configuration for the registry.
|
||||
// VerificationConfig = map[string]*updater.VerificationOptions{
|
||||
// "": { // Default.
|
||||
// TrustStore: BinarySigningTrustStore,
|
||||
// DownloadPolicy: updater.SignaturePolicyRequire,
|
||||
// DiskLoadPolicy: updater.SignaturePolicyWarn,
|
||||
// },
|
||||
// "all/intel/": nil, // Disable until IntelHub supports signing.
|
||||
// }
|
||||
|
||||
// // BinarySigningKeys holds the signing keys in text format.
|
||||
// BinarySigningKeys = []string{
|
||||
// // Safing Code Signing Key #1
|
||||
// "recipient:public-ed25519-key:safing-code-signing-key-1:92bgBLneQUWrhYLPpBDjqHbpFPuNVCPAaivQ951A4aq72HcTiw7R1QmPJwFM1mdePAvEVDjkeb8S4fp2pmRCsRa8HrCvWQEjd88rfZ6TznJMfY4g7P8ioGFjfpyx2ZJ8WCZJG5Qt4Z9nkabhxo2Nbi3iywBTYDLSbP5CXqi7jryW7BufWWuaRVufFFzhwUC2ryWFWMdkUmsAZcvXwde4KLN9FrkWAy61fGaJ8GCwGnGCSitANnU2cQrsGBXZzxmzxwrYD",
|
||||
// // Safing Code Signing Key #2
|
||||
// "recipient:public-ed25519-key:safing-code-signing-key-2:92bgBLneQUWrhYLPpBDjqHbPC2d1o5JMyZFdavWBNVtdvbPfzDewLW95ScXfYPHd3QvWHSWCtB4xpthaYWxSkK1kYiGp68DPa2HaU8yQ5dZhaAUuV4Kzv42pJcWkCeVnBYqgGBXobuz52rFqhDJy3rz7soXEmYhJEJWwLwMeioK3VzN3QmGSYXXjosHMMNC76rjufSoLNtUQUWZDSnHmqbuxbKMCCsjFXUGGhtZVyb7bnu7QLTLk6SKHBJDMB6zdL9sw3",
|
||||
// }
|
||||
|
||||
// // BinarySigningTrustStore is an in-memory trust store with the signing keys.
|
||||
// BinarySigningTrustStore = jess.NewMemTrustStore()
|
||||
// )
|
||||
|
||||
// func init() {
|
||||
// for _, signingKey := range BinarySigningKeys {
|
||||
// rcpt, err := jess.RecipientFromTextFormat(signingKey)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// err = BinarySigningTrustStore.StoreSignet(rcpt)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -1,95 +0,0 @@
|
|||
package helper
|
||||
|
||||
// import (
|
||||
// "fmt"
|
||||
// "runtime"
|
||||
|
||||
// "github.com/tevino/abool"
|
||||
// )
|
||||
|
||||
// const onWindows = runtime.GOOS == "windows"
|
||||
|
||||
// var intelOnly = abool.New()
|
||||
|
||||
// // IntelOnly specifies that only intel data is mandatory.
|
||||
// func IntelOnly() {
|
||||
// intelOnly.Set()
|
||||
// }
|
||||
|
||||
// // 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) {
|
||||
// // Intel
|
||||
// identifiers = append(
|
||||
// identifiers,
|
||||
|
||||
// // Filter lists data
|
||||
// "all/intel/lists/index.dsd",
|
||||
// "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",
|
||||
// )
|
||||
|
||||
// // Stop here if we only want intel data.
|
||||
// if intelOnly.IsSet() {
|
||||
// return identifiers
|
||||
// }
|
||||
|
||||
// // Binaries
|
||||
// if onWindows {
|
||||
// identifiers = append(
|
||||
// identifiers,
|
||||
// PlatformIdentifier("core/portmaster-core.exe"),
|
||||
// PlatformIdentifier("kext/portmaster-kext.sys"),
|
||||
// PlatformIdentifier("kext/portmaster-kext.pdb"),
|
||||
// PlatformIdentifier("start/portmaster-start.exe"),
|
||||
// PlatformIdentifier("notifier/portmaster-notifier.exe"),
|
||||
// PlatformIdentifier("notifier/portmaster-wintoast.dll"),
|
||||
// PlatformIdentifier("app2/portmaster-app.zip"),
|
||||
// )
|
||||
// } else {
|
||||
// identifiers = append(
|
||||
// identifiers,
|
||||
// PlatformIdentifier("core/portmaster-core"),
|
||||
// PlatformIdentifier("start/portmaster-start"),
|
||||
// PlatformIdentifier("notifier/portmaster-notifier"),
|
||||
// PlatformIdentifier("app2/portmaster-app"),
|
||||
// )
|
||||
// }
|
||||
|
||||
// // 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",
|
||||
// )
|
||||
|
||||
// return identifiers
|
||||
// }
|
||||
|
||||
// // AutoUnpackUpdates returns assets that need unpacking.
|
||||
// func AutoUnpackUpdates() []string {
|
||||
// if intelOnly.IsSet() {
|
||||
// return []string{}
|
||||
// }
|
||||
|
||||
// return []string{
|
||||
// PlatformIdentifier("app/portmaster-app.zip"),
|
||||
// PlatformIdentifier("app2/portmaster-app.zip"),
|
||||
// }
|
||||
// }
|
|
@ -1,6 +1,7 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -18,13 +19,14 @@ type UpdateIndex struct {
|
|||
IndexURLs []string
|
||||
IndexFile string
|
||||
AutoApply bool
|
||||
NeedsRestart bool
|
||||
}
|
||||
|
||||
func (ui *UpdateIndex) DownloadIndexFile(client *http.Client) (err error) {
|
||||
func (ui *UpdateIndex) DownloadIndexFile(ctx context.Context, client *http.Client) (err error) {
|
||||
// Make sure dir exists
|
||||
_ = os.MkdirAll(ui.DownloadDirectory, defaultDirMode)
|
||||
for _, url := range ui.IndexURLs {
|
||||
err = ui.downloadIndexFileFromURL(client, url)
|
||||
err = ui.downloadIndexFileFromURL(ctx, client, url)
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed while downloading index file %s", err)
|
||||
continue
|
||||
|
@ -36,9 +38,18 @@ func (ui *UpdateIndex) DownloadIndexFile(client *http.Client) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (ui *UpdateIndex) downloadIndexFileFromURL(client *http.Client, url string) error {
|
||||
func (ui *UpdateIndex) downloadIndexFileFromURL(ctx context.Context, client *http.Client, url string) error {
|
||||
// Request the index file
|
||||
resp, err := client.Get(url)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, http.NoBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create GET request to %s: %w", url, err)
|
||||
}
|
||||
if UserAgent != "" {
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
}
|
||||
|
||||
// Perform request
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed GET request to %s: %w", url, err)
|
||||
}
|
||||
|
|
|
@ -1,70 +1 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/base/database"
|
||||
)
|
||||
|
||||
const (
|
||||
onWindows = runtime.GOOS == "windows"
|
||||
|
||||
enableSoftwareUpdatesKey = "core/automaticUpdates"
|
||||
enableIntelUpdatesKey = "core/automaticIntelUpdates"
|
||||
|
||||
// VersionUpdateEvent is emitted every time a new
|
||||
// version of a monitored resource is selected.
|
||||
// During module initialization VersionUpdateEvent
|
||||
// is also emitted.
|
||||
VersionUpdateEvent = "active version update"
|
||||
|
||||
// ResourceUpdateEvent is emitted every time the
|
||||
// updater successfully performed a resource update.
|
||||
// ResourceUpdateEvent is emitted even if no new
|
||||
// versions are available. Subscribers are expected
|
||||
// to check if new versions of their resources are
|
||||
// available by checking File.UpgradeAvailable().
|
||||
ResourceUpdateEvent = "resource update"
|
||||
)
|
||||
|
||||
var (
|
||||
userAgentFromFlag string
|
||||
updateServerFromFlag string
|
||||
|
||||
db = database.NewInterface(&database.Options{
|
||||
Local: true,
|
||||
Internal: true,
|
||||
})
|
||||
|
||||
// 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.
|
||||
UserAgent = fmt.Sprintf("Portmaster (%s %s)", runtime.GOOS, runtime.GOARCH)
|
||||
)
|
||||
|
||||
const (
|
||||
updateTaskRepeatDuration = 1 * time.Hour
|
||||
)
|
||||
|
||||
func stop() error {
|
||||
// if registry != nil {
|
||||
// err := registry.Cleanup()
|
||||
// if err != nil {
|
||||
// log.Warningf("updates: failed to clean up registry: %s", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RootPath returns the root path used for storing updates.
|
||||
func RootPath() string {
|
||||
// if !module.Online() {
|
||||
// return ""
|
||||
// }
|
||||
|
||||
// return registry.StorageDir().Path
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
semver "github.com/hashicorp/go-version"
|
||||
|
@ -15,7 +17,29 @@ import (
|
|||
"github.com/safing/portmaster/service/mgr"
|
||||
)
|
||||
|
||||
const updateAvailableNotificationID = "updates:update-available"
|
||||
const (
|
||||
updateTaskRepeatDuration = 1 * time.Hour
|
||||
updateAvailableNotificationID = "updates:update-available"
|
||||
|
||||
// VersionUpdateEvent is emitted every time a new
|
||||
// version of a monitored resource is selected.
|
||||
// During module initialization VersionUpdateEvent
|
||||
// is also emitted.
|
||||
VersionUpdateEvent = "active version update"
|
||||
|
||||
// ResourceUpdateEvent is emitted every time the
|
||||
// updater successfully performed a resource update.
|
||||
// ResourceUpdateEvent is emitted even if no new
|
||||
// versions are available. Subscribers are expected
|
||||
// to check if new versions of their resources are
|
||||
// available by checking File.UpgradeAvailable().
|
||||
ResourceUpdateEvent = "resource update"
|
||||
)
|
||||
|
||||
// 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.
|
||||
var UserAgent = fmt.Sprintf("Portmaster (%s %s)", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
type File struct {
|
||||
id string
|
||||
|
@ -75,7 +99,7 @@ func New(instance instance, name string, index UpdateIndex) (*Updates, error) {
|
|||
|
||||
// Events
|
||||
module.updateCheckWorkerMgr = m.NewWorkerMgr("update checker", module.checkForUpdates, nil)
|
||||
module.updateCheckWorkerMgr.Repeat(1 * time.Hour)
|
||||
module.updateCheckWorkerMgr.Repeat(updateTaskRepeatDuration)
|
||||
module.upgraderWorkerMgr = m.NewWorkerMgr("upgrader", module.applyUpdates, nil)
|
||||
|
||||
var err error
|
||||
|
@ -86,44 +110,10 @@ func New(instance instance, name string, index UpdateIndex) (*Updates, error) {
|
|||
|
||||
// Add bundle artifacts to registry.
|
||||
module.processBundle(module.bundle)
|
||||
err = module.registerEndpoints()
|
||||
if err != nil {
|
||||
log.Errorf("failed to register endpoints: %s", err)
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
func (u *Updates) registerEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Name: "Check for update",
|
||||
Description: "Trigger update check",
|
||||
Path: "updates/check",
|
||||
Read: api.PermitAnyone,
|
||||
ActionFunc: func(ar *api.Request) (msg string, err error) {
|
||||
u.updateCheckWorkerMgr.Go()
|
||||
return "Check for updates triggered", nil
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Name: "Apply update",
|
||||
Description: "Triggers update",
|
||||
Path: "updates/apply",
|
||||
Read: api.PermitAnyone,
|
||||
ActionFunc: func(ar *api.Request) (msg string, err error) {
|
||||
u.upgraderWorkerMgr.Go()
|
||||
return "Apply updates triggered", nil
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (reg *Updates) processBundle(bundle *Bundle) {
|
||||
for _, artifact := range bundle.Artifacts {
|
||||
artifactPath := fmt.Sprintf("%s/%s", reg.updateIndex.Directory, artifact.Filename)
|
||||
|
@ -131,9 +121,9 @@ func (reg *Updates) processBundle(bundle *Bundle) {
|
|||
}
|
||||
}
|
||||
|
||||
func (u *Updates) checkForUpdates(_ *mgr.WorkerCtx) error {
|
||||
func (u *Updates) checkForUpdates(wc *mgr.WorkerCtx) error {
|
||||
httpClient := http.Client{}
|
||||
err := u.updateIndex.DownloadIndexFile(&httpClient)
|
||||
err := u.updateIndex.DownloadIndexFile(wc.Ctx(), &httpClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download index file: %s", err)
|
||||
}
|
||||
|
@ -155,16 +145,23 @@ func (u *Updates) checkForUpdates(_ *mgr.WorkerCtx) error {
|
|||
}
|
||||
|
||||
log.Infof("updates: check complete: downloading new version: %s %s", u.updateBundle.Name, u.updateBundle.Version)
|
||||
err = u.downloadUpdates(&httpClient)
|
||||
err = u.downloadUpdates(wc.Ctx(), &httpClient)
|
||||
if err != nil {
|
||||
log.Errorf("updates: failed to download bundle: %s", err)
|
||||
} else {
|
||||
notifications.NotifyPrompt(updateAvailableNotificationID, "Update available", "Apply update and restart?", notifications.Action{
|
||||
ID: "apply",
|
||||
Text: "Apply",
|
||||
Type: notifications.ActionTypeInjectEvent,
|
||||
Payload: "apply-updates",
|
||||
})
|
||||
if u.updateIndex.AutoApply {
|
||||
u.upgraderWorkerMgr.Go()
|
||||
} else {
|
||||
notifications.NotifyPrompt(updateAvailableNotificationID, "Update available", "Apply update and restart?", notifications.Action{
|
||||
ID: "apply",
|
||||
Text: "Apply",
|
||||
Type: notifications.ActionTypeWebhook,
|
||||
Payload: notifications.ActionTypeWebhookPayload{
|
||||
Method: "POST",
|
||||
URL: "updates/apply",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -183,7 +180,7 @@ func (u *Updates) checkVersionIncrement() (bool, error) {
|
|||
return downloadVersion.GreaterThan(currentVersion), nil
|
||||
}
|
||||
|
||||
func (u *Updates) downloadUpdates(client *http.Client) error {
|
||||
func (u *Updates) downloadUpdates(ctx context.Context, client *http.Client) error {
|
||||
if u.updateBundle == nil {
|
||||
// checkForUpdates needs to be called before this.
|
||||
return fmt.Errorf("no valid update bundle found")
|
||||
|
@ -193,7 +190,7 @@ func (u *Updates) downloadUpdates(client *http.Client) error {
|
|||
if err != nil {
|
||||
log.Warningf("updates: error while coping file from current to update: %s", err)
|
||||
}
|
||||
u.updateBundle.DownloadAndVerify(client, u.updateIndex.DownloadDirectory)
|
||||
u.updateBundle.DownloadAndVerify(ctx, client, u.updateIndex.DownloadDirectory)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -208,18 +205,23 @@ func (u *Updates) applyUpdates(_ *mgr.WorkerCtx) error {
|
|||
return fmt.Errorf("there is no new version to apply")
|
||||
}
|
||||
|
||||
// Verify files of the downloaded files.
|
||||
err = u.updateBundle.Verify(u.updateIndex.DownloadDirectory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply update: %s", err)
|
||||
return fmt.Errorf("failed to verify downloaded files: %s", err)
|
||||
}
|
||||
|
||||
// New version is downloaded and verified. Start the update process
|
||||
log.Infof("update: starting update: %s %s -> %s", u.bundle.Name, u.bundle.Version, u.updateBundle.Version)
|
||||
err = switchFolders(u.updateIndex, *u.updateBundle)
|
||||
if err != nil {
|
||||
// TODO(vladimir): Send notification to UI
|
||||
log.Errorf("updates: failed to apply updates: %s", err)
|
||||
} else {
|
||||
// TODO(vladimir): Prompt user to restart?
|
||||
u.instance.Restart()
|
||||
if u.updateIndex.NeedsRestart {
|
||||
u.instance.Restart()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -274,7 +276,7 @@ func (u *Updates) GetFile(id string) (*File, error) {
|
|||
|
||||
// Stop stops the module.
|
||||
func (u *Updates) Stop() error {
|
||||
return stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
type instance interface {
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
updateFailed = "updates:failed"
|
||||
updateSuccess = "updates:success"
|
||||
updateSuccessPending = "updates:success-pending"
|
||||
updateSuccessDownloaded = "updates:success-downloaded"
|
||||
|
||||
failedUpdateNotifyDurationThreshold = 24 * time.Hour
|
||||
failedUpdateNotifyCountThreshold = 3
|
||||
)
|
||||
|
||||
var updateFailedCnt = new(atomic.Int32)
|
||||
|
||||
func (u *Updates) notificationsEnabled() bool {
|
||||
return u.instance.Notifications() != nil
|
||||
}
|
||||
|
||||
// func notifyUpdateSuccess(force bool) {
|
||||
// if !module.notificationsEnabled() {
|
||||
// return
|
||||
// }
|
||||
|
||||
// updateFailedCnt.Store(0)
|
||||
// module.states.Clear()
|
||||
// updateState := registry.GetState().Updates
|
||||
|
||||
// flavor := updateSuccess
|
||||
// switch {
|
||||
// case len(updateState.PendingDownload) > 0:
|
||||
// // Show notification if there are pending downloads.
|
||||
// flavor = updateSuccessPending
|
||||
// case updateState.LastDownloadAt != nil &&
|
||||
// time.Since(*updateState.LastDownloadAt) < 5*time.Second:
|
||||
// // Show notification if we downloaded something within the last minute.
|
||||
// flavor = updateSuccessDownloaded
|
||||
// case force:
|
||||
// // Always show notification if update was manually triggered.
|
||||
// default:
|
||||
// // Otherwise, the update was uneventful. Do not show notification.
|
||||
// return
|
||||
// }
|
||||
|
||||
// switch flavor {
|
||||
// case updateSuccess:
|
||||
// notifications.Notify(¬ifications.Notification{
|
||||
// EventID: updateSuccess,
|
||||
// Type: notifications.Info,
|
||||
// Title: "Portmaster Is Up-To-Date",
|
||||
// Message: "Portmaster successfully checked for updates. Everything is up to date.\n\n" + getUpdatingInfoMsg(),
|
||||
// Expires: time.Now().Add(1 * time.Minute).Unix(),
|
||||
// AvailableActions: []*notifications.Action{
|
||||
// {
|
||||
// ID: "ack",
|
||||
// Text: "OK",
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
|
||||
// case updateSuccessPending:
|
||||
// msg := fmt.Sprintf(
|
||||
// `%d updates are available for download:
|
||||
|
||||
// - %s
|
||||
|
||||
// Press "Download Now" to download and automatically apply all pending updates. You will be notified of important updates that need restarting.`,
|
||||
// len(updateState.PendingDownload),
|
||||
// strings.Join(updateState.PendingDownload, "\n- "),
|
||||
// )
|
||||
|
||||
// notifications.Notify(¬ifications.Notification{
|
||||
// EventID: updateSuccess,
|
||||
// Type: notifications.Info,
|
||||
// Title: fmt.Sprintf("%d Updates Available", len(updateState.PendingDownload)),
|
||||
// Message: msg,
|
||||
// AvailableActions: []*notifications.Action{
|
||||
// {
|
||||
// ID: "ack",
|
||||
// Text: "OK",
|
||||
// },
|
||||
// {
|
||||
// ID: "download",
|
||||
// Text: "Download Now",
|
||||
// Type: notifications.ActionTypeWebhook,
|
||||
// Payload: ¬ifications.ActionTypeWebhookPayload{
|
||||
// URL: apiPathCheckForUpdates + "?download",
|
||||
// ResultAction: "display",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
|
||||
// case updateSuccessDownloaded:
|
||||
// msg := fmt.Sprintf(
|
||||
// `%d updates were downloaded and applied:
|
||||
|
||||
// - %s
|
||||
|
||||
// %s
|
||||
// `,
|
||||
// len(updateState.LastDownload),
|
||||
// strings.Join(updateState.LastDownload, "\n- "),
|
||||
// getUpdatingInfoMsg(),
|
||||
// )
|
||||
|
||||
// notifications.Notify(¬ifications.Notification{
|
||||
// EventID: updateSuccess,
|
||||
// Type: notifications.Info,
|
||||
// Title: fmt.Sprintf("%d Updates Applied", len(updateState.LastDownload)),
|
||||
// Message: msg,
|
||||
// Expires: time.Now().Add(1 * time.Minute).Unix(),
|
||||
// AvailableActions: []*notifications.Action{
|
||||
// {
|
||||
// ID: "ack",
|
||||
// Text: "OK",
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
func getUpdatingInfoMsg() string {
|
||||
switch {
|
||||
case enableSoftwareUpdates() && enableIntelUpdates():
|
||||
return "You will be notified of important updates that need restarting."
|
||||
case enableIntelUpdates():
|
||||
return "Automatic software updates are disabled, but you will be notified when a new software update is ready to be downloaded and applied."
|
||||
default:
|
||||
return "Automatic software updates are disabled. Please check for updates regularly yourself."
|
||||
}
|
||||
}
|
||||
|
||||
// func notifyUpdateCheckFailed(force bool, err error) {
|
||||
// if !module.notificationsEnabled() {
|
||||
// return
|
||||
// }
|
||||
|
||||
// failedCnt := updateFailedCnt.Add(1)
|
||||
// lastSuccess := registry.GetState().Updates.LastSuccessAt
|
||||
|
||||
// switch {
|
||||
// case force:
|
||||
// // Always show notification if update was manually triggered.
|
||||
// case failedCnt < failedUpdateNotifyCountThreshold:
|
||||
// // Not failed often enough for notification.
|
||||
// return
|
||||
// case lastSuccess == nil:
|
||||
// // No recorded successful update.
|
||||
// case time.Now().Add(-failedUpdateNotifyDurationThreshold).Before(*lastSuccess):
|
||||
// // Failed too recently for notification.
|
||||
// return
|
||||
// }
|
||||
|
||||
// notifications.NotifyWarn(
|
||||
// updateFailed,
|
||||
// "Update Check Failed",
|
||||
// fmt.Sprintf(
|
||||
// "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 error was: %s",
|
||||
// err,
|
||||
// ),
|
||||
// notifications.Action{
|
||||
// Text: "Try Again Now",
|
||||
// Type: notifications.ActionTypeWebhook,
|
||||
// Payload: ¬ifications.ActionTypeWebhookPayload{
|
||||
// URL: apiPathCheckForUpdates,
|
||||
// ResultAction: "display",
|
||||
// },
|
||||
// },
|
||||
// ).SyncWithState(module.states)
|
||||
// }
|
|
@ -1,8 +0,0 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package updates
|
||||
|
||||
func upgradeSystemIntegration() error {
|
||||
return nil
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
package updates
|
||||
|
||||
// import (
|
||||
// "crypto/sha256"
|
||||
// _ "embed"
|
||||
// "encoding/hex"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "io/fs"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
|
||||
// "github.com/tevino/abool"
|
||||
// "golang.org/x/exp/slices"
|
||||
|
||||
// "github.com/safing/portmaster/base/dataroot"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// )
|
||||
|
||||
// var (
|
||||
// portmasterCoreServiceFilePath = "portmaster.service"
|
||||
// portmasterNotifierServiceFilePath = "portmaster_notifier.desktop"
|
||||
// backupExtension = ".backup"
|
||||
|
||||
// //go:embed assets/portmaster.service
|
||||
// currentPortmasterCoreServiceFile []byte
|
||||
|
||||
// checkedSystemIntegration = abool.New()
|
||||
|
||||
// // ErrRequiresManualUpgrade is returned when a system integration file requires a manual upgrade.
|
||||
// ErrRequiresManualUpgrade = errors.New("requires a manual upgrade")
|
||||
// )
|
||||
|
||||
// func upgradeSystemIntegration() {
|
||||
// // Check if we already checked the system integration.
|
||||
// if !checkedSystemIntegration.SetToIf(false, true) {
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Upgrade portmaster core systemd service.
|
||||
// err := upgradeSystemIntegrationFile(
|
||||
// "portmaster core systemd service",
|
||||
// filepath.Join(dataroot.Root().Path, portmasterCoreServiceFilePath),
|
||||
// 0o0600,
|
||||
// currentPortmasterCoreServiceFile,
|
||||
// []string{
|
||||
// "bc26dd37e6953af018ad3676ee77570070e075f2b9f5df6fa59d65651a481468", // Commit 19c76c7 on 2022-01-25
|
||||
// "cc0cb49324dfe11577e8c066dd95cc03d745b50b2153f32f74ca35234c3e8cb5", // Commit ef479e5 on 2022-01-24
|
||||
// "d08a3b5f3aee351f8e120e6e2e0a089964b94c9e9d0a9e5fa822e60880e315fd", // Commit b64735e on 2021-12-07
|
||||
// },
|
||||
// )
|
||||
// if err != nil {
|
||||
// log.Warningf("updates: %s", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Upgrade portmaster notifier systemd user service.
|
||||
// // Permissions only!
|
||||
// err = upgradeSystemIntegrationFile(
|
||||
// "portmaster notifier systemd user service",
|
||||
// filepath.Join(dataroot.Root().Path, portmasterNotifierServiceFilePath),
|
||||
// 0o0644,
|
||||
// nil, // Do not update contents.
|
||||
// nil, // Do not update contents.
|
||||
// )
|
||||
// if err != nil {
|
||||
// log.Warningf("updates: %s", err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// // upgradeSystemIntegrationFile upgrades the file contents and permissions.
|
||||
// // System integration files are not necessarily present and may also be
|
||||
// // edited by third parties, such as the OS itself or other installers.
|
||||
// // The supplied hashes must be sha256 hex-encoded.
|
||||
// func upgradeSystemIntegrationFile(
|
||||
// name string,
|
||||
// filePath string,
|
||||
// fileMode fs.FileMode,
|
||||
// fileData []byte,
|
||||
// permittedUpgradeHashes []string,
|
||||
// ) error {
|
||||
// // Upgrade file contents.
|
||||
// if len(fileData) > 0 {
|
||||
// if err := upgradeSystemIntegrationFileContents(name, filePath, fileData, permittedUpgradeHashes); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Upgrade file permissions.
|
||||
// if fileMode != 0 {
|
||||
// if err := upgradeSystemIntegrationFilePermissions(name, filePath, fileMode); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // upgradeSystemIntegrationFileContents upgrades the file contents.
|
||||
// // System integration files are not necessarily present and may also be
|
||||
// // edited by third parties, such as the OS itself or other installers.
|
||||
// // The supplied hashes must be sha256 hex-encoded.
|
||||
// func upgradeSystemIntegrationFileContents(
|
||||
// name string,
|
||||
// filePath string,
|
||||
// fileData []byte,
|
||||
// permittedUpgradeHashes []string,
|
||||
// ) error {
|
||||
// // Read existing file.
|
||||
// existingFileData, err := os.ReadFile(filePath)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, os.ErrNotExist) {
|
||||
// return nil
|
||||
// }
|
||||
// return fmt.Errorf("failed to read %s at %s: %w", name, filePath, err)
|
||||
// }
|
||||
|
||||
// // Check if file is already the current version.
|
||||
// existingSum := sha256.Sum256(existingFileData)
|
||||
// existingHexSum := hex.EncodeToString(existingSum[:])
|
||||
// currentSum := sha256.Sum256(fileData)
|
||||
// currentHexSum := hex.EncodeToString(currentSum[:])
|
||||
// if existingHexSum == currentHexSum {
|
||||
// log.Debugf("updates: %s at %s is up to date", name, filePath)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Check if we are allowed to upgrade from the existing file.
|
||||
// if !slices.Contains[[]string, string](permittedUpgradeHashes, existingHexSum) {
|
||||
// return fmt.Errorf("%s at %s (sha256:%s) %w, as it is not a previously published version and cannot be automatically upgraded - try installing again", name, filePath, existingHexSum, ErrRequiresManualUpgrade)
|
||||
// }
|
||||
|
||||
// // Start with upgrade!
|
||||
|
||||
// // Make backup of existing file.
|
||||
// err = CopyFile(filePath, filePath+backupExtension)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf(
|
||||
// "failed to create backup of %s from %s to %s: %w",
|
||||
// name,
|
||||
// filePath,
|
||||
// filePath+backupExtension,
|
||||
// err,
|
||||
// )
|
||||
// }
|
||||
|
||||
// // Open destination file for writing.
|
||||
// // atomicDstFile, err := renameio.TempFile(registry.TmpDir().Path, filePath)
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("failed to create tmp file to update %s at %s: %w", name, filePath, err)
|
||||
// // }
|
||||
// // defer atomicDstFile.Cleanup() //nolint:errcheck // ignore error for now, tmp dir will be cleaned later again anyway
|
||||
|
||||
// // // Write file.
|
||||
// // _, err = io.Copy(atomicDstFile, bytes.NewReader(fileData))
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
|
||||
// // // Finalize file.
|
||||
// // err = atomicDstFile.CloseAtomicallyReplace()
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("failed to finalize update of %s at %s: %w", name, filePath, err)
|
||||
// // }
|
||||
|
||||
// log.Warningf("updates: %s at %s was upgraded to %s - a reboot may be required", name, filePath, currentHexSum)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // upgradeSystemIntegrationFilePermissions upgrades the file permissions.
|
||||
// // System integration files are not necessarily present and may also be
|
||||
// // edited by third parties, such as the OS itself or other installers.
|
||||
// func upgradeSystemIntegrationFilePermissions(
|
||||
// name string,
|
||||
// filePath string,
|
||||
// fileMode fs.FileMode,
|
||||
// ) error {
|
||||
// // Get current file permissions.
|
||||
// stat, err := os.Stat(filePath)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, os.ErrNotExist) {
|
||||
// return nil
|
||||
// }
|
||||
// return fmt.Errorf("failed to read %s file metadata at %s: %w", name, filePath, err)
|
||||
// }
|
||||
|
||||
// // If permissions are as expected, do nothing.
|
||||
// if stat.Mode().Perm() == fileMode {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Otherwise, set correct permissions.
|
||||
// err = os.Chmod(filePath, fileMode)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to update %s file permissions at %s: %w", name, filePath, err)
|
||||
// }
|
||||
|
||||
// log.Warningf("updates: %s file permissions at %s updated to %v", name, filePath, fileMode)
|
||||
// return nil
|
||||
// }
|
|
@ -1,49 +0,0 @@
|
|||
package updates
|
||||
|
||||
// import (
|
||||
// "github.com/safing/portmaster/base/database/record"
|
||||
// "github.com/safing/portmaster/base/runtime"
|
||||
// "github.com/safing/portmaster/base/updater"
|
||||
// )
|
||||
|
||||
// var pushRegistryStatusUpdate runtime.PushFunc
|
||||
|
||||
// // RegistryStateExport is a wrapper to export the registry state.
|
||||
// type RegistryStateExport struct {
|
||||
// record.Base
|
||||
// *updater.RegistryState
|
||||
// }
|
||||
|
||||
// func exportRegistryState(s *updater.RegistryState) *RegistryStateExport {
|
||||
// // if s == nil {
|
||||
// // state := registry.GetState()
|
||||
// // s = &state
|
||||
// // }
|
||||
|
||||
// export := &RegistryStateExport{
|
||||
// RegistryState: s,
|
||||
// }
|
||||
|
||||
// export.CreateMeta()
|
||||
// export.SetKey("runtime:core/updates/state")
|
||||
|
||||
// return export
|
||||
// }
|
||||
|
||||
// func pushRegistryState(s *updater.RegistryState) {
|
||||
// export := exportRegistryState(s)
|
||||
// pushRegistryStatusUpdate(export)
|
||||
// }
|
||||
|
||||
// func registerRegistryStateProvider() (err error) {
|
||||
// registryStateProvider := runtime.SimpleValueGetterFunc(func(_ string) ([]record.Record, error) {
|
||||
// return []record.Record{exportRegistryState(nil)}, nil
|
||||
// })
|
||||
|
||||
// pushRegistryStatusUpdate, err = runtime.Register("core/updates/state", registryStateProvider)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
|
@ -29,6 +30,7 @@ func switchFolders(updateIndex UpdateIndex, newBundle Bundle) error {
|
|||
}
|
||||
|
||||
// Move current version files into purge folder.
|
||||
log.Debugf("updates: removing the old version")
|
||||
for _, file := range files {
|
||||
currentFilepath := filepath.Join(updateIndex.Directory, file.Name())
|
||||
purgePath := filepath.Join(updateIndex.PurgeDirectory, file.Name())
|
||||
|
@ -39,6 +41,7 @@ func switchFolders(updateIndex UpdateIndex, newBundle Bundle) error {
|
|||
}
|
||||
|
||||
// Move the new index file
|
||||
log.Debugf("updates: installing the new version")
|
||||
indexFile := filepath.Join(updateIndex.DownloadDirectory, updateIndex.IndexFile)
|
||||
newIndexFile := filepath.Join(updateIndex.Directory, updateIndex.IndexFile)
|
||||
err = os.Rename(indexFile, newIndexFile)
|
||||
|
@ -52,9 +55,27 @@ func switchFolders(updateIndex UpdateIndex, newBundle Bundle) error {
|
|||
toFilepath := filepath.Join(updateIndex.Directory, artifact.Filename)
|
||||
err = os.Rename(fromFilepath, toFilepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to move file %s: %w", fromFilepath, err)
|
||||
log.Errorf("failed to move file %s: %s", fromFilepath, err)
|
||||
} else {
|
||||
log.Debugf("updates: %s moved", artifact.Filename)
|
||||
}
|
||||
|
||||
// Special case for linux.
|
||||
// When installed the portmaster ui path is `/usr/bin/portmaster`. During update the ui will be placed in `/usr/lib/portmaster/portmaster`
|
||||
// After an update the original binary should be deleted and replaced by symlink
|
||||
// `/usr/bin/portmaster` -> `/usr/lib/portmaster/portmaster`
|
||||
if runtime.GOOS == "linux" && artifact.Filename == "portmaster" && artifact.Platform == currentPlatform {
|
||||
err = makeSymlinkForUI(updateIndex.Directory)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create symlink for the ui: %s", err)
|
||||
} else {
|
||||
log.Infof("ui symlink successfully created")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("updates: update complete")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -76,3 +97,11 @@ func deleteUnfinishedDownloads(rootDir string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeSymlinkForUI(directory string) error {
|
||||
err := os.Symlink(filepath.Join(directory, "portmaster"), "/usr/bin/portmaster")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create symlink: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,403 +0,0 @@
|
|||
package updates
|
||||
|
||||
// import (
|
||||
// "context"
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "os/exec"
|
||||
// "path/filepath"
|
||||
// "regexp"
|
||||
// "strings"
|
||||
// "time"
|
||||
|
||||
// processInfo "github.com/shirou/gopsutil/process"
|
||||
// "github.com/tevino/abool"
|
||||
|
||||
// "github.com/safing/portmaster/base/dataroot"
|
||||
// "github.com/safing/portmaster/base/info"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/notifications"
|
||||
// "github.com/safing/portmaster/base/rng"
|
||||
// "github.com/safing/portmaster/base/updater"
|
||||
// "github.com/safing/portmaster/service/mgr"
|
||||
// )
|
||||
|
||||
// const (
|
||||
// upgradedSuffix = "-upgraded"
|
||||
// exeExt = ".exe"
|
||||
// )
|
||||
|
||||
// var (
|
||||
// upgraderActive = abool.NewBool(false)
|
||||
|
||||
// pmCtrlUpdate *updater.File
|
||||
// pmCoreUpdate *updater.File
|
||||
|
||||
// spnHubUpdate *updater.File
|
||||
|
||||
// rawVersionRegex = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+b?\*?$`)
|
||||
// )
|
||||
|
||||
// func initUpgrader() error {
|
||||
// // module.EventResourcesUpdated.AddCallback("run upgrades", upgrader)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func upgrader(m *mgr.WorkerCtx, _ struct{}) (cancel bool, err error) {
|
||||
// // Lock runs, but discard additional runs.
|
||||
// if !upgraderActive.SetToIf(false, true) {
|
||||
// return false, nil
|
||||
// }
|
||||
// defer upgraderActive.SetTo(false)
|
||||
|
||||
// // Upgrade portmaster-start.
|
||||
// err = upgradePortmasterStart()
|
||||
// if err != nil {
|
||||
// log.Warningf("updates: failed to upgrade portmaster-start: %s", err)
|
||||
// }
|
||||
|
||||
// // Upgrade based on binary.
|
||||
// binBaseName := strings.Split(filepath.Base(os.Args[0]), "_")[0]
|
||||
// switch binBaseName {
|
||||
// case "portmaster-core":
|
||||
// // Notify about upgrade.
|
||||
// if err := upgradeCoreNotify(); err != nil {
|
||||
// log.Warningf("updates: failed to notify about core upgrade: %s", err)
|
||||
// }
|
||||
|
||||
// // Fix chrome sandbox permissions.
|
||||
// // if err := helper.EnsureChromeSandboxPermissions(registry); err != nil {
|
||||
// // log.Warningf("updates: failed to handle electron upgrade: %s", err)
|
||||
// // }
|
||||
|
||||
// // Upgrade system integration.
|
||||
// upgradeSystemIntegration()
|
||||
|
||||
// case "spn-hub":
|
||||
// // Trigger upgrade procedure.
|
||||
// if err := upgradeHub(); err != nil {
|
||||
// log.Warningf("updates: failed to initiate hub upgrade: %s", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false, nil
|
||||
// }
|
||||
|
||||
// func upgradeCoreNotify() error {
|
||||
// if pmCoreUpdate != nil && !pmCoreUpdate.UpgradeAvailable() {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // make identifier
|
||||
// identifier := "core/portmaster-core" // identifier, use forward slash!
|
||||
// if onWindows {
|
||||
// identifier += exeExt
|
||||
// }
|
||||
|
||||
// // get newest portmaster-core
|
||||
// // newFile, err := GetPlatformFile(identifier)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
// // pmCoreUpdate = newFile
|
||||
|
||||
// // check for new version
|
||||
// if info.VersionNumber() != pmCoreUpdate.Version() {
|
||||
// n := notifications.Notify(¬ifications.Notification{
|
||||
// EventID: "updates:core-update-available",
|
||||
// Type: notifications.Info,
|
||||
// Title: fmt.Sprintf(
|
||||
// "Portmaster Update v%s Is Ready!",
|
||||
// pmCoreUpdate.Version(),
|
||||
// ),
|
||||
// Category: "Core",
|
||||
// Message: fmt.Sprintf(
|
||||
// `A new Portmaster version is ready to go! Restart the Portmaster to upgrade to %s.`,
|
||||
// pmCoreUpdate.Version(),
|
||||
// ),
|
||||
// ShowOnSystem: true,
|
||||
// AvailableActions: []*notifications.Action{
|
||||
// // TODO: Use special UI action in order to reload UI on restart.
|
||||
// {
|
||||
// ID: "restart",
|
||||
// Text: "Restart",
|
||||
// },
|
||||
// {
|
||||
// ID: "later",
|
||||
// Text: "Not now",
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// n.SetActionFunction(upgradeCoreNotifyActionHandler)
|
||||
|
||||
// log.Debugf("updates: new portmaster version available, sending notification to user")
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func upgradeCoreNotifyActionHandler(_ context.Context, n *notifications.Notification) error {
|
||||
// switch n.SelectedActionID {
|
||||
// case "restart":
|
||||
// log.Infof("updates: user triggered restart via core update notification")
|
||||
// RestartNow()
|
||||
// case "later":
|
||||
// n.Delete()
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func upgradeHub() error {
|
||||
// if spnHubUpdate != nil && !spnHubUpdate.UpgradeAvailable() {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Make identifier for getting file from updater.
|
||||
// identifier := "hub/spn-hub" // identifier, use forward slash!
|
||||
// if onWindows {
|
||||
// identifier += exeExt
|
||||
// }
|
||||
|
||||
// // Get newest spn-hub file.
|
||||
// // newFile, err := GetPlatformFile(identifier)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
// // spnHubUpdate = newFile
|
||||
|
||||
// // Check if the new version is different.
|
||||
// if info.GetInfo().Version != spnHubUpdate.Version() {
|
||||
// // Get random delay with up to three hours.
|
||||
// delayMinutes, err := rng.Number(3 * 60)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// // Delay restart for at least one hour for preparations.
|
||||
// DelayedRestart(time.Duration(delayMinutes+60) * time.Minute)
|
||||
|
||||
// // Increase update checks in order to detect aborts better.
|
||||
// // if !disableTaskSchedule {
|
||||
// // module.updateBinaryWorkerMgr.Repeat(10 * time.Minute)
|
||||
// // }
|
||||
// } else {
|
||||
// AbortRestart()
|
||||
|
||||
// // Set update task schedule back to normal.
|
||||
// // if !disableTaskSchedule {
|
||||
// // module.updateBinaryWorkerMgr.Repeat(updateTaskRepeatDuration)
|
||||
// // }
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func upgradePortmasterStart() error {
|
||||
// filename := "portmaster-start"
|
||||
// if onWindows {
|
||||
// filename += exeExt
|
||||
// }
|
||||
|
||||
// // check if we can upgrade
|
||||
// if pmCtrlUpdate == nil || pmCtrlUpdate.UpgradeAvailable() {
|
||||
// // get newest portmaster-start
|
||||
// // newFile, err := GetPlatformFile("start/" + filename) // identifier, use forward slash!
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
// // pmCtrlUpdate = newFile
|
||||
// } else {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // update portmaster-start in data root
|
||||
// rootPmStartPath := filepath.Join(dataroot.Root().Path, filename)
|
||||
// err := upgradeBinary(rootPmStartPath, pmCtrlUpdate)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func warnOnIncorrectParentPath() {
|
||||
// expectedFileName := "portmaster-start"
|
||||
// if onWindows {
|
||||
// expectedFileName += exeExt
|
||||
// }
|
||||
|
||||
// // upgrade parent process, if it's portmaster-start
|
||||
// parent, err := processInfo.NewProcess(int32(os.Getppid()))
|
||||
// if err != nil {
|
||||
// log.Tracef("could not get parent process: %s", err)
|
||||
// return
|
||||
// }
|
||||
// parentName, err := parent.Name()
|
||||
// if err != nil {
|
||||
// log.Tracef("could not get parent process name: %s", err)
|
||||
// return
|
||||
// }
|
||||
// if parentName != expectedFileName {
|
||||
// // Only warn about this if not in dev mode.
|
||||
// if !devMode() {
|
||||
// log.Warningf("updates: parent process does not seem to be portmaster-start, name is %s", parentName)
|
||||
// }
|
||||
|
||||
// // TODO(ppacher): once we released a new installer and folks had time
|
||||
// // to update we should send a module warning/hint to the
|
||||
// // UI notifying the user that he's still using portmaster-control.
|
||||
// return
|
||||
// }
|
||||
|
||||
// // parentPath, err := parent.Exe()
|
||||
// // if err != nil {
|
||||
// // log.Tracef("could not get parent process path: %s", err)
|
||||
// // return
|
||||
// // }
|
||||
|
||||
// // absPath, err := filepath.Abs(parentPath)
|
||||
// // if err != nil {
|
||||
// // log.Tracef("could not get absolut parent process path: %s", err)
|
||||
// // return
|
||||
// // }
|
||||
|
||||
// // root := filepath.Dir(registry.StorageDir().Path)
|
||||
// // if !strings.HasPrefix(absPath, root) {
|
||||
// // log.Warningf("detected unexpected path %s for portmaster-start", absPath)
|
||||
// // notifications.NotifyWarn(
|
||||
// // "updates:unsupported-parent",
|
||||
// // "Unsupported Launcher",
|
||||
// // fmt.Sprintf(
|
||||
// // "The Portmaster has been launched by an unexpected %s binary at %s. Please configure your system to use the binary at %s as this version will be kept up to date automatically.",
|
||||
// // expectedFileName,
|
||||
// // absPath,
|
||||
// // filepath.Join(root, expectedFileName),
|
||||
// // ),
|
||||
// // )
|
||||
// // }
|
||||
// }
|
||||
|
||||
// func upgradeBinary(fileToUpgrade string, file *updater.File) error {
|
||||
// fileExists := false
|
||||
// _, err := os.Stat(fileToUpgrade)
|
||||
// if err == nil {
|
||||
// // file exists and is accessible
|
||||
// fileExists = true
|
||||
// }
|
||||
|
||||
// if fileExists {
|
||||
// // get current version
|
||||
// var currentVersion string
|
||||
// cmd := exec.Command(fileToUpgrade, "version", "--short")
|
||||
// out, err := cmd.Output()
|
||||
// if err == nil {
|
||||
// // abort if version matches
|
||||
// currentVersion = strings.Trim(strings.TrimSpace(string(out)), "*")
|
||||
// if currentVersion == file.Version() {
|
||||
// log.Debugf("updates: %s is already v%s", fileToUpgrade, file.Version())
|
||||
// // already up to date!
|
||||
// return nil
|
||||
// }
|
||||
// } else {
|
||||
// log.Warningf("updates: failed to run %s to get version for upgrade check: %s", fileToUpgrade, err)
|
||||
// currentVersion = "0.0.0"
|
||||
// }
|
||||
|
||||
// // test currentVersion for sanity
|
||||
// if !rawVersionRegex.MatchString(currentVersion) {
|
||||
// log.Debugf("updates: version string returned by %s is invalid: %s", fileToUpgrade, currentVersion)
|
||||
// }
|
||||
|
||||
// // try removing old version
|
||||
// err = os.Remove(fileToUpgrade)
|
||||
// if err != nil {
|
||||
// // ensure tmp dir is here
|
||||
// // err = registry.TmpDir().Ensure()
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("could not prepare tmp directory for moving file that needs upgrade: %w", err)
|
||||
// // }
|
||||
|
||||
// // maybe we're on windows and it's in use, try moving
|
||||
// // err = os.Rename(fileToUpgrade, filepath.Join(
|
||||
// // registry.TmpDir().Path,
|
||||
// // fmt.Sprintf(
|
||||
// // "%s-%d%s",
|
||||
// // filepath.Base(fileToUpgrade),
|
||||
// // time.Now().UTC().Unix(),
|
||||
// // upgradedSuffix,
|
||||
// // ),
|
||||
// // ))
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("unable to move file that needs upgrade: %w", err)
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // copy upgrade
|
||||
// err = CopyFile(file.Path(), fileToUpgrade)
|
||||
// if err != nil {
|
||||
// // try again
|
||||
// time.Sleep(1 * time.Second)
|
||||
// err = CopyFile(file.Path(), fileToUpgrade)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// // check permissions
|
||||
// if !onWindows {
|
||||
// info, err := os.Stat(fileToUpgrade)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to get file info on %s: %w", fileToUpgrade, err)
|
||||
// }
|
||||
// if info.Mode() != 0o0755 {
|
||||
// err := os.Chmod(fileToUpgrade, 0o0755) //nolint:gosec // Set execute permissions.
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to set permissions on %s: %w", fileToUpgrade, err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// log.Infof("updates: upgraded %s to v%s", fileToUpgrade, file.Version())
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // CopyFile atomically copies a file using the update registry's tmp dir.
|
||||
// func CopyFile(srcPath, dstPath string) error {
|
||||
// // check tmp dir
|
||||
// // err := registry.TmpDir().Ensure()
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("could not prepare tmp directory for copying file: %w", err)
|
||||
// // }
|
||||
|
||||
// // open file for writing
|
||||
// // atomicDstFile, err := renameio.TempFile(registry.TmpDir().Path, dstPath)
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("could not create temp file for atomic copy: %w", err)
|
||||
// // }
|
||||
// // defer atomicDstFile.Cleanup() //nolint:errcheck // ignore error for now, tmp dir will be cleaned later again anyway
|
||||
|
||||
// // // open source
|
||||
// // srcFile, err := os.Open(srcPath)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
// // defer func() {
|
||||
// // _ = srcFile.Close()
|
||||
// // }()
|
||||
|
||||
// // // copy data
|
||||
// // _, err = io.Copy(atomicDstFile, srcFile)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
|
||||
// // // finalize file
|
||||
// // err = atomicDstFile.CloseAtomicallyReplace()
|
||||
// // if err != nil {
|
||||
// // return fmt.Errorf("updates: failed to finalize copy to file %s: %w", dstPath, err)
|
||||
// // }
|
||||
|
||||
// return nil
|
||||
// }
|
Loading…
Add table
Reference in a new issue