Skip to content

Commit

Permalink
Add syncing states with notifications for new module system
Browse files Browse the repository at this point in the history
  • Loading branch information
dhaavi committed Jul 18, 2024
1 parent b93ed09 commit d2c6ab5
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 187 deletions.
164 changes: 53 additions & 111 deletions base/notifications/module-mirror.go
Original file line number Diff line number Diff line change
@@ -1,116 +1,58 @@
package notifications

import (
// "github.com/safing/portbase/modules"
// "github.com/safing/portmaster/base/log"
// "github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/service/mgr"
)

// AttachToModule attaches the notification to a module and changes to the
// notification will be reflected on the module failure status.
// func (n *Notification) AttachToState(state *mgr.StateMgr) {
// if state == nil {
// log.Warningf("notifications: invalid usage: cannot attach %s to nil module", n.EventID)
// return
// }

// n.lock.Lock()
// defer n.lock.Unlock()

// if n.State != Active {
// log.Warningf("notifications: cannot attach module to inactive notification %s", n.EventID)
// return
// }
// if n.belongsTo != nil {
// log.Warningf("notifications: cannot override attached module for notification %s", n.EventID)
// return
// }

// // Attach module.
// n.belongsTo = state

// // Set module failure status.
// switch n.Type { //nolint:exhaustive
// case Info:
// m.Hint(n.EventID, n.Title, n.Message)
// case Warning:
// m.Warning(n.EventID, n.Title, n.Message)
// case Error:
// m.Error(n.EventID, n.Title, n.Message)
// default:
// log.Warningf("notifications: incompatible type for attaching to module in notification %s", n.EventID)
// m.Error(n.EventID, n.Title, n.Message+" [incompatible notification type]")
// }
// }

// // resolveModuleFailure removes the notification from the module failure status.
// func (n *Notification) resolveModuleFailure() {
// if n.belongsTo != nil {
// // Resolve failure in attached module.
// n.belongsTo.Resolve(n.EventID)

// // Reset attachment in order to mitigate duplicate failure resolving.
// // Re-attachment is prevented by the state check when attaching.
// n.belongsTo = nil
// }
// }

// func init() {
// modules.SetFailureUpdateNotifyFunc(mirrorModuleStatus)
// }

// func mirrorModuleStatus(moduleFailure uint8, id, title, msg string) {
// // Ignore "resolve all" requests.
// if id == "" {
// return
// }

// // Get notification from storage.
// n, ok := getNotification(id)
// if ok {
// // The notification already exists.

// // Check if we should delete it.
// if moduleFailure == modules.FailureNone && !n.Meta().IsDeleted() {

// // Remove belongsTo, as the deletion was already triggered by the module itself.
// n.Lock()
// n.belongsTo = nil
// n.Unlock()

// n.Delete()
// }

// return
// }

// // A notification for the given ID does not yet exists, create it.
// n = &Notification{
// EventID: id,
// Title: title,
// Message: msg,
// AvailableActions: []*Action{
// {
// Text: "Get Help",
// Type: ActionTypeOpenURL,
// Payload: "https://safing.io/support/",
// },
// },
// }

// switch moduleFailure {
// case modules.FailureNone:
// return
// case modules.FailureHint:
// n.Type = Info
// n.AvailableActions = nil
// case modules.FailureWarning:
// n.Type = Warning
// n.ShowOnSystem = true
// case modules.FailureError:
// n.Type = Error
// n.ShowOnSystem = true
// }

// Notify(n)
// }
// SyncWithState syncs the notification to a state in the given state mgr.
// The state will be removed when the notification is removed.
func (n *Notification) SyncWithState(state *mgr.StateMgr) {
if state == nil {
log.Warningf("notifications: invalid usage: cannot attach %s to nil module", n.EventID)
return
}

n.lock.Lock()
defer n.lock.Unlock()

if n.Meta().IsDeleted() {
log.Warningf("notifications: cannot attach module to deleted notification %s", n.EventID)
return
}
if n.State != Active {
log.Warningf("notifications: cannot attach module to inactive notification %s", n.EventID)
return
}
if n.belongsTo != nil {
log.Warningf("notifications: cannot override attached module for notification %s", n.EventID)
return
}

// Attach module.
n.belongsTo = state

// Create state with same ID.
state.Add(mgr.State{
ID: n.EventID,
Name: n.Title,
Message: n.Message,
Type: notifTypeToStateType(n.Type),
Data: n.EventData,
})
}

