Switch to using sysfs+pmount for device mounting in linux. Should allow for the device to be disconnected without unmounting and subsequently re-connected and still detected in calibre.

This commit is contained in:
Kovid Goyal 2009-06-03 13:18:29 -07:00
parent 45ed449af1
commit 2815d05ccc
5 changed files with 148 additions and 134 deletions

View File

@ -62,7 +62,6 @@ class PRS505(CLI, Device):
'''.encode('utf8')) '''.encode('utf8'))
return True return True
except: except:
self._card_prefix = None
import traceback import traceback
traceback.print_exc() traceback.print_exc()
return False return False

View File

@ -6,7 +6,7 @@ intended to be subclassed with the relevant parts implemented for a particular
device. This class handles device detection. device. This class handles device detection.
''' '''
import os, subprocess, time, re import os, subprocess, time, re, sys, glob
from itertools import repeat from itertools import repeat
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
@ -36,6 +36,7 @@ class Device(DeviceConfig, DevicePlugin):
MAIN_MEMORY_VOLUME_LABEL = '' MAIN_MEMORY_VOLUME_LABEL = ''
STORAGE_CARD_VOLUME_LABEL = '' STORAGE_CARD_VOLUME_LABEL = ''
STORAGE_CARD2_VOLUME_LABEL = None
FDI_TEMPLATE = \ FDI_TEMPLATE = \
''' '''
@ -345,63 +346,119 @@ class Device(DeviceConfig, DevicePlugin):
raise DeviceError(_('Unable to detect the %s disk drive.') raise DeviceError(_('Unable to detect the %s disk drive.')
%self.__class__.__name__) %self.__class__.__name__)
devnodes = [] devnodes, ok = [], {}
for x, isfile in walk(usb_dir): for x, isfile in walk(usb_dir):
if not isfile and '/block/' in x: if not isfile and '/block/' in x:
parts = x.split('/') parts = x.split('/')
idx = parts.index('block') idx = parts.index('block')
if idx == len(parts)-2: if idx == len(parts)-2:
devnodes.append(parts[idx+1]) sz = j(x, 'size')
node = parts[idx+1]
try:
exists = int(open(sz).read()) > 0
if exists:
node = self.find_largest_partition(x)
ok[node] = True
else:
ok[node] = False
except:
ok[node] = False
devnodes.append(node)
devnodes.sort() devnodes.sort()
devnodes += list(repeat(None, 2)) devnodes += list(repeat(None, 3))
return devnodes[:3] return tuple(['/dev/'+x if ok.get(x, False) else None for x in devnodes[:3]])
def node_mountpoint(self, node):
for line in open('/proc/mounts').readlines():
line = line.split()
if line[0] == node:
return line[1]
return None
def find_largest_partition(self, path):
node = path.split('/')[-1]
nodes = []
for x in glob.glob(path+'/'+node+'*'):
sz = x + '/size'
if not os.access(sz, os.R_OK):
continue
try:
sz = int(open(sz).read())
except:
continue
if sz > 0:
nodes.append((x.split('/')[-1], sz))
nodes.sort(cmp=lambda x, y: cmp(x[1], y[1]))
if not nodes:
return node
return nodes[-1][0]
def open_linux(self): def open_linux(self):
import dbus
bus = dbus.SystemBus()
hm = dbus.Interface(bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager"), "org.freedesktop.Hal.Manager")
def conditional_mount(dev): def mount(node, type):
mmo = bus.get_object("org.freedesktop.Hal", dev) mp = self.node_mountpoint(node)
label = mmo.GetPropertyString('volume.label', dbus_interface='org.freedesktop.Hal.Device') if mp is not None:
is_mounted = mmo.GetPropertyString('volume.is_mounted', dbus_interface='org.freedesktop.Hal.Device') return mp, 0
mount_point = mmo.GetPropertyString('volume.mount_point', dbus_interface='org.freedesktop.Hal.Device')
fstype = mmo.GetPropertyString('volume.fstype', dbus_interface='org.freedesktop.Hal.Device')
if is_mounted:
return str(mount_point)
mmo.Mount(label, fstype, ['umask=077', 'uid='+str(os.getuid()), 'sync'],
dbus_interface='org.freedesktop.Hal.Device.Volume')
return os.path.normpath('/media/'+label)+'/'
mm = hm.FindDeviceStringMatch(__appname__+'.mainvolume', self.__class__.__name__) if type == 'main':
if not mm: label = self.MAIN_MEMORY_VOLUME_LABEL
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%(self.__class__.__name__,)) if type == 'carda':
self._main_prefix = None label = self.STORAGE_CARD_VOLUME_LABEL
for dev in mm: if type == 'cardb':
try: label = self.STORAGE_CARD2_VOLUME_LABEL
self._main_prefix = conditional_mount(dev)+os.sep if label is None:
label = self.STORAGE_CARD_VOLUME_LABEL + ' 2'
extra = 0
while True:
q = ' (%d)'%extra if extra else ''
if not os.path.exists('/media/'+label+q):
break break
except dbus.exceptions.DBusException: extra += 1
continue if extra:
label += ' (%d)'%extra
if not self._main_prefix: def do_mount(node, label):
raise DeviceError('Could not open device for reading. Try a reboot.') cmd = ['pmount', '-w', '-s']
label = label.replace(' ', '_')
self._card_a_prefix = self._card_b_prefix = None
cards = hm.FindDeviceStringMatch(__appname__+'.cardvolume', self.__class__.__name__)
def mount_card(dev):
try: try:
return conditional_mount(dev)+os.sep p = subprocess.Popen(cmd + [node, label])
except: except OSError:
import traceback raise DeviceError(_('You must install the pmount package.'))
print traceback while p.poll() is None:
time.sleep(0.1)
return p.returncode
ret = do_mount(node, label)
if ret != 0:
return None, ret
return self.node_mountpoint(node)+'/', 0
main, carda, cardb = self.find_device_nodes()
if main is None:
raise DeviceError(_('Unable to detect the %s disk drive.')
%self.__class__.__name__)
mp, ret = mount(main, 'main')
if mp is None:
raise DeviceError(
_('Unable to mount main memory (Error code: %d)')%ret)
if not mp.endswith('/'): mp += '/'
self._main_prefix = mp
cards = [x for x in (carda, cardb) if x is not None]
prefix, typ = '_card_a_prefix', 'carda'
for card in cards:
mp, ret = mount(card, typ)
if mp is None:
print >>sys.stderr, 'Unable to mount card (Error code: %d)'%ret
else:
if not mp.endswith('/'): mp += '/'
setattr(self, prefix, mp)
prefix, typ = '_card_b_prefix', 'cardb'
if len(cards) >= 1:
self._card_a_prefix = mount_card(cards[0])
if len(cards) >=2:
self._card_b_prefix = mount_card(cards[1])
def open(self): def open(self):
time.sleep(5) time.sleep(5)

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Post installation script for linux ''' ''' Post installation script for linux '''
import sys, os, shutil import sys, os, shutil
from subprocess import check_call, call from subprocess import check_call
from calibre import __version__, __appname__ from calibre import __version__, __appname__
from calibre.customize.ui import device_plugins from calibre.customize.ui import device_plugins
@ -263,49 +263,6 @@ def setup_udev_rules(group_file, reload, fatal_errors):
'''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,) '''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,)
) )
udev.close() udev.close()
fdi = open_file('/usr/share/hal/fdi/policy/20thirdparty/10-calibre.fdi')
manifest.append(fdi.name)
fdi.write('<?xml version="1.0" encoding="UTF-8"?>\n\n<deviceinfo version="0.2">\n')
for cls in DEVICES:
fdi.write(\
'''
<device>
<match key="usb_device.vendor_id" int="%(vendor_id)s">
<match key="usb_device.product_id" int="%(product_id)s">
<match key="usb_device.device_revision_bcd" int="%(bcd)s">
<merge key="calibre.deviceclass" type="string">%(cls)s</merge>
</match>
</match>
</match>
</device>
'''%dict(cls=cls.__class__.__name__, vendor_id=cls.VENDOR_ID, product_id=cls.PRODUCT_ID,
prog=__appname__, bcd=cls.BCD))
fdi.write('\n'+cls.get_fdi())
fdi.write('\n</deviceinfo>\n')
fdi.close()
if reload:
called = False
for hal in ('hald', 'hal', 'haldaemon'):
hal = os.path.join('/etc/init.d', hal)
if os.access(hal, os.X_OK):
call((hal, 'restart'))
called = True
break
if not called and os.access('/etc/rc.d/rc.hald', os.X_OK):
call(('/etc/rc.d/rc.hald', 'restart'))
try:
check_call('udevadm control --reload_rules', shell=True)
except:
try:
check_call('udevcontrol reload_rules', shell=True)
except:
try:
check_call('/etc/init.d/udev reload', shell=True)
except:
if fatal_errors:
raise Exception("Couldn't reload udev, you may have to reboot")
print >>sys.stderr, "Couldn't reload udev, you may have to reboot"
return manifest return manifest
def option_parser(): def option_parser():
@ -314,7 +271,7 @@ def option_parser():
parser.add_option('--use-destdir', action='store_true', default=False, dest='destdir', parser.add_option('--use-destdir', action='store_true', default=False, dest='destdir',
help='If set, respect the environment variable DESTDIR when installing files') help='If set, respect the environment variable DESTDIR when installing files')
parser.add_option('--do-not-reload-udev-hal', action='store_true', dest='dont_reload', default=False, parser.add_option('--do-not-reload-udev-hal', action='store_true', dest='dont_reload', default=False,
help='If set, do not try to reload udev rules and HAL FDI files') help='Does nothing. Present for legacy reasons.')
parser.add_option('--group-file', default='/etc/group', dest='group_file', parser.add_option('--group-file', default='/etc/group', dest='group_file',
help='File from which to read group information. Default: %default') help='File from which to read group information. Default: %default')
parser.add_option('--dont-check-root', action='store_true', default=False, dest='no_root', parser.add_option('--dont-check-root', action='store_true', default=False, dest='no_root',

View File

@ -21,6 +21,7 @@ DEPENDENCIES = [
('dnspython', '1.6.0', 'dnspython', 'dnspython', 'dnspython', 'dnspython'), ('dnspython', '1.6.0', 'dnspython', 'dnspython', 'dnspython', 'dnspython'),
('poppler', '0.10.5', 'poppler', 'poppler', 'poppler', 'poppler'), ('poppler', '0.10.5', 'poppler', 'poppler', 'poppler', 'poppler'),
('podofo', '0.7', 'podofo', 'podofo', 'podofo', 'podofo'), ('podofo', '0.7', 'podofo', 'podofo', 'podofo', 'podofo'),
('pmount', '0.9.19', 'pmount', 'pmount', 'pmount', 'pmount'),
] ]