Commit from One Laptop Per Child: Translation System by user HoboPrimate. 31 of 31...
[journal-activity.git] / volumesmanager.py
blob6147681653d4d29133239b65ceb7ff72877b63d9
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
17 import os
18 import logging
19 from gettext import gettext as _
21 import gobject
22 import dbus
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'
42 __gsignals__ = {
43 'volume-added': (gobject.SIGNAL_RUN_FIRST,
44 gobject.TYPE_NONE,
45 ([object])),
46 'volume-removed': (gobject.SIGNAL_RUN_FIRST,
47 gobject.TYPE_NONE,
48 ([object]))
51 def __init__(self):
52 gobject.GObject.__init__(self)
54 self._volumes = []
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(),
60 None, False))
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):
69 try:
70 self._add_hal_device(udi)
71 except Exception, e:
72 logging.error('Exception when mounting device %r: %r' % (udi, e))
74 def get_volumes(self):
75 return self._volumes
77 def _get_volume_by_udi(self, udi):
78 for volume in self._volumes:
79 if volume.udi == udi:
80 return volume
81 return None
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':
99 return False
100 # Ignore root.
101 if device.GetProperty('volume.mount_point') == '/':
102 return False
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'):
110 return False
112 return True
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,
126 'DeviceRemoved',
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)
132 return
134 label = device.GetProperty('volume.label')
135 fs_type = device.GetProperty('volume.fstype')
136 valid_options = device.GetProperty('volume.mount.valid_options')
137 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')
154 mount_point = label
155 if not mount_point:
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
161 mounted = False
162 i = 0
163 while not mounted:
164 try:
165 if i > 0:
166 volume.Mount('%s_%d' % (mount_point, i), fs_type, options)
167 else:
168 volume.Mount(mount_point, fs_type, options)
169 mounted = True
170 except dbus.DBusException, e:
171 if i < 100 and e.get_dbus_name() == \
172 'org.freedesktop.Hal.Device.Volume.MountPointNotAvailable':
173 i += 1
174 else:
175 raise
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):
185 # return
187 proxy = bus.get_object(HAL_SERVICE_NAME, udi)
188 device = dbus.Interface(proxy, HAL_DEVICE_IFACE)
189 try:
190 is_mounted = device.GetProperty('volume.is_mounted')
191 except dbus.DBusException, e:
192 logging.debug('e: %s' % e)
193 return
195 if is_mounted:
196 if self._get_volume_by_udi(udi) is not None:
197 # device already mounted in the datastore
198 return
199 volume_id = self._mount_in_datastore(udi)
200 else:
201 self.unmount_from_datastore(udi)
202 return
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))
219 if not mount_id:
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')
226 if not volume_name:
227 volume_name = device.GetProperty('volume.uuid')
228 volume = Volume(mount_id,
229 volume_name,
230 self._get_icon_for_volume(udi),
231 profile.get_color(),
232 udi,
233 True)
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)
248 # return
250 proxy = bus.get_object(HAL_SERVICE_NAME, udi)
251 device = dbus.Interface(proxy, HAL_DEVICE_IFACE)
252 try:
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)
257 return
259 if is_mounted:
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)
277 volume.Unmount([])
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'
291 else:
292 return 'media-flash-usb'
294 class Volume(object):
295 def __init__(self, id, name, icon_name, icon_color, udi, can_unmount):
296 self.id = id
297 self.name = name
298 self.icon_name = icon_name
299 self.icon_color = icon_color
300 self.udi = udi
301 self.can_unmount = can_unmount
303 def unmount(self):
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