~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/python-daemon/daemon/pidlockfile.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# daemon/pidlockfile.py
 
4
# Part of python-daemon, an implementation of PEP 3143.
 
5
#
 
6
# Copyright © 2008–2010 Ben Finney <ben+python@benfinney.id.au>
 
7
#
 
8
# This is free software: you may copy, modify, and/or distribute this work
 
9
# under the terms of the Python Software Foundation License, version 2 or
 
10
# later as published by the Python Software Foundation.
 
11
# No warranty expressed or implied. See the file LICENSE.PSF-2 for details.
 
12
 
 
13
 
 
14
""" Lockfile behaviour implemented via Unix PID files.
 
15
    """
 
16
 
 
17
import os
 
18
import errno
 
19
 
 
20
from lockfile import (
 
21
    FileLock,
 
22
    AlreadyLocked, LockFailed,
 
23
    NotLocked, NotMyLock,
 
24
    )
 
25
 
 
26
 
 
27
class PIDFileError(Exception):
 
28
    """ Abstract base class for errors specific to PID files. """
 
29
 
 
30
class PIDFileParseError(ValueError, PIDFileError):
 
31
    """ Raised when parsing contents of PID file fails. """
 
32
 
 
33
 
 
34
class PIDLockFile(FileLock, object):
 
35
    """ Lockfile implemented as a Unix PID file.
 
36
 
 
37
        The PID file is named by the attribute `path`. When locked,
 
38
        the file will be created with a single line of text,
 
39
        containing the process ID (PID) of the process that acquired
 
40
        the lock.
 
41
 
 
42
        The lock is acquired and maintained as per `LinkFileLock`.
 
43
 
 
44
        """
 
45
 
 
46
    def read_pid(self):
 
47
        """ Get the PID from the lock file.
 
48
            """
 
49
        result = read_pid_from_pidfile(self.path)
 
50
        return result
 
51
 
 
52
    def acquire(self, *args, **kwargs):
 
53
        """ Acquire the lock.
 
54
 
 
55
            Locks the PID file then creates the PID file for this
 
56
            lock. The `timeout` parameter is used as for the
 
57
            `LinkFileLock` class.
 
58
 
 
59
            """
 
60
        super(PIDLockFile, self).acquire(*args, **kwargs)
 
61
        try:
 
62
            write_pid_to_pidfile(self.path)
 
63
        except OSError, exc:
 
64
            error = LockFailed("%(exc)s" % vars())
 
65
            raise error
 
66
 
 
67
    def release(self):
 
68
        """ Release the lock.
 
69
 
 
70
            Removes the PID file then releases the lock, or raises an
 
71
            error if the current process does not hold the lock.
 
72
 
 
73
            """
 
74
        if self.i_am_locking():
 
75
            remove_existing_pidfile(self.path)
 
76
        super(PIDLockFile, self).release()
 
77
 
 
78
    def break_lock(self):
 
79
        """ Break an existing lock.
 
80
 
 
81
            If the lock is held, breaks the lock and removes the PID
 
82
            file.
 
83
 
 
84
            """
 
85
        super(PIDLockFile, self).break_lock()
 
86
        remove_existing_pidfile(self.path)
 
87
 
 
88
 
 
89
class TimeoutPIDLockFile(PIDLockFile):
 
90
    """ Lockfile with default timeout, implemented as a Unix PID file.
 
91
 
 
92
        This uses the ``PIDLockFile`` implementation, with the
 
93
        following changes:
 
94
 
 
95
        * The `acquire_timeout` parameter to the initialiser will be
 
96
          used as the default `timeout` parameter for the `acquire`
 
97
          method.
 
98
 
 
99
        """
 
100
 
 
101
    def __init__(self, path, acquire_timeout=None, *args, **kwargs):
 
102
        """ Set up the parameters of a DaemonRunnerLock. """
 
103
        self.acquire_timeout = acquire_timeout
 
104
        super(TimeoutPIDLockFile, self).__init__(path, *args, **kwargs)
 
105
 
 
106
    def acquire(self, timeout=None, *args, **kwargs):
 
107
        """ Acquire the lock. """
 
108
        if timeout is None:
 
109
            timeout = self.acquire_timeout
 
110
        super(TimeoutPIDLockFile, self).acquire(timeout, *args, **kwargs)
 
111
 
 
112
 
 
113
def read_pid_from_pidfile(pidfile_path):
 
114
    """ Read the PID recorded in the named PID file.
 
115
 
 
116
        Read and return the numeric PID recorded as text in the named
 
117
        PID file. If the PID file does not exist, return ``None``. If
 
118
        the content is not a valid PID, raise ``PIDFileParseError``.
 
119
 
 
120
        """
 
121
    pid = None
 
122
    pidfile = None
 
123
    try:
 
124
        pidfile = open(pidfile_path, 'r')
 
125
    except IOError, exc:
 
126
        if exc.errno == errno.ENOENT:
 
127
            pass
 
128
        else:
 
129
            raise
 
130
 
 
131
    if pidfile:
 
132
        # According to the FHS 2.3 section on PID files in ‘/var/run’:
 
133
        #
 
134
        #   The file must consist of the process identifier in
 
135
        #   ASCII-encoded decimal, followed by a newline character. …
 
136
        #
 
137
        #   Programs that read PID files should be somewhat flexible
 
138
        #   in what they accept; i.e., they should ignore extra
 
139
        #   whitespace, leading zeroes, absence of the trailing
 
140
        #   newline, or additional lines in the PID file.
 
141
 
 
142
        line = pidfile.readline().strip()
 
143
        try:
 
144
            pid = int(line)
 
145
        except ValueError:
 
146
            raise PIDFileParseError(
 
147
                "PID file %(pidfile_path)r contents invalid" % vars())
 
148
        pidfile.close()
 
149
 
 
150
    return pid
 
151
 
 
152
 
 
153
def write_pid_to_pidfile(pidfile_path):
 
154
    """ Write the PID in the named PID file.
 
155
 
 
156
        Get the numeric process ID (“PID”) of the current process
 
157
        and write it to the named file as a line of text.
 
158
 
 
159
        """
 
160
    open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY)
 
161
    open_mode = (
 
162
        ((os.R_OK | os.W_OK) << 6) |
 
163
        ((os.R_OK) << 3) |
 
164
        ((os.R_OK)))
 
165
    pidfile_fd = os.open(pidfile_path, open_flags, open_mode)
 
166
    pidfile = os.fdopen(pidfile_fd, 'w')
 
167
 
 
168
    # According to the FHS 2.3 section on PID files in ‘/var/run’:
 
169
    #
 
170
    #   The file must consist of the process identifier in
 
171
    #   ASCII-encoded decimal, followed by a newline character. For
 
172
    #   example, if crond was process number 25, /var/run/crond.pid
 
173
    #   would contain three characters: two, five, and newline.
 
174
 
 
175
    pid = os.getpid()
 
176
    line = "%(pid)d\n" % vars()
 
177
    pidfile.write(line)
 
178
    pidfile.close()
 
179
 
 
180
 
 
181
def remove_existing_pidfile(pidfile_path):
 
182
    """ Remove the named PID file if it exists.
 
183
 
 
184
        Remove the named PID file. Ignore the condition if the file
 
185
        does not exist, since that only means we are already in the
 
186
        desired state.
 
187
 
 
188
        """
 
189
    try:
 
190
        os.remove(pidfile_path)
 
191
    except OSError, exc:
 
192
        if exc.errno == errno.ENOENT:
 
193
            pass
 
194
        else:
 
195
            raise