Commit b6522276 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

utils.io.WritePidFile: Improve error reporting



If the PID file is already locked by another process, try to read
the content and report it as part of the error message.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 2dbc6857
......@@ -82,6 +82,12 @@ class LockError(GenericError):
pass
class PidFileLockError(LockError):
"""PID file is already locked by another process.
"""
class HypervisorError(GenericError):
"""Hypervisor-related exception.
......
......@@ -701,13 +701,24 @@ def ReadPidFile(pidfile):
logging.exception("Can't read pid file")
return 0
return _ParsePidFileContents(raw_data)
def _ParsePidFileContents(data):
"""Tries to extract a process ID from a PID file's content.
@type data: string
@rtype: int
@return: Zero if nothing could be read, PID otherwise
"""
try:
pid = int(raw_data)
except (TypeError, ValueError), err:
pid = int(data)
except (TypeError, ValueError):
logging.info("Can't parse pid file contents", exc_info=True)
return 0
return pid
else:
return pid
def ReadLockedPidFile(path):
......@@ -834,13 +845,21 @@ def WritePidFile(pidfile):
"""
# We don't rename nor truncate the file to not drop locks under
# existing processes
fd_pidfile = os.open(pidfile, os.O_WRONLY | os.O_CREAT, 0600)
fd_pidfile = os.open(pidfile, os.O_RDWR | os.O_CREAT, 0600)
# Lock the PID file (and fail if not possible to do so). Any code
# wanting to send a signal to the daemon should try to lock the PID
# file before reading it. If acquiring the lock succeeds, the daemon is
# no longer running and the signal should not be sent.
filelock.LockFile(fd_pidfile)
try:
filelock.LockFile(fd_pidfile)
except errors.LockError:
msg = ["PID file '%s' is already locked by another process" % pidfile]
# Try to read PID file
pid = _ParsePidFileContents(os.read(fd_pidfile, 100))
if pid > 0:
msg.append(", PID read from file is %s" % pid)
raise errors.PidFileLockError("".join(msg))
os.write(fd_pidfile, "%d\n" % os.getpid())
......
......@@ -711,7 +711,7 @@ class TestPidFileFunctions(unittest.TestCase):
read_pid = utils.ReadPidFile(pid_file)
self.failUnlessEqual(read_pid, os.getpid())
self.failUnless(utils.IsProcessAlive(read_pid))
self.failUnlessRaises(errors.LockError, utils.WritePidFile,
self.failUnlessRaises(errors.PidFileLockError, utils.WritePidFile,
self.f_dpn('test'))
os.close(fd)
utils.RemoveFile(self.f_dpn("test"))
......@@ -747,11 +747,28 @@ class TestPidFileFunctions(unittest.TestCase):
read_pid = utils.ReadPidFile(pid_file)
self.failUnlessEqual(read_pid, new_pid)
self.failUnless(utils.IsProcessAlive(new_pid))
# Try writing to locked file
try:
utils.WritePidFile(pid_file)
except errors.PidFileLockError, err:
errmsg = str(err)
self.assertTrue(errmsg.endswith(" %s" % new_pid),
msg=("Error message ('%s') didn't contain correct"
" PID (%s)" % (errmsg, new_pid)))
else:
self.fail("Writing to locked file didn't fail")
utils.KillProcess(new_pid, waitpid=True)
self.failIf(utils.IsProcessAlive(new_pid))
utils.RemoveFile(self.f_dpn('child'))
self.failUnlessRaises(errors.ProgrammerError, utils.KillProcess, 0)
def testExceptionType(self):
# Make sure the PID lock error is a subclass of LockError in case some code
# depends on it
self.assertTrue(issubclass(errors.PidFileLockError, errors.LockError))
def tearDown(self):
shutil.rmtree(self.dir)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment