@ -2,27 +2,33 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux freebsd
// Package monitor provides facilities for monitoring network
// interface changes.
package monitor
import (
"sync"
"time"
"tailscale.com/types/logger"
)
// Message represents a message returned from a connection.
// TODO(]|[): currently messages are being discarded, so the
// properties of the message haven't been defined.
type Message interface { }
// message represents a message returned from an osMon.
//
// TODO: currently messages are being discarded, so the properties of
// the message haven't been defined.
type message interface { }
// Conn represents the connection that is being monitored.
type Conn interface {
// osMon is the interface that each operating system-specific
// implementation of the link monitor must implement.
type osMon interface {
Close ( ) error
Receive ( ) ( Message , error )
// Receive returns a new network interface change message. It
// should block until there's either something to return, or
// until the osMon is closed. After a Close, the returned
// error is ignored.
Receive ( ) ( message , error )
}
// ChangeFunc is a callback function that's called when
@ -33,41 +39,68 @@ type ChangeFunc func()
type Mon struct {
logf logger . Logf
cb ChangeFunc
conn Conn
om osMon // nil means not supported on this platform
change chan struct { }
stop chan struct { }
onceStart sync . Once
started bool
goroutines sync . WaitGroup
}
// New instantiates and starts a monitoring instance. Change notifications
// are propagated to the callback function.
// The returned monitor is inactive until it's started by the Start method.
func New ( logf logger . Logf , callback ChangeFunc ) ( * Mon , error ) {
conn, err := NewCon n( )
om, err := newOSMo n( )
if err != nil {
return nil , err
}
ret := & Mon {
return & Mon {
logf : logf ,
cb : callback ,
conn: conn ,
om: om ,
change : make ( chan struct { } , 1 ) ,
stop : make ( chan struct { } ) ,
}
go ret . pump ( )
go ret . debounce ( )
return ret , nil
} , nil
}
// Start starts the monitor.
// A monitor can only be started & closed once.
func ( m * Mon ) Start ( ) {
m . onceStart . Do ( func ( ) {
if m . om == nil {
return
}
m . started = true
m . goroutines . Add ( 2 )
go m . pump ( )
go m . debounce ( )
} )
}
// Close is used to close the underlying connection.
// Close closes the monitor.
// It may only be called once.
func ( m * Mon ) Close ( ) error {
close ( m . stop )
return m . conn . Close ( )
var err error
if m . om != nil {
err = m . om . Close ( )
}
// If it was previously started, wait for those goroutines to finish.
m . onceStart . Do ( func ( ) { } )
if m . started {
m . goroutines . Wait ( )
}
return err
}
// pump continuously retrieves messages from the connection, notifying
// the change channel of changes, and stopping when a stop is issued.
func ( m * Mon ) pump ( ) {
defer m . goroutines . Done ( )
for {
_ , err := m . conn . Receive ( )
_ , err := m . om . Receive ( )
if err != nil {
select {
case <- m . stop :
@ -90,6 +123,7 @@ func (m *Mon) pump() {
// debounce calls the callback function with a delay between events
// and exits when a stop is issued.
func ( m * Mon ) debounce ( ) {
defer m . goroutines . Done ( )
for {
select {
case <- m . stop :