xref: /netbsd-src/lib/libutil/pidfile.c (revision 4b2769fe52b586c4d7d9e7cc506c2247ed060a04)
1*4b2769feSandvar /*	$NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $	*/
2fb695fdcSthorpej 
3fb695fdcSthorpej /*-
46b9006e8Sroy  * Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.
5fb695fdcSthorpej  * All rights reserved.
6fb695fdcSthorpej  *
7fb695fdcSthorpej  * This code is derived from software contributed to The NetBSD Foundation
86b9006e8Sroy  * by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples.
9fb695fdcSthorpej  *
10fb695fdcSthorpej  * Redistribution and use in source and binary forms, with or without
11fb695fdcSthorpej  * modification, are permitted provided that the following conditions
12fb695fdcSthorpej  * are met:
13fb695fdcSthorpej  * 1. Redistributions of source code must retain the above copyright
14fb695fdcSthorpej  *    notice, this list of conditions and the following disclaimer.
15fb695fdcSthorpej  * 2. Redistributions in binary form must reproduce the above copyright
16fb695fdcSthorpej  *    notice, this list of conditions and the following disclaimer in the
17fb695fdcSthorpej  *    documentation and/or other materials provided with the distribution.
18fb695fdcSthorpej  *
19fb695fdcSthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20fb695fdcSthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21fb695fdcSthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
222788a3f2Staca  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23fb695fdcSthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24fb695fdcSthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25fb695fdcSthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26fb695fdcSthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27fb695fdcSthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28fb695fdcSthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29fb695fdcSthorpej  * POSSIBILITY OF SUCH DAMAGE.
30fb695fdcSthorpej  */
31fb695fdcSthorpej 
32fb695fdcSthorpej #include <sys/cdefs.h>
33fb695fdcSthorpej #if defined(LIBC_SCCS) && !defined(lint)
34*4b2769feSandvar __RCSID("$NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $");
35fb695fdcSthorpej #endif
36fb695fdcSthorpej 
37fb695fdcSthorpej #include <sys/param.h>
3825527158Sjmmv 
396b9006e8Sroy #include <errno.h>
406b9006e8Sroy #include <fcntl.h>
416b9006e8Sroy #include <inttypes.h>
42fb695fdcSthorpej #include <paths.h>
4325527158Sjmmv #include <stdbool.h>
44fb695fdcSthorpej #include <stdlib.h>
45fb695fdcSthorpej #include <stdio.h>
462a957ed7Stron #include <string.h>
47fb695fdcSthorpej #include <unistd.h>
48fb695fdcSthorpej #include <util.h>
49fb695fdcSthorpej 
502a957ed7Stron static pid_t pidfile_pid;
516b9006e8Sroy static char pidfile_path[PATH_MAX];
526b9006e8Sroy static int pidfile_fd = -1;
53fb695fdcSthorpej 
546b9006e8Sroy /* Closes pidfile resources.
556b9006e8Sroy  *
566b9006e8Sroy  * Returns 0 on success, otherwise -1. */
576b9006e8Sroy static int
pidfile_close(void)586b9006e8Sroy pidfile_close(void)
596b9006e8Sroy {
606b9006e8Sroy 	int error;
616b9006e8Sroy 
626b9006e8Sroy 	pidfile_pid = 0;
636b9006e8Sroy 	error = close(pidfile_fd);
646b9006e8Sroy 	pidfile_fd = -1;
656b9006e8Sroy 	pidfile_path[0] = '\0';
666b9006e8Sroy 	return error;
676b9006e8Sroy }
686b9006e8Sroy 
696b9006e8Sroy /* Truncate, close and unlink an existent pidfile,
706b9006e8Sroy  * if and only if it was created by this process.
716b9006e8Sroy  * The pidfile is truncated because we may have dropped permissions
726b9006e8Sroy  * or entered a chroot and thus unable to unlink it.
736b9006e8Sroy  *
746b9006e8Sroy  * Returns 0 on truncation success, otherwise -1. */
756b9006e8Sroy int
pidfile_clean(void)766b9006e8Sroy pidfile_clean(void)
776b9006e8Sroy {
786b9006e8Sroy 	int error;
796b9006e8Sroy 
806b9006e8Sroy 	if (pidfile_fd == -1) {
816b9006e8Sroy 		errno = EBADF;
826b9006e8Sroy 		return -1;
836b9006e8Sroy 	}
846b9006e8Sroy 
856b9006e8Sroy 	if (pidfile_pid != getpid())
866b9006e8Sroy 		error = EPERM;
876b9006e8Sroy 	else if (ftruncate(pidfile_fd, 0) == -1)
886b9006e8Sroy 		error = errno;
896b9006e8Sroy 	else {
906b9006e8Sroy 		(void) unlink(pidfile_path);
916b9006e8Sroy 		error = 0;
926b9006e8Sroy 	}
936b9006e8Sroy 
946b9006e8Sroy 	(void) pidfile_close();
956b9006e8Sroy 
966b9006e8Sroy 	if (error != 0) {
976b9006e8Sroy 		errno = error;
986b9006e8Sroy 		return -1;
996b9006e8Sroy 	}
1006b9006e8Sroy 	return 0;
1016b9006e8Sroy }
1026b9006e8Sroy 
1036b9006e8Sroy /* atexit shim for pidfile_clean */
10425527158Sjmmv static void
pidfile_cleanup(void)10525527158Sjmmv pidfile_cleanup(void)
106fb695fdcSthorpej {
107fb695fdcSthorpej 
1086b9006e8Sroy 	pidfile_clean();
1092a957ed7Stron }
110fb695fdcSthorpej 
1116b9006e8Sroy /* Constructs a name for a pidfile in the default location (/var/run).
1126b9006e8Sroy  * If 'bname' is NULL, uses the name of the current program for the name of
11325527158Sjmmv  * the pidfile.
11425527158Sjmmv  *
1156b9006e8Sroy  * Returns 0 on success, otherwise -1. */
1166b9006e8Sroy static int
pidfile_varrun_path(char * path,size_t len,const char * bname)1176b9006e8Sroy pidfile_varrun_path(char *path, size_t len, const char *bname)
11825527158Sjmmv {
11925527158Sjmmv 
1200afe4227Schristos 	if (bname == NULL)
1210afe4227Schristos 		bname = getprogname();
12225527158Sjmmv 
12325527158Sjmmv 	/* _PATH_VARRUN includes trailing / */
1246b9006e8Sroy 	if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len)
1256b9006e8Sroy 	{
1266b9006e8Sroy 		errno = ENAMETOOLONG;
1276b9006e8Sroy 		return -1;
1286b9006e8Sroy 	}
1296b9006e8Sroy 	return 0;
13025527158Sjmmv }
13125527158Sjmmv 
1326b9006e8Sroy /* Returns the process ID inside path on success, otherwise -1.
133*4b2769feSandvar  * If no path is given, use the last pidfile path, otherwise the default one. */
1346b9006e8Sroy pid_t
pidfile_read(const char * path)1356b9006e8Sroy pidfile_read(const char *path)
13625527158Sjmmv {
1376b9006e8Sroy 	char dpath[PATH_MAX], buf[16], *eptr;
1386b9006e8Sroy 	int fd, error;
1396b9006e8Sroy 	ssize_t n;
1406b9006e8Sroy 	pid_t pid;
14125527158Sjmmv 
1424b95ed96Sroy 	if (path == NULL && pidfile_path[0] != '\0')
1436b9006e8Sroy 		path = pidfile_path;
1444b95ed96Sroy 	if (path == NULL || strchr(path, '/') == NULL) {
1454b95ed96Sroy 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
14625527158Sjmmv 			return -1;
1476b9006e8Sroy 		path = dpath;
1486b9006e8Sroy 	}
14925527158Sjmmv 
1506b9006e8Sroy 	if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK)) == -1)
15125527158Sjmmv 		return  -1;
1526b9006e8Sroy 	n = read(fd, buf, sizeof(buf) - 1);
1536b9006e8Sroy 	error = errno;
1546b9006e8Sroy 	(void) close(fd);
1556b9006e8Sroy 	if (n == -1) {
1566b9006e8Sroy 		errno = error;
15725527158Sjmmv 		return -1;
1582a957ed7Stron 	}
1596b9006e8Sroy 	buf[n] = '\0';
1606b9006e8Sroy 	pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);
1616b9006e8Sroy 	if (error && !(error == ENOTSUP && *eptr == '\n')) {
1626b9006e8Sroy 		errno = error;
1636b9006e8Sroy 		return -1;
1646b9006e8Sroy 	}
1656b9006e8Sroy 	return pid;
1666b9006e8Sroy }
1676b9006e8Sroy 
1686b9006e8Sroy /* Locks the pidfile specified by path and writes the process pid to it.
1696b9006e8Sroy  * The new pidfile is "registered" in the global variables pidfile_fd,
1706b9006e8Sroy  * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3)
1716b9006e8Sroy  * can check if we are recreating the same file or a new one.
1726b9006e8Sroy  *
1736b9006e8Sroy  * Returns 0 on success, otherwise the pid of the process who owns the
1746b9006e8Sroy  * lock if it can be read, otherwise -1. */
1756b9006e8Sroy pid_t
pidfile_lock(const char * path)1766b9006e8Sroy pidfile_lock(const char *path)
1776b9006e8Sroy {
1786b9006e8Sroy 	char dpath[PATH_MAX];
1796b9006e8Sroy 	static bool registered_atexit = false;
1806b9006e8Sroy 
1816b9006e8Sroy 	/* Register for cleanup with atexit. */
1826b9006e8Sroy 	if (!registered_atexit) {
1836b9006e8Sroy 		if (atexit(pidfile_cleanup) == -1)
1846b9006e8Sroy 			return -1;
1856b9006e8Sroy 		registered_atexit = true;
1866b9006e8Sroy 	}
1876b9006e8Sroy 
1886b9006e8Sroy 	if (path == NULL || strchr(path, '/') == NULL) {
1894b95ed96Sroy 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
1906b9006e8Sroy 			return -1;
1916b9006e8Sroy 		path = dpath;
1926b9006e8Sroy 	}
1936b9006e8Sroy 
1946b9006e8Sroy 	/* If path has changed (no good reason), clean up the old pidfile. */
1954b95ed96Sroy 	if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0)
19667f77b27Sroy 		pidfile_clean();
1976b9006e8Sroy 
1986b9006e8Sroy 	if (pidfile_fd == -1) {
1996b9006e8Sroy 		pidfile_fd = open(path,
2006b9006e8Sroy 		    O_WRONLY | O_CREAT | O_CLOEXEC | O_NONBLOCK | O_EXLOCK,
2016b9006e8Sroy 		    0644);
2026b9006e8Sroy 		if (pidfile_fd == -1) {
2036b9006e8Sroy 			pid_t pid;
2046b9006e8Sroy 
2056b9006e8Sroy 			if (errno == EAGAIN) {
2066b9006e8Sroy 				/* The pidfile is locked, return the process ID
2076b9006e8Sroy 				 * it contains.
208ba5c90c4Smsaitoh 				 * If successful, set errno to EEXIST. */
2096b9006e8Sroy 				if ((pid = pidfile_read(path)) != -1)
2106b9006e8Sroy 					errno = EEXIST;
2116b9006e8Sroy 			} else
2126b9006e8Sroy 				pid = -1;
2136b9006e8Sroy 
2146b9006e8Sroy 			return pid;
2156b9006e8Sroy 		}
2166b9006e8Sroy 		strlcpy(pidfile_path, path, sizeof(pidfile_path));
2176b9006e8Sroy 	}
2182a957ed7Stron 
2192a957ed7Stron 	pidfile_pid = getpid();
2202a957ed7Stron 
2216b9006e8Sroy 	/* Truncate the file, as we could be re-writing it.
2226b9006e8Sroy 	 * Then write the process ID. */
2236b9006e8Sroy 	if (ftruncate(pidfile_fd, 0) == -1 ||
2246b9006e8Sroy 	    lseek(pidfile_fd, 0, SEEK_SET) == -1 ||
2256b9006e8Sroy 	    dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1)
2266b9006e8Sroy 	{
2276b9006e8Sroy 		int error = errno;
22825527158Sjmmv 
2296b9006e8Sroy 		pidfile_cleanup();
2306b9006e8Sroy 		errno = error;
2316b9006e8Sroy 		return -1;
2326b9006e8Sroy 	}
2336b9006e8Sroy 
2346b9006e8Sroy 	/* Hold the fd open to persist the lock. */
235c2c2980fSitojun 	return 0;
236fb695fdcSthorpej }
237fb695fdcSthorpej 
2386b9006e8Sroy /* The old function.
2396b9006e8Sroy  * Historical behaviour is that pidfile is not re-written
2406b9006e8Sroy  * if path has not changed.
2416b9006e8Sroy  *
2426b9006e8Sroy  * Returns 0 on success, otherwise -1.
2436b9006e8Sroy  * As such we have no way of knowing the process ID who owns the lock. */
24425527158Sjmmv int
pidfile(const char * path)24525527158Sjmmv pidfile(const char *path)
246fb695fdcSthorpej {
2476b9006e8Sroy 	pid_t pid;
24825527158Sjmmv 
2496b9006e8Sroy 	pid = pidfile_lock(path);
2506b9006e8Sroy 	return pid == 0 ? 0 : -1;
251fb695fdcSthorpej }
252