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  }