func notifTypeToStateType(notifType Type) mgr.StateType {
switch notifType {
case Info:
return mgr.StateTypeHint
case Warning:
return mgr.StateTypeWarning
case Prompt:
return mgr.StateTypeUndefined
case Error:
return mgr.StateTypeError
default:
return mgr.StateTypeUndefined
}
}
12 changes: 10 additions & 2 deletions base/notifications/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type Notification struct { //nolint:maligned

// belongsTo holds the state this notification belongs to. The notification
// lifecycle will be mirrored to the specified failure status.
// belongsTo *mgr.StateMgr
belongsTo *mgr.StateMgr

lock sync.Mutex
actionFunction NotificationActionFn // call function to process action
Expand Down Expand Up @@ -425,6 +425,11 @@ func (n *Notification) delete(pushUpdate bool) {
n.lock.Lock()
defer n.lock.Unlock()

// Check if notification is already deleted.
if n.Meta().IsDeleted() {
return
}

// Save ID for deletion
id = n.EventID

Expand All @@ -442,7 +447,10 @@ func (n *Notification) delete(pushUpdate bool) {
dbController.PushUpdate(n)
}

// n.resolveModuleFailure()
// Remove the connected state.
if n.belongsTo != nil {
n.belongsTo.Remove(n.EventID)
}
}

// Expired notifies the caller when the notification has expired.
Expand Down
2 changes: 1 addition & 1 deletion service/compat/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (issue *systemIssue) notify(err error) {
notifications.Notify(n)

systemIssueNotification = n
// n.AttachToModule(module)
n.SyncWithState(module.states)

// Report the raw error as module error.
// FIXME(vladimir): Is there a need for this kind of error reporting?
Expand Down
50 changes: 39 additions & 11 deletions service/mgr/states.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,32 @@ type StateMgr struct {

// State describes the state of a manager or module.
type State struct {
ID string // Required.
Name string // Required.
Message string // Optional.
Type StateType // Optional.
Time time.Time // Optional, will be set to current time if not set.
Data any // Optional.
// ID is a program-unique ID.
// It must not only be unique within the StateMgr, but for the whole program,
// as it may be re-used with related systems.
// Required.
ID string

// Name is the name of the state.
// This may also serve as a notification title.
// Required.
Name string

// Message is a more detailed message about the state.
// Optional.
Message string

// Type defines the type of the state.
// Optional.
Type StateType

// Time is the time when the state was created or the originating incident occured.
// Optional, will be set to current time if not set.
Time time.Time

// Data can hold any additional data necessary for further processing of connected systems.
// Optional.
Data any
}

// StateType defines commonly used states.
Expand Down Expand Up @@ -59,6 +79,7 @@ type StateUpdate struct {
States []State
}

// StatefulModule is used for interface checks on modules.
type StatefulModule interface {
States() *StateMgr
}
Expand Down Expand Up @@ -87,10 +108,10 @@ func (m *StateMgr) Add(s State) {
}

// Update or add state.
index := slices.IndexFunc[[]State, State](m.states, func(es State) bool {
index := slices.IndexFunc(m.states, func(es State) bool {
return es.ID == s.ID
})
if index > 0 {
if index >= 0 {
m.states[index] = s
} else {
m.states = append(m.states, s)
Expand All @@ -104,11 +125,18 @@ func (m *StateMgr) Remove(id string) {
m.statesLock.Lock()
defer m.statesLock.Unlock()

slices.DeleteFunc[[]State, State](m.states, func(s State) bool {
return s.ID == id
var entryRemoved bool
m.states = slices.DeleteFunc(m.states, func(s State) bool {
if s.ID == id {
entryRemoved = true
return true
}
return false
})

m.statesEventMgr.Submit(m.export())
if entryRemoved {
m.statesEventMgr.Submit(m.export())
}
}

// Clear removes all states.
Expand Down
13 changes: 6 additions & 7 deletions service/nameserver/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func startListener(ip net.IP, port uint16, first bool) {
}

func handleListenError(err error, ip net.IP, port uint16, primaryListener bool) {
// var n *notifications.Notification
var n *notifications.Notification

// Create suffix for secondary listener
var secondaryEventIDSuffix string
Expand Down Expand Up @@ -208,7 +208,7 @@ func handleListenError(err error, ip net.IP, port uint16, primaryListener bool)
}

// Notify user about conflicting service.
_ = notifications.Notify(&notifications.Notification{
n = notifications.Notify(&notifications.Notification{
EventID: eventIDConflictingService + secondaryEventIDSuffix,
Type: notifications.Error,
Title: "Conflicting DNS Software",
Expand All @@ -225,7 +225,7 @@ func handleListenError(err error, ip net.IP, port uint16, primaryListener bool)
})
} else {
// If no conflict is found, report the error directly.
_ = notifications.Notify(&notifications.Notification{
n = notifications.Notify(&notifications.Notification{
EventID: eventIDListenerFailed + secondaryEventIDSuffix,
Type: notifications.Error,
Title: "Secure DNS Error",
Expand All @@ -238,10 +238,9 @@ func handleListenError(err error, ip net.IP, port uint16, primaryListener bool)
}

// Attach error to module, if primary listener.
// TODO(vladimir): is this needed?
// if primaryListener {
// n.AttachToModule(module)
// }
if primaryListener {
n.SyncWithState(module.states)
}
}

func stop() error {
Expand Down
2 changes: 0 additions & 2 deletions service/netenv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ func (ne *NetEnv) Stop() error {
}

func prep() error {
// FIXME(vladimir): Does this need to be in the prep function.
checkForIPv6Stack()

if err := registerAPIEndpoints(); err != nil {
Expand All @@ -59,7 +58,6 @@ func prep() error {
return err
}

// FIXME(vladimir): Does this need to be in the prep function.
return prepLocation()
}

Expand Down
3 changes: 1 addition & 2 deletions service/resolver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ This notification will go away when Portmaster detects a working configured DNS
notifications.Notify(n)

failingResolverNotification = n
// TODO(vladimir): is this needed?
// n.AttachToModule(module)
n.SyncWithState(module.states)
}

func resetFailingResolversNotification() {
Expand Down
Loading

0 comments on commit d2c6ab5

Please sign in to comment.