Index: head/lib/libutil/pidfile.c
===================================================================
--- head/lib/libutil/pidfile.c	(revision 345595)
+++ head/lib/libutil/pidfile.c	(revision 345596)
@@ -1,325 +1,329 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/capsicum.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libgen.h>
 #include <libutil.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
 struct pidfh {
 	int	pf_dirfd;
 	int	pf_fd;
 	char	pf_dir[MAXPATHLEN + 1];
 	char	pf_filename[MAXPATHLEN + 1];
 	dev_t	pf_dev;
 	ino_t	pf_ino;
 };
 
 static int _pidfile_remove(struct pidfh *pfh, int freeit);
 
 static int
 pidfile_verify(const struct pidfh *pfh)
 {
 	struct stat sb;
 
 	if (pfh == NULL || pfh->pf_fd == -1)
 		return (EDOOFUS);
 	/*
 	 * Check remembered descriptor.
 	 */
 	if (fstat(pfh->pf_fd, &sb) == -1)
 		return (errno);
 	if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
 		return (EDOOFUS);
 	return (0);
 }
 
 static int
 pidfile_read(int dirfd, const char *filename, pid_t *pidptr)
 {
 	char buf[16], *endptr;
 	int error, fd, i;
 
 	fd = openat(dirfd, filename, O_RDONLY | O_CLOEXEC);
 	if (fd == -1)
 		return (errno);
 
 	i = read(fd, buf, sizeof(buf) - 1);
 	error = errno;	/* Remember errno in case close() wants to change it. */
 	close(fd);
 	if (i == -1)
 		return (error);
 	else if (i == 0)
 		return (EAGAIN);
 	buf[i] = '\0';
 
 	*pidptr = strtol(buf, &endptr, 10);
 	if (endptr != &buf[i])
 		return (EINVAL);
 
 	return (0);
 }
 
 struct pidfh *
-pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
+pidfile_open(const char *pathp, mode_t mode, pid_t *pidptr)
 {
+	char path[MAXPATHLEN];
 	struct pidfh *pfh;
 	struct stat sb;
 	int error, fd, dirfd, dirlen, filenamelen, count;
 	struct timespec rqtp;
 	cap_rights_t caprights;
 
 	pfh = malloc(sizeof(*pfh));
 	if (pfh == NULL)
 		return (NULL);
 
-	if (path == NULL) {
+	if (pathp == NULL) {
 		dirlen = snprintf(pfh->pf_dir, sizeof(pfh->pf_dir),
 		    "/var/run/");
 		filenamelen = snprintf(pfh->pf_filename,
 		    sizeof(pfh->pf_filename), "%s.pid", getprogname());
 	} else {
-		dirlen = snprintf(pfh->pf_dir, sizeof(pfh->pf_dir),
-		    "%s", path);
-		filenamelen = snprintf(pfh->pf_filename,
-		    sizeof(pfh->pf_filename), "%s", path);
-
-		dirname(pfh->pf_dir);
-		basename(pfh->pf_filename);
+		if (strlcpy(path, pathp, sizeof(path)) >= sizeof(path)) {
+			free(pfh);
+			errno = ENAMETOOLONG;
+			return (NULL);
+		}
+		dirlen = strlcpy(pfh->pf_dir, dirname(path),
+		    sizeof(pfh->pf_dir));
+		(void)strlcpy(path, pathp, sizeof(path));
+		filenamelen = strlcpy(pfh->pf_filename, basename(path),
+		    sizeof(pfh->pf_filename));
 	}
 
 	if (dirlen >= (int)sizeof(pfh->pf_dir) ||
 	    filenamelen >= (int)sizeof(pfh->pf_filename)) {
 		free(pfh);
 		errno = ENAMETOOLONG;
 		return (NULL);
 	}
 
 	dirfd = open(pfh->pf_dir, O_CLOEXEC | O_DIRECTORY | O_NONBLOCK);
 	if (dirfd == -1) {
 		error = errno;
 		free(pfh);
 		errno = error;
 		return (NULL);
 	}
 
 	/*
 	 * Open the PID file and obtain exclusive lock.
 	 * We truncate PID file here only to remove old PID immediately,
 	 * PID file will be truncated again in pidfile_write(), so
 	 * pidfile_write() can be called multiple times.
 	 */
 	fd = flopenat(dirfd, pfh->pf_filename,
 	    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NONBLOCK, mode);
 	if (fd == -1) {
 		if (errno == EWOULDBLOCK) {
 			if (pidptr == NULL) {
 				errno = EEXIST;
 			} else {
 				count = 20;
 				rqtp.tv_sec = 0;
 				rqtp.tv_nsec = 5000000;
 				for (;;) {
 					errno = pidfile_read(dirfd,
 					    pfh->pf_filename, pidptr);
 					if (errno != EAGAIN || --count == 0)
 						break;
 					nanosleep(&rqtp, 0);
 				}
 				if (errno == EAGAIN)
 					*pidptr = -1;
 				if (errno == 0 || errno == EAGAIN)
 					errno = EEXIST;
 			}
 		}
 		error = errno;
 		close(dirfd);
 		free(pfh);
 		errno = error;
 		return (NULL);
 	}
 
 	/*
 	 * Remember file information, so in pidfile_write() we are sure we write
 	 * to the proper descriptor.
 	 */
 	if (fstat(fd, &sb) == -1) {
 		goto failed;
 	}
 
 	if (cap_rights_limit(dirfd,
 	    cap_rights_init(&caprights, CAP_UNLINKAT)) < 0 && errno != ENOSYS) {
 		goto failed;
 	}
 
 	if (cap_rights_limit(fd, cap_rights_init(&caprights, CAP_PWRITE,
 	    CAP_FSTAT, CAP_FTRUNCATE)) < 0 &&
 	    errno != ENOSYS) {
 		goto failed;
 	}
 
 	pfh->pf_dirfd = dirfd;
 	pfh->pf_fd = fd;
 	pfh->pf_dev = sb.st_dev;
 	pfh->pf_ino = sb.st_ino;
 
 	return (pfh);
 
 failed:
 	error = errno;
 	unlinkat(dirfd, pfh->pf_filename, 0);
 	close(dirfd);
 	close(fd);
 	free(pfh);
 	errno = error;
 	return (NULL);
 }
 
 int
 pidfile_write(struct pidfh *pfh)
 {
 	char pidstr[16];
 	int error, fd;
 
 	/*
 	 * Check remembered descriptor, so we don't overwrite some other
 	 * file if pidfile was closed and descriptor reused.
 	 */
 	errno = pidfile_verify(pfh);
 	if (errno != 0) {
 		/*
 		 * Don't close descriptor, because we are not sure if it's ours.
 		 */
 		return (-1);
 	}
 	fd = pfh->pf_fd;
 
 	/*
 	 * Truncate PID file, so multiple calls of pidfile_write() are allowed.
 	 */
 	if (ftruncate(fd, 0) == -1) {
 		error = errno;
 		_pidfile_remove(pfh, 0);
 		errno = error;
 		return (-1);
 	}
 
 	snprintf(pidstr, sizeof(pidstr), "%u", getpid());
 	if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
 		error = errno;
 		_pidfile_remove(pfh, 0);
 		errno = error;
 		return (-1);
 	}
 
 	return (0);
 }
 
 int
 pidfile_close(struct pidfh *pfh)
 {
 	int error;
 
 	error = pidfile_verify(pfh);
 	if (error != 0) {
 		errno = error;
 		return (-1);
 	}
 
 	if (close(pfh->pf_fd) == -1)
 		error = errno;
 	if (close(pfh->pf_dirfd) == -1 && error == 0)
 		error = errno;
 
 	free(pfh);
 	if (error != 0) {
 		errno = error;
 		return (-1);
 	}
 	return (0);
 }
 
 static int
 _pidfile_remove(struct pidfh *pfh, int freeit)
 {
 	int error;
 
 	error = pidfile_verify(pfh);
 	if (error != 0) {
 		errno = error;
 		return (-1);
 	}
 
 	if (unlinkat(pfh->pf_dirfd, pfh->pf_filename, 0) == -1)
 		error = errno;
 	if (close(pfh->pf_fd) == -1 && error == 0)
 		error = errno;
 	if (close(pfh->pf_dirfd) == -1 && error == 0)
 		error = errno;
 	if (freeit)
 		free(pfh);
 	else
 		pfh->pf_fd = -1;
 	if (error != 0) {
 		errno = error;
 		return (-1);
 	}
 	return (0);
 }
 
 int
 pidfile_remove(struct pidfh *pfh)
 {
 
 	return (_pidfile_remove(pfh, 1));
 }
 
 int
 pidfile_fileno(const struct pidfh *pfh)
 {
 
 	if (pfh == NULL || pfh->pf_fd == -1) {
 		errno = EDOOFUS;
 		return (-1);
 	}
 	return (pfh->pf_fd);
 }
