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

@ -3,14 +3,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
""" """
Provides a command-line and optional graphical interface to the SONY Reader PRS-500. Provides a command-line and optional graphical interface to the SONY Reader PRS-500.
For usage information run the script. For usage information run the script.
""" """
import StringIO, sys, time, os import StringIO, sys, time, os
from optparse import OptionParser from optparse import OptionParser
from calibre import __version__, iswindows, __appname__ from calibre import __version__, iswindows, __appname__
from calibre.devices.errors import PathError from calibre.devices.errors import PathError
from calibre.utils.terminfo import TerminalController from calibre.utils.terminfo import TerminalController
from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked
from calibre.customize.ui import device_plugins from calibre.customize.ui import device_plugins
@ -29,7 +29,7 @@ def human_readable(size):
return size + suffix return size + suffix
class FileFormatter(object): class FileFormatter(object):
def __init__(self, file, term): def __init__(self, file, term):
self.term = term self.term = term
self.is_dir = file.is_dir self.is_dir = file.is_dir
self.is_readonly = file.is_readonly self.is_readonly = file.is_readonly
@ -38,18 +38,18 @@ class FileFormatter(object):
self.wtime = file.wtime self.wtime = file.wtime
self.name = file.name self.name = file.name
self.path = file.path self.path = file.path
@dynamic_property @dynamic_property
def mode_string(self): def mode_string(self):
doc=""" The mode string for this file. There are only two modes read-only and read-write """ doc=""" The mode string for this file. There are only two modes read-only and read-write """
def fget(self): def fget(self):
mode, x = "-", "-" mode, x = "-", "-"
if self.is_dir: mode, x = "d", "x" if self.is_dir: mode, x = "d", "x"
if self.is_readonly: mode += "r-"+x+"r-"+x+"r-"+x if self.is_readonly: mode += "r-"+x+"r-"+x+"r-"+x
else: mode += "rw"+x+"rw"+x+"rw"+x else: mode += "rw"+x+"rw"+x+"rw"+x
return mode return mode
return property(doc=doc, fget=fget) return property(doc=doc, fget=fget)
@dynamic_property @dynamic_property
def isdir_name(self): def isdir_name(self):
doc='''Return self.name + '/' if self is a directory''' doc='''Return self.name + '/' if self is a directory'''
@ -59,8 +59,8 @@ class FileFormatter(object):
name += '/' name += '/'
return name return name
return property(doc=doc, fget=fget) return property(doc=doc, fget=fget)
@dynamic_property @dynamic_property
def name_in_color(self): def name_in_color(self):
doc=""" The name in ANSI text. Directories are blue, ebooks are green """ doc=""" The name in ANSI text. Directories are blue, ebooks are green """
@ -71,24 +71,24 @@ class FileFormatter(object):
if self.is_dir: cname = blue + self.name + normal if self.is_dir: cname = blue + self.name + normal
else: else:
ext = self.name[self.name.rfind("."):] ext = self.name[self.name.rfind("."):]
if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): cname = green + self.name + normal if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): cname = green + self.name + normal
return cname return cname
return property(doc=doc, fget=fget) return property(doc=doc, fget=fget)
@dynamic_property @dynamic_property
def human_readable_size(self): def human_readable_size(self):
doc=""" File size in human readable form """ doc=""" File size in human readable form """
def fget(self): def fget(self):
return human_readable(self.size) return human_readable(self.size)
return property(doc=doc, fget=fget) return property(doc=doc, fget=fget)
@dynamic_property @dynamic_property
def modification_time(self): def modification_time(self):
doc=""" Last modified time in the Linux ls -l format """ doc=""" Last modified time in the Linux ls -l format """
def fget(self): def fget(self):
return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime)) return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime))
return property(doc=doc, fget=fget) return property(doc=doc, fget=fget)
@dynamic_property @dynamic_property
def creation_time(self): def creation_time(self):
doc=""" Last modified time in the Linux ls -l format """ doc=""" Last modified time in the Linux ls -l format """
@ -104,7 +104,7 @@ def info(dev):
print "Mime type: ", info[3] print "Mime type: ", info[3]
def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, ll=False, cols=0): def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, ll=False, cols=0):
def col_split(l, cols): # split list l into columns def col_split(l, cols): # split list l into columns
rows = len(l) / cols rows = len(l) / cols
if len(l) % cols: if len(l) % cols:
rows += 1 rows += 1
@ -112,8 +112,8 @@ def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, l
for i in range(rows): for i in range(rows):
m.append(l[i::rows]) m.append(l[i::rows])
return m return m
def row_widths(table): # Calculate widths for each column in the row-wise table def row_widths(table): # Calculate widths for each column in the row-wise table
tcols = len(table[0]) tcols = len(table[0])
rowwidths = [ 0 for i in range(tcols) ] rowwidths = [ 0 for i in range(tcols) ]
for row in table: for row in table:
@ -122,19 +122,19 @@ def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, l
rowwidths[c] = len(item) if len(item) > rowwidths[c] else rowwidths[c] rowwidths[c] = len(item) if len(item) > rowwidths[c] else rowwidths[c]
c += 1 c += 1
return rowwidths return rowwidths
output = StringIO.StringIO() output = StringIO.StringIO()
if path.endswith("/") and len(path) > 1: path = path[:-1] if path.endswith("/") and len(path) > 1: path = path[:-1]
dirs = dev.list(path, recurse) dirs = dev.list(path, recurse)
for dir in dirs: for dir in dirs:
if recurse: print >>output, dir[0] + ":" if recurse: print >>output, dir[0] + ":"
lsoutput, lscoloutput = [], [] lsoutput, lscoloutput = [], []
files = dir[1] files = dir[1]
maxlen = 0 maxlen = 0
if ll: # Calculate column width for size column if ll: # Calculate column width for size column
for file in files: for file in files:
size = len(str(file.size)) size = len(str(file.size))
if human_readable_size: if human_readable_size:
file = FileFormatter(file, term) file = FileFormatter(file, term)
size = len(file.human_readable_size) size = len(file.human_readable_size)
if size > maxlen: maxlen = size if size > maxlen: maxlen = size
@ -148,29 +148,29 @@ def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, l
size = str(file.size) size = str(file.size)
if human_readable_size: size = file.human_readable_size if human_readable_size: size = file.human_readable_size
print >>output, file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name print >>output, file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name
if not ll and len(lsoutput) > 0: if not ll and len(lsoutput) > 0:
trytable = [] trytable = []
for colwidth in range(MINIMUM_COL_WIDTH, cols): for colwidth in range(MINIMUM_COL_WIDTH, cols):
trycols = int(cols/colwidth) trycols = int(cols/colwidth)
trytable = col_split(lsoutput, trycols) trytable = col_split(lsoutput, trycols)
works = True works = True
for row in trytable: for row in trytable:
row_break = False row_break = False
for item in row: for item in row:
if len(item) > colwidth - 1: if len(item) > colwidth - 1:
works, row_break = False, True works, row_break = False, True
break break
if row_break: break if row_break: break
if works: break if works: break
rowwidths = row_widths(trytable) rowwidths = row_widths(trytable)
trytablecol = col_split(lscoloutput, len(trytable[0])) trytablecol = col_split(lscoloutput, len(trytable[0]))
for r in range(len(trytable)): for r in range(len(trytable)):
for c in range(len(trytable[r])): for c in range(len(trytable[r])):
padding = rowwidths[c] - len(trytable[r][c]) padding = rowwidths[c] - len(trytable[r][c])
print >>output, trytablecol[r][c], "".ljust(padding), print >>output, trytablecol[r][c], "".ljust(padding),
print >>output print >>output
print >>output print >>output
listing = output.getvalue().rstrip()+ "\n" listing = output.getvalue().rstrip()+ "\n"
output.close() output.close()
return listing return listing
@ -179,20 +179,20 @@ def main():
cols = term.COLS cols = term.COLS
if not cols: # On windows terminal width is unknown if not cols: # On windows terminal width is unknown
cols = 80 cols = 80
parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, books, df, ls, cp, mkdir, touch, cat, rm\n\n"+ parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, books, df, ls, cp, mkdir, touch, cat, rm\n\n"+
"For help on a particular command: %prog command", version=__appname__+" version: " + __version__) "For help on a particular command: %prog command", version=__appname__+" version: " + __version__)
parser.add_option("--log-packets", help="print out packet stream to stdout. "+\ parser.add_option("--log-packets", help="print out packet stream to stdout. "+\
"The numbers in the left column are byte offsets that allow the packet size to be read off easily.", "The numbers in the left column are byte offsets that allow the packet size to be read off easily.",
dest="log_packets", action="store_true", default=False) dest="log_packets", action="store_true", default=False)
parser.remove_option("-h") parser.remove_option("-h")
parser.disable_interspersed_args() # Allow unrecognized options parser.disable_interspersed_args() # Allow unrecognized options
options, args = parser.parse_args() options, args = parser.parse_args()
if len(args) < 1: if len(args) < 1:
parser.print_help() parser.print_help()
return 1 return 1
command = args[0] command = args[0]
args = args[1:] args = args[1:]
dev = None dev = None
@ -207,27 +207,27 @@ def main():
if scanner.is_device_connected(d): if scanner.is_device_connected(d):
dev = d dev = d
dev.reset(log_packets=options.log_packets) dev.reset(log_packets=options.log_packets)
if dev is None: if dev is None:
print >>sys.stderr, 'Unable to find a connected ebook reader.' print >>sys.stderr, 'Unable to find a connected ebook reader.'
return 1 return 1
try: try:
dev.open() dev.open()
if command == "df": if command == "df":
total = dev.total_space(end_session=False) total = dev.total_space(end_session=False)
free = dev.free_space() free = dev.free_space()
where = ("Memory", "Stick", "Card") where = ("Memory", "Stick", "Card")
print "Filesystem\tSize \tUsed \tAvail \tUse%" print "Filesystem\tSize \tUsed \tAvail \tUse%"
for i in range(3): for i in range(3):
print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),\ print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),\
str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%") str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%")
elif command == "books": elif command == "books":
print "Books in main memory:" print "Books in main memory:"
for book in dev.books(): for book in dev.books():
print book print book
print "\nBooks on storage card:" print "\nBooks on storage card:"
for book in dev.books(oncard=True): print book for book in dev.books(oncard=True): print book
elif command == "mkdir": elif command == "mkdir":
parser = OptionParser(usage="usage: %prog mkdir [options] path\nCreate a directory on the device\n\npath must begin with / or card:/") parser = OptionParser(usage="usage: %prog mkdir [options] path\nCreate a directory on the device\n\npath must begin with / or card:/")
if len(args) != 1: if len(args) != 1:
@ -245,7 +245,7 @@ def main():
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return 1 return 1
print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols), print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols),
elif command == "info": elif command == "info":
info(dev) info(dev)
elif command == "cp": elif command == "cp":
@ -259,22 +259,22 @@ def main():
parser.add_option('-f', '--force', dest='force', action='store_true', default=False, parser.add_option('-f', '--force', dest='force', action='store_true', default=False,
help='Overwrite the destination file if it exists already.') help='Overwrite the destination file if it exists already.')
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 2: if len(args) != 2:
parser.print_help() parser.print_help()
return 1 return 1
if args[0].startswith("prs500:"): if args[0].startswith("prs500:"):
outfile = args[1] outfile = args[1]
path = args[0][7:] path = args[0][7:]
if path.endswith("/"): path = path[:-1] if path.endswith("/"): path = path[:-1]
if os.path.isdir(outfile): if os.path.isdir(outfile):
outfile = os.path.join(outfile, path[path.rfind("/")+1:]) outfile = os.path.join(outfile, path[path.rfind("/")+1:])
try: try:
outfile = open(outfile, "wb") outfile = open(outfile, "wb")
except IOError, e: except IOError, e:
print >> sys.stderr, e print >> sys.stderr, e
parser.print_help() parser.print_help()
return 1 return 1
dev.get_file(path, outfile) dev.get_file(path, outfile)
outfile.close() outfile.close()
elif args[1].startswith("prs500:"): elif args[1].startswith("prs500:"):
try: try:
@ -299,7 +299,7 @@ def main():
outfile = sys.stdout outfile = sys.stdout
parser = OptionParser(usage="usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/") parser = OptionParser(usage="usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return 1 return 1
if args[0].endswith("/"): path = args[0][:-1] if args[0].endswith("/"): path = args[0][:-1]
@ -311,15 +311,15 @@ def main():
"and must begin with / or card:/\n\n"+\ "and must begin with / or card:/\n\n"+\
"rm will DELETE the file. Be very CAREFUL") "rm will DELETE the file. Be very CAREFUL")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return 1 return 1
dev.rm(args[0]) dev.rm(args[0])
elif command == "touch": elif command == "touch":
parser = OptionParser(usage="usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+ parser = OptionParser(usage="usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+
"Unfortunately, I cant figure out how to update file times on the device, so if path already exists, touch does nothing" ) "Unfortunately, I cant figure out how to update file times on the device, so if path already exists, touch does nothing" )
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return 1 return 1
dev.touch(args[0]) dev.touch(args[0])

