Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor power_event_watcher for configurable subscriber behavior #1764

Merged
merged 5 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl
// For now, remediation is not performed -- we only log the hardware change.
agent.DetectAndRemediateHardwareChange(ctx, k)

powerEventWatcher, err := powereventwatcher.New(ctx, k, slogger)
powerEventSubscriber := powereventwatcher.NewKnapsackSleepStateUpdater(slogger, k)
powerEventWatcher, err := powereventwatcher.New(ctx, slogger, powerEventSubscriber)
if err != nil {
slogger.Log(ctx, slog.LevelDebug,
"could not init power event watcher",
Expand Down
8 changes: 7 additions & 1 deletion ee/powereventwatcher/power_event_watcher_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ type noOpPowerEventWatcher struct {
interrupted bool
}

func New(ctx context.Context, _ types.Knapsack, _ *slog.Logger) (*noOpPowerEventWatcher, error) {
type noOpKnapsackSleepStateUpdater struct{}

func NewKnapsackSleepStateUpdater(_ *slog.Logger, _ types.Knapsack) *noOpKnapsackSleepStateUpdater {
return &noOpKnapsackSleepStateUpdater{}
}

func New(ctx context.Context, _ *slog.Logger, _ *noOpKnapsackSleepStateUpdater) (*noOpPowerEventWatcher, error) {
_, span := traces.StartSpan(ctx)
defer span.End()

Expand Down
3 changes: 2 additions & 1 deletion ee/powereventwatcher/power_event_watcher_other_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import (
func TestInterrupt_Multiple(t *testing.T) {
t.Parallel()

p, err := New(context.TODO(), typesmocks.NewKnapsack(t), multislogger.NewNopLogger())
ksubscriber := NewKnapsackSleepStateUpdater(multislogger.NewNopLogger(), typesmocks.NewKnapsack(t))
p, err := New(context.TODO(), multislogger.NewNopLogger(), ksubscriber)
require.NoError(t, err)

// Start and then interrupt
Expand Down
114 changes: 80 additions & 34 deletions ee/powereventwatcher/power_event_watcher_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,31 @@ type (

powerEventWatcher struct {
slogger *slog.Logger
knapsack types.Knapsack
powerEventSubscriber powerEventSubscriber
subscriptionHandle uintptr
subscribeProcedure *syscall.LazyProc
unsubscribeProcedure *syscall.LazyProc
renderEventLogProcedure *syscall.LazyProc
interrupt chan struct{}
interrupted bool
}

// powerEventSubscriber is an interface to be implemented by anything utilizing the power event updates.
// implementers are provided to New, and the interface methods below are called as described during relevant updates
powerEventSubscriber interface {
// OnPowerEvent will be called for the provided subscriber whenever any watched event is observed
OnPowerEvent(eventID int) error
// OnStartup will be called when the powerEventWatcher is initially set up, allowing subscribers
// to perform any setup behavior (e.g. cache clearing, state resetting)
OnStartup() error
}

// knapsackSleepStateUpdater implements the powerEventSubscriber interface and
// updates the knapsack.InModernStandby state based on the power events observed
knapsackSleepStateUpdater struct {
knapsack types.Knapsack
slogger *slog.Logger
}
)

const (
Expand All @@ -46,16 +63,66 @@ const (
operationSuccessfulMsg = "The operation completed successfully."
)

func NewKnapsackSleepStateUpdater(slogger *slog.Logger, k types.Knapsack) *knapsackSleepStateUpdater {
return &knapsackSleepStateUpdater{
knapsack: k,
slogger: slogger,
}
}

func (ks *knapsackSleepStateUpdater) OnPowerEvent(eventID int) error {
switch eventID {
case eventIdEnteringModernStandby, eventIdEnteringSleep:
ks.slogger.Log(context.TODO(), slog.LevelDebug,
"system is sleeping",
"event_id", eventID,
)
if err := ks.knapsack.SetInModernStandby(true); err != nil {
ks.slogger.Log(context.TODO(), slog.LevelWarn,
"encountered error setting modern standby value",
"in_modern_standby", true,
"err", err,
)
}
case eventIdExitingModernStandby:
ks.slogger.Log(context.TODO(), slog.LevelDebug,
"system is waking",
"event_id", eventID,
)
if err := ks.knapsack.SetInModernStandby(false); err != nil {
ks.slogger.Log(context.TODO(), slog.LevelWarn,
"encountered error setting modern standby value",
"in_modern_standby", false,
"err", err,
)
}
default:
ks.slogger.Log(context.TODO(), slog.LevelWarn,
"received unexpected event ID in log",
"event_id", eventID,
)
}

return nil
}

func (ks *knapsackSleepStateUpdater) OnStartup() error {
// Clear InModernStandby flag, in case it's cached. We may have missed wake/sleep events
// while launcher was not running, and we want to err on the side of assuming the device
// is awake.
return ks.knapsack.SetInModernStandby(false)
}

// New sets up a subscription to relevant power events with a callback to `onPowerEvent`.
func New(ctx context.Context, k types.Knapsack, slogger *slog.Logger) (*powerEventWatcher, error) {
func New(ctx context.Context, slogger *slog.Logger, pes powerEventSubscriber) (*powerEventWatcher, error) {
_, span := traces.StartSpan(ctx)
defer span.End()

evtApi := syscall.NewLazyDLL("wevtapi.dll")

p := &powerEventWatcher{
slogger: slogger.With("component", "power_event_watcher"),
knapsack: k,
powerEventSubscriber: pes,
subscribeProcedure: evtApi.NewProc("EvtSubscribe"),
unsubscribeProcedure: evtApi.NewProc("EvtClose"),
renderEventLogProcedure: evtApi.NewProc("EvtRender"),
Expand Down Expand Up @@ -97,10 +164,13 @@ func New(ctx context.Context, k types.Knapsack, slogger *slog.Logger) (*powerEve
// Save the handle so that we can close it later
p.subscriptionHandle = subscriptionHandle

// Clear InModernStandby flag, in case it's cached. We may have missed wake/sleep events
// while launcher was not running, and we want to err on the side of assuming the device
// is awake.
k.SetInModernStandby(false)
if err := p.powerEventSubscriber.OnStartup(); err != nil {
// log any issues here but don't prevent creation of the watcher
slogger.Log(ctx, slog.LevelError,
"encountered error issuing subscriber OnStartup",
"err", err,
)
}

return p, nil
}
Expand Down Expand Up @@ -195,34 +265,10 @@ func (p *powerEventWatcher) onPowerEvent(action uint32, _ uintptr, eventHandle u
return ret
}

switch e.System.EventID {
case eventIdEnteringModernStandby, eventIdEnteringSleep:
p.slogger.Log(context.TODO(), slog.LevelDebug,
"system is sleeping",
"event_id", e.System.EventID,
)
if err := p.knapsack.SetInModernStandby(true); err != nil {
p.slogger.Log(context.TODO(), slog.LevelWarn,
"could not enable osquery healthchecks on system sleep",
"err", err,
)
}
case eventIdExitingModernStandby:
p.slogger.Log(context.TODO(), slog.LevelDebug,
"system is waking",
"event_id", e.System.EventID,
)
if err := p.knapsack.SetInModernStandby(false); err != nil {
p.slogger.Log(context.TODO(), slog.LevelWarn,
"could not enable osquery healthchecks on system wake",
"err", err,
)
}
default:
if err := p.powerEventSubscriber.OnPowerEvent(e.System.EventID); err != nil {
p.slogger.Log(context.TODO(), slog.LevelWarn,
"received unexpected event ID in log",
"event_id", e.System.EventID,
"raw_event", string(utf8bytes),
"subscriber encountered error OnPowerEvent update",
"err", err,
)
}

Expand Down
Loading