1 # Copyright (C) 2007, One Laptop Per Child
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 from gettext
import gettext
as _
24 from sugar
import profile
25 from sugar
.datastore
import datastore
27 HAL_SERVICE_NAME
= 'org.freedesktop.Hal'
28 HAL_MANAGER_PATH
= '/org/freedesktop/Hal/Manager'
29 HAL_MANAGER_IFACE
= 'org.freedesktop.Hal.Manager'
30 HAL_DEVICE_IFACE
= 'org.freedesktop.Hal.Device'
31 HAL_VOLUME_IFACE
= 'org.freedesktop.Hal.Device.Volume'
33 MOUNT_OPTION_UID
= 500
34 MOUNT_OPTION_UMASK
= 000
36 _volumes_manager
= None
38 class VolumesManager(gobject
.GObject
):
40 __gtype_name__
= 'VolumesManager'
43 'volume-added': (gobject
.SIGNAL_RUN_FIRST
,
46 'volume-removed': (gobject
.SIGNAL_RUN_FIRST
,
52 gobject
.GObject
.__init
__(self
)
56 # Internal flash is not in HAL
57 internal_fash_id
= datastore
.mounts()[0]['id']
58 self
._volumes
.append(Volume(internal_fash_id
, _('Journal'),
59 'activity-journal', profile
.get_color(),
62 bus
= dbus
.SystemBus()
63 proxy
= bus
.get_object(HAL_SERVICE_NAME
, HAL_MANAGER_PATH
)
64 self
._hal
_manager
= dbus
.Interface(proxy
, HAL_MANAGER_IFACE
)
65 self
._hal
_manager
.connect_to_signal('DeviceAdded', self
._hal
_device
_added
_cb
)
67 for udi
in self
._hal
_manager
.FindDeviceByCapability('volume'):
68 if self
._is
_device
_relevant
(udi
):
70 self
._add
_hal
_device
(udi
)
72 logging
.error('Exception when mounting device %r: %r' % (udi
, e
))
74 def get_volumes(self
):
77 def _get_volume_by_udi(self
, udi
):
78 for volume
in self
._volumes
:
83 def _hal_device_added_cb(self
, udi
):
84 bus
= dbus
.SystemBus()
85 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
86 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
87 if device
.QueryCapability('volume'):
88 logging
.debug('VolumesManager._hal_device_added_cb: %r', udi
)
89 if self
._is
_device
_relevant
(udi
):
90 self
._add
_hal
_device
(udi
)
92 def _is_device_relevant(self
, udi
):
93 bus
= dbus
.SystemBus()
94 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
95 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
97 # Ignore volumes without a filesystem.
98 if device
.GetProperty('volume.fsusage') != 'filesystem':
101 if device
.GetProperty('volume.mount_point') == '/':
104 storage_udi
= device
.GetProperty('block.storage_device')
105 obj
= bus
.get_object(HAL_SERVICE_NAME
, storage_udi
)
106 storage_device
= dbus
.Interface(obj
, HAL_DEVICE_IFACE
)
108 # Ignore non-removable storage.
109 if not storage_device
.GetProperty('storage.hotpluggable'):
114 def _add_hal_device(self
, udi
):
115 logging
.debug('VolumeToolbar._add_hal_device: %r' % udi
)
117 bus
= dbus
.SystemBus()
118 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
119 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
121 # listen to mount/unmount
122 device
.connect_to_signal('PropertyModified',
123 lambda *args
: self
._hal
_device
_property
_modified
_cb
(udi
, *args
))
125 bus
.add_signal_receiver(self
._hal
_device
_removed
_cb
,
127 HAL_MANAGER_IFACE
, HAL_SERVICE_NAME
,
128 HAL_MANAGER_PATH
, arg0
=udi
)
130 if device
.GetProperty('volume.is_mounted'):
131 volume_id
= self
._mount
_in
_datastore
(udi
)
134 label
= device
.GetProperty('volume.label')
135 fs_type
= device
.GetProperty('volume.fstype')
136 valid_options
= device
.GetProperty('volume.mount.valid_options')
139 if 'uid=' in valid_options
:
140 options
.append('uid=%i' % MOUNT_OPTION_UID
)
142 if 'umask=' in valid_options
:
143 options
.append('umask=%i' % MOUNT_OPTION_UMASK
)
145 if 'noatime' in valid_options
:
146 options
.append('noatime')
148 if 'utf8' in valid_options
:
149 options
.append('utf8')
151 if 'iocharset=' in valid_options
:
152 options
.append('iocharset=utf8')
156 mount_point
= device
.GetProperty('volume.uuid')
158 volume
= dbus
.Interface(device_object
, HAL_VOLUME_IFACE
)
160 # Try 100 times to get a mount point
166 volume
.Mount('%s_%d' % (mount_point
, i
), fs_type
, options
)
168 volume
.Mount(mount_point
, fs_type
, options
)
170 except dbus
.DBusException
, e
:
171 if i
< 100 and e
.get_dbus_name() == \
172 'org.freedesktop.Hal.Device.Volume.MountPointNotAvailable':
177 def _hal_device_property_modified_cb(self
, udi
, count
, changes
):
178 if 'volume.is_mounted' in [change
[0] for change
in changes
]:
179 logging
.debug('VolumesManager._hal_device_property_modified: %r' % (udi
))
180 bus
= dbus
.SystemBus()
181 #proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH)
182 #hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE)
183 # TODO: Why this doesn't work?
184 #if not hal_manager.DeviceExists(udi):
187 proxy
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
188 device
= dbus
.Interface(proxy
, HAL_DEVICE_IFACE
)
190 is_mounted
= device
.GetProperty('volume.is_mounted')
191 except dbus
.DBusException
, e
:
192 logging
.debug('e: %s' % e
)
196 if self
._get
_volume
_by
_udi
(udi
) is not None:
197 # device already mounted in the datastore
199 volume_id
= self
._mount
_in
_datastore
(udi
)
201 self
.unmount_from_datastore(udi
)
204 def _mount_in_datastore(self
, udi
):
205 logging
.debug('VolumeToolbar._mount_in_datastore: %r' % udi
)
207 bus
= dbus
.SystemBus()
208 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
209 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
211 mount_point
= device
.GetProperty('volume.mount_point')
212 ds_mounts
= datastore
.mounts()
213 for ds_mount
in ds_mounts
:
214 if mount_point
== ds_mount
['uri']:
215 return ds_mount
['id']
217 mount_id
= datastore
.mount('inplace:' + mount_point
,
218 dict(title
=mount_point
))
220 self
._unmount
_hal
_device
(udi
)
221 raise RuntimeError('datastore.mount(%r, %r) failed.' % (
222 'inplace:' + mount_point
,
223 dict(title
=mount_point
)))
225 volume_name
= device
.GetProperty('volume.label')
227 volume_name
= device
.GetProperty('volume.uuid')
228 volume
= Volume(mount_id
,
230 self
._get
_icon
_for
_volume
(udi
),
234 self
._volumes
.append(volume
)
235 self
.emit('volume-added', volume
)
237 logging
.debug('mounted volume %s' % mount_point
)
239 def _hal_device_removed_cb(self
, udi
):
240 logging
.debug('VolumesManager._hal_device_removed_cb: %r', udi
)
241 bus
= dbus
.SystemBus()
242 #proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH)
243 #hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE)
244 # TODO: Why this doesn't work?
245 #if not hal_manager.DeviceExists(udi):
246 # self._unmount_from_datastore(udi)
247 # self._remove_button(udi)
250 proxy
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
251 device
= dbus
.Interface(proxy
, HAL_DEVICE_IFACE
)
253 is_mounted
= device
.GetProperty('volume.is_mounted')
254 except dbus
.DBusException
, e
:
255 logging
.debug('e: %s' % e
)
256 self
.unmount_from_datastore(udi
)
260 self
._unmount
_from
_datastore
(udi
)
261 self
._unmount
_hal
_device
(udi
)
263 def unmount_from_datastore(self
, udi
):
264 logging
.debug('VolumesManager._unmount_from_datastore: %r', udi
)
265 volume
= self
._get
_volume
_by
_udi
(udi
)
266 if volume
is not None:
267 datastore
.unmount(volume
.id)
269 self
._volumes
.remove(volume
)
270 self
.emit('volume-removed', volume
)
272 def unmount_hal_device(self
, udi
):
273 logging
.debug('VolumesManager._unmount_hal_device: %r', udi
)
274 bus
= dbus
.SystemBus()
275 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
276 volume
= dbus
.Interface(device_object
, HAL_VOLUME_IFACE
)
279 def _get_icon_for_volume(self
, udi
):
280 bus
= dbus
.SystemBus()
281 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
282 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
284 storage_udi
= device
.GetProperty('block.storage_device')
285 obj
= bus
.get_object(HAL_SERVICE_NAME
, storage_udi
)
286 storage_device
= dbus
.Interface(obj
, HAL_DEVICE_IFACE
)
288 storage_drive_type
= storage_device
.GetProperty('storage.drive_type')
289 if storage_drive_type
== 'sd_mmc':
290 return 'media-flash-sd-mmc'
292 return 'media-flash-usb'
294 class Volume(object):
295 def __init__(self
, id, name
, icon_name
, icon_color
, udi
, can_unmount
):
298 self
.icon_name
= icon_name
299 self
.icon_color
= icon_color
301 self
.can_unmount
= can_unmount
304 get_volumes_manager().unmount_from_datastore(self
.udi
)
305 get_volumes_manager().unmount_hal_device(self
.udi
)
307 def get_volumes_manager():
308 global _volumes_manager
309 if _volumes_manager
is None:
310 _volumes_manager
= VolumesManager()
311 return _volumes_manager