def open_linux(self): def mount(node, type): mp = self.node_mountpoint(node) if mp is not None: return mp, 0 def do_mount(node): try: from calibre.devices.udisks import mount mount(node) return 0 except: print('Udisks mount call failed:') import traceback traceback.print_exc() return 1 ret = do_mount(node) 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. Either ' 'the device has already been ejected, or your ' 'kernel is exporting a deprecated version of SYSFS.') % self.__class__.__name__) if is_debugging(): print('\nFound device nodes:', main, carda, cardb) self._linux_mount_map = {} 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._linux_mount_map[main] = mp self._main_prefix = mp self._linux_main_device_node = main cards = [(carda, '_card_a_prefix', 'carda'), (cardb, '_card_b_prefix', 'cardb')] for card, prefix, typ in cards: if card is None: continue mp, ret = mount(card, typ) if mp is None: print('Unable to mount card (Error code: %d)' % ret, file=sys.stderr) else: if not mp.endswith('/'): mp += '/' setattr(self, prefix, mp) self._linux_mount_map[card] = mp self.filter_read_only_mount_points()
def open_windows(self): from calibre.devices.scanner import drive_is_ok from calibre.devices.winusb import get_drive_letters_for_device usbdev = self.device_being_opened debug = is_debugging() or getattr(self, 'do_device_debug', False) try: dlmap = get_drive_letters_for_device(usbdev, debug=debug) except Exception: dlmap = {} if not dlmap.get('drive_letters'): time.sleep(7) dlmap = get_drive_letters_for_device(usbdev, debug=debug) if debug: from pprint import pformat prints(f'Drive letters for {usbdev}') prints(pformat(dlmap)) filtered = set() for dl in dlmap['drive_letters']: pnp_id = dlmap['pnp_id_map'][dl].upper() if dl in dlmap['readonly_drives']: filtered.add(dl) if debug: prints('Ignoring the drive %s as it is readonly' % dl) elif self.windows_filter_pnp_id(pnp_id): filtered.add(dl) if debug: prints( f'Ignoring the drive {dl} because of a PNP filter on {pnp_id}' ) elif not drive_is_ok(dl, debug=debug): filtered.add(dl) if debug: prints( 'Ignoring the drive %s because failed to get free space for it' % dl) dlmap['drive_letters'] = [ dl for dl in dlmap['drive_letters'] if dl not in filtered ] if not dlmap['drive_letters']: raise DeviceError( _('Unable to detect any disk drives for the device: %s. Try rebooting' ) % self.get_gui_name()) drives = {} for drive_letter, which in zip(dlmap['drive_letters'], 'main carda cardb'.split()): drives[which] = drive_letter + ':\\' drives = self.windows_sort_drives(drives) self._main_prefix = drives.get('main') self._card_a_prefix = drives.get('carda', None) self._card_b_prefix = drives.get('cardb', None)
def open_osx(self): from calibre_extensions.usbobserver import get_mounted_filesystems bsd_drives = self.osx_bsd_names() drives = self.osx_sort_names(bsd_drives.copy()) mount_map = get_mounted_filesystems() # macOS 13 Ventura uses a weird scheme for mounted FAT devices of the # form fat://basename_of_bsd_name/basename_of_mountpoint # see https://www.mobileread.com/forums/showthread.php?t=347294 for dev_node in tuple(mount_map): if ':' in dev_node and '//' in dev_node: val = mount_map[dev_node] dev_node = dev_node.split('/')[-2] dev_node = f'/dev/{dev_node}' if dev_node not in mount_map: mount_map[dev_node] = val drives = {k: mount_map.get(v) for k, v in iteritems(drives)} if is_debugging(): print() from pprint import pprint pprint({ 'bsd_drives': bsd_drives, 'mount_map': mount_map, 'drives': drives }) if drives.get('carda') is None and drives.get('cardb') is not None: drives['carda'] = drives.pop('cardb') if drives.get('main') is None and drives.get('carda') is not None: drives['main'] = drives.pop('carda') if drives.get('carda') is None and drives.get('cardb') is not None: drives['carda'] = drives.pop('cardb') if drives.get('main') is None: raise DeviceError( _('Unable to detect the %s mount point. Try rebooting.') % self.__class__.__name__) pat = self.OSX_MAIN_MEM_VOL_PAT if pat is not None and len(drives) > 1 and 'main' in drives: if pat.search(drives['main']) is None: main = drives['main'] for x in ('carda', 'cardb'): if x in drives and pat.search(drives[x]): drives['main'] = drives.pop(x) drives[x] = main break self._main_prefix = drives['main'] + os.sep def get_card_prefix(c): ans = drives.get(c, None) if ans is not None: ans += os.sep return ans self._card_a_prefix = get_card_prefix('carda') self._card_b_prefix = get_card_prefix('cardb')
def is_readonly(mp): if mp is None: return True path = os.path.join(mp, 'calibre_readonly_test') ro = True try: with lopen(path, 'wb'): ro = False except: pass else: try: os.remove(path) except: pass if is_debugging() and ro: print('\nThe mountpoint', mp, 'is readonly, ignoring it') return ro
def find_device_nodes(self, detected_device=None): def walk(base): base = os.path.abspath(os.path.realpath(base)) for x in os.listdir(base): p = os.path.join(base, x) if os.path.islink(p) or not os.access(p, os.R_OK): continue isfile = os.path.isfile(p) yield p, isfile if not isfile: yield from walk(p) def raw2num(raw): raw = raw.lower() if not raw.startswith('0x'): raw = '0x' + raw return int(raw, 16) # Find device node based on vendor, product and bcd d, j = os.path.dirname, os.path.join usb_dir = None if detected_device is None: detected_device = self.detected_device def test(val, attr): q = getattr(detected_device, attr) return q == val def getnum(usb_dir): def rc(q): with open(j(usb_dir, q), 'rb') as f: return raw2num(f.read().decode('utf-8')) return rc for x, isfile in walk('/sys/devices'): if isfile and x.endswith('idVendor'): usb_dir = d(x) for y in ('idProduct', 'idVendor', 'bcdDevice'): if not os.access(j(usb_dir, y), os.R_OK): usb_dir = None break if usb_dir is None: continue ven, prod, bcd = map(getnum(usb_dir), ('idVendor', 'idProduct', 'bcdDevice')) if not (test(ven, 'idVendor') and test(prod, 'idProduct') and test(bcd, 'bcdDevice')): usb_dir = None continue else: break if usb_dir is None: raise DeviceError( _('Unable to detect the %s disk drive.') % self.__class__.__name__) devnodes, ok = [], {} for x, isfile in walk(usb_dir): if not isfile and '/block/' in x: parts = x.split('/') idx = parts.index('block') if idx == len(parts) - 2: sz = j(x, 'size') node = parts[idx + 1] try: with open(sz, 'rb') as szf: exists = int(szf.read().decode('utf-8')) > 0 if exists: node = self.find_largest_partition(x) ok[node] = True else: ok[node] = False except: ok[node] = False if is_debugging() and not ok[node]: print( f'\nIgnoring the node: {node} as could not read size from: {sz}' ) devnodes.append(node) devnodes += list(repeat(None, 3)) ans = ['/dev/' + x if ok.get(x, False) else None for x in devnodes] ans.sort(key=lambda x: x[5:] if x else 'zzzzz') return self.linux_swap_drives(ans[:3])
def debug_print(*args, **kw): base_time = getattr(debug_print, 'base_time', None) if base_time is None: debug_print.base_time = base_time = time.monotonic() if is_debugging(): prints('DEBUG: %6.1f'%(time.monotonic()-base_time), *args, **kw)
def debug(ioreg_to_tmp=False, buf=None, plugins=None, disabled_plugins=None): ''' If plugins is None, then this method calls startup and shutdown on the device plugins. So if you are using it in a context where startup could already have been called (for example in the main GUI), pass in the list of device plugins as the plugins parameter. ''' import textwrap from calibre.customize.ui import device_plugins, disabled_device_plugins from calibre.debug import print_basic_debug_info from calibre.devices.scanner import DeviceScanner from calibre.constants import iswindows, ismacos, debug, is_debugging from calibre import prints from polyglot.io import PolyglotStringIO oldo, olde = sys.stdout, sys.stderr if buf is None: buf = PolyglotStringIO() sys.stdout = sys.stderr = buf out = partial(prints, file=buf) devplugins = device_plugins() if plugins is None else plugins devplugins = list(sorted(devplugins, key=lambda x: x.__class__.__name__)) if plugins is None: for d in devplugins: try: d.startup() except: out('Startup failed for device plugin: %s' % d) if disabled_plugins is None: disabled_plugins = list(disabled_device_plugins()) orig_debug = is_debugging() debug(True) try: print_basic_debug_info(out=buf) s = DeviceScanner() s.scan() devices = (s.devices) if not iswindows: devices = [list(x) for x in devices] for d in devices: for i in range(3): d[i] = hex(d[i]) out('USB devices on system:') out(pprint.pformat(devices)) ioreg = None if ismacos: from calibre.devices.usbms.device import Device mount = '\n'.join( repr(x) for x in Device.osx_run_mount().splitlines()) drives = pprint.pformat(Device.osx_get_usb_drives()) ioreg = 'Output from mount:\n' + mount + '\n\n' ioreg += 'Output from osx_get_usb_drives:\n' + drives + '\n\n' ioreg += Device.run_ioreg().decode('utf-8', 'replace') connected_devices = [] if disabled_plugins: out( '\nDisabled plugins:', textwrap.fill(' '.join( [x.__class__.__name__ for x in disabled_plugins]))) out(' ') else: out('\nNo disabled plugins') found_dev = False for dev in devplugins: if not dev.MANAGES_DEVICE_PRESENCE: continue out('Looking for devices of type:', dev.__class__.__name__) if dev.debug_managed_device_detection(s.devices, buf): found_dev = True break out(' ') if not found_dev: out('Looking for devices...') for dev in devplugins: if dev.MANAGES_DEVICE_PRESENCE: continue connected, det = s.is_device_connected(dev, debug=True) if connected: out('\t\tDetected possible device', dev.__class__.__name__) connected_devices.append((dev, det)) out(' ') errors = {} success = False out('Devices possibly connected:', end=' ') for dev, det in connected_devices: out(dev.name, end=', ') if not connected_devices: out('None', end='') out(' ') for dev, det in connected_devices: out('Trying to open', dev.name, '...', end=' ') dev.do_device_debug = True try: dev.reset(detected_device=det) dev.open(det, None) out('OK') except: import traceback errors[dev] = traceback.format_exc() out('failed') continue dev.do_device_debug = False success = True if hasattr(dev, '_main_prefix'): out('Main memory:', repr(dev._main_prefix)) out('Total space:', dev.total_space()) break if not success and errors: out('Opening of the following devices failed') for dev, msg in errors.items(): out(dev) out(msg) out(' ') if ioreg is not None: ioreg = 'IOREG Output\n' + ioreg out(' ') if ioreg_to_tmp: lopen('/tmp/ioreg.txt', 'w').write(ioreg) out('Dont forget to send the contents of /tmp/ioreg.txt') out('You can open it with the command: open /tmp/ioreg.txt' ) else: out(ioreg) if hasattr(buf, 'getvalue'): return buf.getvalue() finally: debug(orig_debug) sys.stdout = oldo sys.stderr = olde if plugins is None: for d in devplugins: try: d.shutdown() except: pass