"""
A simple module for acquiring pidfile locks (e.g., for use by daemons).
Copyright (C) 2010 by Peter F. Couvares, Syracuse University
mailto: pfcouvar@syr.edu
"""
from __future__ import print_function
import os
import fcntl
import glue.utils
[docs]def get_lock(lockfile):
"""
Tries to write a lockfile containing the current pid. Excepts if
the lockfile already contains the pid of a running process.
Although this should prevent a lock from being granted twice, it
can theoretically deny a lock unjustly in the unlikely event that
the original process is gone but another unrelated process has
been assigned the same pid by the OS.
"""
pidfile = open(lockfile, "a+")
# here we do some meta-locking by getting an exclusive lock on the
# pidfile before reading it, to prevent two daemons from seeing a
# stale lock at the same time, and both trying to run
try:
fcntl.flock(pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError as e:
raise RuntimeError("failed to lock %s: %s" % (lockfile, e))
# we got the file lock, so check for a pid therein
pidfile.seek(0)
pidfile_pid = pidfile.readline().strip()
if pidfile_pid.isdigit():
if glue.utils.pid_exists(int(pidfile_pid)):
raise RuntimeError("pidfile %s contains pid (%s) of a running "
"process" % (lockfile, pidfile_pid))
else:
print("pidfile %s contains stale pid %s; writing new lock" %
(lockfile, pidfile_pid))
# the pidfile didn't exist or was stale, so grab a new lock
pidfile.truncate(0)
pidfile.write("%d\n" % os.getpid())
pidfile.close()
# should be entirely unecessary, but paranoia always served me well
confirm_lock(lockfile)
return True
[docs]def confirm_lock(lockfile):
"""
Confirm that the given lockfile contains our pid.
Should be entirely unecessary, but paranoia always served me well.
"""
pidfile = open(lockfile, "r")
pidfile_pid = pidfile.readline().strip()
pidfile.close()
if int(pidfile_pid) != os.getpid():
raise RuntimeError("pidfile %s contains pid %s; expected pid %s!" %
(lockfile, os.getpid(), pidfile_pid))
return True