Fix manager locking on high-PID systems

This commit is contained in:
Mario Fetka
2026-07-18 11:45:44 +02:00
parent 1fcee37890
commit 0d86ee9d98
+214 -36
View File
@@ -12,6 +12,11 @@
#include <nmlib.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
@@ -57,6 +62,67 @@ static BOOL Exiting = FALSE;
static BOOL ChildError = FALSE;
static BOOL ReloadNow = FALSE;
static int HighestPriority = 0;
static int LockFD = -1;
static pid_t LockOwnerPID = -1;
static pid_t ReadPid(const char *filename);
static BOOL Unlock(const char *lockfile);
static void
CleanupManagerLock(void)
{
if (LockFD != -1) {
Unlock(LOCKFILE);
}
}
/*
* PID files alone are not locks: after an unclean shutdown the PID can be
* reused by an unrelated process. Keep an advisory write lock for the life
* of the manager and use /proc, where available, to recognise pid files left
* by older Bongo versions which did not take that lock.
*/
static BOOL
ManagerProcessIsRunning(pid_t pid)
{
if (pid <= 1) {
return FALSE;
}
#ifdef __linux__
{
char path[64];
char name[64];
FILE *comm;
int length = snprintf(path, sizeof(path), "/proc/%ld/comm", (long)pid);
if (length >= 0 && length < (int)sizeof(path)) {
comm = fopen(path, "r");
if (comm != NULL) {
BOOL matches = FALSE;
if (fgets(name, sizeof(name), comm) != NULL) {
name[strcspn(name, "\r\n")] = '\0';
matches = (strcmp(name, "bongo-manager") == 0);
}
fclose(comm);
return matches;
}
if (errno == ENOENT) {
return FALSE;
}
}
}
#endif
if (kill(pid, 0) == 0) {
return TRUE;
}
return errno == EPERM;
}
static BongoAgent *
FindAgentByPid(pid_t pid)
@@ -302,9 +368,9 @@ StartAgentsWithPriority(int priority, BOOL onlyCrashed, BOOL printMessage)
static BOOL
LoadAgentConfiguration()
{
char *config;
char *config = NULL;
BOOL retcode = FALSE;
BongoJsonNode *node;
BongoJsonNode *node = NULL;
BongoJsonResult res;
GArray *agentlist;
unsigned int i;
@@ -345,7 +411,8 @@ LoadAgentConfiguration()
retcode = TRUE;
finish:
BongoJsonNodeFree(node);
if (node) BongoJsonNodeFree(node);
if (config) MemFree(config);
return retcode;
}
@@ -401,50 +468,137 @@ SignalHandler(int signo)
static int
Lock(BOOL force)
{
struct flock lock;
struct stat status;
int fd;
int remaining;
int written;
int ret;
int mode;
pid_t pid;
char pidstr[20];
pid_t oldPid;
char pidstr[32];
mode = O_CREAT | O_WRONLY;
if (!force) {
mode |= O_EXCL;
}
mode = O_CREAT | O_RDWR;
#ifdef O_CLOEXEC
mode |= O_CLOEXEC;
#endif
#ifdef O_NOFOLLOW
mode |= O_NOFOLLOW;
#endif
fd = open(LOCKFILE, mode, S_IREAD | S_IWRITE);
fd = open(LOCKFILE, mode, S_IRUSR | S_IWUSR);
if (fd == -1) {
return -1;
}
if (fstat(fd, &status) == -1) {
int savedErrno = errno;
close(fd);
errno = savedErrno;
return -1;
}
if (!S_ISREG(status.st_mode)) {
close(fd);
errno = EINVAL;
return -1;
}
if (fchmod(fd, S_IRUSR | S_IWUSR) == -1) {
int savedErrno = errno;
close(fd);
errno = savedErrno;
return -1;
}
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
int savedErrno = errno;
close(fd);
errno = savedErrno;
return -1;
}
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
if (fcntl(fd, F_SETLK, &lock) == -1) {
int savedErrno = errno;
close(fd);
errno = (savedErrno == EACCES || savedErrno == EAGAIN) ?
EEXIST : savedErrno;
return -1;
}
{
char oldPidString[32];
ssize_t count;
char *end;
long value;
oldPid = -1;
count = pread(fd, oldPidString, sizeof(oldPidString) - 1, 0);
if (count > 0) {
oldPidString[count] = '\0';
errno = 0;
value = strtol(oldPidString, &end, 10);
if (errno == 0 && end != oldPidString && value > 1 &&
(*end == '\n' || *end == '\0')) {
oldPid = (pid_t)value;
if ((long)oldPid != value) {
oldPid = -1;
}
}
}
}
if (!force && oldPid != getpid() && ManagerProcessIsRunning(oldPid)) {
close(fd);
errno = EEXIST;
return -1;
}
if (ftruncate(fd, 0) == -1 || lseek(fd, 0, SEEK_SET) == (off_t)-1) {
int savedErrno = errno;
close(fd);
errno = savedErrno;
return -1;
}
pid = getpid();
if ((unsigned long)pid > 999999) {
/* pid < 0 || pid > pidmax ceiling on Solaris */
if (pid <= 1) {
fprintf(stderr, _("bongo-manager: unlikely pid, Exiting\n"));
close(fd);
exit(1);
}
remaining = snprintf(pidstr, sizeof(pidstr), "%ld\n", (long int)pid);
if (remaining < 0 || remaining >= (int)sizeof(pidstr)) {
close(fd);
errno = EOVERFLOW;
return -1;
}
ret = 0;
written = 0;
while (remaining > 0) {
ret = write(fd, pidstr + written, remaining);
if (ret != -1) {
if (ret == 0) {
close(fd);
errno = EIO;
return -1;
}
remaining -= ret;
written += ret;
} else if (errno == EINTR) {
continue;
} else {
int savedErrno = errno;
close(fd);
errno = savedErrno;
return ret;
}
}
if (close(fd) == -1) {
return -1;
}
LockFD = fd;
LockOwnerPID = pid;
return ret;
}
@@ -453,26 +607,56 @@ Lock(BOOL force)
static BOOL
Unlock(const char *lockfile)
{
if (unlink(lockfile) == -1) {
fprintf(stderr, _("bongo-manager: couldn't unlink %s\n"), LOCKFILE);
return FALSE;
BOOL result = TRUE;
if (LockOwnerPID != getpid()) {
if (LockFD != -1) {
close(LockFD);
LockFD = -1;
}
return TRUE;
}
return TRUE;
if (unlink(lockfile) == -1) {
if (errno != ENOENT) {
fprintf(stderr, _("bongo-manager: couldn't unlink %s\n"), LOCKFILE);
result = FALSE;
}
}
if (LockFD != -1) {
if (close(LockFD) == -1) {
result = FALSE;
}
LockFD = -1;
}
LockOwnerPID = -1;
return result;
}
static pid_t
ReadPid(const char *filename)
{
FILE *f;
char pidstr[20];
char pidstr[32];
char *end;
long value;
pid_t ret = -1;
f = fopen(filename, "r");
if (f) {
if (fgets(pidstr, sizeof(pidstr), f)) {
ret = (pid_t)strtol(pidstr, NULL, 10);
errno = 0;
value = strtol(pidstr, &end, 10);
if (errno == 0 && end != pidstr && value > 1 &&
(*end == '\n' || *end == '\0')) {
ret = (pid_t)value;
if ((long)ret != value) {
ret = -1;
}
}
}
fclose(f);
} else {
@@ -650,7 +834,7 @@ main(int argc, char **argv)
if (reload) {
pid_t pid = ReadPid(LOCKFILE);
if (pid <= 0) {
if (pid <= 0 || !ManagerProcessIsRunning(pid)) {
fprintf(stderr, _("bongo-manager: could not open pid file '%s'\n"), LOCKFILE);
exit(1);
}
@@ -668,7 +852,7 @@ main(int argc, char **argv)
if (shutdown) {
pid_t pid = ReadPid(LOCKFILE);
if (pid <= 0) {
if (pid <= 0 || !ManagerProcessIsRunning(pid)) {
fprintf(stderr, _("bongo-manager: could not open pid file '%s'\n"), LOCKFILE);
exit(1);
}
@@ -690,20 +874,10 @@ main(int argc, char **argv)
exit(1);
}
get_lock:
if (Lock(force) == -1) {
if (errno == EEXIST) {
pid_t pid = ReadPid(LOCKFILE);
if (kill(pid, 0) != -1 || errno != ESRCH) {
fprintf(stderr, _("bongo-manager: another bongo-manager process appears to be running.\n"));
fprintf(stderr, _("bongo-manager: run with -s to stop an existing process, or -f to ignore the existing pidfile.\n"));
} else if (!force) {
fprintf(stderr, _("bongo-manager: removing stale lock file in %s.\n"), LOCKFILE);
force = TRUE;
goto get_lock;
} else {
fprintf (stderr, _("bongo-manager: could not remove stale lock file in %s.\n"), LOCKFILE);
}
fprintf(stderr, _("bongo-manager: another bongo-manager process appears to be running.\n"));
fprintf(stderr, _("bongo-manager: run with -s to stop an existing process, or -f to ignore the existing pidfile.\n"));
} else if (errno == EPERM) {
fprintf(stderr, _("bongo-manager: %s user does not have permission to create a lock file in %s\n"),
MsgGetUnprivilegedUser(), XPL_DEFAULT_WORK_DIR);
@@ -714,6 +888,10 @@ get_lock:
goto err_handler;
}
unlockFile = TRUE;
if (atexit(CleanupManagerLock) != 0) {
fprintf(stderr, _("bongo-manager: could not register pid file cleanup\n"));
goto err_handler;
}
ConnStartup(DEFAULT_CONNECTION_TIMEOUT);
@@ -729,7 +907,7 @@ get_lock:
StartStore();
if (!LoadAgentConfiguration()) {
printf(_("bongo-manager: Couldn't load configuration for agents\n"));
exit(-1); // no point continuing...
goto err_handler;
}
StartAgents(FALSE, FALSE);