Esempio n. 1
0
 def _eject_callback(self, drive, result, device):
     try:
         result = drive.eject_finish(result)
     except (gio.Error, GError):
         # XXX notify the user in some way?
         logging.exception('eject failed for %r' % drive)
         result = False
     if not result:
         messages.ShowWarning(
             _('Eject failed'),
             _("Ejecting device '%(name)s' failed.\n\n"
                     "The device is in use.", {'name': device.name})
             ).send_to_frontend()
Esempio n. 2
0
    def _send_disconnect(self, info):
        sync_manager = app.device_manager.get_sync_for_device(info,
                                                              create=False)
        if sync_manager:
            messages.ShowWarning(
                _('Device removed during sync'),
                _(
                    '%(name)s was removed while a sync was in progress.  '
                    'Not all items may have been copied.',
                    {'name': info.name})).send_to_frontend()
            sync_manager.cancel()

        if info.mount:
            del self.info_cache[info.mount]
        messages.TabsChanged('connect', [], [], [info.id]).send_to_frontend()
Esempio n. 3
0
class DeviceTracker(object):
    def __init__(self):
        self._unix_device_to_drive = {}
        self._drive_has_volumes = {}

    def start_tracking(self):
        volume_monitor = gio.volume_monitor_get()
        volume_monitor.connect('drive-connected', self._drive_connected)
        volume_monitor.connect('drive-changed',
                               lambda x, y: log_drive(y, 'changed'))
        volume_monitor.connect('volume-added', self._volume_added)
        volume_monitor.connect('volume-changed', self._volume_changed)
        volume_monitor.connect('mount-added', self._mount_added)
        volume_monitor.connect('mount-removed',
                               lambda x, y: log_mount(y, 'removed'))
        volume_monitor.connect('volume-removed', self._volume_removed)
        volume_monitor.connect('drive-disconnected', self._drive_disconnected)

        for drive in volume_monitor.get_connected_drives():
            self._drive_connected(volume_monitor, drive)
            volumes = drive.get_volumes()
            if volumes:
                for volume in volumes:
                    self._volume_added(volume_monitor, volume)

    @staticmethod
    def _should_ignore_drive(drive):
        """
        Returns True if we should ignore everything about the given drive.
        """
        return drive is None or drive.get_name() == 'CD/DVD Drive'

    def _get_volume_info(self, volume):
        id_ = volume.get_identifier('unix-device')
        mount = size = remaining = None
        mount = volume.get_mount()
        if mount:
            mount = mount.get_root().get_path()
            if mount and os.path.exists(mount):
                if mount[-1] != os.path.sep:
                    mount = mount + os.path.sep  # make sure it ends with a /
                statinfo = os.statvfs(mount)
                size = statinfo.f_frsize * statinfo.f_blocks
                remaining = statinfo.f_frsize * statinfo.f_bavail
        # bz19369: we have to handle get_drive() returnning None
        if volume.get_drive():
            name = volume.get_drive().get_name()
        else:
            logging.warn("get_drive() returned %s for %s", volume.get_drive(),
                         mount)
            name = None
        return id_, {
            'name': name,
            'visible_name': volume.get_name(),
            'mount': mount,
            'size': size,
            'remaining': remaining
        }

    def _drive_connected(self, volume_monitor, drive):
        log_drive(drive, 'connected')
        if self._should_ignore_drive(drive):
            return
        logging.debug('seen device: %r', drive.get_name())
        id_ = drive.get_identifier('unix-device')
        volumes = drive.get_volumes()
        if volumes:
            # so we ignore the disconnected event, instead deferring to the
            # volume events
            self._drive_has_volumes.setdefault(id_, 0)
        else:
            self._unix_device_to_drive[id_] = drive
            app.device_manager.device_connected(id_, name=drive.get_name())

    def _drive_disconnected(self, volume_monitor, drive):
        log_drive(drive, 'disconnected')
        if self._should_ignore_drive(drive):
            return
        id_ = drive.get_identifier('unix-device')
        if self._drive_has_volumes.get(id_):
            # don't send an event; the volumes will do that
            del self._drive_has_volumes[id_]
        else:
            del self._unix_device_to_drive[id_]
            app.device_manager.device_disconnected(id_)

    def _volume_added(self, volume_monitor, volume):
        log_volume(volume, 'added')
        id_, info = self._get_volume_info(volume)
        # have to set this so that we can access it when the volume is removed
        self._unix_device_to_drive[id_] = volume.get_drive()
        if volume is None or self._should_ignore_drive(volume.get_drive()):
            return
        drive_id = volume.get_drive().get_identifier('unix-device')
        if drive_id != id_ and drive_id in self._unix_device_to_drive:
            # we sent a "connect message" for the drive; disconnect the extra
            # device before we connect the volume (#17891)
            del self._unix_device_to_drive[drive_id]
            app.device_manager.device_disconnected(drive_id)
        app.device_manager.device_connected(id_, **info)
        self._drive_has_volumes.setdefault(drive_id, 0)
        self._drive_has_volumes[drive_id] += 1

    def _volume_changed(self, volume_monitor, volume):
        log_volume(volume, 'changed')
        if volume is None or self._should_ignore_drive(volume.get_drive()):
            return
        try:
            id_, info = self._get_volume_info(volume)
        except AttributeError:
            # comes up when the device is rudely removed
            return
        else:
            app.device_manager.device_changed(id_, **info)

    def _mount_added(self, volume_monitor, mount):
        log_mount(mount, 'added')
        if mount.get_volume():
            self._volume_changed(volume_monitor, mount.get_volume())

    def _volume_removed(self, volume_monitor, volume):
        log_volume(volume, 'removed')
        if volume is None:
            return
        drive = volume.get_drive()
        if drive and self._should_ignore_drive(drive):
            return
        id_ = volume.get_identifier('unix-device')
        del self._unix_device_to_drive[id_]
        app.device_manager.device_disconnected(id_)
        if drive is None:  # can be None on force-disconnect
            return
        drive_id = drive.get_identifier('unix-device')
        try:
            self._drive_has_volumes[drive_id] -= 1
        except KeyError:
            logging.warn("KeyError in _volume_removed", exc_info=True)
            return
        if self._drive_has_volumes[drive_id] == 0:
            # re-add the bare device
            del self._drive_has_volumes[drive_id]
            self._drive_connected(volume_monitor, volume.get_drive())

    def eject(self, device):
        if device.id not in self._unix_device_to_drive:
            return
        drive = self._unix_device_to_drive[device.id]
        drive.eject(self._eject_callback, gio.MOUNT_UNMOUNT_NONE, None, device)

    def _eject_callback(self, drive, result, device):
        try:
            result = drive.eject_finish(result)
        except (gio.Error, GError), e:
            # double check that the eject really failed.  I (BDK) have seen
            # the very strange error: Drive is activatable and not running.
            if any(v.get_mount() is not None for v in drive.get_volumes()):
                logging.exception('eject failed for %r' % drive)
                result = False
            else:
                logging.warning('eject succeeded, but returned error '
                                '%s for %r' % (e, drive))
                result = True
        if not result:
            messages.ShowWarning(
                _('Eject failed'),
                _(
                    "Ejecting device '%(name)s' failed.\n\n"
                    "The device is in use.",
                    {'name': device.name})).send_to_frontend()