+ import errno
+ import fcntl
+
+ class BDPoll(Thread):
+ CHECK_INTERVAL = 2000
+ MSG_MEDIUM_REMOVED = 1
+ MSG_MEDIUM_INSERTED = 2
+ MSG_POLL_FINISHED = 4
+ def __init__(self):
+ Thread.__init__(self)
+ self.__sema = Semaphore(0)
+ self.__lock = Lock()
+ self.running = False
+ self.devices_to_poll = { }
+ self.messages = ThreadQueue()
+ self.checkTimer = eTimer()
+ self.checkTimer.callback.append(self.timeout)
+ self.checkTimer.start(BDPoll.CHECK_INTERVAL, True)
+ self.mp = ePythonMessagePump()
+ self.mp.recv_msg.get().append(self.gotThreadMsg)
+ self.start()
+
+ def gotThreadMsg(self, msg):
+ msg = self.messages.pop()
+ if msg[0] == BDPoll.MSG_MEDIUM_REMOVED:
+ print "MSG_MEDIUM_REMOVED"
+ harddiskmanager.removeHotplugPartition(msg[1])
+ elif msg[0] == BDPoll.MSG_MEDIUM_INSERTED:
+ print "MSG_MEDIUM_INSERTED"
+ harddiskmanager.addHotplugPartition(msg[1])
+ elif msg[0] == BDPoll.MSG_POLL_FINISHED:
+ self.checkTimer.start(BDPoll.CHECK_INTERVAL, True)
+
+ def timeout(self):
+ self.__sema.release() # start bdpoll loop in thread
+
+ def is_mounted(self, dev):
+ mounts = file('/proc/mounts').read()
+ return mounts.find(dev) != -1
+
+ def run(self):
+ sema = self.__sema
+ lock = self.__lock
+ messages = self.messages
+ mp = self.mp
+ self.running = True
+ while self.running:
+ sema.acquire()
+ self.__lock.acquire()
+ devices_to_poll = self.devices_to_poll.items()
+ self.__lock.release()
+ devices_to_poll_processed = [ ]
+ for device, state in devices_to_poll:
+ got_media = False
+ is_cdrom, prev_media_state = state
+ if is_cdrom:
+ try:
+ fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK | os.O_EXCL)
+ except OSError, err:
+ if err.errno == errno.EBUSY:
+ print "open cdrom exclusive failed:",
+ if not self.is_mounted(device):
+ print "not mounted"
+ continue
+ try:
+ print "mounted... try non exclusive"
+ fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK)
+ except OSError, err:
+ print "open cdrom not exclusive failed", os.strerror(err.errno)
+ continue
+ #here the fs must be valid!
+ try:
+ ret = fcntl.ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)
+ except IOError, err:
+ print "ioctl CDROM_DRIVE_STATUS failed", os.strerror(err.errno)
+ else:
+ if ret in (CDS_NO_INFO, CDS_NO_DISC, CDS_TRAY_OPEN, CDS_DRIVE_NOT_READY):
+ pass
+ elif ret == CDS_DISC_OK:
+ #todo new kernels support events to userspace event on media change
+ #but not 2.6.18.... see hotplug-ng bdpoll.c
+ got_media = True
+ os.close(fd)
+ else:
+ try:
+ fd = os.open("/dev/" + device, os.O_RDONLY)
+ except OSError, err:
+ if err.errno == ENOMEDIUM:
+ pass
+ else:
+ print "open non cdrom failed", os.strerror(err.errno)
+ continue
+ else:
+ got_media = True
+ os.close(fd)
+ if prev_media_state:
+ if not got_media:
+ print "media removal detected on", device
+ try:
+ fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK)
+ except OSError, err:
+ print "open device for blkrrpart ioctl failed", os.strerror(err.errno)
+ else:
+ try:
+ fcntl.ioctl(fd, BLKRRPART)
+ except IOError, err:
+ print "ioctl BLKRRPART failed", os.strerror(err.errno)
+ os.close(fd)
+ else:
+ if got_media:
+ print "media insertion detected on", device
+ devices_to_poll_processed.append((device, is_cdrom, got_media))
+ self.__lock.acquire()
+ for device, is_cdrom, state in devices_to_poll_processed:
+ old_state = self.devices_to_poll.get(device)
+ if old_state is not None and old_state[1] != state:
+ msg = state and BDPoll.MSG_MEDIUM_INSERTED or BDPoll.MSG_MEDIUM_REMOVED
+ self.devices_to_poll[device] = (is_cdrom, state)
+ messages.push((msg, device))
+ mp.send(0)
+
+ self.__lock.release()
+ messages.push((self.MSG_POLL_FINISHED,))
+ mp.send(0)
+
+ def addDevice(self, device, is_cdrom, inserted):
+ self.__lock.acquire()
+ if device in self.devices_to_poll:
+ print "device", device, "already in bdpoll"
+ else:
+ print "add device", device, "to bdpoll current state:",
+ if inserted:
+ print "medium inserted"
+ else:
+ print "medium removed"
+ self.devices_to_poll[device] = (is_cdrom, inserted)
+ self.__lock.release()
+
+ def removeDevice(self, device):
+ self.__lock.acquire()
+ if device in self.devices_to_poll:
+ print "device", device, "removed from bdpoll"
+ del self.devices_to_poll[device]
+ else:
+ print "try to del not exist device", device, "from bdpoll"
+ self.__lock.release()
+
+ netlink = Netlink()
+ bdpoll = BDPoll()
+ for blockdev, removable, is_cdrom, medium_found in harddiskmanager.devices_scanned_on_init:
+ if removable or is_cdrom:
+ bdpoll.addDevice(blockdev, is_cdrom, medium_found)
+ else:
+ from twisted.internet.protocol import Protocol, Factory
+ from twisted.internet import reactor
+
+ try:
+ import os
+ os.remove("/tmp/hotplug.socket")
+ except OSError:
+ pass
+
+ class Hotplug(Protocol):
+ def connectionMade(self):
+ print "HOTPLUG connection!"
+ self.received = ""
+
+ def dataReceived(self, data):
+ print "hotplug:", data
+ self.received += data
+ print "complete", self.received
+
+ def connectionLost(self, reason):
+ print "HOTPLUG connection lost!"
+ data = self.received.split('\0')[:-1]
+ v = {}
+
+ for x in data:
+ i = x.find('=')
+ var, val = x[:i], x[i+1:]
+ v[var] = val
+
+ processHotplugData(self, v)