github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/osutil/udev/netlink/conn.go (about) 1 package netlink 2 3 import ( 4 "fmt" 5 "os" 6 "syscall" 7 "time" 8 ) 9 10 type Mode int 11 12 // Mode determines event source: kernel events or udev-processed events. 13 // See libudev/libudev-monitor.c. 14 const ( 15 KernelEvent Mode = 1 16 // Events that are processed by udev - much richer, with more attributes (such as vendor info, serial numbers and more). 17 UdevEvent Mode = 2 18 ) 19 20 // Generic connection 21 type NetlinkConn struct { 22 Fd int 23 Addr syscall.SockaddrNetlink 24 } 25 26 type UEventConn struct { 27 NetlinkConn 28 } 29 30 // Connect allow to connect to system socket AF_NETLINK with family NETLINK_KOBJECT_UEVENT to 31 // catch events about block/char device 32 // see: 33 // - http://elixir.free-electrons.com/linux/v3.12/source/include/uapi/linux/netlink.h#L23 34 // - http://elixir.free-electrons.com/linux/v3.12/source/include/uapi/linux/socket.h#L11 35 func (c *UEventConn) Connect(mode Mode) (err error) { 36 37 if c.Fd, err = syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_KOBJECT_UEVENT); err != nil { 38 return 39 } 40 41 c.Addr = syscall.SockaddrNetlink{ 42 Family: syscall.AF_NETLINK, 43 Groups: uint32(mode), 44 Pid: uint32(os.Getpid()), 45 } 46 47 if err = syscall.Bind(c.Fd, &c.Addr); err != nil { 48 syscall.Close(c.Fd) 49 } 50 51 return 52 } 53 54 // Close allow to close file descriptor and socket bound 55 func (c *UEventConn) Close() error { 56 return syscall.Close(c.Fd) 57 } 58 59 // ReadMsg allow to read an entire uevent msg 60 func (c *UEventConn) ReadMsg() (msg []byte, err error) { 61 var n int 62 63 buf := make([]byte, os.Getpagesize()) 64 for { 65 // Just read how many bytes are available in the socket 66 if n, _, err = syscall.Recvfrom(c.Fd, buf, syscall.MSG_PEEK); err != nil { 67 return 68 } 69 70 // If all message could be store inside the buffer : break 71 if n < len(buf) { 72 break 73 } 74 75 // Increase size of buffer if not enough 76 buf = make([]byte, len(buf)+os.Getpagesize()) 77 } 78 79 // Now read complete data 80 n, _, err = syscall.Recvfrom(c.Fd, buf, 0) 81 if err != nil { 82 return 83 } 84 85 // Extract only real data from buffer and return that 86 msg = buf[:n] 87 88 return 89 } 90 91 // ReadMsg allow to read an entire uevent msg 92 func (c *UEventConn) ReadUEvent() (*UEvent, error) { 93 msg, err := c.ReadMsg() 94 if err != nil { 95 return nil, err 96 } 97 98 return ParseUEvent(msg) 99 } 100 101 // Monitor run in background a worker to read netlink msg in loop and notify 102 // when msg receive inside a queue using channel. 103 // To be notified with only relevant message, use Matcher. 104 func (c *UEventConn) Monitor(queue chan UEvent, errors chan error, matcher Matcher) (stop func(stopTimeout time.Duration) (ok bool)) { 105 if matcher != nil { 106 if err := matcher.Compile(); err != nil { 107 errors <- fmt.Errorf("Wrong matcher, err: %v", err) 108 return func(time.Duration) bool { 109 return true 110 } 111 } 112 } 113 114 quitting := make(chan struct{}) 115 quit := make(chan struct{}) 116 117 readableOrStop, stop1, err := RawSockStopper(c.Fd) 118 if err != nil { 119 errors <- fmt.Errorf("Internal error: %v", err) 120 return func(time.Duration) bool { 121 return true 122 } 123 } 124 // c.Fd is set to non-blocking at this point 125 126 stop = func(stopTimeout time.Duration) bool { 127 close(quitting) 128 stop1() 129 select { 130 case <-quit: 131 return true 132 case <-time.After(stopTimeout): 133 } 134 return false 135 } 136 137 go func() { 138 EventReading: 139 for { 140 _, err := readableOrStop() 141 if err != nil { 142 errors <- fmt.Errorf("Internal error: %v", err) 143 return 144 } 145 select { 146 case <-quitting: 147 close(quit) 148 return 149 default: 150 uevent, err := c.ReadUEvent() 151 // underlying file descriptor is 152 // non-blocking here, be paranoid if 153 // for some reason we get here after 154 // readableOrStop but the read would 155 // block anyway 156 if errno, ok := err.(syscall.Errno); ok && errno.Temporary() { 157 continue EventReading 158 } 159 if err != nil { 160 errors <- fmt.Errorf("Unable to parse uevent, err: %v", err) 161 continue 162 } 163 164 if matcher != nil { 165 if !matcher.Evaluate(*uevent) { 166 continue // Drop uevent if not match 167 } 168 } 169 170 queue <- *uevent 171 } 172 } 173 }() 174 return stop 175 }