7 this file is part of the project scolasync. It is a rewrite of
8 usbDisk.py to take in account udisks2.
10 Copyright (C) 2014 Georges Khaznadar <georgesk@ofset.org>
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 licence[
'en']=licence_en
27 dependences=
"python3-dbus python3-dbus.mainloop.qt"
30 import dbus, subprocess, os, os.path, re, time, threading, logging, inspect
31 from dbus.mainloop.glib
import DBusGMainLoop, threads_init
32 from gi.repository
import Gio, GLib, UDisks
41 logging.basicConfig(level=logging.DEBUG)
44 callerframerecord = inspect.stack()[caller]
45 frame = callerframerecord[0]
46 info = inspect.getframeinfo(frame)
47 return " -- file={0}, function={1}, line={2}".format(
48 info.filename, info.function, info.lineno
62 if type(obj)==type(
""):
65 path= obj.get_object_path()
66 posUnderscore=path.rfind(
"_")
67 posSlash=path.rfind(
"/")
68 if posUnderscore > posSlash:
69 path=path[:posUnderscore]
79 stat = os.statvfs(device)
82 free = stat.f_bsize * stat.f_bavail
83 total = stat.f_bsize * stat.f_blocks
89 no_options = GLib.Variant(
'a{sv}', {})
96 '/org/freedesktop/UDisks2/block_devices/loop',
98 '/org/freedesktop/UDisks2/block_devices/dm_',
100 '/org/freedesktop/UDisks2/block_devices/ram',
101 '/org/freedesktop/UDisks2/block_devices/zram',
103 '/org/freedesktop/UDisks2/drives/',
120 def __init__(self, logger=logging, diskClass=object):
130 DBusGMainLoop(set_as_default=
True)
132 self.
bus = dbus.SystemBus()
133 self.
udisks = UDisks.Client.new_sync(
None)
134 self.
manager = self.udisks.get_object_manager()
137 'profile': [
'man',
'obj'],
141 'profile': [
'man',
'obj'],
145 'profile': [
'man',
'obj'],
148 'interface-removed': {
149 'profile': [
'man',
'obj'],
152 'interface-proxy-properties-changed': {
153 'profile': [
'man',
'obj',
'interface'],
162 self.
addHook(
'interface-added',
164 self.
addHook(
'interface-removed',
166 self.
addHook(
'interface-proxy-properties-changed',
179 if inspect.getargspec(func).args == self.
cbHooks[signal][
'profile']:
180 cb=self.manager.connect(signal,func)
181 self.
cbHooks[signal][
'hooks'].append(cb)
198 return fs.call_mount_sync(no_options,
None)
199 except GLib.GError
as e:
200 if 'UDisks2.Error.AlreadyMounted' in e.message:
201 m=re.match(
r".*already mounted[^/]*([^\']+).*",e.message)
203 elif 'UDisks2.Error.DeviceBusy' in e.message:
207 time.sleep(retryDelay)
208 timeout -= retryDelay
216 for obj
in self.manager.get_objects():
226 def _interesting_obj(self, obj):
233 for boring
in not_interesting:
234 if path.startswith(boring):
235 return interesting, drive, partition
238 block = obj.get_block()
240 return interesting, drive, partition
243 drive_name = block.get_cached_property(
'Drive').get_string()
244 if drive_name ==
'/':
245 return interesting, drive, partition
247 drive = self.udisks.get_object(drive_name).get_drive()
250 if drive
and drive.get_cached_property(
'Optical').get_boolean():
251 return interesting, drive, partition
255 partition = obj.get_partition()
256 return interesting, drive, partition
263 def _udisks_obj_added(self, obj):
278 for s
in obj.get_block().get_cached_property(
'Symlinks'):
279 if b
'/dev/disk/by-id/usb' in bytes(s):
291 def _udisks_partition_added(self, obj, drive, partition):
293 block = obj.get_block()
294 self.logger.debug(QApplication.translate(
"uDisk",
"Partition ajoutée %s",
None, QApplication.UnicodeUTF8) % path+
inspectData())
295 fstype = block.get_cached_property(
'IdType').get_string()
296 parent = partition.get_cached_property(
'Table').get_string()
297 total = drive.get_cached_property(
'Size').get_uint64()
300 fs = obj.get_filesystem()
302 mount_points = fs.get_cached_property(
'MountPoints').get_bytestring_array()
303 if len(mount_points)>0:
304 mount= mount_points[0]
305 if not mount
and fstype ==
'vfat':
309 logging.exception(QApplication.translate(
"uDisk",
"Échec au montage du disque : %s",
None, QApplication.UnicodeUTF8) % path)
314 self.logger.debug(QApplication.translate(
"uDisk",
"On n'ajoute pas le disque : partition non-USB",
None, QApplication.UnicodeUTF8)+
inspectData())
316 self.logger.debug(QApplication.translate(
"uDisk",
"On n'ajoute pas le disque : partition vide",
None, QApplication.UnicodeUTF8)+
inspectData())
319 path=path, mp=mount, isUsb=isUsb,
320 vendor=drive.get_cached_property(
'Vendor').get_string(),
321 model=drive.get_cached_property(
'Model').get_string(),
324 serial=block.get_cached_property(
'Drive').get_string().split(
'_')[-1],
325 uuid=block.get_cached_property(
'IdUUID').get_string(),
328 device=block.get_cached_property(
'Device').get_bytestring().decode(
'utf-8'),
334 def _udisks_drive_added(self, obj, drive, part):
336 block = obj.get_block()
338 self.logger.debug(QApplication.translate(
"uDisk",
"Disque déjà ajouté auparavant : %s",
None, QApplication.UnicodeUTF8) % path+
inspectData())
340 self.logger.debug(QApplication.translate(
"uDisk",
"Disque ajouté : %s",
None, QApplication.UnicodeUTF8) % path+
inspectData())
341 size = drive.get_cached_property(
'Size').get_uint64()
346 self.logger.debug(QApplication.translate("uDisk","On n'ajoute pas le disque : partition à 0 octets.",None, QApplication.UnicodeUTF8)+inspectData())
351 self.logger.debug(QApplication.translate(
"uDisk",
"On n'ajoute pas le disque : partition non-USB",
None, QApplication.UnicodeUTF8)+
inspectData())
357 vendor=drive.get_cached_property(
'Vendor').get_string(),
358 model=drive.get_cached_property(
'Model').get_string(),
359 serial=block.get_cached_property(
'Drive').get_string().split(
'_')[-1],
360 uuid=block.get_cached_property(
'IdUUID').get_string(),
362 device=block.get_cached_property(
'Device').get_bytestring().decode(
'utf-8'),
368 def _device_changed(self, obj):
370 self.logger.debug(QApplication.translate(
"uDisk",
"Changement pour le disque %s",
None, QApplication.UnicodeUTF8) % path+
inspectData())
378 def _udisks_obj_removed(self, obj):
380 logging.debug(QApplication.translate(
"uDisk",
"Disque débranché du système : %s",
None, QApplication.UnicodeUTF8) % path)
382 self.targets.pop(path)
417 def __init__(self, path, mp='', isUsb=False, vendor='', model='', parent=None,
418 fstype=
'', serial=
'', uuid=
'',
419 free=0, capacity=0, device=
'', firstFat=
None, selected=
True):
438 "1mp":QApplication.translate(
"uDisk",
"point de montage",
None, QApplication.UnicodeUTF8),
439 "2capacity":QApplication.translate(
"uDisk",
"taille",
None, QApplication.UnicodeUTF8),
440 "3vendor":QApplication.translate(
"uDisk",
"marque",
None, QApplication.UnicodeUTF8),
441 "4model":QApplication.translate(
"uDisk",
"modèle de disque",
None, QApplication.UnicodeUTF8),
442 "5stickid":QApplication.translate(
"uDisk",
"numéro de série",
None, QApplication.UnicodeUTF8),
445 _specialItems={
"0Check":QApplication.translate(
"uDisk",
"cocher",
None, QApplication.UnicodeUTF8)}
447 _ItemPattern=re.compile(
"[0-9]?(.*)")
467 result= list(uDisk2._specialItems.keys())+ list(uDisk2._itemNames.keys())
468 return sorted(result)
470 headers = staticmethod(headers)
494 return self.
fstype==
"vfat"
509 prefix=
"\n"+
" "*indent
511 props=[
"mp",
"parent",
"fstype",
"stickid",
"uuid",
"vendor",
"model",
"devStuff",
"free",
"capacity"]
513 r+=prefix+
"%s = %s" %(prop, getattr(self,prop))
531 m=uDisk2._ItemPattern.match(self.
headers()[n])
533 return getattr(self, m.group(1))
549 elif n <= len(propListe):
559 if mount_paths==
None:
562 while len(mount_paths)==0
and leftTries >0:
563 leftTries = leftTries - 1
566 subprocess.call(
"udisks --mount %s > /dev/null" %path,shell=
True)
568 print(
"STILL TO DEBUG: is the mount OK? is self.mp updated?")
576 raise Exception (
"Could not mount the VFAT after 5 tries.")
603 def __init__(self, access="disk", diskClass=uDisk2):
604 UDisksBackend.__init__(self, diskClass=diskClass)
623 if self.
access==
"firstFat":
625 uDisk2(p,self).ensureMounted()
642 result=self.
summary()==other.summary()
695 r=
"Available USB disks\n"
696 r+=
"===================\n"
697 for d
in sorted(self.
disks()):
698 r+=
"%s\n" %(self.
targets[d].devStuff)
699 partlist=self.
parts(d)
702 for part
in partlist:
703 r+=
" %s\n" %(self.
targets[part].devStuff,)
712 r=
"Available USB disks\n"
713 r+=
"===================\n"
714 for d
in self.
disks():
716 partlist=self.
parts(d)
719 for part
in sorted(partlist):
720 r+=
" %s\n" %(self.
targets[part].devStuff)
721 r+=self.
targets[part].valuableProperties(12)+
"\n"
733 path=self.targets.keys()[n]
734 elif self.
access==
"firstFat":
747 elif self.
access==
"firstFat":
765 if self.
targets[p].fstype==
"vfat":
778 for p
in self.fatPaths:
779 if p.split(
"/")[-1]==s:
787 if __name__==
"__main__":
793 QMainWindow.__init__(self)
796 quitbutton = QPushButton(
'Examinez le terminal\nbranchez et débranchez des clés USB, puis\nQuittez', self)
797 QObject.connect(quitbutton, SIGNAL(
"clicked()"), self.close)
798 self.setCentralWidget(quitbutton)
805 print([s.split(
"/")[-1]
for s
in machin.targets.keys()])
806 machin.modified=
False
807 machin.addHook(
'object-added', print_targets_if_modif)
808 machin.addHook(
'object-removed', print_targets_if_modif)
810 app = QApplication(sys.argv)
813 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.