lock.py 5.68 KB
Newer Older
bernat's avatar
bernat committed
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
bernat's avatar
bernat committed
3

4
""" Gestion de lock
bernat's avatar
bernat committed
5

6
Copyright (C) Frédéric Pauget
bernat's avatar
bernat committed
7 8 9
Licence : GPLv2
"""

10
import os,string,time,sys, affich_tools
11
from commands import getoutput
12
from user_tests import getuser
13
from fcntl import lockf, LOCK_EX, LOCK_NB, LOCK_UN
14
import errno, random
bernat's avatar
bernat committed
15

16
def wait_lock(lock_name, lock_comment='', d=None, retry=0.2):
17
    """Attend la disponibilité d'un lock en utilisant le framework Twisted.
bernat's avatar
bernat committed
18 19

    Si d est de type Deferred, on est en mode asynchrone.
20
    retry permet de réessayer dans `retry' secondes.
bernat's avatar
bernat committed
21 22 23
    """
    from twisted.internet import reactor, defer
    try:
bernat's avatar
bernat committed
24 25
        try:
            make_lock(lock_name, lock_comment, nowait=1, quiet=True)
26 27
        except AssertionError:
            raise
bernat's avatar
bernat committed
28
        except:
29 30 31
            print "*** Probleme lors de l'obtention du lock..."
            import traceback
            traceback.print_exc()
bernat's avatar
bernat committed
32
            # On a sans doute pas le lock et c'est pas moi qui me tape
33
            # à debugguer les trucs à Fred.
bernat's avatar
bernat committed
34 35
            raise AssertionError

bernat's avatar
bernat committed
36 37 38 39 40
        # On a le lock
        if not d:
            # On est en mode synchrone
            return defer.succeed(None)
        # On appelle le callback car on est en mode asynchrone
bernat's avatar
bernat committed
41
        print "We got the lock, in asynchronous mode"
bernat's avatar
bernat committed
42
        d.callback(None)
bernat's avatar
bernat committed
43 44 45 46 47
    except AssertionError:
        # On a pas le lock
        if not d:
            # On est en mode synchrone, on va passer en asynchrone
            d = defer.Deferred()
48 49 50
        # On essaie de nouveau plus tard
        newretry = min(retry * 2, 4) + random.random() * 0.1 * retry
        reactor.callLater(retry, wait_lock, lock_name, lock_comment, d, newretry)
bernat's avatar
bernat committed
51 52
        return d

bernat's avatar
bernat committed
53
def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
54
    """ Création d'un lock
55
    si nowait=1 fait un sys.exit(254) quand un ancien lock actif est rencontré
56
    """
57
    return
bernat's avatar
bernat committed
58 59 60 61 62 63
    lock_dir = '/var/lock/gestion'
    try:
        os.mkdir(lock_dir)
    except OSError:
        pass
    lock_file = "%s/%s" % (lock_dir, lock_name)
64

65
    # On créé une zone d'exclusion
66 67 68 69 70
    lock_fd_dl=open("%s-dotlock" % lock_file, "w")
    # On demande un verrou exclusif
    try:
        lockf(lock_fd_dl, LOCK_EX | LOCK_NB)
    except IOError, e:
bernat's avatar
bernat committed
71
        if e.errno not in [errno.EACCES, errno.EAGAIN]:
72
            raise
bernat's avatar
bernat committed
73 74 75 76 77
        if nowait:
            if quiet:
                # On va plutot lever une exception
                raise AssertionError('In critical section')
            else:
78
                sys.stderr.write('\tpropriétaire : inconnu\n\tpid : inconnu\n\tdémarré depuis inconnu\n')
79
                sys.exit(254)
bernat's avatar
bernat committed
80
        else:
81
            # La procédure de lock est deja en cours d'execution, on essaie un peu plus tard
bernat's avatar
bernat committed
82
            time.sleep(0.5)
83
            # On enleve le verrou système
84 85
            lockf(lock_fd_dl, LOCK_UN)
            lock_fd_dl.close()
bernat's avatar
bernat committed
86
            return make_lock(lock_name, lock_comment)
87

bernat's avatar
bernat committed
88
    if os.path.isfile(lock_file) :
bernat's avatar
bernat committed
89
        ### Lock existant
90

bernat's avatar
bernat committed
91 92 93 94 95
        # Lecture du lock
        fd = open(lock_file, "r")
        pid= fd.readline().strip()
        user = fd.readline().strip()
        fd.close()
96

bernat's avatar
bernat committed
97 98 99 100 101
        # Informations sur le processus lockant
        if os.system( "ps ax | grep -q '^%s '" % pid ) :
            # Le script lockant ne tourne plus
            os.remove(lock_file)
        elif nowait :
bernat's avatar
bernat committed
102 103
            if not quiet:
                sys.stderr.write('Lock : %s\n' % lock_file)
pauget's avatar
pauget committed
104
            l=getoutput("ps ax -o pid,etime | awk '($1 == %s)'" % pid)
bernat's avatar
bernat committed
105
            data = [ user , pid , l.strip() ]
106

bernat's avatar
bernat committed
107 108 109 110 111
            # Formatate de etime
            s = data[-1].split('-')
            if len(s)==2 :
                txt = '%s jour(s) ' % s[0]
                s=s[1]
112
            else :
bernat's avatar
bernat committed
113 114
                txt = ''
                s=s[0]
115

bernat's avatar
bernat committed
116 117 118 119 120 121 122
            s = s.split(':')
            if len(s) == 3 :
                txt = '%sh%smin%ss' % tuple(s)
            elif len(s) == 2 :
                txt = '%smin%ss' % tuple(s)
            else :
                txt = '???'
123

bernat's avatar
bernat committed
124
            data[-1]=txt
bernat's avatar
bernat committed
125 126

            if not quiet:
127
                sys.stderr.write('\tpropriétaire : %s\n\tpid : %s\n\tdémarré depuis %s\n' % tuple(data) )
bernat's avatar
bernat committed
128 129 130 131
                sys.exit(254)
            else:
                # On va plutot lever une exception
                raise AssertionError(tuple(data))
bernat's avatar
bernat committed
132 133 134 135 136 137 138
        else :
            # Il faut attendre
            a = affich_tools.anim('\tattente du lock')
            for i in range(8) :
                time.sleep(1)
                a.cycle()
            sys.stdout.write('\r')
139
            # On enleve le verrou système
140 141
            lockf(lock_fd_dl, LOCK_UN)
            lock_fd_dl.close()
bernat's avatar
bernat committed
142
            return make_lock(lock_name, lock_comment)
143

bernat's avatar
bernat committed
144
    ### Prise du lock
145
    lock_fd = file(lock_file,"w")
146 147 148 149 150
    try:
        utilisateur = getuser()
    except:
        utilisateur = "inconnu"
    lock_fd.write("%s\n%s\n%s" % (os.getpid(), utilisateur, lock_comment) )
bernat's avatar
bernat committed
151 152
    lock_fd.close()

153
    # On enleve le verrou système
154 155
    lockf(lock_fd_dl, LOCK_UN)
    lock_fd_dl.close()
156

157

bernat's avatar
bernat committed
158 159
def remove_lock( lock_name ) :
    """ Destruction du lock """
160
    return
161
    # On créé une zone d'exclusion
bernat's avatar
bernat committed
162 163
    lock_dir = '/var/lock/gestion'
    lock_file = "%s/%s" % (lock_dir, lock_name)
164 165 166 167 168 169 170 171

    lock_fd_dl=open("%s-dotlock" % lock_file, "w")
    # On demande un verrou exclusif
    try:
        lockf(lock_fd_dl, LOCK_EX | LOCK_NB)
    except IOError, e:
        if e.errno not in [errno.EACCES, errno.EAGAIN]:
            raise
172
        # Déjà locké
173 174
        time.sleep(0.5)
        return remove_lock(lock_name)
bernat's avatar
bernat committed
175
    try :
bernat's avatar
bernat committed
176 177 178 179
        fd = open(lock_file, "r")
        if fd.readline().strip()=="%s" % os.getpid():
            os.remove(lock_file)
            fd.close()
bernat's avatar
bernat committed
180
    except :
bernat's avatar
bernat committed
181
        pass
182

183
    # On enleve le verrou système
184 185
    lockf(lock_fd_dl, LOCK_UN)
    lock_fd_dl.close()