# usbThread.py

Aller à la documentation de ce fichier.

    00001 # -*- coding: utf-8 -*-
    00002 # $Id: usbThread.py 47 2011-06-13 10:20:14Z georgesk $
    00003
    00004 licenceEn="""
    00005     file usbThread.py
    00006     this file is part of the project scolasync
    00007
    00008     Copyright (C) 2010 Georges Khaznadar <georgesk@ofset.org>
    00009
    00010     This program is free software: you can redistribute it and/or modify
    00011     it under the terms of the GNU General Public License as published by
    00012     the Free Software Foundation, either version3 of the License, or
    00013     (at your option) any later version.
    00014
    00015     This program is distributed in the hope that it will be useful,
    00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00018     GNU General Public License for more details.
    00019
    00020     You should have received a copy of the GNU General Public License
    00021     along with this program.  If not, see <http://www.gnu.org/licenses/>.
    00022 """
    00023
    00024 import subprocess, threading, re, os.path, time
    00025
    00026 _threadNumber=0
    00027
    00028 ##
    00029 #
    00030 #     Une classe pour tenir un registre des threads concernant les baladeurs.
    00031 #
    00032 class ThreadRegister:
    00033
    00034     ##
    00035     #
    00036     #         Le constructure met en place un dictionnaire
    00037     #
    00038     def __init__(self):
    00039         self.dico={}
    00040
    00041     def __str__(self):
    00042         return "ThreadRegister: %s" %self.dico
    00043
    00044     ##
    00045     #
    00046     #         @param ud un disque
    00047     #         @param thread un thread
    00048     #         Empile un thread pour le baladeur ud
    00049     #
    00050     def push(self, ud, thread):
    00051         if ud.owner not in self.dico.keys():
    00052             self.dico[ud.owner]=[thread]
    00053         else:
    00054             self.dico[ud.owner].append(thread)
    00055
    00056     ##
    00057     #
    00058     #         @param ud un disque
    00059     #         @param thread un thread
    00060     #         Dépile un thread pour le baladeur ud
    00061     #
    00062     def pop(self, ud, thread):
    00063         self.dico[ud.owner].remove(thread)
    00064
    00065     ##
    00066     #
    00067     #         Indique si le disque est occupé par des threads
    00068     #         @param owner le propriétaire du disque
    00069     #         @return les données associées par le dictionnaire
    00070     #
    00071     def busy(self, owner):
    00072         if owner in self.dico.keys():
    00073             return self.dico[owner]
    00074         return []
    00075
    00076 globalThreads=ThreadRegister()
    00077
    00078 ##
    00079 #
    00080 #     Évite d'avoir des <i>slashes</i> dans un nom de thread
    00081 #     @return la fin du nom de chemin, après le dernier <i>slash</i> ;
    00082 #     si le chemin ne finit pas bien, remplace les <i>slashes</i> par
    00083 #     des sous-tirets "_".
    00084 #
    00085 def _sanitizePath(path):
    00086     pattern=re.compile(".*([^/]+)")
    00087     m=pattern.match(str(path))
    00088     if m:
    00089         return m.group(1)
    00090     else:
    00091         return str(path).replace('/','_')
    00092
    00093 ##
    00094 #
    00095 #     fabrique un nom de thread commençant par th_, suivi d'un nombre unique,
    00096 #     suivi d'une chaîne relative à la clé USB
    00097 #     @param ud une instance de uDisk
    00098 #     @return un nom de thread unique
    00099 #
    00100 def _threadName(ud):
    00101     global _threadNumber
    00102     name="th_%04d_%s" %(_threadNumber,_sanitizePath(ud.path))
    00103     _threadNumber+=1
    00104     return name
    00105
    00106 ##
    00107 #
    00108 #     Renvoie la date et l'heure dans un format court
    00109 #     @return une chaîne donnée par strftime et le format %Y/%m/%d-%H:%M:%S
    00110 #
    00111 def _date():
    00112     return time.strftime("%Y/%m/%d-%H:%M:%S")
    00113
    00114 ##
    00115 #
    00116 #     Lance une commande dans un shell, et journalise la réussite ou l'échec
    00117 #     @param cmd la commande shell, si cette commande contient des points-virgules elle sera découpée en plusieurs sous-commandes, chacune traitée séparément.
    00118 #     @param logfile le fichier de journalisation
    00119 #
    00120 def _call(cmd, logfile):
    00121     for command in cmd.split(";"):
    00122         okToLog="echo [%s] Success: %s >> %s" %(_date(), command, logfile)
    00123         koToLog="echo [%s] Error: %s >> %s" %(_date(), command, logfile)
    00124         cmd1="(%s && %s) || %s" %(command, okToLog, koToLog)
    00125         subprocess.call(cmd1, shell=True)
    00126
    00127 ##
    00128 #
    00129 #     Une classe abstraite
    00130 #      Cette classe sert de creuset pour les classe servant aux copies
    00131 #      et aux effacement.
    00132 #
    00133 class abstractThreadUSB(threading.Thread):
    00134     ##
    00135     #
    00136     #         Constructeur
    00137     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
    00138     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00139     #         @param fileList la liste des fichiers à traiter
    00140     #         @param subdir un sous-répertoire de la clé USB
    00141     #         @param dest un répertoire de destination si nécessaire, None par défaut
    00142     #         @param logfile un fichier de journalisation, /dev/null par défaut
    00143     #
    00144     def __init__(self,ud, fileList, subdir, dest=None, logfile="/dev/null"):
    00145         threading.Thread.__init__(self,target=self.toDo,
    00146                                   args=(ud, fileList, subdir, dest, logfile),
    00147                                   name=_threadName(ud))
    00148         self.cmd=u"echo This is an abstract method, don't call it"
    00149         self.ud=ud
    00150         ud.threadRunning=True
    00151         self.fileList=fileList
    00152         self.subdir=subdir
    00153         self.dest=dest
    00154         self.logfile=logfile
    00155
    00156     ##
    00157     #
    00158     #         Renvoie une chaîne informative sur le thread
    00159     #         @return une chaine donnant des informations sur ce qui va
    00160     #         se passer dans le thread qui a été créé.
    00161     #
    00162     def __str__(self):
    00163         result="%s(\n" %self.threadType()
    00164         result+="  ud       = %s\n" %self.ud
    00165         result+="  fileList = %s\n" %self.fileList
    00166         result+="  subdir   = %s\n" %self.subdir
    00167         result+="  dest     = %s\n" %self.dest
    00168         result+="  logfile  = %s\n" %self.logfile
    00169         result+="  cmd      = %s\n" %self.cmd
    00170         result+="\n"
    00171         return result
    00172
    00173     ##
    00174     #
    00175     #         @return une chaîne courte qui informe sur le type de thread
    00176     #
    00177     def threadType(self):
    00178         return "abstractThreadUSB"
    00179
    00180     ##
    00181     #
    00182     #         La fonction abstraite pour les choses à faire
    00183     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00184     #         @param fileList la liste des fichiers à traiter
    00185     #         @param subdir un sous-répertoire de la clé USB
    00186     #         @param dest un répertoire de destination
    00187     #         @param logfile un fichier de journalisation
    00188     #
    00189     def toDo(self, ud, fileList, subdir, dest, logfile):
    00190         # ça ne fait rien du tout pour un thread abstrait
    00191         pass
    00192
    00193 ##
    00194 #
    00195 #     Classe pour les threads copiant vers les clés USB
    00196 #
    00197 class threadCopyToUSB(abstractThreadUSB):
    00198     ##
    00199     #
    00200     #         Constructeur
    00201     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
    00202     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00203     #         @param fileList la liste des fichiers à copier
    00204     #         @param subdir le sous-répertoire de la clé USB où faire la copie
    00205     #         @param logfile un fichier de journalisation, /dev/null par défaut
    00206     #
    00207     def __init__(self,ud, fileList, subdir, logfile="/dev/null"):
    00208         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile)
    00209         self.cmd=u"mkdir -p '{toDir}'; cp -R '{fromFile}' '{toFile}'"
    00210
    00211     ##
    00212     #
    00213     #         @return une chaîne courte qui informe sur le type de thread
    00214     #
    00215     def threadType(self):
    00216         return "threadCopyToUSB"
    00217
    00218     ##
    00219     #
    00220     #         Copie une liste de fichiers vers une clé USB sous un répertoire donné.
    00221     #          Ce répertoire est composé de ud.visibleDir() joint au
    00222     #          sous-répertoire subdir.
    00223     #          À chaque fichier ou répertoire copié, une ligne est journalisée dans le
    00224     #          fichier de journal de l'application.
    00225     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00226     #         @param fileList la liste des fichiers à copier
    00227     #         @param logfile un fichier de journalisation
    00228     #         @param subdir le sous-répertoire de la clé USB où faire la copie
    00229     #
    00230     def toDo(self, ud, fileList, subdir, dest, logfile):
    00231         global globalThreads
    00232         globalThreads.push(ud, self)
    00233         while subdir[0]=='/':
    00234             subdir=subdir[1:]
    00235         destpath=os.path.join(ud.ensureMounted(),ud.visibleDir(),subdir)
    00236         for f in fileList:
    00237             fileName=os.path.basename(f)
    00238             cmd=self.cmd.format(fromFile=f,
    00239                                 toDir=destpath,
    00240                                 toFile=os.path.join(destpath,fileName))
    00241             _call(cmd,logfile)
    00242         globalThreads.pop(ud, self)
    00243
    00244 ##
    00245 #
    00246 #     Classe pour les threads copiant depuis les clés USB
    00247 #
    00248 class threadCopyFromUSB(abstractThreadUSB):
    00249     ##
    00250     #
    00251     #         Constructeur
    00252     #         Crée un thread pour copier une liste de fichiers depuis une clé USB
    00253     #         vers un répertoire de disque.
    00254     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00255     #         @param fileList la liste des fichiers à copier
    00256     #         @param subdir le sous-répertoire de la clé USB d'où faire la copie
    00257     #         @param dest un répertoire de destination
    00258     #         @param logfile un fichier de journalisation, /dev/null par défaut
    00259     #
    00260     def __init__(self,ud, fileList, subdir=".", dest="/tmp",
    00261                  rootPath="/", logfile="/dev/null"):
    00262         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
    00263                                    logfile=logfile)
    00264         self.rootPath=rootPath
    00265         self.cmd=u"mkdir -p '{toDir}'; cp -R '{fromPath}' '{toPath}'"
    00266
    00267     ##
    00268     #
    00269     #         Copie une liste de fichiers d'une clé USB sous un répertoire donné.
    00270     #          À chaque fichier ou répertoire copié, une ligne est journalisée
    00271     #          dans le fichier de journal de l'application.
    00272     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00273     #         @param fileList la liste des fichiers à copier
    00274     #         @param dest un répertoire de destination
    00275     #         @param logfile un fichier de journalisation
    00276     #         @param subdir le sous-répertoire de la clé USB où faire la copie
    00277     #
    00278     def toDo(self, ud, fileList, subdir, dest, logfile):
    00279         global globalThreads
    00280         globalThreads.push(ud, self)
    00281         for f in fileList:
    00282             ## prend le fichier ou le répertoire sur le disque courant
    00283             fromPath=os.path.join(ud.ensureMounted(), f)
    00284             owner=ud.ownerByDb()
    00285             ## personnalise le nom de la destination
    00286             newName=u"%s_%s" %(owner,f)
    00287             ## calcule le point de copie et le répertoire à créer s'il le faut
    00288             toPath=os.path.join(dest,newName)
    00289             toDir=os.path.dirname(toPath)
    00290             cmd=self.cmd.format(fromPath=fromPath, toPath=toPath, toDir=toDir)
    00291             _call(cmd,logfile)
    00292         globalThreads.pop(ud, self)
    00293
    00294 ##
    00295 #
    00296 #     Classe pour les threads effaçant des sous-arbres dans les clés USB
    00297 #
    00298 class threadDeleteInUSB(abstractThreadUSB):
    00299     ##
    00300     #
    00301     #         Constructeur
    00302     #          Crée un thread pour supprimer une liste de fichiers dans une clé USB.
    00303     #         @param ud l'instance uDisk correspondant à une partition de clé USB
    00304     #         @param fileList la liste des fichiers à supprimer
    00305     #         @param subdir le sous-répertoire de la clé USB où faire les suppressions
    00306     #         @param logfile un fichier de journalisation, /dev/null par défaut
    00307     #
    00308     def __init__(self,ud, fileList, subdir, logfile="/dev/null"):
    00309         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile)
    00310         self.cmd=u"rm -rf '{toDel}'"
    00311
    00312     ##
    00313     #
    00314     #         Supprime une liste de fichiers dans une clé USB.
    00315     #          La liste est prise sous un répertoire donné. Le répertoire visible
    00316     #          qui dépend du constructuer d ela clé est pris en compte.
    00317     #          À chaque fichier ou répertoire supprimé, une ligne est
    00318     #          journalisée dans le fichier de journal de l'application.
    00319     #         @param l'instance uDisk correspondant à une partition de clé USB
    00320     #         @param fileList la liste des fichiers à copier
    00321     #         @param dest un répertoire de destination
    00322     #         @param logfile un fichier de journalisation
    00323     #         @param subdir le sous-répertoire de la clé USB où faire la copie
    00324     #
    00325     def toDo(self, ud, fileList, subdir, dest, logfile):
    00326         global globalThreads
    00327         globalThreads.push(ud, self)
    00328         for f in fileList:
    00329             toDel=os.path.join(ud.ensureMounted(), f)
    00330             cmd=self.cmd.format(toDel=toDel)
    00331             _call(cmd,logfile)
    00332         globalThreads.pop(ud, self)
    00333