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