Opening a file descriptor and then closing it always discards any locks
held on the underlying file, even if the file is still open as another file
descriptor. This meant that calling read_pidfile() on the process's own
pidfile would discard the lock and make other OVS processes think that the
process had died. This commit fixes the problem.
/* --pidfile: Name of pidfile (null if none). */
static char *pidfile;
/* --pidfile: Name of pidfile (null if none). */
static char *pidfile;
+/* Device and inode of pidfile, so we can avoid reopening it. */
+static dev_t pidfile_dev;
+static ino_t pidfile_ino;
+
/* --overwrite-pidfile: Create pidfile even if one already exists and is
locked? */
static bool overwrite_pidfile;
/* --overwrite-pidfile: Create pidfile even if one already exists and is
locked? */
static bool overwrite_pidfile;
close(fd);
} else {
/* Keep 'fd' open to retain the lock. */
close(fd);
} else {
/* Keep 'fd' open to retain the lock. */
+ struct stat s;
+
+ if (!fstat(fd, &s)) {
+ pidfile_dev = s.st_dev;
+ pidfile_ino = s.st_ino;
+ } else {
+ VLOG_ERR("%s: fstat failed: %s",
+ pidfile, strerror(errno));
+ }
{
char line[128];
struct flock lck;
{
char line[128];
struct flock lck;
+ if ((pidfile_ino || pidfile_dev)
+ && !stat(pidfile, &s)
+ && s.st_ino == pidfile_ino && s.st_dev == pidfile_dev) {
+ /* It's our own pidfile. We can't afford to open it, because closing
+ * *any* fd for a file that a process has locked also releases all the
+ * locks on that file.
+ *
+ * Fortunately, we know the associated pid anyhow: */
+ return getpid();
+ }
+
file = fopen(pidfile, "r");
if (!file) {
error = errno;
file = fopen(pidfile, "r");
if (!file) {
error = errno;
# --pidfile: Name of pidfile (null if none).
_pidfile = None
# --pidfile: Name of pidfile (null if none).
_pidfile = None
+# Our pidfile's inode and device, if we have created one.
+_pidfile_dev = None
+_pidfile_ino = None
+
# --overwrite-pidfile: Create pidfile even if one already exists and is locked?
_overwrite_pidfile = False
# --overwrite-pidfile: Create pidfile even if one already exists and is locked?
_overwrite_pidfile = False
logging.error("%s: create failed: %s"
% (tmpfile, os.strerror(e.errno)))
return
logging.error("%s: create failed: %s"
% (tmpfile, os.strerror(e.errno)))
return
try:
fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError, e:
try:
fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError, e:
+ s = os.fstat(file.fileno())
+ _pidfile_dev = s.st_dev
+ _pidfile_ino = s.st_ino
+
def daemonize():
"""If configured with set_pidfile() or set_detach(), creates the pid file
and detaches from the foreground session."""
def daemonize():
"""If configured with set_pidfile() or set_detach(), creates the pid file
and detaches from the foreground session."""
def read_pidfile(pidfile):
"""Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if
successful, otherwise a negative errno value."""
def read_pidfile(pidfile):
"""Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if
successful, otherwise a negative errno value."""
+ if _pidfile_dev is not None:
+ try:
+ s = os.stat(pidfile)
+ if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
+ # It's our own pidfile. We can't afford to open it,
+ # because closing *any* fd for a file that a process
+ # has locked also releases all the locks on that file.
+ #
+ # Fortunately, we know the associated pid anyhow.
+ return os.getpid()
+ except OSError:
+ pass
+
try:
file = open(pidfile, "r")
except IOError, e:
try:
file = open(pidfile, "r")
except IOError, e: