@@ -64,8 +64,9 @@ import (
6464// and to further reduce the risk of accessing unexported methods or fields of [LocalBackend], the host interacts
6565// with it via the [Backend] interface.
6666type ExtensionHost struct {
67- b Backend
68- logf logger.Logf // prefixed with "ipnext:"
67+ b Backend
68+ hooks ipnext.Hooks
69+ logf logger.Logf // prefixed with "ipnext:"
6970
7071 // allExtensions holds the extensions in the order they were registered,
7172 // including those that have not yet attempted initialization or have failed to initialize.
@@ -84,22 +85,6 @@ type ExtensionHost struct {
8485 // doEnqueueBackendOperation adds an asynchronous [LocalBackend] operation to the workQueue.
8586 doEnqueueBackendOperation func (func (Backend ))
8687
87- // profileStateChangeCbs are callbacks that are invoked when the current login profile
88- // or its [ipn.Prefs] change, after those changes have been made. The current login profile
89- // may be changed either because of a profile switch, or because the profile information
90- // was updated by [LocalBackend.SetControlClientStatus], including when the profile
91- // is first populated and persisted.
92- profileStateChangeCbs []ipnext.ProfileStateChangeCallback
93- // backgroundProfileResolvers are registered background profile resolvers.
94- // They're used to determine the profile to use when no GUI/CLI client is connected.
95- backgroundProfileResolvers []ipnext.ProfileResolver
96- // auditLoggers are registered [AuditLogProvider]s.
97- // Each provider is called to get an [ipnauth.AuditLogFunc] when an auditable action
98- // is about to be performed. If an audit logger returns an error, the action is denied.
99- auditLoggers []ipnext.AuditLogProvider
100- // newControlClientCbs are the functions to be called when a new control client is created.
101- newControlClientCbs []ipnext.NewControlClientCallback
102-
10388 shuttingDown atomic.Bool
10489
10590 // mu protects the following fields.
@@ -208,6 +193,15 @@ func (h *ExtensionHost) Init() {
208193 }
209194}
210195
196+ var zeroHooks ipnext.Hooks
197+
198+ func (h * ExtensionHost ) Hooks () * ipnext.Hooks {
199+ if h == nil {
200+ return & zeroHooks
201+ }
202+ return & h .hooks
203+ }
204+
211205func (h * ExtensionHost ) init () {
212206 defer h .initDone .Store (true )
213207
@@ -360,24 +354,6 @@ func (h *ExtensionHost) SendNotifyAsync(n ipn.Notify) {
360354 })
361355}
362356
363- // addFuncHook appends non-nil fn to hooks.
364- func addFuncHook [F any ](h * ExtensionHost , hooks * []F , fn F ) {
365- if h .initDone .Load () {
366- panic ("invalid callback register after init" )
367- }
368- if reflect .ValueOf (fn ).IsZero () {
369- panic ("nil function hook" )
370- }
371- * hooks = append (* hooks , fn )
372- }
373-
374- // RegisterProfileStateChangeCallback implements [ipnext.ProfileServices].
375- func (h * ExtensionHost ) RegisterProfileStateChangeCallback (cb ipnext.ProfileStateChangeCallback ) {
376- if h != nil {
377- addFuncHook (h , & h .profileStateChangeCbs , cb )
378- }
379- }
380-
381357// NotifyProfileChange invokes registered profile state change callbacks
382358// and updates the current profile and prefs in the host.
383359// It strips private keys from the [ipn.Prefs] before preserving
@@ -397,7 +373,7 @@ func (h *ExtensionHost) NotifyProfileChange(profile ipn.LoginProfileView, prefs
397373 h .currentProfile = profile
398374 h .mu .Unlock ()
399375
400- for _ , cb := range h .profileStateChangeCbs {
376+ for _ , cb := range h .hooks . ProfileStateChange {
401377 cb (profile , prefs , sameNode )
402378 }
403379}
@@ -421,18 +397,11 @@ func (h *ExtensionHost) NotifyProfilePrefsChanged(profile ipn.LoginProfileView,
421397 // Get the callbacks to be invoked.
422398 h .mu .Unlock ()
423399
424- for _ , cb := range h .profileStateChangeCbs {
400+ for _ , cb := range h .hooks . ProfileStateChange {
425401 cb (profile , newPrefs , true )
426402 }
427403}
428404
429- // RegisterBackgroundProfileResolver implements [ipnext.ProfileServices].
430- func (h * ExtensionHost ) RegisterBackgroundProfileResolver (resolver ipnext.ProfileResolver ) {
431- if h != nil {
432- addFuncHook (h , & h .backgroundProfileResolvers , resolver )
433- }
434- }
435-
436405func (h * ExtensionHost ) active () bool {
437406 return h != nil && ! h .shuttingDown .Load ()
438407}
@@ -455,7 +424,7 @@ func (h *ExtensionHost) DetermineBackgroundProfile(profiles ipnext.ProfileStore)
455424
456425 // Attempt to resolve the background profile using the registered
457426 // background profile resolvers (e.g., [ipn/desktop.desktopSessionsExt] on Windows).
458- for _ , resolver := range h .backgroundProfileResolvers {
427+ for _ , resolver := range h .hooks . BackgroundProfileResolvers {
459428 if profile := resolver (profiles ); profile .Valid () {
460429 return profile
461430 }
@@ -466,37 +435,20 @@ func (h *ExtensionHost) DetermineBackgroundProfile(profiles ipnext.ProfileStore)
466435 return ipn.LoginProfileView {}
467436}
468437
469- // RegisterControlClientCallback implements [ipnext.Host].
470- func (h * ExtensionHost ) RegisterControlClientCallback (cb ipnext.NewControlClientCallback ) {
471- if h != nil {
472- addFuncHook (h , & h .newControlClientCbs , cb )
473- }
474- }
475-
476438// NotifyNewControlClient invokes all registered control client callbacks.
477439// It returns callbacks to be executed when the control client shuts down.
478440func (h * ExtensionHost ) NotifyNewControlClient (cc controlclient.Client , profile ipn.LoginProfileView ) (ccShutdownCbs []func ()) {
479441 if ! h .active () {
480442 return nil
481443 }
482- if len (h .newControlClientCbs ) > 0 {
483- ccShutdownCbs = make ([]func (), 0 , len (h .newControlClientCbs ))
484- for _ , cb := range h .newControlClientCbs {
485- if shutdown := cb (cc , profile ); shutdown != nil {
486- ccShutdownCbs = append (ccShutdownCbs , shutdown )
487- }
444+ for _ , cb := range h .hooks .NewControlClient {
445+ if shutdown := cb (cc , profile ); shutdown != nil {
446+ ccShutdownCbs = append (ccShutdownCbs , shutdown )
488447 }
489448 }
490449 return ccShutdownCbs
491450}
492451
493- // RegisterAuditLogProvider implements [ipnext.Host].
494- func (h * ExtensionHost ) RegisterAuditLogProvider (provider ipnext.AuditLogProvider ) {
495- if h != nil {
496- addFuncHook (h , & h .auditLoggers , provider )
497- }
498- }
499-
500452// AuditLogger returns a function that reports an auditable action
501453// to all registered audit loggers. It fails if any of them returns an error,
502454// indicating that the action cannot be logged and must not be performed.
@@ -510,8 +462,8 @@ func (h *ExtensionHost) AuditLogger() ipnauth.AuditLogFunc {
510462 if ! h .active () {
511463 return func (tailcfg.ClientAuditAction , string ) error { return nil }
512464 }
513- loggers := make ([]ipnauth.AuditLogFunc , 0 , len (h .auditLoggers ))
514- for _ , provider := range h .auditLoggers {
465+ loggers := make ([]ipnauth.AuditLogFunc , 0 , len (h .hooks . AuditLoggers ))
466+ for _ , provider := range h .hooks . AuditLoggers {
515467 loggers = append (loggers , provider ())
516468 }
517469 return func (action tailcfg.ClientAuditAction , details string ) error {
0 commit comments