View File

@ -60,9 +60,8 @@ class PRS505(CLI, Device):
<cache xmlns="http://www.kinoma.com/FskCache/1"> <cache xmlns="http://www.kinoma.com/FskCache/1">
</cache> </cache>
'''.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
@ -226,5 +225,5 @@ class PRS505(CLI, Device):
f.close() f.close()
write_card_prefix(self._card_a_prefix, 1) write_card_prefix(self._card_a_prefix, 1)
write_card_prefix(self._card_b_prefix, 2) write_card_prefix(self._card_b_prefix, 2)
self.report_progress(1.0, _('Sending metadata to device...')) self.report_progress(1.0, _('Sending metadata to device...'))

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:
break label = self.STORAGE_CARD_VOLUME_LABEL + ' 2'
except dbus.exceptions.DBusException: extra = 0
continue while True:
q = ' (%d)'%extra if extra else ''
if not os.path.exists('/media/'+label+q):
break
extra += 1
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(' ', '_')
try:
p = subprocess.Popen(cmd + [node, label])
except OSError:
raise DeviceError(_('You must install the pmount package.'))
while p.poll() is None:
time.sleep(0.1)
return p.returncode
self._card_a_prefix = self._card_b_prefix = None ret = do_mount(node, label)
cards = hm.FindDeviceStringMatch(__appname__+'.cardvolume', self.__class__.__name__) if ret != 0:
return None, ret
return self.node_mountpoint(node)+'/', 0
def mount_card(dev):
try:
return conditional_mount(dev)+os.sep
except:
import traceback
print traceback
if len(cards) >= 1: main, carda, cardb = self.find_device_nodes()
self._card_a_prefix = mount_card(cards[0]) if main is None:
if len(cards) >=2: raise DeviceError(_('Unable to detect the %s disk drive.')
self._card_b_prefix = mount_card(cards[1]) %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'
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'),
] ]