Merge pull request #299 from safing/feature/patch-set-1
Improvements for notifications and events
This commit is contained in:
54
core/api.go
54
core/api.go
@@ -1,7 +1,9 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/log"
|
||||
@@ -11,19 +13,33 @@ import (
|
||||
"github.com/safing/portmaster/updates"
|
||||
)
|
||||
|
||||
const (
|
||||
eventShutdown = "shutdown"
|
||||
eventRestart = "restart"
|
||||
)
|
||||
|
||||
func registerEvents() {
|
||||
module.RegisterEvent(eventShutdown, true)
|
||||
module.RegisterEvent(eventRestart, true)
|
||||
}
|
||||
|
||||
func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "core/shutdown",
|
||||
Read: api.PermitSelf,
|
||||
ActionFunc: shutdown,
|
||||
Path: "core/shutdown",
|
||||
Write: api.PermitSelf,
|
||||
ActionFunc: shutdown,
|
||||
Name: "Shut Down Portmaster",
|
||||
Description: "Shut down the Portmaster Core Service and all UI components.",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "core/restart",
|
||||
Read: api.PermitAdmin,
|
||||
ActionFunc: restart,
|
||||
Path: "core/restart",
|
||||
Write: api.PermitAdmin,
|
||||
ActionFunc: restart,
|
||||
Name: "Restart Portmaster",
|
||||
Description: "Restart the Portmaster Core Service.",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,15 +66,35 @@ func registerAPIEndpoints() error {
|
||||
// shutdown shuts the Portmaster down.
|
||||
func shutdown(_ *api.Request) (msg string, err error) {
|
||||
log.Warning("core: user requested shutdown via action")
|
||||
// Do not use a worker, as this would block itself here.
|
||||
go modules.Shutdown() //nolint:errcheck
|
||||
|
||||
module.StartWorker("shutdown", func(context.Context) error {
|
||||
// Notify everyone of the shutdown.
|
||||
module.TriggerEvent(eventShutdown, nil)
|
||||
// Wait a bit for the event to propagate.
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Do not run in worker, as this would block itself here.
|
||||
go modules.Shutdown() //nolint:errcheck
|
||||
return nil
|
||||
})
|
||||
|
||||
return "shutdown initiated", nil
|
||||
}
|
||||
|
||||
// restart restarts the Portmaster.
|
||||
func restart(_ *api.Request) (msg string, err error) {
|
||||
log.Info("core: user requested restart via action")
|
||||
updates.RestartNow()
|
||||
|
||||
module.StartWorker("restart", func(context.Context) error {
|
||||
// Notify everyone of the shutdown.
|
||||
module.TriggerEvent(eventRestart, nil)
|
||||
// Wait a bit for the event to propagate.
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
updates.RestartNow()
|
||||
return nil
|
||||
})
|
||||
|
||||
return "restart initiated", nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
// Configuration Keys.
|
||||
@@ -19,13 +18,12 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&defaultNetworkServiceMode, "network-service", false, "force network service mode")
|
||||
}
|
||||
|
||||
func logFlagOverrides() {
|
||||
if defaultNetworkServiceMode {
|
||||
log.Warningf("core: %s config is being forced by the -network-service flag", CfgNetworkServiceKey)
|
||||
}
|
||||
flag.BoolVar(
|
||||
&defaultNetworkServiceMode,
|
||||
"network-service",
|
||||
false,
|
||||
"set default network service mode; configuration is stronger",
|
||||
)
|
||||
}
|
||||
|
||||
func registerConfig() error {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/database/storage"
|
||||
)
|
||||
|
||||
// StorageInterface provices a storage.Interface to the storage manager.
|
||||
type StorageInterface struct {
|
||||
storage.InjectBase
|
||||
}
|
||||
|
||||
// Get returns a database record.
|
||||
func (s *StorageInterface) Get(key string) (record.Record, error) {
|
||||
msg := newMessage(key)
|
||||
splittedKey := strings.Split(key, "/")
|
||||
|
||||
switch splittedKey[0] {
|
||||
case "module":
|
||||
return controlModule(msg, splittedKey)
|
||||
default:
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
}
|
||||
|
||||
func controlModule(msg *MessageRecord, splittedKey []string) (record.Record, error) {
|
||||
// format: module/moduleName/action/param
|
||||
var moduleName string
|
||||
var action string
|
||||
var param string
|
||||
var err error
|
||||
|
||||
// parse elements
|
||||
switch len(splittedKey) {
|
||||
case 4:
|
||||
param = splittedKey[3]
|
||||
fallthrough
|
||||
case 3:
|
||||
moduleName = splittedKey[1]
|
||||
action = splittedKey[2]
|
||||
default:
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
// execute
|
||||
switch action {
|
||||
case "trigger":
|
||||
err = module.InjectEvent(fmt.Sprintf("user triggered the '%s/%s' event", moduleName, param), moduleName, param, nil)
|
||||
default:
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
msg.Message = err.Error()
|
||||
} else {
|
||||
msg.Success = true
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func registerControlDatabase() error {
|
||||
_, err := database.Register(&database.Database{
|
||||
Name: "control",
|
||||
Description: "Control Interface for the Portmaster",
|
||||
StorageType: "injected",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = database.InjectDatabase("control", &StorageInterface{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessageRecord is a simple record used for control database feedback
|
||||
type MessageRecord struct {
|
||||
record.Base
|
||||
sync.Mutex
|
||||
|
||||
Success bool
|
||||
Message string
|
||||
}
|
||||
|
||||
func newMessage(key string) *MessageRecord {
|
||||
m := &MessageRecord{}
|
||||
m.SetKey("control:" + key)
|
||||
m.UpdateMeta()
|
||||
return m
|
||||
}
|
||||
17
core/core.go
17
core/core.go
@@ -33,12 +33,15 @@ func prep() error {
|
||||
registerEvents()
|
||||
|
||||
// init config
|
||||
logFlagOverrides()
|
||||
err := registerConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := registerAPIEndpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -47,18 +50,6 @@ func start() error {
|
||||
return fmt.Errorf("failed to start plattform-specific components: %s", err)
|
||||
}
|
||||
|
||||
if err := registerEventHooks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := registerControlDatabase(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := registerAPIEndpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registerLogCleaner()
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// DEPRECATED: remove in v0.7
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/updates"
|
||||
)
|
||||
|
||||
const (
|
||||
eventShutdown = "shutdown"
|
||||
eventRestart = "restart"
|
||||
)
|
||||
|
||||
func registerEvents() {
|
||||
module.RegisterEvent(eventShutdown)
|
||||
module.RegisterEvent(eventRestart)
|
||||
}
|
||||
|
||||
func registerEventHooks() error {
|
||||
err := module.RegisterEventHook("core", eventShutdown, "execute shutdown", shutdownHook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = module.RegisterEventHook("core", eventRestart, "execute shutdown", restartHook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// shutdownHook shuts the Portmaster down.
|
||||
func shutdownHook(ctx context.Context, _ interface{}) error {
|
||||
log.Warning("core: user requested shutdown")
|
||||
// Do not use a worker, as this would block itself here.
|
||||
go modules.Shutdown() //nolint:errcheck
|
||||
return nil
|
||||
}
|
||||
|
||||
// restartHook restarts the Portmaster.
|
||||
func restartHook(ctx context.Context, data interface{}) error {
|
||||
log.Info("core: user requested restart")
|
||||
updates.RestartNow()
|
||||
return nil
|
||||
}
|
||||
@@ -15,7 +15,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&disableInterception, "disable-interception", false, "disable packet interception - this breaks a lot of functionality")
|
||||
flag.BoolVar(&disableInterception, "disable-interception", false, "disable packet interception; this breaks a lot of functionality")
|
||||
}
|
||||
|
||||
// Start starts the interception.
|
||||
|
||||
@@ -3,9 +3,6 @@ package interception
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portbase/utils/osdetail"
|
||||
"github.com/safing/portmaster/firewall/interception/windowskext"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
"github.com/safing/portmaster/updates"
|
||||
@@ -33,7 +30,6 @@ func start(ch chan packet.Packet) error {
|
||||
}
|
||||
|
||||
go windowskext.Handler(ch)
|
||||
go checkWindowsDNSCache()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -42,28 +38,3 @@ func start(ch chan packet.Packet) error {
|
||||
func stop() error {
|
||||
return windowskext.Stop()
|
||||
}
|
||||
|
||||
func checkWindowsDNSCache() {
|
||||
status, err := osdetail.GetServiceStatus("dnscache")
|
||||
if err != nil {
|
||||
log.Warningf("firewall/interception: failed to check status of Windows DNS-Client: %s", err)
|
||||
}
|
||||
|
||||
if status == osdetail.StatusStopped {
|
||||
err := osdetail.EnableDNSCache()
|
||||
if err != nil {
|
||||
log.Warningf("firewall/interception: failed to enable Windows Service \"DNS Client\" (dnscache): %s", err)
|
||||
} else {
|
||||
log.Warningf("firewall/interception: successfully enabled the dnscache")
|
||||
notifyRebootRequired()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notifyRebootRequired() {
|
||||
(¬ifications.Notification{
|
||||
EventID: "interception:windows-dnscache-reboot-required",
|
||||
Message: "Please restart your system to complete Portmaster integration.",
|
||||
Type: notifications.Warning,
|
||||
}).Save()
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&packetMetricsDestination, "write-packet-metrics", "", "Write packet metrics to the specified file")
|
||||
flag.StringVar(&packetMetricsDestination, "write-packet-metrics", "", "write packet metrics to the specified file")
|
||||
}
|
||||
|
||||
type (
|
||||
|
||||
@@ -16,12 +16,10 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
filterlistsDisabled = "filterlists:disabled"
|
||||
filterlistsUpdateFailed = "filterlists:update-failed"
|
||||
filterlistsStaleDataSurvived = "filterlists:staledata"
|
||||
filterlistsStaleDataDescr = "Removing stale filter list records failed. Some connections may be overblocked."
|
||||
filterlistsUpdateInProgress = "filterlists:update-in-progress"
|
||||
filterlistsUpdateInProgressDescr = "Performance slightly degraded during list update."
|
||||
filterlistsDisabled = "filterlists:disabled"
|
||||
filterlistsUpdateFailed = "filterlists:update-failed"
|
||||
filterlistsStaleDataSurvived = "filterlists:staledata"
|
||||
filterlistsUpdateInProgress = "filterlists:update-in-progress"
|
||||
)
|
||||
|
||||
// booleans mainly used to decouple the module
|
||||
@@ -91,7 +89,7 @@ func start() error {
|
||||
|
||||
if err != nil {
|
||||
log.Debugf("intel/filterlists: blocklists disabled, waiting for update (%s)", err)
|
||||
module.Warning(filterlistsDisabled, "Blocklist features disabled, waiting for update")
|
||||
warnAboutDisabledFilterLists()
|
||||
} else {
|
||||
log.Debugf("intel/filterlists: using cache database")
|
||||
close(filterListsLoaded)
|
||||
@@ -104,3 +102,11 @@ func stop() error {
|
||||
filterListsLoaded = make(chan struct{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func warnAboutDisabledFilterLists() {
|
||||
module.Warning(
|
||||
filterlistsDisabled,
|
||||
"Filter Lists Are Initializing",
|
||||
"Filter lists are being downloaded and set up in the background. Until this initialization is finished, the filter lists are disabled and will not block anything.",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/updater"
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
@@ -22,18 +23,20 @@ func tryListUpdate(ctx context.Context) error {
|
||||
err := performUpdate(ctx)
|
||||
|
||||
if err != nil {
|
||||
if !isLoaded() {
|
||||
module.Error(filterlistsDisabled, err.Error())
|
||||
} else {
|
||||
module.Warning(filterlistsUpdateFailed, err.Error())
|
||||
// Check if the module already has a failure status set. If not, set a
|
||||
// generic one with the returned error.
|
||||
failureStatus, _, _ := module.FailureStatus()
|
||||
if failureStatus < modules.FailureWarning {
|
||||
module.Warning(
|
||||
filterlistsUpdateFailed,
|
||||
"Filter Lists Update Failed",
|
||||
fmt.Sprintf("The Portmaster failed to process a filter lists update. Filtering capabilities are currently either impaired or not available at all. Error: %s", err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// if the module is in an error, warning or hint state resolve that right now.
|
||||
module.Resolve(filterlistsDisabled)
|
||||
module.Resolve(filterlistsStaleDataSurvived)
|
||||
module.Resolve(filterlistsUpdateInProgress)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,8 +47,6 @@ func performUpdate(ctx context.Context) error {
|
||||
}
|
||||
defer updateInProgress.UnSet()
|
||||
|
||||
module.Hint(filterlistsUpdateInProgress, filterlistsUpdateInProgressDescr)
|
||||
|
||||
// First, update the list index.
|
||||
err := updateListIndex()
|
||||
if err != nil {
|
||||
@@ -119,7 +120,11 @@ func performUpdate(ctx context.Context) error {
|
||||
// if we failed to remove all stale cache entries
|
||||
// we abort now WITHOUT updating the database version. This means
|
||||
// we'll try again during the next update.
|
||||
module.Warning(filterlistsStaleDataSurvived, filterlistsStaleDataDescr)
|
||||
module.Warning(
|
||||
filterlistsStaleDataSurvived,
|
||||
"Filter Lists May Overblock",
|
||||
fmt.Sprintf("The Portmaster failed to delete outdated filter list data. Filtering capabilities are fully available, but overblocking may occur. Error: %s", err.Error()),
|
||||
)
|
||||
return fmt.Errorf("failed to cleanup stale cache records: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -132,6 +137,8 @@ func performUpdate(ctx context.Context) error {
|
||||
log.Infof("intel/filterlists: successfully migrated cache database to %s", highestVersion.Version())
|
||||
}
|
||||
|
||||
// The list update suceeded, resolve any states.
|
||||
module.Resolve("")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/core"
|
||||
)
|
||||
|
||||
@@ -15,10 +14,9 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
nameserverAddressFlag string
|
||||
nameserverAddressConfig config.StringOption
|
||||
|
||||
defaultNameserverAddress = "localhost:53"
|
||||
nameserverAddress string
|
||||
nameserverAddressConfig config.StringOption
|
||||
|
||||
networkServiceMode config.BoolOption
|
||||
)
|
||||
@@ -29,22 +27,12 @@ func init() {
|
||||
defaultNameserverAddress = "0.0.0.0:53"
|
||||
}
|
||||
|
||||
flag.StringVar(&nameserverAddressFlag, "nameserver-address", "", "override nameserver listen address")
|
||||
}
|
||||
|
||||
func logFlagOverrides() {
|
||||
if nameserverAddressFlag != "" {
|
||||
log.Warning("nameserver: dns/listenAddress default config is being overridden by the -nameserver-address flag")
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultNameserverAddress() string {
|
||||
// check if overridden
|
||||
if nameserverAddressFlag != "" {
|
||||
return nameserverAddressFlag
|
||||
}
|
||||
// return internal default
|
||||
return defaultNameserverAddress
|
||||
flag.StringVar(
|
||||
&nameserverAddress,
|
||||
"nameserver-address",
|
||||
defaultNameserverAddress,
|
||||
"set default nameserver address; configuration is stronger",
|
||||
)
|
||||
}
|
||||
|
||||
func registerConfig() error {
|
||||
@@ -55,7 +43,7 @@ func registerConfig() error {
|
||||
OptType: config.OptTypeString,
|
||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
DefaultValue: getDefaultNameserverAddress(),
|
||||
DefaultValue: nameserverAddress,
|
||||
ValidationRegex: "^(localhost|[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}|\\[[:0-9A-Fa-f]+\\]):[0-9]{1,5}$",
|
||||
RequiresRestart: true,
|
||||
Annotations: config.Annotations{
|
||||
@@ -66,7 +54,7 @@ func registerConfig() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameserverAddressConfig = config.GetAsString(CfgDefaultNameserverAddressKey, getDefaultNameserverAddress())
|
||||
nameserverAddressConfig = config.GetAsString(CfgDefaultNameserverAddressKey, nameserverAddress)
|
||||
|
||||
networkServiceMode = config.Concurrent.GetAsBool(core.CfgNetworkServiceKey, false)
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@ func prep() error {
|
||||
}
|
||||
|
||||
func start() error {
|
||||
logFlagOverrides()
|
||||
|
||||
if err := registerMetrics(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -55,14 +55,25 @@ func checkForConflictingService(ip net.IP, port uint16) error {
|
||||
|
||||
// Notify the user that we killed something.
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: "namserver:stopped-conflicting-service",
|
||||
Type: notifications.Info,
|
||||
Title: "Conflicting DNS Service",
|
||||
Category: "Secure DNS",
|
||||
EventID: "namserver:stopped-conflicting-service",
|
||||
Type: notifications.Info,
|
||||
Title: "Stopped Conflicting DNS Client",
|
||||
Message: fmt.Sprintf(
|
||||
"The Portmaster stopped a conflicting name service (pid %d) to gain required system integration.",
|
||||
"The Portmaster stopped a conflicting DNS client (pid %d) to gain required system integration. If you are running another DNS client on this device on purpose, you can the check the documentation if it is compatible with the Portmaster.",
|
||||
killed,
|
||||
),
|
||||
ShowOnSystem: true,
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
{
|
||||
Text: "Open Docs",
|
||||
Type: notifications.ActionTypeOpenURL,
|
||||
Payload: "https://docs.safing.io/portmaster/install/status/software-compatibility",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Restart nameserver via service-worker logic.
|
||||
|
||||
@@ -16,8 +16,8 @@ var (
|
||||
|
||||
func init() {
|
||||
module = modules.Register("netenv", prep, start, nil)
|
||||
module.RegisterEvent(NetworkChangedEvent)
|
||||
module.RegisterEvent(OnlineStatusChangedEvent)
|
||||
module.RegisterEvent(NetworkChangedEvent, true)
|
||||
module.RegisterEvent(OnlineStatusChangedEvent, true)
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
|
||||
"github.com/safing/portbase/notifications"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
@@ -243,24 +241,29 @@ func setCaptivePortal(portalURL *url.URL) {
|
||||
// notify
|
||||
cleanUpPortalNotification()
|
||||
captivePortalNotification = notifications.Notify(¬ifications.Notification{
|
||||
EventID: "netenv:captive-portal",
|
||||
Type: notifications.Info,
|
||||
Title: "Captive Portal",
|
||||
Category: "Core",
|
||||
Message: fmt.Sprintf(
|
||||
"Portmaster detected a captive portal at %s",
|
||||
captivePortal.Domain,
|
||||
),
|
||||
EventData: captivePortal,
|
||||
EventID: "netenv:captive-portal",
|
||||
Type: notifications.Info,
|
||||
Title: "Captive Portal Detected",
|
||||
Message: "The Portmaster detected a captive portal. You might experience limited network connectivity until the portal is handled.",
|
||||
ShowOnSystem: true,
|
||||
EventData: captivePortal,
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
Text: "Open Portal",
|
||||
Type: notifications.ActionTypeOpenURL,
|
||||
Payload: captivePortal.URL,
|
||||
},
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "Ignore",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func cleanUpPortalNotification() {
|
||||
if captivePortalNotification != nil {
|
||||
err := captivePortalNotification.Delete()
|
||||
if err != nil && err != database.ErrNotFound {
|
||||
log.Warningf("netenv: failed to delete old captive portal notification: %s", err)
|
||||
}
|
||||
captivePortalNotification.Delete()
|
||||
captivePortalNotification = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,8 @@ func updateGlobalConfigProfile(ctx context.Context, task *modules.Task) error {
|
||||
// Add module warning to inform user.
|
||||
module.Warning(
|
||||
globalConfigProfileErrorID,
|
||||
fmt.Sprintf("Failed to process global settings: %s", err),
|
||||
"Internal Settings Failure",
|
||||
fmt.Sprintf("Some global settings might not be applied correctly. You can try restarting the Portmaster to resolve this problem. Error: %s", err),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,12 @@ func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit
|
||||
// Get from database.
|
||||
profile, err = getProfile(scopedID)
|
||||
|
||||
// Check if the request is for a special profile that may need a reset.
|
||||
if err == nil && specialProfileNeedsReset(profile) {
|
||||
// Trigger creation of special profile.
|
||||
err = database.ErrNotFound
|
||||
}
|
||||
|
||||
// If we cannot find a profile, check if the request is for a special
|
||||
// profile we can create.
|
||||
if errors.Is(err, database.ErrNotFound) {
|
||||
|
||||
@@ -97,10 +97,15 @@ type Profile struct { //nolint:maligned // not worth the effort
|
||||
// an object) need to be concatenated for the settings database
|
||||
// path.
|
||||
Config map[string]interface{}
|
||||
|
||||
// ApproxLastUsed holds a UTC timestamp in seconds of
|
||||
// when this Profile was approximately last used.
|
||||
// For performance reasons not every single usage is saved.
|
||||
ApproxLastUsed int64
|
||||
// LastEdited hols the UTC timestamp in seconds when the profile was last
|
||||
// edited by the user. This is not set automatically, but has to be manually
|
||||
// set by the user interface.
|
||||
LastEdited int64
|
||||
// Created holds the UTC timestamp in seconds when the
|
||||
// profile has been created.
|
||||
Created int64
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/notifications"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnidentifiedProfileID is the profile ID used for unidentified processes.
|
||||
UnidentifiedProfileID = "_unidentified"
|
||||
@@ -76,7 +85,19 @@ func getSpecialProfile(profileID, linkedPath string) *Profile {
|
||||
return New(SourceLocal, SystemProfileID, linkedPath, nil)
|
||||
|
||||
case SystemResolverProfileID:
|
||||
return New(SourceLocal, SystemResolverProfileID, linkedPath, nil)
|
||||
return New(
|
||||
SourceLocal,
|
||||
SystemResolverProfileID,
|
||||
linkedPath,
|
||||
map[string]interface{}{
|
||||
CfgOptionServiceEndpointsKey: []string{
|
||||
"+ Localhost", // Allow everything from localhost.
|
||||
"+ LAN UDP/5353", // Allow inbound mDNS requests and multicast replies.
|
||||
"+ LAN UDP/5355", // Allow inbound LLMNR requests and multicast replies.
|
||||
"+ LAN UDP/1900", // Allow inbound SSDP requests and multicast replies.
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
case PortmasterProfileID:
|
||||
profile := New(SourceLocal, PortmasterProfileID, linkedPath, nil)
|
||||
@@ -117,3 +138,63 @@ func getSpecialProfile(profileID, linkedPath string) *Profile {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// specialProfileNeedsReset is used as a workaround until we can properly use
|
||||
// profile layering in a way that it is also correctly handled by the UI. We
|
||||
// check if the special profile has not been changed by the user and if not,
|
||||
// check if the profile is outdated and can be upgraded.
|
||||
func specialProfileNeedsReset(profile *Profile) bool {
|
||||
switch {
|
||||
case profile.Source != SourceLocal:
|
||||
// Special profiles live in the local scope only.
|
||||
return false
|
||||
case profile.LastEdited > 0:
|
||||
// Profile was edited - don't override user settings.
|
||||
return false
|
||||
}
|
||||
|
||||
switch profile.ID {
|
||||
case SystemResolverProfileID:
|
||||
return canBeUpgraded(profile, "10.5.2021")
|
||||
default:
|
||||
// Not a special profile or no upgrade available yet.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func canBeUpgraded(profile *Profile, upgradeDate string) bool {
|
||||
// Parse upgrade date.
|
||||
upgradeTime, err := time.Parse("2.1.2006", upgradeDate)
|
||||
if err != nil {
|
||||
log.Warningf("profile: failed to parse date %q: %s", upgradeDate, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the upgrade is applicable.
|
||||
if profile.Meta().Created < upgradeTime.Unix() {
|
||||
log.Infof("profile: upgrading special profile %s", profile.ScopedID())
|
||||
|
||||
notifications.NotifyInfo(
|
||||
"profiles:upgraded-special-profile-"+profile.ID,
|
||||
profile.Name+" Settings Upgraded",
|
||||
// TODO: Remove disclaimer.
|
||||
fmt.Sprintf(
|
||||
"The %s settings were automatically upgraded. The current app settings have been replaced, as the Portmaster did not detect any changes made by you. Please note that settings upgrades before June 2021 might not detect previous changes correctly and you might want to review the new settings.",
|
||||
profile.Name,
|
||||
),
|
||||
notifications.Action{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
notifications.Action{
|
||||
Text: "Open Settings",
|
||||
Type: notifications.ActionTypeOpenProfile,
|
||||
Payload: profile.ScopedID(),
|
||||
},
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func registerAPI() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "dns/clear",
|
||||
Read: api.PermitUser,
|
||||
Write: api.PermitUser,
|
||||
ActionFunc: clearNameCache,
|
||||
Name: "Clear cached DNS records",
|
||||
Description: "Deletes all saved DNS records from the database.",
|
||||
@@ -29,16 +29,16 @@ func registerAPI() error {
|
||||
}
|
||||
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `dns/cache`,
|
||||
Path: `dns/cache/{query:[a-z0-9\.-]{0,512}\.[A-Z]{1,32}}`,
|
||||
Read: api.PermitUser,
|
||||
RecordFunc: func(r *api.Request) (record.Record, error) {
|
||||
return recordDatabase.Get(nameRecordsKeyPrefix + r.URL.Query().Get("q"))
|
||||
return recordDatabase.Get(nameRecordsKeyPrefix + r.URLVars["query"])
|
||||
},
|
||||
Name: "Get DNS Record from Cache",
|
||||
Description: "Returns cached dns records from the internal cache.",
|
||||
Parameters: []api.Parameter{{
|
||||
Method: http.MethodGet,
|
||||
Field: "q",
|
||||
Field: "query (in path)",
|
||||
Value: "fqdn and query type",
|
||||
Description: "Specify the query like this: `example.com.A`.",
|
||||
}},
|
||||
|
||||
@@ -15,15 +15,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ClearNameCacheEvent is a triggerable event that clears the name record cache.
|
||||
ClearNameCacheEvent = "clear name cache"
|
||||
|
||||
module *modules.Module
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("resolver", prep, start, nil, "base", "netenv")
|
||||
module.RegisterEvent(ClearNameCacheEvent)
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
@@ -80,18 +76,6 @@ func start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DEPRECATED: remove in v0.7
|
||||
// cache clearing
|
||||
err = module.RegisterEventHook(
|
||||
"resolver",
|
||||
ClearNameCacheEvent,
|
||||
ClearNameCacheEvent,
|
||||
clearNameCacheEventHandler,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
module.StartServiceWorker(
|
||||
"mdns handler",
|
||||
5*time.Second,
|
||||
|
||||
@@ -240,7 +240,8 @@ func (brc *BasicResolverConn) IsFailing() bool {
|
||||
// Reset failure status if the network changed since the last query.
|
||||
if brc.networkChangedFlag.IsSet() {
|
||||
brc.networkChangedFlag.Refresh()
|
||||
brc.ResetFailure()
|
||||
brc.fails = 0
|
||||
brc.failing.UnSet()
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -227,16 +227,22 @@ func loadResolvers() {
|
||||
)
|
||||
|
||||
if len(newResolvers) == 0 {
|
||||
msg := "no (valid) dns servers found in configuration or system, falling back to defaults"
|
||||
log.Warningf("resolver: %s", msg)
|
||||
module.Warning(missingResolversErrorID, msg)
|
||||
log.Warning("resolver: no (valid) dns server found in config or system, falling back to global defaults")
|
||||
module.Warning(
|
||||
missingResolversErrorID,
|
||||
"Using Factory Default DNS Servers",
|
||||
"The Portmaster could not find any (valid) DNS servers in the settings or system. In order to prevent being disconnected, the factory defaults are being used instead.",
|
||||
)
|
||||
|
||||
// load defaults directly, overriding config system
|
||||
newResolvers = getConfiguredResolvers(defaultNameServers)
|
||||
if len(newResolvers) == 0 {
|
||||
msg = "no (valid) dns servers found in configuration or system"
|
||||
log.Criticalf("resolver: %s", msg)
|
||||
module.Error(missingResolversErrorID, msg)
|
||||
log.Critical("resolver: no (valid) dns server found in config, system or global defaults")
|
||||
module.Error(
|
||||
missingResolversErrorID,
|
||||
"No DNS Server Configured",
|
||||
"The Portmaster could not find any (valid) DNS servers in the settings or system. You will experience severe connectivity problems until resolved.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
ui/api.go
Normal file
35
ui/api.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
resources "github.com/cookieo9/resources-go"
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
func registerAPIEndpoints() error {
|
||||
return api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "ui/reload",
|
||||
Write: api.PermitUser,
|
||||
ActionFunc: reloadUI,
|
||||
Name: "Reload UI Assets",
|
||||
Description: "Removes all assets from the cache and reloads the current (possibly updated) version from disk when requested.",
|
||||
})
|
||||
}
|
||||
|
||||
func reloadUI(_ *api.Request) (msg string, err error) {
|
||||
appsLock.Lock()
|
||||
defer appsLock.Unlock()
|
||||
|
||||
// close all bundles.
|
||||
for id, bundle := range apps {
|
||||
err := bundle.Close()
|
||||
if err != nil {
|
||||
log.Warningf("ui: failed to close bundle %s: %s", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset index.
|
||||
apps = make(map[string]*resources.BundleSequence)
|
||||
|
||||
return "all ui bundles successfully reloaded", nil
|
||||
}
|
||||
27
ui/module.go
27
ui/module.go
@@ -1,11 +1,8 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/safing/portbase/dataroot"
|
||||
|
||||
resources "github.com/cookieo9/resources-go"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
@@ -23,7 +20,9 @@ func init() {
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
module.RegisterEvent(eventReload)
|
||||
if err := registerAPIEndpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return registerRoutes()
|
||||
}
|
||||
@@ -42,25 +41,5 @@ func start() error {
|
||||
log.Warningf("ui: failed to create safe exec dir: %s", err)
|
||||
}
|
||||
|
||||
return module.RegisterEventHook("ui", eventReload, "reload assets", reloadUI)
|
||||
}
|
||||
|
||||
func reloadUI(ctx context.Context, _ interface{}) error {
|
||||
log.Info("core: user/UI requested UI reload")
|
||||
|
||||
appsLock.Lock()
|
||||
defer appsLock.Unlock()
|
||||
|
||||
// close all bundles
|
||||
for id, bundle := range apps {
|
||||
err := bundle.Close()
|
||||
if err != nil {
|
||||
log.Warningf("ui: failed to close bundle %s: %s", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
// reset index
|
||||
apps = make(map[string]*resources.BundleSequence)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
24
updates/api.go
Normal file
24
updates/api.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/api"
|
||||
)
|
||||
|
||||
const (
|
||||
apiPathCheckForUpdates = "updates/check"
|
||||
)
|
||||
|
||||
func registerAPIEndpoints() error {
|
||||
return api.RegisterEndpoint(api.Endpoint{
|
||||
Path: apiPathCheckForUpdates,
|
||||
Write: api.PermitUser,
|
||||
ActionFunc: func(_ *api.Request) (msg string, err error) {
|
||||
if err := TriggerUpdate(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "triggered update check", nil
|
||||
},
|
||||
Name: "Check for Updates",
|
||||
Description: "Triggers checking for updates.",
|
||||
})
|
||||
}
|
||||
@@ -3,12 +3,15 @@ package updates
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/safing/portbase/notifications"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
const (
|
||||
cfgDevModeKey = "core/devMode"
|
||||
cfgDevModeKey = "core/devMode"
|
||||
updatesDisabledNotificationID = "updates:disabled"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -85,7 +88,6 @@ func initConfig() {
|
||||
|
||||
func updateRegistryConfig(_ context.Context, _ interface{}) error {
|
||||
changed := false
|
||||
forceUpdate := false
|
||||
|
||||
if releaseChannel() != previousReleaseChannel {
|
||||
registry.SetBeta(releaseChannel() == releaseChannelBeta)
|
||||
@@ -102,20 +104,33 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error {
|
||||
if enableUpdates() != updatesCurrentlyEnabled {
|
||||
updatesCurrentlyEnabled = enableUpdates()
|
||||
changed = true
|
||||
forceUpdate = updatesCurrentlyEnabled
|
||||
}
|
||||
|
||||
if changed {
|
||||
registry.SelectVersions()
|
||||
module.TriggerEvent(VersionUpdateEvent, nil)
|
||||
|
||||
if forceUpdate {
|
||||
module.Resolve(updateFailed)
|
||||
_ = TriggerUpdate()
|
||||
log.Infof("updates: automatic updates enabled again.")
|
||||
} else if !updatesCurrentlyEnabled {
|
||||
module.Warning(updateFailed, "Automatic updates are disabled! This also affects security updates and threat intelligence.")
|
||||
log.Warningf("updates: automatic updates are now disabled.")
|
||||
if updatesCurrentlyEnabled {
|
||||
module.Resolve("")
|
||||
if err := TriggerUpdate(); err != nil {
|
||||
log.Warningf("updates: failed to trigger update: %s", err)
|
||||
}
|
||||
log.Infof("updates: automatic updates are now enabled")
|
||||
} else {
|
||||
notifications.NotifyWarn(
|
||||
updatesDisabledNotificationID,
|
||||
"Automatic Updates Disabled",
|
||||
"Automatic updates are disabled through configuration. Please note that this is potentially dangerous, as this also affects security updates as well as the filter lists and threat intelligence feeds.",
|
||||
notifications.Action{
|
||||
ID: "change",
|
||||
Text: "Change",
|
||||
Type: notifications.ActionTypeOpenSetting,
|
||||
Payload: ¬ifications.ActionTypeOpenSettingPayload{
|
||||
Key: enableUpdatesKey,
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
log.Warningf("updates: automatic updates are now disabled")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/safing/portbase/dataroot"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portbase/updater"
|
||||
)
|
||||
|
||||
@@ -41,10 +42,6 @@ const (
|
||||
// to check if new versions of their resources are
|
||||
// available by checking File.UpgradeAvailable().
|
||||
ResourceUpdateEvent = "resource update"
|
||||
|
||||
// TriggerUpdateEvent is the event that can be emitted
|
||||
// by the updates module to trigger an update.
|
||||
TriggerUpdateEvent = "trigger update"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -68,18 +65,18 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
updateInProgress = "update-in-progress"
|
||||
updateInProcessDescr = "Portmaster is currently checking and downloading updates."
|
||||
updateFailed = "update-failed"
|
||||
updateInProgress = "updates:in-progress"
|
||||
updateFailed = "updates:failed"
|
||||
updateSuccess = "updates:success"
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register(ModuleName, prep, start, stop, "base")
|
||||
module.RegisterEvent(VersionUpdateEvent)
|
||||
module.RegisterEvent(ResourceUpdateEvent)
|
||||
module.RegisterEvent(VersionUpdateEvent, true)
|
||||
module.RegisterEvent(ResourceUpdateEvent, true)
|
||||
|
||||
flag.StringVar(&userAgentFromFlag, "update-agent", "", "Sets the user agent for requests to the update server")
|
||||
flag.BoolVar(&staging, "staging", false, "Use staging update channel (for testing only)")
|
||||
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 {
|
||||
@@ -109,7 +106,7 @@ func prep() error {
|
||||
return err
|
||||
}
|
||||
|
||||
module.RegisterEvent(TriggerUpdateEvent)
|
||||
return registerAPIEndpoints()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -127,18 +124,6 @@ func start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := module.RegisterEventHook(
|
||||
module.Name,
|
||||
TriggerUpdateEvent,
|
||||
"Check for and download available updates",
|
||||
func(context.Context, interface{}) error {
|
||||
_ = TriggerUpdate()
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create registry
|
||||
registry = &updater.ResourceRegistry{
|
||||
Name: ModuleName,
|
||||
@@ -244,17 +229,21 @@ func start() error {
|
||||
|
||||
// TriggerUpdate queues the update task to execute ASAP.
|
||||
func TriggerUpdate() error {
|
||||
if !module.Online() {
|
||||
if !module.OnlineSoon() {
|
||||
return fmt.Errorf("module not enabled")
|
||||
}
|
||||
switch {
|
||||
case !module.OnlineSoon():
|
||||
return fmt.Errorf("updates module is disabled")
|
||||
|
||||
case !module.Online():
|
||||
updateASAP = true
|
||||
} else {
|
||||
|
||||
case !enableUpdates():
|
||||
return fmt.Errorf("automatic updating is disabled")
|
||||
|
||||
default:
|
||||
updateTask.StartASAP()
|
||||
log.Debugf("updates: triggering update to run as soon as possible")
|
||||
}
|
||||
|
||||
log.Debugf("updates: triggering update to run as soon as possible")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -278,13 +267,37 @@ func checkForUpdates(ctx context.Context) (err error) {
|
||||
}
|
||||
defer log.Debugf("updates: finished checking for updates")
|
||||
|
||||
module.Hint(updateInProgress, updateInProcessDescr)
|
||||
|
||||
defer func() {
|
||||
if err == nil {
|
||||
module.Resolve(updateInProgress)
|
||||
module.Resolve(updateFailed)
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: updateSuccess,
|
||||
Type: notifications.Info,
|
||||
Title: "Update Check Successful",
|
||||
Message: "The Portmaster successfully checked for updates and downloaded any available updates. Most updates are applied automatically. You will be notified of important updates that need restarting.",
|
||||
Expires: time.Now().Add(1 * time.Minute).Unix(),
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
module.Warning(updateFailed, "Failed to update: "+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.",
|
||||
notifications.Action{
|
||||
ID: "retry",
|
||||
Text: "Try Again Now",
|
||||
Type: notifications.ActionTypeWebhook,
|
||||
Payload: ¬ifications.ActionTypeWebhookPayload{
|
||||
URL: apiPathCheckForUpdates,
|
||||
ResultAction: "display",
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -109,10 +109,10 @@ func upgradeCoreNotify() error {
|
||||
),
|
||||
Category: "Core",
|
||||
Message: fmt.Sprintf(
|
||||
`:tada: Update to **Portmaster v%s** is available!
|
||||
Please restart the Portmaster to apply the update.`,
|
||||
`A new Portmaster version is available! Restart the Portmaster to upgrade to %s.`,
|
||||
pmCoreUpdate.Version(),
|
||||
),
|
||||
ShowOnSystem: true,
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "restart",
|
||||
@@ -135,18 +135,10 @@ Please restart the Portmaster to apply the update.`,
|
||||
func upgradeCoreNotifyActionHandler(_ context.Context, n *notifications.Notification) error {
|
||||
switch n.SelectedActionID {
|
||||
case "restart":
|
||||
// Cannot directly trigger due to import loop.
|
||||
err := module.InjectEvent(
|
||||
"user triggered restart via notification",
|
||||
"core",
|
||||
"restart",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to trigger restart via notification: %s", err)
|
||||
}
|
||||
log.Infof("updates: user triggered restart via core update notification")
|
||||
RestartNow()
|
||||
case "later":
|
||||
return n.Delete()
|
||||
n.Delete()
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -257,19 +249,16 @@ func warnOnIncorrectParentPath() {
|
||||
root := filepath.Dir(registry.StorageDir().Path)
|
||||
if !strings.HasPrefix(absPath, root) {
|
||||
log.Warningf("detected unexpected path %s for portmaster-start", absPath)
|
||||
|
||||
notifications.Notify(¬ifications.Notification{
|
||||
EventID: "updates:unsupported-parent",
|
||||
Type: notifications.Warning,
|
||||
Title: "Unsupported Launcher",
|
||||
Category: "Core",
|
||||
Message: 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.",
|
||||
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),
|
||||
),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user