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 }