1 from Plugins.Plugin import PluginDescriptor
2 from Components.Harddisk import harddiskmanager
3 from Tools.Directories import fileExists
8 def processHotplugData(self, v):
10 action = v.get("ACTION")
11 device = v.get("DEVPATH")
12 physdevpath = v.get("PHYSDEVPATH")
13 media_state = v.get("X_E2_MEDIA_STATUS")
15 dev = device.split('/')[-1]
17 if action is not None and action == "add":
18 error, blacklisted, removable, is_cdrom, partitions, medium_found = harddiskmanager.addHotplugPartition(dev, physdevpath)
19 if bdpoll and removable or is_cdrom:
20 bdpoll.addDevice(dev, is_cdrom, medium_found)
21 elif action is not None and action == "remove":
23 bdpoll.removeDevice(dev)
24 harddiskmanager.removeHotplugPartition(dev)
25 elif media_state is not None:
26 if media_state == '1':
27 harddiskmanager.removeHotplugPartition(dev)
28 harddiskmanager.addHotplugPartition(dev, physdevpath)
29 elif media_state == '0':
30 harddiskmanager.removeHotplugPartition(dev)
32 for callback in hotplugNotifier:
34 callback(dev, action or media_state)
35 except AttributeError:
36 hotplugNotifier.remove(callback)
38 CDROM_DRIVE_STATUS = 0x5326
39 CDROM_MEDIA_CHANGED = 0x5325
40 CDSL_CURRENT = ((int)(~0>>1))
44 CDS_DRIVE_NOT_READY = 3
49 IOC_TYPESHIFT = (IOC_NRSHIFT+IOC_NRBITS)
50 BLKRRPART = ((0x12<<IOC_TYPESHIFT) | (95<<IOC_NRSHIFT))
52 def autostart(reason, **kwargs):
54 print "starting hotplug handler"
56 if fileExists('/dev/.udev'):
59 from enigma import eSocketNotifier, eTimer, ePythonMessagePump
61 from select import POLLIN, POLLPRI
65 self.netlink = socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 15)
66 self.netlink.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
67 self.netlink.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
68 self.netlink.bind((0, 1))
69 self.sn = eSocketNotifier(self.netlink.fileno(), POLLIN|POLLPRI)
70 self.sn.callback.append(self.dataAvail)
72 def dataAvail(self, what):
73 received = self.netlink.recvfrom(16384)
74 # print "HOTPLUG(%d):" %(what), received
76 data = received[0].split('\0')[:-1]
81 var, val = x[:i], x[i+1:]
84 if v['SUBSYSTEM'] == 'block' and v['ACTION'] in ('add', 'remove'):
85 processHotplugData(self, v)
87 from threading import Thread, Semaphore, Lock
114 class BDPoll(Thread):
115 CHECK_INTERVAL = 2000
116 MSG_MEDIUM_REMOVED = 1
117 MSG_MEDIUM_INSERTED = 2
118 MSG_POLL_FINISHED = 4
120 Thread.__init__(self)
121 self.__sema = Semaphore(0)
124 self.devices_to_poll = { }
125 self.messages = ThreadQueue()
126 self.checkTimer = eTimer()
127 self.checkTimer.callback.append(self.timeout)
128 self.checkTimer.start(BDPoll.CHECK_INTERVAL, True)
129 self.mp = ePythonMessagePump()
130 self.mp.recv_msg.get().append(self.gotThreadMsg)
133 def gotThreadMsg(self, msg):
134 msg = self.messages.pop()
135 if msg[0] == BDPoll.MSG_MEDIUM_REMOVED:
136 print "MSG_MEDIUM_REMOVED"
137 harddiskmanager.removeHotplugPartition(msg[1])
138 elif msg[0] == BDPoll.MSG_MEDIUM_INSERTED:
139 print "MSG_MEDIUM_INSERTED"
140 harddiskmanager.addHotplugPartition(msg[1])
141 elif msg[0] == BDPoll.MSG_POLL_FINISHED:
142 self.checkTimer.start(BDPoll.CHECK_INTERVAL, True)
145 self.__sema.release() # start bdpoll loop in thread
147 def is_mounted(self, dev):
148 mounts = file('/proc/mounts').read()
149 return mounts.find(dev) != -1
154 messages = self.messages
159 self.__lock.acquire()
160 devices_to_poll = self.devices_to_poll.items()
161 self.__lock.release()
162 devices_to_poll_processed = [ ]
163 for device, state in devices_to_poll:
165 is_cdrom, prev_media_state = state
168 fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK | os.O_EXCL)
170 if err.errno == errno.EBUSY:
171 print "open cdrom exclusive failed:",
172 if not self.is_mounted(device):
176 print "mounted... try non exclusive"
177 fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK)
179 print "open cdrom not exclusive failed", os.strerror(err.errno)
181 #here the fs must be valid!
183 ret = fcntl.ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)
185 print "ioctl CDROM_DRIVE_STATUS failed", os.strerror(err.errno)
187 if ret in (CDS_NO_INFO, CDS_NO_DISC, CDS_TRAY_OPEN, CDS_DRIVE_NOT_READY):
189 elif ret == CDS_DISC_OK:
190 #todo new kernels support events to userspace event on media change
191 #but not 2.6.18.... see hotplug-ng bdpoll.c
196 fd = os.open("/dev/" + device, os.O_RDONLY)
198 if err.errno == ENOMEDIUM:
201 print "open non cdrom failed", os.strerror(err.errno)
208 print "media removal detected on", device
210 fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK)
212 print "open device for blkrrpart ioctl failed", os.strerror(err.errno)
215 fcntl.ioctl(fd, BLKRRPART)
217 print "ioctl BLKRRPART failed", os.strerror(err.errno)
221 print "media insertion detected on", device
222 devices_to_poll_processed.append((device, is_cdrom, got_media))
223 self.__lock.acquire()
224 for device, is_cdrom, state in devices_to_poll_processed:
225 old_state = self.devices_to_poll.get(device)
226 if old_state is not None and old_state[1] != state:
227 msg = state and BDPoll.MSG_MEDIUM_INSERTED or BDPoll.MSG_MEDIUM_REMOVED
228 self.devices_to_poll[device] = (is_cdrom, state)
229 messages.push((msg, device))
232 self.__lock.release()
233 messages.push((self.MSG_POLL_FINISHED,))
236 def addDevice(self, device, is_cdrom, inserted):
237 self.__lock.acquire()
238 if device in self.devices_to_poll:
239 print "device", device, "already in bdpoll"
241 print "add device", device, "to bdpoll current state:",
243 print "medium inserted"
245 print "medium removed"
246 self.devices_to_poll[device] = (is_cdrom, inserted)
247 self.__lock.release()
249 def removeDevice(self, device):
250 self.__lock.acquire()
251 if device in self.devices_to_poll:
252 print "device", device, "removed from bdpoll"
253 del self.devices_to_poll[device]
255 print "try to del not exist device", device, "from bdpoll"
256 self.__lock.release()
260 for blockdev, removable, is_cdrom, medium_found in harddiskmanager.devices_scanned_on_init:
261 if removable or is_cdrom:
262 bdpoll.addDevice(blockdev, is_cdrom, medium_found)
264 from twisted.internet.protocol import Protocol, Factory
265 from twisted.internet import reactor
269 os.remove("/tmp/hotplug.socket")
273 class Hotplug(Protocol):
274 def connectionMade(self):
275 print "HOTPLUG connection!"
278 def dataReceived(self, data):
279 print "hotplug:", data
280 self.received += data
281 print "complete", self.received
283 def connectionLost(self, reason):
284 print "HOTPLUG connection lost!"
285 data = self.received.split('\0')[:-1]
290 var, val = x[:i], x[i+1:]
293 processHotplugData(self, v)
296 factory.protocol = Hotplug
297 reactor.listenUNIX("/tmp/hotplug.socket", factory)
299 def Plugins(**kwargs):
300 return PluginDescriptor(name = "Hotplug", description = "listens to hotplug events", where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = True, fnc = autostart)