6 this file is part of the project scolasync. It is a rewrite of
7 usbDisk.py to take in account udisks2.
9 Copyright (C) 2014 Georges Khaznadar <georgesk@ofset.org>
11 This program is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 licence[
'en']=licence_en
26 dependences=
"python3-dbus python3-dbus.mainloop.qt"
29 import dbus, subprocess, os, os.path, re, time, threading, logging, inspect
30 from dbus.mainloop.glib
import DBusGMainLoop, threads_init
31 from gi.repository
import Gio, GLib, UDisks
40 logging.basicConfig(level=logging.DEBUG)
43 callerframerecord = inspect.stack()[caller]
44 frame = callerframerecord[0]
45 info = inspect.getframeinfo(frame)
46 return " -- file={0}, function={1}, line={2}".format(
47 info.filename, info.function, info.lineno
61 if type(obj)==type(
""):
64 path= obj.get_object_path()
65 posUnderscore=path.rfind(
"_")
66 posSlash=path.rfind(
"/")
67 if posUnderscore > posSlash:
68 path=path[:posUnderscore]
78 stat = os.statvfs(device)
81 free = stat.f_bsize * stat.f_bavail
82 total = stat.f_bsize * stat.f_blocks
88 no_options = GLib.Variant(
'a{sv}', {})
95 '/org/freedesktop/UDisks2/block_devices/loop',
97 '/org/freedesktop/UDisks2/block_devices/dm_',
99 '/org/freedesktop/UDisks2/block_devices/ram',
100 '/org/freedesktop/UDisks2/block_devices/zram',
102 '/org/freedesktop/UDisks2/drives/',
119 def __init__(self, logger=logging, diskClass=object):
129 DBusGMainLoop(set_as_default=
True)
131 self.
bus = dbus.SystemBus()
132 self.
udisks = UDisks.Client.new_sync(
None)
133 self.
manager = self.udisks.get_object_manager()
136 'profile': [
'man',
'obj'],
140 'profile': [
'man',
'obj'],
144 'profile': [
'man',
'obj'],
147 'interface-removed': {
148 'profile': [
'man',
'obj'],
151 'interface-proxy-properties-changed': {
152 'profile': [
'man',
'obj',
'interface'],
161 self.
addHook(
'interface-added',
163 self.
addHook(
'interface-removed',
165 self.
addHook(
'interface-proxy-properties-changed',
178 if inspect.getargspec(func).args == self.
cbHooks[signal][
'profile']:
179 cb=self.manager.connect(signal,func)
180 self.
cbHooks[signal][
'hooks'].append(cb)
197 return fs.call_mount_sync(no_options,
None)
198 except GLib.GError
as e:
199 if 'UDisks2.Error.AlreadyMounted' in e.message:
200 m=re.match(
r".*already mounted[^/]*([^\']+).*",e.message)
202 elif 'UDisks2.Error.DeviceBusy' in e.message:
206 time.sleep(retryDelay)
207 timeout -= retryDelay
215 for obj
in self.manager.get_objects():
225 def _interesting_obj(self, obj):
232 for boring
in not_interesting:
233 if path.startswith(boring):
234 return interesting, drive, partition
237 block = obj.get_block()
239 return interesting, drive, partition
242 drive_name = block.get_cached_property(
'Drive').get_string()
243 if drive_name ==
'/':
244 return interesting, drive, partition
246 drive = self.udisks.get_object(drive_name).get_drive()
249 if drive
and drive.get_cached_property(
'Optical').get_boolean():
250 return interesting, drive, partition
254 partition = obj.get_partition()
255 return interesting, drive, partition
262 def _udisks_obj_added(self, obj):
277 for s
in obj.get_block().get_cached_property(
'Symlinks'):
278 if b
'/dev/disk/by-id/usb' in bytes(s):
290 def _udisks_partition_added(self, obj, drive, partition):
292 block = obj.get_block()
293 self.logger.debug(QApplication.translate(
"uDisk",
"Partition ajoutée %s",
None) % path+
inspectData())
294 fstype = block.get_cached_property(
'IdType').get_string()
295 parent = partition.get_cached_property(
'Table').get_string()
296 total = drive.get_cached_property(
'Size').get_uint64()
299 fs = obj.get_filesystem()
301 mount_points = fs.get_cached_property(
'MountPoints').get_bytestring_array()
302 if len(mount_points)>0:
303 mount= mount_points[0]
304 if not mount
and fstype ==
'vfat':
308 logging.exception(QApplication.translate(
"uDisk",
"Échec au montage du disque : %s",
None) % path)
313 self.logger.debug(QApplication.translate(
"uDisk",
"On n'ajoute pas le disque : partition non-USB",
None)+
inspectData())
315 self.logger.debug(QApplication.translate(
"uDisk",
"On n'ajoute pas le disque : partition vide",
None)+
inspectData())
318 path=path, mp=mount, isUsb=isUsb,
319 vendor=drive.get_cached_property(
'Vendor').get_string(),
320 model=drive.get_cached_property(
'Model').get_string(),
323 serial=block.get_cached_property(
'Drive').get_string().split(
'_')[-1],
324 uuid=block.get_cached_property(
'IdUUID').get_string(),
327 device=block.get_cached_property(
'Device').get_bytestring().decode(
'utf-8'),
333 def _udisks_drive_added(self, obj, drive, part):
335 block = obj.get_block()
337 self.logger.debug(QApplication.translate(
"uDisk",
"Disque déjà ajouté auparavant : %s",
None) % path+
inspectData())
339 self.logger.debug(QApplication.translate(
"uDisk",
"Disque ajouté : %s",
None) % path+
inspectData())
340 size = drive.get_cached_property(
'Size').get_uint64()
345 self.logger.debug(QApplication.translate("uDisk","On n'ajoute pas le disque : partition à 0 octets.",None)+inspectData())
350 self.logger.debug(QApplication.translate(
"uDisk",
"On n'ajoute pas le disque : partition non-USB",
None)+
inspectData())
356 vendor=drive.get_cached_property(
'Vendor').get_string(),
357 model=drive.get_cached_property(
'Model').get_string(),
358 serial=block.get_cached_property(
'Drive').get_string().split(
'_')[-1],
359 uuid=block.get_cached_property(
'IdUUID').get_string(),
361 device=block.get_cached_property(
'Device').get_bytestring().decode(
'utf-8'),
367 def _device_changed(self, obj):
369 self.logger.debug(QApplication.translate(
"uDisk",
"Changement pour le disque %s",
None) % path+
inspectData())
377 def _udisks_obj_removed(self, obj):
379 logging.debug(QApplication.translate(
"uDisk",
"Disque débranché du système : %s",
None) % path)
381 self.targets.pop(path)
416 def __init__(self, path, mp='', isUsb=False, vendor='', model='', parent=None,
417 fstype=
'', serial=
'', uuid=
'',
418 free=0, capacity=0, device=
'', firstFat=
None, selected=
True):
437 "1mp":QApplication.translate(
"uDisk",
"point de montage",
None),
438 "2capacity":QApplication.translate(
"uDisk",
"taille",
None),
439 "3vendor":QApplication.translate(
"uDisk",
"marque",
None),
440 "4model":QApplication.translate(
"uDisk",
"modèle de disque",
None),
441 "5stickid":QApplication.translate(
"uDisk",
"numéro de série",
None),
444 _specialItems={
"0Check":QApplication.translate(
"uDisk",
"cocher",
None)}
446 _ItemPattern=re.compile(
"[0-9]?(.*)")
466 result= list(uDisk2._specialItems.keys())+ list(uDisk2._itemNames.keys())
467 return sorted(result)
469 headers = staticmethod(headers)
493 return self.
fstype==
"vfat"
508 prefix=
"\n"+
" "*indent
510 props=[
"mp",
"parent",
"fstype",
"stickid",
"uuid",
"vendor",
"model",
"devStuff",
"free",
"capacity"]
512 r+=prefix+
"%s = %s" %(prop, getattr(self,prop))
530 m=uDisk2._ItemPattern.match(self.
headers()[n])
532 return getattr(self, m.group(1))
548 elif n <= len(propListe):
558 if mount_paths==
None:
561 while len(mount_paths)==0
and leftTries >0:
562 leftTries = leftTries - 1
565 subprocess.call(
"udisks --mount %s > /dev/null" %path,shell=
True)
567 print(
"STILL TO DEBUG: is the mount OK? is self.mp updated?")
575 raise Exception (
"Could not mount the VFAT after 5 tries.")
602 def __init__(self, access="disk", diskClass=uDisk2):
603 UDisksBackend.__init__(self, diskClass=diskClass)
622 if self.
access==
"firstFat":
624 uDisk2(p,self).ensureMounted()
641 result=self.
summary()==other.summary()
694 r=
"Available USB disks\n"
695 r+=
"===================\n"
696 for d
in sorted(self.
disks()):
697 r+=
"%s\n" %(self.
targets[d].devStuff)
698 partlist=self.
parts(d)
701 for part
in partlist:
702 r+=
" %s\n" %(self.
targets[part].devStuff,)
711 r=
"Available USB disks\n"
712 r+=
"===================\n"
713 for d
in self.
disks():
715 partlist=self.
parts(d)
718 for part
in sorted(partlist):
719 r+=
" %s\n" %(self.
targets[part].devStuff)
720 r+=self.
targets[part].valuableProperties(12)+
"\n"
732 path=self.targets.keys()[n]
733 elif self.
access==
"firstFat":
746 elif self.
access==
"firstFat":
764 if self.
targets[p].fstype==
"vfat":
777 for p
in self.fatPaths:
778 if p.split(
"/")[-1]==s:
786 if __name__==
"__main__":
792 QMainWindow.__init__(self)
795 quitbutton = QPushButton(
'Examinez le terminal\nbranchez et débranchez des clés USB, puis\nQuittez', self)
796 quitbutton.clicked.connect(self.close)
797 self.setCentralWidget(quitbutton)
804 print([s.split(
"/")[-1]
for s
in machin.targets.keys()])
805 machin.modified=
False
806 machin.addHook(
'object-added', print_targets_if_modif)
807 machin.addHook(
'object-removed', print_targets_if_modif)
809 app = QApplication(sys.argv)
812 sys.exit(app.exec_())
modified
self.modified signifie une modification récente, à prendre en compte par une application au niveau ut...
def ensureMounted(self)
Permet de s'assurer qu'une partition ou un disque sera bien monté
une classe pour représenter un disque ou une partition.
def isDosFat(self)
Permet de reconnaitre les partitions DOS-FAT.
def parts(self, d)
Récolte les partitions d'un disque.
def __getitem__(self, n)
Renvoie un élément de listage de données internes au disque.
def __init__
Le constructeur.
def _udisks_partition_added(self, obj, drive, partition)
Fonction de rappel pour l'ajout d'une partition, met à jour self.targets.
def detect_devices(self)
Fait un inventaire des disques.
def contains(self, ud)
Permet de déterminer si un disque est dans la collection.
def __str__(self)
Fournit une représentation imprimable.
def __init__
Le constructeur.
def parts_ud(self, d)
Récolte les partitions d'un disque.
def _udisks_drive_added(self, obj, drive, part)
def __init__
Le constructeur.
def disks(self)
Récolte les enregistrements de niveau supérieur de self.targets.
def __len__(self)
Renseigne sur la longueur de la collection.
diskClass
self.targets est un dictionnaire des disques détectés les clés sont les paths et les contenus des ins...
def _interesting_obj(self, obj)
trouve si un objet est intéressant à cataloguer
def safePath(obj)
Récupère de façon sûre le path d'une instance de UDisksObjectProxy.
def print_targets_if_modif(man, obj)
def fs_size(device)
Renvoie la taille d'un système de fichier et la place disponible.
def getFirstFats(self)
Facilite l'accès aux partitions de type DOS-FAT, et a des effets de bord :
def _device_changed(self, obj)
def summary(self)
Fournit une représentation imprimable d'un résumé
def _udisks_obj_removed(self, obj)
Fonction de rappel déclenchée par le retrait d'un disque.
def _udisks_obj_added(self, obj)
Fonction de rappel pour les ajouts de disque.
une classe pour représenter la collection des disques USB connectés
def retry_mount
Essaie de monter un système de fichier jusqu'à ce qu'il cesse d'échouer avec "Busy", ou que l'erreur soit "déjà monté".
def addHook(self, signal, func)
ajoute une fonction à appeler pour un signal nommé, et enregistre cette fonction dans self...
def finishInit(self)
Fin de l'initialisation.
def __str__(self)
Fournit une représentation imprimable.
def __getitem__(self, n)
Renvoye le nième disque.
def title(self)
Permet d'obtenir un identifiant unique de disque.
def compare(self, other)
Sert à comparer deux collections de disques, par exemple une collection passée et une collection prés...
def objIsUsb(self, obj)
détermine si un périphérique est de type USB
def unNumberProp(self, n)
retire le numéro des en-têtes pour en faire un nom de propriété valide pour interroger dbus ...
Cette classe a été inspirée par le projet USBcreator.
def disks_ud(self)
Récolte les enregistrements de niveau supérieur de self.targets.
def mountFirstFats(self)
fabrique la liste des partitions FAT, monte les partitions FAT si elles ne le sont pas ...
def valuableProperties
Facilite l'accès aux propriétés intéressantes d'une instance.
def uniqueId(self)
renvoie un identifiant unique.