xref: /dflybsd-src/contrib/dhcpcd/compat/pidfile.c (revision 5422d4140b73a6e65966a6f40831066aa79c4714)
1*f3744ac9SRoy Marples /*	$NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $	*/
27827cba2SAaron LI 
37827cba2SAaron LI /*-
47827cba2SAaron LI  * Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.
57827cba2SAaron LI  * All rights reserved.
67827cba2SAaron LI  *
77827cba2SAaron LI  * This code is derived from software contributed to The NetBSD Foundation
87827cba2SAaron LI  * by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples.
97827cba2SAaron LI  *
107827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
117827cba2SAaron LI  * modification, are permitted provided that the following conditions
127827cba2SAaron LI  * are met:
137827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
147827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
157827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
167827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
177827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
187827cba2SAaron LI  *
197827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
207827cba2SAaron LI  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
217827cba2SAaron LI  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
227827cba2SAaron LI  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
237827cba2SAaron LI  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
247827cba2SAaron LI  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
257827cba2SAaron LI  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
267827cba2SAaron LI  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
277827cba2SAaron LI  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
287827cba2SAaron LI  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
297827cba2SAaron LI  * POSSIBILITY OF SUCH DAMAGE.
307827cba2SAaron LI  */
317827cba2SAaron LI 
327827cba2SAaron LI #include <sys/param.h>
337827cba2SAaron LI 
347827cba2SAaron LI #include <errno.h>
357827cba2SAaron LI #include <fcntl.h>
367827cba2SAaron LI #include <inttypes.h>
377827cba2SAaron LI #include <limits.h>
387827cba2SAaron LI #include <paths.h>
397827cba2SAaron LI #include <stdbool.h>
407827cba2SAaron LI #include <stdlib.h>
417827cba2SAaron LI #include <stdio.h>
427827cba2SAaron LI #include <string.h>
437827cba2SAaron LI #include <unistd.h>
447827cba2SAaron LI 
457827cba2SAaron LI #include <sys/file.h>	/* for flock(2) */
467827cba2SAaron LI #include "config.h"
477827cba2SAaron LI #include "defs.h"
487827cba2SAaron LI 
497827cba2SAaron LI static pid_t pidfile_pid;
507827cba2SAaron LI static char pidfile_path[PATH_MAX];
517827cba2SAaron LI static int pidfile_fd = -1;
527827cba2SAaron LI 
537827cba2SAaron LI /* Closes pidfile resources.
547827cba2SAaron LI  *
557827cba2SAaron LI  * Returns 0 on success, otherwise -1. */
567827cba2SAaron LI static int
pidfile_close(void)577827cba2SAaron LI pidfile_close(void)
587827cba2SAaron LI {
597827cba2SAaron LI 	int error;
607827cba2SAaron LI 
617827cba2SAaron LI 	pidfile_pid = 0;
627827cba2SAaron LI 	error = close(pidfile_fd);
637827cba2SAaron LI 	pidfile_fd = -1;
647827cba2SAaron LI 	pidfile_path[0] = '\0';
657827cba2SAaron LI 	return error;
667827cba2SAaron LI }
677827cba2SAaron LI 
687827cba2SAaron LI /* Truncate, close and unlink an existent pidfile,
697827cba2SAaron LI  * if and only if it was created by this process.
707827cba2SAaron LI  * The pidfile is truncated because we may have dropped permissions
717827cba2SAaron LI  * or entered a chroot and thus unable to unlink it.
727827cba2SAaron LI  *
737827cba2SAaron LI  * Returns 0 on truncation success, otherwise -1. */
747827cba2SAaron LI int
pidfile_clean(void)757827cba2SAaron LI pidfile_clean(void)
767827cba2SAaron LI {
777827cba2SAaron LI 	int error;
787827cba2SAaron LI 
797827cba2SAaron LI 	if (pidfile_fd == -1) {
807827cba2SAaron LI 		errno = EBADF;
817827cba2SAaron LI 		return -1;
827827cba2SAaron LI 	}
837827cba2SAaron LI 
847827cba2SAaron LI 	if (pidfile_pid != getpid())
857827cba2SAaron LI 		error = EPERM;
867827cba2SAaron LI 	else if (ftruncate(pidfile_fd, 0) == -1)
877827cba2SAaron LI 		error = errno;
887827cba2SAaron LI 	else {
89d4fb1e02SRoy Marples #ifndef HAVE_PLEDGE /* Avoid a pledge violating segfault. */
907827cba2SAaron LI 		(void)unlink(pidfile_path);
91d4fb1e02SRoy Marples #endif
927827cba2SAaron LI 		error = 0;
937827cba2SAaron LI 	}
947827cba2SAaron LI 
957827cba2SAaron LI 	(void) pidfile_close();
967827cba2SAaron LI 
977827cba2SAaron LI 	if (error != 0) {
987827cba2SAaron LI 		errno = error;
997827cba2SAaron LI 		return -1;
1007827cba2SAaron LI 	}
1017827cba2SAaron LI 	return 0;
1027827cba2SAaron LI }
1037827cba2SAaron LI 
1047827cba2SAaron LI /* atexit shim for pidfile_clean */
1057827cba2SAaron LI static void
pidfile_cleanup(void)1067827cba2SAaron LI pidfile_cleanup(void)
1077827cba2SAaron LI {
1087827cba2SAaron LI 
1097827cba2SAaron LI 	pidfile_clean();
1107827cba2SAaron LI }
1117827cba2SAaron LI 
1127827cba2SAaron LI /* Constructs a name for a pidfile in the default location (/var/run).
1137827cba2SAaron LI  * If 'bname' is NULL, uses the name of the current program for the name of
1147827cba2SAaron LI  * the pidfile.
1157827cba2SAaron LI  *
1167827cba2SAaron LI  * Returns 0 on success, otherwise -1. */
1177827cba2SAaron LI static int
pidfile_varrun_path(char * path,size_t len,const char * bname)1187827cba2SAaron LI pidfile_varrun_path(char *path, size_t len, const char *bname)
1197827cba2SAaron LI {
1207827cba2SAaron LI 
1217827cba2SAaron LI 	if (bname == NULL)
1227827cba2SAaron LI 		bname = PACKAGE;
1237827cba2SAaron LI 
1247827cba2SAaron LI 	/* _PATH_VARRUN includes trailing / */
1257827cba2SAaron LI 	if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len)
1267827cba2SAaron LI 	{
1277827cba2SAaron LI 		errno = ENAMETOOLONG;
1287827cba2SAaron LI 		return -1;
1297827cba2SAaron LI 	}
1307827cba2SAaron LI 	return 0;
1317827cba2SAaron LI }
1327827cba2SAaron LI 
1337827cba2SAaron LI /* Returns the process ID inside path on success, otherwise -1.
134*f3744ac9SRoy Marples  * If no path is given, use the last pidfile path, otherwise the default one. */
1357827cba2SAaron LI pid_t
pidfile_read(const char * path)1367827cba2SAaron LI pidfile_read(const char *path)
1377827cba2SAaron LI {
1387827cba2SAaron LI 	char dpath[PATH_MAX], buf[16], *eptr;
1397827cba2SAaron LI 	int fd, error;
1407827cba2SAaron LI 	ssize_t n;
1417827cba2SAaron LI 	pid_t pid;
1427827cba2SAaron LI 
1437827cba2SAaron LI 	if (path == NULL && pidfile_path[0] != '\0')
1447827cba2SAaron LI 		path = pidfile_path;
1457827cba2SAaron LI 	if (path == NULL || strchr(path, '/') == NULL) {
1467827cba2SAaron LI 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
1477827cba2SAaron LI 			return -1;
1487827cba2SAaron LI 		path = dpath;
1497827cba2SAaron LI 	}
1507827cba2SAaron LI 
1517827cba2SAaron LI 	if ((fd = open(path, O_RDONLY | O_NONBLOCK)) == -1)
1527827cba2SAaron LI 		return  -1;
1537827cba2SAaron LI 	n = read(fd, buf, sizeof(buf) - 1);
1547827cba2SAaron LI 	error = errno;
1557827cba2SAaron LI 	(void) close(fd);
1567827cba2SAaron LI 	if (n == -1) {
1577827cba2SAaron LI 		errno = error;
1587827cba2SAaron LI 		return -1;
1597827cba2SAaron LI 	}
1607827cba2SAaron LI 	buf[n] = '\0';
1617827cba2SAaron LI 	pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);
1627827cba2SAaron LI 	if (error && !(error == ENOTSUP && *eptr == '\n')) {
1637827cba2SAaron LI 		errno = error;
1647827cba2SAaron LI 		return -1;
1657827cba2SAaron LI 	}
1667827cba2SAaron LI 	return pid;
1677827cba2SAaron LI }
1687827cba2SAaron LI 
1697827cba2SAaron LI /* Locks the pidfile specified by path and writes the process pid to it.
1707827cba2SAaron LI  * The new pidfile is "registered" in the global variables pidfile_fd,
1717827cba2SAaron LI  * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3)
1727827cba2SAaron LI  * can check if we are recreating the same file or a new one.
1737827cba2SAaron LI  *
1747827cba2SAaron LI  * Returns 0 on success, otherwise the pid of the process who owns the
1757827cba2SAaron LI  * lock if it can be read, otherwise -1. */
1767827cba2SAaron LI pid_t
pidfile_lock(const char * path)1777827cba2SAaron LI pidfile_lock(const char *path)
1787827cba2SAaron LI {
1797827cba2SAaron LI 	char dpath[PATH_MAX];
1807827cba2SAaron LI 	static bool registered_atexit = false;
1817827cba2SAaron LI 
1827827cba2SAaron LI 	/* Register for cleanup with atexit. */
1837827cba2SAaron LI 	if (!registered_atexit) {
1847827cba2SAaron LI 		if (atexit(pidfile_cleanup) == -1)
1857827cba2SAaron LI 			return -1;
1867827cba2SAaron LI 		registered_atexit = true;
1877827cba2SAaron LI 	}
1887827cba2SAaron LI 
1897827cba2SAaron LI 	if (path == NULL || strchr(path, '/') == NULL) {
1907827cba2SAaron LI 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
1917827cba2SAaron LI 			return -1;
1927827cba2SAaron LI 		path = dpath;
1937827cba2SAaron LI 	}
1947827cba2SAaron LI 
1957827cba2SAaron LI 	/* If path has changed (no good reason), clean up the old pidfile. */
1967827cba2SAaron LI 	if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0)
1977827cba2SAaron LI 		pidfile_clean();
1987827cba2SAaron LI 
1997827cba2SAaron LI 	if (pidfile_fd == -1) {
2007827cba2SAaron LI 		int fd, opts;
2017827cba2SAaron LI 
2027827cba2SAaron LI 		opts = O_WRONLY | O_CREAT | O_NONBLOCK;
2037827cba2SAaron LI #ifdef O_CLOEXEC
2047827cba2SAaron LI 		opts |= O_CLOEXEC;
2057827cba2SAaron LI #endif
2067827cba2SAaron LI #ifdef O_EXLOCK
2077827cba2SAaron LI 		opts |=	O_EXLOCK;
2087827cba2SAaron LI #endif
2097827cba2SAaron LI 		if ((fd = open(path, opts, 0644)) == -1)
2107827cba2SAaron LI 			goto return_pid;
2117827cba2SAaron LI #ifndef O_CLOEXEC
2127827cba2SAaron LI 		if ((opts = fcntl(fd, F_GETFD)) == -1 ||
2136e63cc1fSRoy Marples 		    fcntl(fd, F_SETFL, opts | FD_CLOEXEC) == -1)
2147827cba2SAaron LI 		{
2157827cba2SAaron LI 			int error = errno;
2167827cba2SAaron LI 
2177827cba2SAaron LI 			(void) close(fd);
2187827cba2SAaron LI 			errno = error;
2197827cba2SAaron LI 			return -1;
2207827cba2SAaron LI 		}
2217827cba2SAaron LI #endif
2227827cba2SAaron LI #ifndef O_EXLOCK
2237827cba2SAaron LI 		if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
2247827cba2SAaron LI 			int error = errno;
2257827cba2SAaron LI 
2267827cba2SAaron LI 			(void) close(fd);
2277827cba2SAaron LI 			if (error != EAGAIN) {
2287827cba2SAaron LI 				errno = error;
2297827cba2SAaron LI 				return -1;
2307827cba2SAaron LI 			}
2317827cba2SAaron LI 			fd = -1;
2327827cba2SAaron LI 		}
2337827cba2SAaron LI #endif
2347827cba2SAaron LI 
2357827cba2SAaron LI return_pid:
2367827cba2SAaron LI 		if (fd == -1) {
2377827cba2SAaron LI 			pid_t pid;
2387827cba2SAaron LI 
2397827cba2SAaron LI 			if (errno == EAGAIN) {
2407827cba2SAaron LI 				/* The pidfile is locked, return the process ID
2417827cba2SAaron LI 				 * it contains.
242*f3744ac9SRoy Marples 				 * If successful, set errno to EEXIST. */
2437827cba2SAaron LI 				if ((pid = pidfile_read(path)) != -1)
2447827cba2SAaron LI 					errno = EEXIST;
2457827cba2SAaron LI 			} else
2467827cba2SAaron LI 				pid = -1;
2477827cba2SAaron LI 
2487827cba2SAaron LI 			return pid;
2497827cba2SAaron LI 		}
2507827cba2SAaron LI 		pidfile_fd = fd;
2517827cba2SAaron LI 		strlcpy(pidfile_path, path, sizeof(pidfile_path));
2527827cba2SAaron LI 	}
2537827cba2SAaron LI 
2547827cba2SAaron LI 	pidfile_pid = getpid();
2557827cba2SAaron LI 
2567827cba2SAaron LI 	/* Truncate the file, as we could be re-writing it.
2577827cba2SAaron LI 	 * Then write the process ID. */
2587827cba2SAaron LI 	if (ftruncate(pidfile_fd, 0) == -1 ||
2597827cba2SAaron LI 	    lseek(pidfile_fd, 0, SEEK_SET) == -1 ||
2607827cba2SAaron LI 	    dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1)
2617827cba2SAaron LI 	{
2627827cba2SAaron LI 		int error = errno;
2637827cba2SAaron LI 
2647827cba2SAaron LI 		pidfile_cleanup();
2657827cba2SAaron LI 		errno = error;
2667827cba2SAaron LI 		return -1;
2677827cba2SAaron LI 	}
2687827cba2SAaron LI 
2697827cba2SAaron LI 	/* Hold the fd open to persist the lock. */
2707827cba2SAaron LI 	return 0;
2717827cba2SAaron LI }
272