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 }