diff --git a/src/calibre/devices/scanner.py b/src/calibre/devices/scanner.py index 9b729a3561..dd0cb12d73 100644 --- a/src/calibre/devices/scanner.py +++ b/src/calibre/devices/scanner.py @@ -8,7 +8,7 @@ manner. import sys, os, re from threading import RLock -from calibre.constants import iswindows, isosx, plugins, islinux +from calibre.constants import iswindows, isosx, plugins, islinux, isfreebsd osx_scanner = win_scanner = linux_scanner = None @@ -155,17 +155,80 @@ class LinuxScanner(object): ans.add(tuple(dev)) return ans +class FreeBSDScanner(object): + + def __call__(self): + ans = set([]) + import dbus + devs = [] + + try: + bus = dbus.SystemBus() + manager = dbus.Interface(bus.get_object('org.freedesktop.Hal', + '/org/freedesktop/Hal/Manager'), 'org.freedesktop.Hal.Manager') + paths = manager.FindDeviceStringMatch('freebsd.driver','da') + for path in paths: + obj = bus.get_object('org.freedesktop.Hal', path) + objif = dbus.Interface(obj, 'org.freedesktop.Hal.Device') + devif = objif + parentdriver = None + while parentdriver != 'umass': + try: + obj = bus.get_object('org.freedesktop.Hal', + objif.GetProperty('info.parent')) + objif = dbus.Interface(obj, 'org.freedesktop.Hal.Device') + try: + parentdriver = objif.GetProperty('freebsd.driver') + except dbus.exceptions.DBusException, e: + continue + except dbus.exceptions.DBusException, e: + break + if parentdriver != 'umass': + continue + dev = [] + try: + dev.append(objif.GetProperty('usb.vendor_id')) + dev.append(objif.GetProperty('usb.product_id')) + dev.append(objif.GetProperty('usb.device_revision_bcd')) + except dbus.exceptions.DBusException, e: + continue + try: + dev.append(objif.GetProperty('info.vendor')) + except: + dev.append('') + try: + dev.append(objif.GetProperty('info.product')) + except: + dev.append('') + try: + dev.append(objif.GetProperty('usb.serial')) + except: + dev.append('') + dev.append(path) + ans.add(tuple(dev)) + except dbus.exceptions.DBusException, e: + print >>sys.stderr, "Execution failed:", e + return ans + + + linux_scanner = None if islinux: linux_scanner = LinuxScanner() +freebsd_scanner = None + +if isfreebsd: + freebsd_scanner = FreeBSDScanner() + + class DeviceScanner(object): def __init__(self, *args): if isosx and osx_scanner is None: raise RuntimeError('The Python extension usbobserver must be available on OS X.') - self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner + self.scanner = win_scanner if iswindows else osx_scanner if isosx else freebsd_scanner if isfreebsd else linux_scanner self.devices = [] def scan(self): diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index e364ef4c20..1aa6dd1a40 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -677,19 +677,21 @@ class Device(DeviceConfig, DevicePlugin): self._card_a_prefix = self._card_b_prefix self._card_b_prefix = None + # ------------------------------------------------------ # # open for FreeBSD -# find the device node or nodes that match the S/N we already have from the scanner -# and attempt to mount each one -# 1. get list of disk devices from sysctl -# 2. compare that list with the one from camcontrol -# 3. and see if it has a matching s/n -# 6. find any partitions/slices associated with each node -# 7. attempt to mount, using calibre-mount-helper, each one -# 8. when finished, we have a list of mount points and associated device nodes +# find the device node or nodes that match the S/N we already have from the scanner +# and attempt to mount each one +# 1. get list of devices in /dev with matching s/n etc. +# 2. get list of volumes associated with each +# 3. attempt to mount each one using Hal +# 4. when finished, we have a list of mount points and associated dbus nodes # def open_freebsd(self): + import dbus + # There should be some way to access the -v arg... + verbose = False # this gives us access to the S/N, etc. of the reader that the scanner has found # and the match routines for some of that data, like s/n, vendor ID, etc. @@ -698,129 +700,149 @@ class Device(DeviceConfig, DevicePlugin): if not d.serial: raise DeviceError("Device has no S/N. Can't continue") return False + + vols=[] + + bus = dbus.SystemBus() + manager = dbus.Interface(bus.get_object('org.freedesktop.Hal', + '/org/freedesktop/Hal/Manager'), 'org.freedesktop.Hal.Manager') + paths = manager.FindDeviceStringMatch('usb.serial',d.serial) + for path in paths: + objif = dbus.Interface(bus.get_object('org.freedesktop.Hal', path), 'org.freedesktop.Hal.Device') + # Extra paranoia... + try: + if d.idVendor == objif.GetProperty('usb.vendor_id') and \ + d.idProduct == objif.GetProperty('usb.product_id') and \ + d.manufacturer == objif.GetProperty('usb.vendor') and \ + d.product == objif.GetProperty('usb.product') and \ + d.serial == objif.GetProperty('usb.serial'): + dpaths = manager.FindDeviceStringMatch('storage.originating_device', path) + for dpath in dpaths: + devif = dbus.Interface(bus.get_object('org.freedesktop.Hal', dpath), 'org.freedesktop.Hal.Device') + try: + vpaths = manager.FindDeviceStringMatch('block.storage_device', dpath) + for vpath in vpaths: + try: + vdevif = dbus.Interface(bus.get_object('org.freedesktop.Hal', vpath), 'org.freedesktop.Hal.Device') + if not vdevif.GetProperty('block.is_volume'): + continue + if vdevif.GetProperty('volume.fsusage') != 'filesystem': + continue + volif = dbus.Interface(bus.get_object('org.freedesktop.Hal', vpath), 'org.freedesktop.Hal.Device.Volume') + pdevif = dbus.Interface(bus.get_object('org.freedesktop.Hal', vdevif.GetProperty('info.parent')), 'org.freedesktop.Hal.Device') + vol = {'node': pdevif.GetProperty('block.device'), + 'dev': vdevif, + 'vol': volif, + 'label': vdevif.GetProperty('volume.label')} + vols.append(vol) + except dbus.exceptions.DBusException, e: + print e + continue + except dbus.exceptions.DBusException, e: + print e + continue + except dbus.exceptions.DBusException, e: + continue - devs={} - di=0 - ndevs=4 # number of possible devices per reader (main, carda, cardb, launcher) + def ocmp(x,y): + if x['node'] < y['node']: + return -1 + if x['node'] > y['node']: + return 1 + return 0 - #get list of disk devices - p=subprocess.Popen(["sysctl", "kern.disks"], stdout=subprocess.PIPE) - kdsks=subprocess.Popen(["sed", "s/kern.disks: //"], stdin=p.stdout, stdout=subprocess.PIPE).communicate()[0] - p.stdout.close() - #print kdsks - for dvc in kdsks.split(): - # for each one that's also in the list of cam devices ... - p=subprocess.Popen(["camcontrol", "devlist"], stdout=subprocess.PIPE) - devmatch=subprocess.Popen(["grep", dvc], stdin=p.stdout, stdout=subprocess.PIPE).communicate()[0] - p.stdout.close() - if devmatch: - #print "Checking ", devmatch - # ... see if we can get a S/N from the actual device node - sn=subprocess.Popen(["camcontrol", "inquiry", dvc, "-S"], stdout=subprocess.PIPE).communicate()[0] - sn=sn[0:-1] # drop the trailing newline - #print "S/N = ", sn - if sn and d.match_serial(sn): - # we have a matching s/n, record this device node - #print "match found: ", dvc - devs[di]=dvc - di += 1 + vols.sort(cmp=ocmp) - # sort the list of devices - for i in range(1,ndevs+1): - for j in reversed(range(1,i)): - if devs[j-1] > devs[j]: - x=devs[j-1] - devs[j-1]=devs[j] - devs[j]=x - #print devs - - # now we need to see if any of these have slices/partitions + if verbose: + print "FBSD: ", vols + mtd=0 - label="READER" # could use something more unique, like S/N or productID... - cmd = '/usr/local/bin/calibre-mount-helper' - cmd = [cmd, 'mount'] - for i in range(0,ndevs): - cmd2="ls /dev/"+devs[i]+"*" - p=subprocess.Popen(cmd2, shell=True, stdout=subprocess.PIPE) - devs[i]=subprocess.Popen(["cut", "-d", "/", "-f" "3"], stdin=p.stdout, stdout=subprocess.PIPE).communicate()[0] - p.stdout.close() - # try all the nodes to see what we can mount - for dev in devs[i].split(): - mp='/media/'+label+'-'+dev - mmp = mp - if mmp.endswith('/'): - mmp = mmp[:-1] - #print "trying ", dev, "on", mp + for vol in vols: + mp = '' + if vol['dev'].GetProperty('volume.is_mounted'): + mp = vol['dev'].GetProperty('volume.mount_point') + else: + if verbose: + print "FBSD: trying ", vol['label'], "on", 'Calibre-'+labels[i] try: - p = subprocess.Popen(cmd + ["/dev/"+dev, mmp]) - except OSError: - raise DeviceError(_('Could not find mount helper: %s.')%cmd[0]) - while p.poll() is None: - time.sleep(0.1) + vol['vol'].Mount('Calibre-'+vol['label'], + vol['dev'].GetProperty('volume.fstype'), []) + loops = 0 + while not vol['dev'].GetProperty('volume.is_mounted'): + time.sleep(1) + loops += 1 + if loops > 100: + print "ERROR: Timeout waiting for mount to complete" + continue + mp = vol['dev'].GetProperty('volume.mount_point') + except dbus.exceptions.DBusException, e: + print "Failed to mount ", e + continue - if p.returncode == 0: - #print " mounted", dev - if i == 0: - self._main_prefix = mp - self._main_dev = "/dev/"+dev - #print "main = ", self._main_dev, self._main_prefix - if i == 1: - self._card_a_prefix = mp - self._card_a_dev = "/dev/"+dev - #print "card a = ", self._card_a_dev, self._card_a_prefix - if i == 2: - self._card_b_prefix = mp - self._card_b_dev = "/dev/"+dev - #print "card b = ", self._card_b_dev, self._card_b_prefix + # Mount Point becomes Mount Path + mp += '/' - mtd += 1 - break + if verbose: + print "FBSD: mounted", vol['label'], "on", mp + if mtd == 0: + self._main_prefix = mp + self._main_vol = vol['vol'] + if verbose: + print "FBSD: main = ", self._main_prefix + if mtd == 1: + self._card_a_prefix = mp + self._card_a_vol = vol['vol'] + if verbose: + print "FBSD: card a = ", self._card_a_prefix + if mtd == 2: + self._card_b_prefix = mp + self._card_b_vol = vol['vol'] + if verbose: + print "FBSD: card b = ", self._card_b_prefix + # Note that mtd is used as a bool... not incrementing is fine. + break + mtd += 1 if mtd > 0: return True - else : - return False + raise DeviceError(_('Unable to mount the device')) + # # ------------------------------------------------------ # -# this one is pretty simple: -# just umount each of the previously -# mounted filesystems, using the mount helper +# this one is pretty simple: +# just umount each of the previously +# mounted filesystems, using the stored volume object # def eject_freebsd(self): - cmd = '/usr/local/bin/calibre-mount-helper' - cmd = [cmd, 'eject'] + import dbus + # There should be some way to access the -v arg... + verbose = False if self._main_prefix: - #print "umount main:", cmd, self._main_dev, self._main_prefix + if verbose: + print "FBSD: umount main:", self._main_prefix try: - p = subprocess.Popen(cmd + [self._main_dev, self._main_prefix]) - except OSError: - raise DeviceError( - _('Could not find mount helper: %s.')%cmd[0]) - while p.poll() is None: - time.sleep(0.1) + self._main_vol.Unmount([]) + except dbus.exceptions.DBusException, e: + print 'Unable to eject ', e if self._card_a_prefix: - #print "umount card a:", cmd, self._card_a_dev, self._card_a_prefix + if verbose: + print "FBSD: umount card a:", self._card_a_prefix try: - p = subprocess.Popen(cmd + [self._card_a_dev, self._card_a_prefix]) - except OSError: - raise DeviceError( - _('Could not find mount helper: %s.')%cmd[0]) - while p.poll() is None: - time.sleep(0.1) + self._card_a_vol.Unmount([]) + except dbus.exceptions.DBusException, e: + print 'Unable to eject ', e if self._card_b_prefix: - #print "umount card b:", cmd, self._card_b_dev, self._card_b_prefix + if verbose: + print "FBSD: umount card b:", self._card_b_prefix try: - p = subprocess.Popen(cmd + [self._card_b_dev, self._card_b_prefix]) - except OSError: - raise DeviceError( - _('Could not find mount helper: %s.')%cmd[0]) - while p.poll() is None: - time.sleep(0.1) + self._card_b_vol.Unmount([]) + except dbus.exceptions.DBusException, e: + print 'Unable to eject ', e self._main_prefix = None self._card_a_prefix = None @@ -839,11 +861,10 @@ class Device(DeviceConfig, DevicePlugin): time.sleep(7) self.open_linux() if isfreebsd: - self._main_dev = self._card_a_dev = self._card_b_dev = None + self._main_vol = self._card_a_vol = self._card_b_vol = None try: self.open_freebsd() except DeviceError: - subprocess.Popen(["camcontrol", "rescan", "all"]) time.sleep(2) self.open_freebsd() if iswindows: