github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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  }