Fix manager locking on high-PID systems
This commit is contained in:
+214
-36
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user