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