When pidfile is already locked and has zero length, do not return

success and zero pid from pidfile_read(). Return EAGAIN instead. Sleep
up to three times for 5 ms while waiting for pidfile to be written.

mount(8) does the kill(mountpid, SIGHUP). If mountd pidfile is truncated,
that would result in the SIGHUP delivered to the mount' process group
instead of the mountd.

Found and analyzed by:	Peter Holm
Tested by:	Peter Holm, kris
Reviewed by:	pjd
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2007-10-12 10:38:05 +00:00
parent 7183e1c010
commit fefc6803cf
2 changed files with 19 additions and 1 deletions

View File

@ -168,6 +168,10 @@ Specified pidfile's name is too long.
.It Bq Er EINVAL
Some process already holds the lock on the given pidfile, but PID read
from there is invalid.
.It Bq Er EAGAIN
Some process already holds the lock on the given pidfile, but the file
is truncated. Most likely, the existing daemon is writing new PID into
the file.
.El
.Pp
The

View File

@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <err.h>
#include <errno.h>
#include <libutil.h>
@ -74,6 +75,8 @@ pidfile_read(const char *path, pid_t *pidptr)
close(fd);
if (i == -1)
return (error);
else if (i == 0)
return (EAGAIN);
buf[i] = '\0';
*pidptr = strtol(buf, &endptr, 10);
@ -88,7 +91,8 @@ pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
{
struct pidfh *pfh;
struct stat sb;
int error, fd, len;
int error, fd, len, count;
struct timespec rqtp;
pfh = malloc(sizeof(*pfh));
if (pfh == NULL)
@ -115,10 +119,20 @@ pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
fd = flopen(pfh->pf_path,
O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
if (fd == -1) {
count = 0;
rqtp.tv_sec = 0;
rqtp.tv_nsec = 5000000;
if (errno == EWOULDBLOCK && pidptr != NULL) {
again:
errno = pidfile_read(pfh->pf_path, pidptr);
if (errno == 0)
errno = EEXIST;
else if (errno == EAGAIN) {
if (++count <= 3) {
nanosleep(&rqtp, 0);
goto again;
}
}
}
free(pfh);
return (NULL);