xref: /netbsd-src/lib/libutil/pidfile.c (revision 4b2769fe52b586c4d7d9e7cc506c2247ed060a04)
1 /*	$NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $	*/
2 
3 /*-
4  * Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $");
35 #endif
36 
37 #include <sys/param.h>
38 
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <inttypes.h>
42 #include <paths.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <util.h>
49 
50 static pid_t pidfile_pid;
51 static char pidfile_path[PATH_MAX];
52 static int pidfile_fd = -1;
53 
54 /* Closes pidfile resources.
55  *
56  * Returns 0 on success, otherwise -1. */
57 static int
pidfile_close(void)58 pidfile_close(void)
59 {
60 	int error;
61 
62 	pidfile_pid = 0;
63 	error = close(pidfile_fd);
64 	pidfile_fd = -1;
65 	pidfile_path[0] = '\0';
66 	return error;
67 }
68 
69 /* Truncate, close and unlink an existent pidfile,
70  * if and only if it was created by this process.
71  * The pidfile is truncated because we may have dropped permissions
72  * or entered a chroot and thus unable to unlink it.
73  *
74  * Returns 0 on truncation success, otherwise -1. */
75 int
pidfile_clean(void)76 pidfile_clean(void)
77 {
78 	int error;
79 
80 	if (pidfile_fd == -1) {
81 		errno = EBADF;
82 		return -1;
83 	}
84 
85 	if (pidfile_pid != getpid())
86 		error = EPERM;
87 	else if (ftruncate(pidfile_fd, 0) == -1)
88 		error = errno;
89 	else {
90 		(void) unlink(pidfile_path);
91 		error = 0;
92 	}
93 
94 	(void) pidfile_close();
95 
96 	if (error != 0) {
97 		errno = error;
98 		return -1;
99 	}
100 	return 0;
101 }
102 
103 /* atexit shim for pidfile_clean */
104 static void
pidfile_cleanup(void)105 pidfile_cleanup(void)
106 {
107 
108 	pidfile_clean();
109 }
110 
111 /* Constructs a name for a pidfile in the default location (/var/run).
112  * If 'bname' is NULL, uses the name of the current program for the name of
113  * the pidfile.
114  *
115  * Returns 0 on success, otherwise -1. */
116 static int
pidfile_varrun_path(char * path,size_t len,const char * bname)117 pidfile_varrun_path(char *path, size_t len, const char *bname)
118 {
119 
120 	if (bname == NULL)
121 		bname = getprogname();
122 
123 	/* _PATH_VARRUN includes trailing / */
124 	if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len)
125 	{
126 		errno = ENAMETOOLONG;
127 		return -1;
128 	}
129 	return 0;
130 }
131 
132 /* Returns the process ID inside path on success, otherwise -1.
133  * If no path is given, use the last pidfile path, otherwise the default one. */
134 pid_t
pidfile_read(const char * path)135 pidfile_read(const char *path)
136 {
137 	char dpath[PATH_MAX], buf[16], *eptr;
138 	int fd, error;
139 	ssize_t n;
140 	pid_t pid;
141 
142 	if (path == NULL && pidfile_path[0] != '\0')
143 		path = pidfile_path;
144 	if (path == NULL || strchr(path, '/') == NULL) {
145 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
146 			return -1;
147 		path = dpath;
148 	}
149 
150 	if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK)) == -1)
151 		return  -1;
152 	n = read(fd, buf, sizeof(buf) - 1);
153 	error = errno;
154 	(void) close(fd);
155 	if (n == -1) {
156 		errno = error;
157 		return -1;
158 	}
159 	buf[n] = '\0';
160 	pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);
161 	if (error && !(error == ENOTSUP && *eptr == '\n')) {
162 		errno = error;
163 		return -1;
164 	}
165 	return pid;
166 }
167 
168 /* Locks the pidfile specified by path and writes the process pid to it.
169  * The new pidfile is "registered" in the global variables pidfile_fd,
170  * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3)
171  * can check if we are recreating the same file or a new one.
172  *
173  * Returns 0 on success, otherwise the pid of the process who owns the
174  * lock if it can be read, otherwise -1. */
175 pid_t
pidfile_lock(const char * path)176 pidfile_lock(const char *path)
177 {
178 	char dpath[PATH_MAX];
179 	static bool registered_atexit = false;
180 
181 	/* Register for cleanup with atexit. */
182 	if (!registered_atexit) {
183 		if (atexit(pidfile_cleanup) == -1)
184 			return -1;
185 		registered_atexit = true;
186 	}
187 
188 	if (path == NULL || strchr(path, '/') == NULL) {
189 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
190 			return -1;
191 		path = dpath;
192 	}
193 
194 	/* If path has changed (no good reason), clean up the old pidfile. */
195 	if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0)
196 		pidfile_clean();
197 
198 	if (pidfile_fd == -1) {
199 		pidfile_fd = open(path,
200 		    O_WRONLY | O_CREAT | O_CLOEXEC | O_NONBLOCK | O_EXLOCK,
201 		    0644);
202 		if (pidfile_fd == -1) {
203 			pid_t pid;
204 
205 			if (errno == EAGAIN) {
206 				/* The pidfile is locked, return the process ID
207 				 * it contains.
208 				 * If successful, set errno to EEXIST. */
209 				if ((pid = pidfile_read(path)) != -1)
210 					errno = EEXIST;
211 			} else
212 				pid = -1;
213 
214 			return pid;
215 		}
216 		strlcpy(pidfile_path, path, sizeof(pidfile_path));
217 	}
218 
219 	pidfile_pid = getpid();
220 
221 	/* Truncate the file, as we could be re-writing it.
222 	 * Then write the process ID. */
223 	if (ftruncate(pidfile_fd, 0) == -1 ||
224 	    lseek(pidfile_fd, 0, SEEK_SET) == -1 ||
225 	    dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1)
226 	{
227 		int error = errno;
228 
229 		pidfile_cleanup();
230 		errno = error;
231 		return -1;
232 	}
233 
234 	/* Hold the fd open to persist the lock. */
235 	return 0;
236 }
237 
238 /* The old function.
239  * Historical behaviour is that pidfile is not re-written
240  * if path has not changed.
241  *
242  * Returns 0 on success, otherwise -1.
243  * As such we have no way of knowing the process ID who owns the lock. */
244 int
pidfile(const char * path)245 pidfile(const char *path)
246 {
247 	pid_t pid;
248 
249 	pid = pidfile_lock(path);
250 	return pid == 0 ? 0 : -1;
251 }
252