Index: head/lib/libutil/tests/pidfile_test.c
===================================================================
--- head/lib/libutil/tests/pidfile_test.c	(revision 345595)
+++ head/lib/libutil/tests/pidfile_test.c	(revision 345596)
@@ -1,293 +1,328 @@
 /*-
  * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer
  *    in this position and unchanged.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/wait.h>
 #include <sys/event.h>
 
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <libutil.h>
 
 /*
  * We need a signal handler so kill(2) will interrupt the child
  * instead of killing it.
  */
 static void
 signal_handler(int sig)
 {
 	(void)sig;
 }
 
 /*
  * Test that pidfile_open() can create a pidfile and that pidfile_write()
  * can write to it.
  */
 static const char *
 test_pidfile_uncontested(void)
 {
 	const char *fn = "test_pidfile_uncontested";
 	struct pidfh *pf;
 	pid_t other = 0;
 
 	unlink(fn);
 	pf = pidfile_open(fn, 0600, &other);
 	if (pf == NULL && other != 0)
 		return ("pidfile exists and is locked");
 	if (pf == NULL)
 		return (strerror(errno));
 	if (pidfile_write(pf) != 0) {
 		pidfile_close(pf);
 		unlink(fn);
 		return ("failed to write PID");
 	}
 	pidfile_close(pf);
 	unlink(fn);
 	return (NULL);
 }
 
 /*
  * Test that pidfile_open() locks against self.
  */
 static const char *
 test_pidfile_self(void)
 {
 	const char *fn = "test_pidfile_self";
 	struct pidfh *pf1, *pf2;
 	pid_t other = 0;
 	int serrno;
 
 	unlink(fn);
 	pf1 = pidfile_open(fn, 0600, &other);
 	if (pf1 == NULL && other != 0)
 		return ("pidfile exists and is locked");
 	if (pf1 == NULL)
 		return (strerror(errno));
 	if (pidfile_write(pf1) != 0) {
 		serrno = errno;
 		pidfile_close(pf1);
 		unlink(fn);
 		return (strerror(serrno));
 	}
 	// second open should fail
 	pf2 = pidfile_open(fn, 0600, &other);
 	if (pf2 != NULL) {
 		pidfile_close(pf1);
 		pidfile_close(pf2);
 		unlink(fn);
 		return ("managed to opened pidfile twice");
 	}
 	if (other != getpid()) {
 		pidfile_close(pf1);
 		unlink(fn);
 		return ("pidfile contained wrong PID");
 	}
 	pidfile_close(pf1);
 	unlink(fn);
 	return (NULL);
 }
 
 /*
  * Common code for test_pidfile_{contested,inherited}.
  */
 static const char *
 common_test_pidfile_child(const char *fn, int parent_open)
 {
 	struct pidfh *pf = NULL;
 	pid_t other = 0, pid = 0;
 	int fd[2], serrno, status;
 	struct kevent event, ke;
 	char ch;
 	int kq;
 
 	unlink(fn);
 	if (pipe(fd) != 0)
 		return (strerror(errno));
 
 	if (parent_open) {
 		pf = pidfile_open(fn, 0600, &other);
 		if (pf == NULL && other != 0)
 			return ("pidfile exists and is locked");
 		if (pf == NULL)
 			return (strerror(errno));
 	}
 
 	pid = fork();
 	if (pid == -1)
 		return (strerror(errno));
 	if (pid == 0) {
 		// child
 		close(fd[0]);
 		signal(SIGINT, signal_handler);
 		if (!parent_open) {
 			pf = pidfile_open(fn, 0600, &other);
 			if (pf == NULL && other != 0)
 				return ("pidfile exists and is locked");
 			if (pf == NULL)
 				return (strerror(errno));
 		}
 		if (pidfile_write(pf) != 0) {
 			serrno = errno;
 			pidfile_close(pf);
 			unlink(fn);
 			return (strerror(serrno));
 		}
 		if (pf == NULL)
 			_exit(1);
 		if (pidfile_write(pf) != 0)
 			_exit(2);
 		kq = kqueue();
 		if (kq == -1)
 			_exit(3);
 		EV_SET(&ke, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
 		/* Attach event to the kqueue. */
 		if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0)
 			_exit(4);
 		/* Inform the parent we are ready to receive SIGINT */
 		if (write(fd[1], "*", 1) != 1)
 			_exit(5);
 		/* Wait for SIGINT received */
 		if (kevent(kq, NULL, 0, &event, 1, NULL) != 1)
 			_exit(6);
 		_exit(0);
 	}
 	// parent
 	close(fd[1]);
 	if (pf)
 		pidfile_close(pf);
 
 	// wait for the child to signal us
 	if (read(fd[0], &ch, 1) != 1) {
 		serrno = errno;
 		unlink(fn);
 		kill(pid, SIGTERM);
 		errno = serrno;
 		return (strerror(errno));
 	}
 
 	// We shouldn't be able to lock the same pidfile as our child
 	pf = pidfile_open(fn, 0600, &other);
 	if (pf != NULL) {
 		pidfile_close(pf);
 		unlink(fn);
 		return ("managed to lock contested pidfile");
 	}
 
 	// Failed to lock, but not because it was contested
 	if (other == 0) {
 		unlink(fn);
 		return (strerror(errno));
 	}
 
 	// Locked by the wrong process
 	if (other != pid) {
 		unlink(fn);
 		return ("pidfile contained wrong PID");
 	}
 
 	// check our child's fate
 	if (pf)
 		pidfile_close(pf);
 	unlink(fn);
 	if (kill(pid, SIGINT) != 0)
 		return (strerror(errno));
 	if (waitpid(pid, &status, 0) == -1)
 		return (strerror(errno));
 	if (WIFSIGNALED(status))
 		return ("child caught signal");
 	if (WEXITSTATUS(status) != 0) 
 		return ("child returned non-zero status");
 
 	// success
 	return (NULL);
 }
 
 /*
  * Test that pidfile_open() fails when attempting to open a pidfile that
  * is already locked, and that it returns the correct PID.
  */
 static const char *
 test_pidfile_contested(void)
 {
 	const char *fn = "test_pidfile_contested";
 	const char *result;
 
 	result = common_test_pidfile_child(fn, 0);
 	return (result);
 }
 
 /*
  * Test that the pidfile lock is inherited.
  */
 static const char *
 test_pidfile_inherited(void)
 {
 	const char *fn = "test_pidfile_inherited";
 	const char *result;
 
 	result = common_test_pidfile_child(fn, 1);
 	return (result);
 }
 
