github.com/haraldrudell/parl@v0.4.176/pnet/interface-cache.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pnet
     7  
     8  import (
     9  	"net"
    10  	"sync"
    11  	"sync/atomic"
    12  
    13  	"github.com/haraldrudell/parl/perrors"
    14  )
    15  
    16  const (
    17  	NoCache  NameCacher = iota // name cache should not be used in the api call
    18  	Update                     // use name cache and update the cache first
    19  	NoUpdate                   // use name cache without update. Mechanic for ongoing name cache update is required
    20  )
    21  
    22  // NameCacher contaisn instructions for interface-name cache read: [NoCache] [Update] [NoUpdate]
    23  type NameCacher uint8
    24  
    25  // InterfaceCache is a cache mapping per-boot stable network interface index #1 to name "lo0". Thread-Safe
    26  type InterfaceCache struct {
    27  	updateLock sync.Mutex
    28  	m          atomic.Pointer[map[IfIndex]string] // write behind lock
    29  }
    30  
    31  // NewInterfaceCache returns a cache mapiing network-interfac eindex to name. Thread-Safe
    32  func NewInterfaceCache() (interfaceCache *InterfaceCache) {
    33  	return &InterfaceCache{}
    34  }
    35  
    36  // Init loads the cache, an opeation that may fail. Funtional chaining. Thread-Safe
    37  func (i *InterfaceCache) Init() (i0 *InterfaceCache) {
    38  	if _, err := i.Update(); err != nil {
    39  		panic(err)
    40  	}
    41  	return i
    42  }
    43  
    44  // CachedName retrieves a name by index with optional cache update. Thread-Safe
    45  //   - default is to update
    46  //   - unknown index returns empty string
    47  func (i *InterfaceCache) CachedName(ifIndex IfIndex, noUpdate ...NameCacher) (name string, err error) {
    48  	var cacher = Update
    49  	if len(noUpdate) > 0 {
    50  		cacher = noUpdate[0]
    51  	}
    52  
    53  	if cacher != Update {
    54  		name = i.CachedNameNoUpdate(ifIndex)
    55  		return
    56  	}
    57  
    58  	var m map[IfIndex]string
    59  	if m, err = i.Update(); err != nil {
    60  		return
    61  	}
    62  	name = m[ifIndex]
    63  
    64  	return
    65  }
    66  
    67  // CachedNameNoUpdate retrieves a name by index with no cache update. Thread-Safe
    68  func (i *InterfaceCache) CachedNameNoUpdate(ifIndex IfIndex) (name string) {
    69  	var m map[IfIndex]string
    70  	if mp := i.m.Load(); mp != nil {
    71  		m = *mp
    72  	} else {
    73  		panic(perrors.NewPF("map never updated"))
    74  	}
    75  
    76  	name = m[ifIndex]
    77  	return
    78  }
    79  
    80  // Update updates the cache by reading all system interfaces. Thread-Safe
    81  func (i *InterfaceCache) Update() (m map[IfIndex]string, err error) {
    82  	i.updateLock.Lock()
    83  	defer i.updateLock.Unlock()
    84  
    85  	// get interfaces
    86  	var interfaces []net.Interface
    87  	if interfaces, err = Interfaces(); err != nil {
    88  		return
    89  	}
    90  
    91  	// get map
    92  	if mp := i.m.Load(); mp != nil {
    93  		m = *mp
    94  	} else {
    95  		m = make(map[IfIndex]string)
    96  	}
    97  
    98  	// populate map
    99  	for ix := 0; ix < len(interfaces); ix++ {
   100  		ifp := &interfaces[ix]
   101  		if ifp.Name == "" {
   102  			continue
   103  		}
   104  		var ifIndex IfIndex
   105  		if ifIndex, err = NewIfIndexInt(ifp.Index); err != nil {
   106  			return
   107  		}
   108  		m[ifIndex] = ifp.Name
   109  	}
   110  
   111  	// store atomically
   112  	i.m.Store(&m)
   113  
   114  	return
   115  }
   116  
   117  // Map returns a copy of the current cache. Thread-Safe
   118  func (i *InterfaceCache) Map() (m map[IfIndex]string) {
   119  	i.updateLock.Lock()
   120  	defer i.updateLock.Unlock()
   121  
   122  	if mp := i.m.Load(); mp != nil {
   123  		m = make(map[IfIndex]string, len(*mp))
   124  		for k, v := range *mp {
   125  			m[k] = v
   126  		}
   127  	}
   128  	return
   129  }
   130  
   131  // Map replaces the cache. Thread-Safe
   132  //   - do not read from or write to newMap after SetMap
   133  func (i *InterfaceCache) SetMap(newMap map[IfIndex]string) (oldMap map[IfIndex]string) {
   134  	i.updateLock.Lock()
   135  	defer i.updateLock.Unlock()
   136  
   137  	if mp := i.m.Swap(&newMap); mp != nil {
   138  		oldMap = *mp
   139  	}
   140  	return
   141  }