github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/udevevent/monitor.go (about)

     1  /*
     2  Copyright 2018 The OpenEBS Author
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package udevevent
    18  
    19  import (
    20  	"errors"
    21  	"syscall"
    22  
    23  	libudevwrapper "github.com/openebs/node-disk-manager/pkg/udev"
    24  	"github.com/openebs/node-disk-manager/pkg/util"
    25  )
    26  
    27  type UdevEventType uint
    28  
    29  // monitor contains udev and udevmonitor struct
    30  type monitor struct {
    31  	udev        *libudevwrapper.Udev
    32  	udevMonitor *libudevwrapper.UdevMonitor
    33  }
    34  
    35  // UdevEvent is a wrapper for an event received from udev
    36  type UdevEvent struct {
    37  	*libudevwrapper.UdevDevice
    38  	eventType UdevEventType
    39  }
    40  
    41  // Subscription is used to receive events from udev
    42  type Subscription struct {
    43  	targetChannel   chan UdevEvent
    44  	subscribedTypes []UdevEventType
    45  }
    46  
    47  const (
    48  	EventTypeAdd UdevEventType = iota
    49  	EventTypeRemove
    50  	EventTypeChange
    51  )
    52  
    53  var subscriptions []*Subscription
    54  
    55  var ErrInvalidSubscription = errors.New("invailid subscription")
    56  
    57  // newMonitor returns monitor struct in success
    58  // we can get fd and monitor using this struct
    59  func newMonitor() (*monitor, error) {
    60  	udev, err := libudevwrapper.NewUdev()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	udevMonitor, err := udev.NewDeviceFromNetlink(libudevwrapper.UDEV_SOURCE)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	err = udevMonitor.AddSubsystemFilter(libudevwrapper.UDEV_SUBSYSTEM)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	err = udevMonitor.EnableReceiving()
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	monitor := &monitor{
    77  		udev:        udev,
    78  		udevMonitor: udevMonitor,
    79  	}
    80  	return monitor, nil
    81  }
    82  
    83  // setup returns file descriptor value to monitor system
    84  func (m *monitor) setup() (int, error) {
    85  	return m.udevMonitor.GetFd()
    86  }
    87  
    88  // free frees udev and udevMonitor pointer
    89  func (m *monitor) free() {
    90  	if m.udev != nil {
    91  		m.udev.UnrefUdev()
    92  	}
    93  	if m.udevMonitor != nil {
    94  		m.udevMonitor.UdevMonitorUnref()
    95  	}
    96  }
    97  
    98  // process get device info which is attached or detached
    99  // generate one new event and sent it to udev probe
   100  func (m *monitor) process(fd int) error {
   101  	fds := &syscall.FdSet{}
   102  	util.FD_ZERO(fds)
   103  	util.FD_SET(fds, int(fd))
   104  	ret, _ := syscall.Select(int(fd)+1, fds, nil, nil, nil)
   105  	if ret <= 0 {
   106  		return errors.New("unable to apply select call")
   107  	}
   108  	if !util.FD_ISSET(fds, int(fd)) {
   109  		return errors.New("unable to set fd")
   110  	}
   111  	device, err := m.udevMonitor.ReceiveDevice()
   112  	if err != nil {
   113  		return err
   114  	}
   115  	// if device is not disk or partition, do not process it
   116  	if !device.IsDisk() && !device.IsParitition() {
   117  		device.UdevDeviceUnref()
   118  		return nil
   119  	}
   120  	var eventType UdevEventType
   121  	switch device.GetAction() {
   122  	case libudevwrapper.UDEV_ACTION_ADD:
   123  		eventType = EventTypeAdd
   124  	case libudevwrapper.UDEV_ACTION_REMOVE:
   125  		eventType = EventTypeRemove
   126  	case libudevwrapper.UDEV_ACTION_CHANGE:
   127  		eventType = EventTypeChange
   128  	default:
   129  		return errors.New("unknown udev action")
   130  	}
   131  	event := UdevEvent{device, eventType}
   132  	dispatchEvent(event)
   133  	return nil
   134  }
   135  
   136  func dispatchEvent(event UdevEvent) {
   137  	for _, sub := range subscriptions {
   138  		hasType := false
   139  		for _, eventType := range sub.subscribedTypes {
   140  			if eventType == event.eventType {
   141  				hasType = true
   142  				break
   143  			}
   144  		}
   145  		if hasType {
   146  			sub.targetChannel <- event
   147  		}
   148  	}
   149  }
   150  
   151  //Monitor start monitoring on udev source
   152  func Monitor() <-chan error {
   153  	errChan := make(chan error)
   154  	go func() {
   155  		monitor, err := newMonitor()
   156  		if err != nil {
   157  			errChan <- err
   158  		}
   159  		defer monitor.free()
   160  		fd, err := monitor.setup()
   161  		if err != nil {
   162  			errChan <- err
   163  		}
   164  		for {
   165  			err := monitor.process(fd)
   166  			if err != nil {
   167  				errChan <- err
   168  			}
   169  		}
   170  	}()
   171  	return errChan
   172  }
   173  
   174  // Subscribe to udev events. Optionally pass eventTypes to filter the events
   175  // based on their type
   176  func Subscribe(eventTypes ...UdevEventType) *Subscription {
   177  	if len(eventTypes) == 0 {
   178  		eventTypes = []UdevEventType{EventTypeAdd, EventTypeRemove, EventTypeChange}
   179  	}
   180  	subscription := Subscription{
   181  		targetChannel:   make(chan UdevEvent),
   182  		subscribedTypes: eventTypes,
   183  	}
   184  	subscriptions = append(subscriptions, &subscription)
   185  	return &subscription
   186  }
   187  
   188  // Usubscribe from an active subscription
   189  func Unsubscribe(sub *Subscription) error {
   190  	if sub == nil || sub.targetChannel == nil || sub.subscribedTypes == nil {
   191  		return ErrInvalidSubscription
   192  	}
   193  	var deleteIndex = -1
   194  	for idx, subscription := range subscriptions {
   195  		if subscription == sub {
   196  			deleteIndex = idx
   197  		}
   198  	}
   199  	close(sub.targetChannel)
   200  	if deleteIndex == len(subscriptions)-1 {
   201  		subscriptions = subscriptions[:deleteIndex]
   202  	} else if deleteIndex == 0 {
   203  		subscriptions = subscriptions[1:]
   204  	} else {
   205  		subscriptions = append(subscriptions[:deleteIndex], subscriptions[deleteIndex+1:]...)
   206  	}
   207  	return nil
   208  }
   209  
   210  func (s *Subscription) Events() <-chan UdevEvent {
   211  	return s.targetChannel
   212  }
   213  
   214  func (u UdevEvent) GetType() UdevEventType {
   215  	return u.eventType
   216  }
   217  
   218  func (u UdevEvent) GetAction() UdevEventType {
   219  	return u.eventType
   220  }
   221  
   222  func (uevt UdevEventType) String() string {
   223  	switch uevt {
   224  	case EventTypeAdd:
   225  		return libudevwrapper.UDEV_ACTION_ADD
   226  	case EventTypeRemove:
   227  		return libudevwrapper.UDEV_ACTION_REMOVE
   228  	case EventTypeChange:
   229  		return libudevwrapper.UDEV_ACTION_CHANGE
   230  	default:
   231  		return "unknown"
   232  	}
   233  }