+/*
+ * Make sure we handle relative pidfile paths correctly.
+ */
+static const char *
+test_pidfile_relative(void)
+{
+	char path[PATH_MAX], pid[32], tmpdir[PATH_MAX];
+	struct pidfh *pfh;
+	int fd;
+
+	(void)snprintf(tmpdir, sizeof(tmpdir), "%s.XXXXXX", __func__);
+	if (mkdtemp(tmpdir) == NULL)
+		return (strerror(errno));
+	(void)snprintf(path, sizeof(path), "%s/pidfile", tmpdir);
+
+	pfh = pidfile_open(path, 0600, NULL);
+	if (pfh == NULL)
+		return (strerror(errno));
+	if (pidfile_write(pfh) != 0)
+		return (strerror(errno));
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return (strerror(errno));
+	if (read(fd, pid, sizeof(pid)) < 0)
+		return (strerror(errno));
+	if (atoi(pid) != getpid())
+		return ("pid mismatch");
+	if (close(fd) != 0)
+		return (strerror(errno));
+	if (pidfile_close(pfh) != 0)
+		return (strerror(errno));
+	return (NULL);
+}
+
 static struct test {
 	const char *name;
 	const char *(*func)(void);
 } t[] = {
 	{ "pidfile_uncontested", test_pidfile_uncontested },
 	{ "pidfile_self", test_pidfile_self },
 	{ "pidfile_contested", test_pidfile_contested },
 	{ "pidfile_inherited", test_pidfile_inherited },
+	{ "pidfile_relative", test_pidfile_relative },
 };
 
 int
 main(void)
 {
 	const char *result;
 	int i, nt;
 
 	nt = sizeof(t) / sizeof(*t);
 	printf("1..%d\n", nt);
 	for (i = 0; i < nt; ++i) {
 		if ((result = t[i].func()) != NULL)
 			printf("not ok %d - %s # %s\n", i + 1,
 			    t[i].name, result);
 		else
 			printf("ok %d - %s\n", i + 1,
 			    t[i].name);
 	}
 	exit(0);
 }