mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Port freebsd device open/eject code to use jeepney
Note that I have no way to test this code
This commit is contained in:
parent
5319cacd41
commit
54e510a415
@ -12,18 +12,22 @@ 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, sys, glob
|
import glob
|
||||||
from itertools import repeat
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from itertools import repeat
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG, isfreebsd, islinux, ismacos, iswindows
|
||||||
from calibre.devices.interface import DevicePlugin
|
|
||||||
from calibre.devices.errors import DeviceError
|
from calibre.devices.errors import DeviceError
|
||||||
|
from calibre.devices.interface import DevicePlugin
|
||||||
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
||||||
from calibre.constants import iswindows, islinux, ismacos, isfreebsd
|
|
||||||
from calibre.utils.filenames import ascii_filename as sanitize
|
from calibre.utils.filenames import ascii_filename as sanitize
|
||||||
from polyglot.builtins import iteritems, string_or_bytes, map
|
from polyglot.builtins import iteritems, map, string_or_bytes
|
||||||
|
|
||||||
if ismacos:
|
if ismacos:
|
||||||
osx_sanitize_name_pat = re.compile(r'[.-]')
|
osx_sanitize_name_pat = re.compile(r'[.-]')
|
||||||
@ -649,7 +653,6 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
# 4. when finished, we have a list of mount points and associated dbus nodes
|
# 4. when finished, we have a list of mount points and associated dbus nodes
|
||||||
#
|
#
|
||||||
def open_freebsd(self):
|
def open_freebsd(self):
|
||||||
import dbus
|
|
||||||
# There should be some way to access the -v arg...
|
# There should be some way to access the -v arg...
|
||||||
verbose = False
|
verbose = False
|
||||||
|
|
||||||
@ -660,107 +663,17 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
if not d.serial:
|
if not d.serial:
|
||||||
raise DeviceError("Device has no S/N. Can't continue")
|
raise DeviceError("Device has no S/N. Can't continue")
|
||||||
return False
|
return False
|
||||||
|
from .hal import get_hal
|
||||||
vols=[]
|
hal = get_hal()
|
||||||
|
vols = hal.get_volumes(d)
|
||||||
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'):
|
|
||||||
midpath = manager.FindDeviceStringMatch('info.parent', path)
|
|
||||||
dpaths = manager.FindDeviceStringMatch(
|
|
||||||
'storage.originating_device', path) + manager.FindDeviceStringMatch('storage.originating_device', midpath[0])
|
|
||||||
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 as e:
|
|
||||||
print(e)
|
|
||||||
continue
|
|
||||||
except dbus.exceptions.DBusException as e:
|
|
||||||
print(e)
|
|
||||||
continue
|
|
||||||
except dbus.exceptions.DBusException:
|
|
||||||
continue
|
|
||||||
|
|
||||||
vols.sort(key=lambda x: x['node'])
|
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print("FBSD: ", vols)
|
print("FBSD: ", vols)
|
||||||
|
|
||||||
mtd=0
|
ok, mv = hal.mount_volumes(vols)
|
||||||
|
if not ok:
|
||||||
for vol in vols:
|
raise DeviceError(_('Unable to mount the device'))
|
||||||
mp = ''
|
for k, v in mv.items():
|
||||||
if vol['dev'].GetProperty('volume.is_mounted'):
|
setattr(self, k, v)
|
||||||
mp = vol['dev'].GetProperty('volume.mount_point')
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
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 as e:
|
|
||||||
print("Failed to mount ", e)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Mount Point becomes Mount Path
|
|
||||||
mp += '/'
|
|
||||||
|
|
||||||
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
|
|
||||||
raise DeviceError(_('Unable to mount the device'))
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
@ -770,37 +683,18 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
# mounted filesystems, using the stored volume object
|
# mounted filesystems, using the stored volume object
|
||||||
#
|
#
|
||||||
def eject_freebsd(self):
|
def eject_freebsd(self):
|
||||||
import dbus
|
from .hal import get_hal
|
||||||
# There should be some way to access the -v arg...
|
hal = get_hal()
|
||||||
verbose = False
|
|
||||||
|
|
||||||
if self._main_prefix:
|
if self._main_prefix:
|
||||||
if verbose:
|
hal.unmount(self._main_vol)
|
||||||
print("FBSD: umount main:", self._main_prefix)
|
|
||||||
try:
|
|
||||||
self._main_vol.Unmount([])
|
|
||||||
except dbus.exceptions.DBusException as e:
|
|
||||||
print('Unable to eject ', e)
|
|
||||||
|
|
||||||
if self._card_a_prefix:
|
if self._card_a_prefix:
|
||||||
if verbose:
|
hal.unmount(self._card_a_vol)
|
||||||
print("FBSD: umount card a:", self._card_a_prefix)
|
|
||||||
try:
|
|
||||||
self._card_a_vol.Unmount([])
|
|
||||||
except dbus.exceptions.DBusException as e:
|
|
||||||
print('Unable to eject ', e)
|
|
||||||
|
|
||||||
if self._card_b_prefix:
|
if self._card_b_prefix:
|
||||||
if verbose:
|
hal.unmount(self._card_b_vol)
|
||||||
print("FBSD: umount card b:", self._card_b_prefix)
|
|
||||||
try:
|
|
||||||
self._card_b_vol.Unmount([])
|
|
||||||
except dbus.exceptions.DBusException as e:
|
|
||||||
print('Unable to eject ', e)
|
|
||||||
|
|
||||||
self._main_prefix = None
|
self._main_prefix = self._main_vol = None
|
||||||
self._card_a_prefix = None
|
self._card_a_prefix = self._card_a_vol = None
|
||||||
self._card_b_prefix = None
|
self._card_b_prefix = self._card_b_vol = None
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
def open(self, connected_device, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
|
138
src/calibre/devices/usbms/hal.py
Normal file
138
src/calibre/devices/usbms/hal.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import time
|
||||||
|
from jeepney import (
|
||||||
|
DBusAddress, DBusErrorResponse, MessageType, Properties, new_method_call
|
||||||
|
)
|
||||||
|
from jeepney.io.blocking import open_dbus_connection
|
||||||
|
from calibre.constants import DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
class HAL:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.bus = open_dbus_connection('SYSTEM')
|
||||||
|
|
||||||
|
def send(self, msg):
|
||||||
|
reply = self.bus.send_and_get_reply(msg)
|
||||||
|
if reply.header.message_type is MessageType.error:
|
||||||
|
raise DBusErrorResponse(reply)
|
||||||
|
return reply.body[0]
|
||||||
|
|
||||||
|
def call(self, addr, method, sig='', *args):
|
||||||
|
if sig:
|
||||||
|
return self.send(new_method_call(addr, method, sig, args))
|
||||||
|
return self.send(new_method_call(addr, method))
|
||||||
|
|
||||||
|
def prop(self, addr, name):
|
||||||
|
return self.send(Properties(addr).get(name))
|
||||||
|
|
||||||
|
def addr(self, path, interface):
|
||||||
|
return DBusAddress(path, bus_name='org.freedesktop.Hal', interface=f'org.freedesktop.Hal.{interface}')
|
||||||
|
|
||||||
|
def get_volume(self, vpath):
|
||||||
|
vdevif = self.addr(vpath, 'Device')
|
||||||
|
if not self.prop(vdevif, 'block.is_volume') or self.prop(vdevif, 'volume.fsusage') != 'filesystem':
|
||||||
|
return
|
||||||
|
volif = self.addr(vpath, 'Volume')
|
||||||
|
pdevif = self.addr(self.prop(volif, 'info.parent'), 'Device')
|
||||||
|
return {'node': self.prop(pdevif, 'block.device'),
|
||||||
|
'dev': vdevif,
|
||||||
|
'vol': volif,
|
||||||
|
'label': self.prop(vdevif, 'volume.label')}
|
||||||
|
|
||||||
|
def get_volumes(self, d):
|
||||||
|
vols = []
|
||||||
|
manager = self.addr('/org/freedesktop/Hal/Manager', 'Manager')
|
||||||
|
paths = self.call(manager, 'FindDeviceStringMatch', 'ss', 'usb.serial', d.serial)
|
||||||
|
for path in paths:
|
||||||
|
objif = self.addr(path, 'Device')
|
||||||
|
|
||||||
|
# Extra paranoia...
|
||||||
|
try:
|
||||||
|
if d.idVendor == self.prop(objif, 'usb.vendor_id') and \
|
||||||
|
d.idProduct == self.prop(objif, 'usb.product_id') and \
|
||||||
|
d.manufacturer == self.prop(objif, 'usb.vendor') and \
|
||||||
|
d.product == self.prop(objif, 'usb.product') and \
|
||||||
|
d.serial == self.prop(objif, 'usb.serial'):
|
||||||
|
midpath = self.call(manager, 'FindDeviceStringMatch', 'ss', 'info.parent', path)
|
||||||
|
dpaths = self.call(manager, 'FindDeviceStringMatch', 'ss', 'storage.originating_device', path
|
||||||
|
) + self.call(manager, 'FindDeviceStringMatch', 'ss', 'storage.originating_device', midpath[0])
|
||||||
|
for dpath in dpaths:
|
||||||
|
try:
|
||||||
|
vpaths = self.call(manager, 'FindDeviceStringMatch', 'block.storage_device', dpath)
|
||||||
|
for vpath in vpaths:
|
||||||
|
try:
|
||||||
|
vol = self.get_volume(vpath)
|
||||||
|
if vol is not None:
|
||||||
|
vols.append(vol)
|
||||||
|
except DBusErrorResponse as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
except DBusErrorResponse as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
except DBusErrorResponse:
|
||||||
|
continue
|
||||||
|
vols.sort(key=lambda x: x['node'])
|
||||||
|
return vols
|
||||||
|
|
||||||
|
def get_mount_point(self, vol):
|
||||||
|
if not self.prop(vol['dev'], 'volume.is_mounted'):
|
||||||
|
fstype = self.prop(vol['dev'], 'volume.fstype')
|
||||||
|
self.call(vol['vol'], 'Mount', 'ssas', 'Calibre-'+vol['label'], fstype, [])
|
||||||
|
loops = 0
|
||||||
|
while not self.prop(vol['dev'], 'volume.is_mounted'):
|
||||||
|
time.sleep(1)
|
||||||
|
loops += 1
|
||||||
|
if loops > 100:
|
||||||
|
raise Exception("ERROR: Timeout waiting for mount to complete")
|
||||||
|
return self.prop(vol['dev'], 'volume.mount_point')
|
||||||
|
|
||||||
|
def mount_volumes(self, volumes):
|
||||||
|
mtd=0
|
||||||
|
ans = {
|
||||||
|
'_main_prefix': None, '_main_vol': None,
|
||||||
|
'_card_a_prefix': None, '_card_a_vol': None,
|
||||||
|
'_card_b_prefix': None, '_card_b_vol': None,
|
||||||
|
}
|
||||||
|
for vol in volumes:
|
||||||
|
try:
|
||||||
|
mp = self.get_mount_point(vol)
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed to mount: {vol['label']}", e)
|
||||||
|
continue
|
||||||
|
# Mount Point becomes Mount Path
|
||||||
|
mp += '/'
|
||||||
|
if DEBUG:
|
||||||
|
print("FBSD: mounted", vol['label'], "on", mp)
|
||||||
|
if mtd == 0:
|
||||||
|
ans['_main_prefix'], ans['_main_vol'] = mp, vol['vol']
|
||||||
|
if DEBUG:
|
||||||
|
print("FBSD: main = ", mp)
|
||||||
|
elif mtd == 1:
|
||||||
|
ans['_card_a_prefix'], ans['_card_a_vol'] = mp, vol['vol']
|
||||||
|
if DEBUG:
|
||||||
|
print("FBSD: card a = ", mp)
|
||||||
|
elif mtd == 2:
|
||||||
|
ans['_card_b_prefix'], ans['_card_b_vol'] = mp, vol['vol']
|
||||||
|
if DEBUG:
|
||||||
|
print("FBSD: card b = ", mp)
|
||||||
|
break
|
||||||
|
mtd += 1
|
||||||
|
|
||||||
|
return mtd > 0, ans
|
||||||
|
|
||||||
|
def unmount(self, vol):
|
||||||
|
try:
|
||||||
|
self.call(vol, 'Unmount', 'as', [])
|
||||||
|
except DBusErrorResponse as e:
|
||||||
|
print('Unable to eject ', e)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hal():
|
||||||
|
if not hasattr(get_hal, 'ans'):
|
||||||
|
get_hal.ans = HAL()
|
||||||
|
return get_hal.ans
|
Loading…
x
Reference in New Issue
Block a user