xref: /dragonfly/contrib/dhcpcd/compat/pidfile.c (revision d8d5b238)
1 /*	$NetBSD: pidfile.c,v 1.14 2016/04/12 20:40:43 roy 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/param.h>
33 
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <inttypes.h>
37 #include <limits.h>
38 #include <paths.h>
39 #include <stdbool.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include <sys/file.h>	/* for flock(2) */
46 #include "config.h"
47 #include "defs.h"
48 
49 static pid_t pidfile_pid;
50 static char pidfile_path[PATH_MAX];
51 static int pidfile_fd = -1;
52 
53 /* Closes pidfile resources.
54  *
55  * Returns 0 on success, otherwise -1. */
56 static int
57 pidfile_close(void)
58 {
59 	int error;
60 
61 	pidfile_pid = 0;
62 	error = close(pidfile_fd);
63 	pidfile_fd = -1;
64 	pidfile_path[0] = '\0';
65 	return error;
66 }
67 
68 /* Truncate, close and unlink an existent pidfile,
69  * if and only if it was created by this process.
70  * The pidfile is truncated because we may have dropped permissions
71  * or entered a chroot and thus unable to unlink it.
72  *
73  * Returns 0 on truncation success, otherwise -1. */
74 int
75 pidfile_clean(void)
76 {
77 	int error;
78 
79 	if (pidfile_fd == -1) {
80 		errno = EBADF;
81 		return -1;
82 	}
83 
84 	if (pidfile_pid != getpid())
85 		error = EPERM;
86 	else if (ftruncate(pidfile_fd, 0) == -1)
87 		error = errno;
88 	else {
89 		(void) unlink(pidfile_path);
90 		error = 0;
91 	}
92 
93 	(void) pidfile_close();
94 
95 	if (error != 0) {
96 		errno = error;
97 		return -1;
98 	}
99 	return 0;
100 }
101 
102 /* atexit shim for pidfile_clean */
103 static void
104 pidfile_cleanup(void)
105 {
106 
107 	pidfile_clean();
108 }
109 
110 /* Constructs a name for a pidfile in the default location (/var/run).
111  * If 'bname' is NULL, uses the name of the current program for the name of
112  * the pidfile.
113  *
114  * Returns 0 on success, otherwise -1. */
115 static int
116 pidfile_varrun_path(char *path, size_t len, const char *bname)
117 {
118 
119 	if (bname == NULL)
120 		bname = PACKAGE;
121 
122 	/* _PATH_VARRUN includes trailing / */
123 	if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len)
124 	{
125 		errno = ENAMETOOLONG;
126 		return -1;
127 	}
128 	return 0;
129 }
130 
131 /* Returns the process ID inside path on success, otherwise -1.
132  * If no path is given, use the last pidfile path, othewise the default one. */
133 pid_t
134 pidfile_read(const char *path)
135 {
136 	char dpath[PATH_MAX], buf[16], *eptr;
137 	int fd, error;
138 	ssize_t n;
139 	pid_t pid;
140 
141 	if (path == NULL && pidfile_path[0] != '\0')
142 		path = pidfile_path;
143 	if (path == NULL || strchr(path, '/') == NULL) {
144 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
145 			return -1;
146 		path = dpath;
147 	}
148 
149 	if ((fd = open(path, O_RDONLY | O_NONBLOCK)) == -1)
150 		return  -1;
151 	n = read(fd, buf, sizeof(buf) - 1);
152 	error = errno;
153 	(void) close(fd);
154 	if (n == -1) {
155 		errno = error;
156 		return -1;
157 	}
158 	buf[n] = '\0';
159 	pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);
160 	if (error && !(error == ENOTSUP && *eptr == '\n')) {
161 		errno = error;
162 		return -1;
163 	}
164 	return pid;
165 }
166 
167 /* Locks the pidfile specified by path and writes the process pid to it.
168  * The new pidfile is "registered" in the global variables pidfile_fd,
169  * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3)
170  * can check if we are recreating the same file or a new one.
171  *
172  * Returns 0 on success, otherwise the pid of the process who owns the
173  * lock if it can be read, otherwise -1. */
174 pid_t
175 pidfile_lock(const char *path)
176 {
177 	char dpath[PATH_MAX];
178 	static bool registered_atexit = false;
179 
180 	/* Register for cleanup with atexit. */
181 	if (!registered_atexit) {
182 		if (atexit(pidfile_cleanup) == -1)
183 			return -1;
184 		registered_atexit = true;
185 	}
186 
187 	if (path == NULL || strchr(path, '/') == NULL) {
188 		if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
189 			return -1;
190 		path = dpath;
191 	}
192 
193 	/* If path has changed (no good reason), clean up the old pidfile. */
194 	if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0)
195 		pidfile_clean();
196 
197 	if (pidfile_fd == -1) {
198 		int fd, opts;
199 
200 		opts = O_WRONLY | O_CREAT | O_NONBLOCK;
201 #ifdef O_CLOEXEC
202 		opts |= O_CLOEXEC;
203 #endif
204 #ifdef O_EXLOCK
205 		opts |=	O_EXLOCK;
206 #endif
207 		if ((fd = open(path, opts, 0644)) == -1)
208 			goto return_pid;
209 #ifndef O_CLOEXEC
210 		if ((opts = fcntl(fd, F_GETFD)) == -1 ||
211 		    fctnl(fd, F_SETFL, opts | FD_CLOEXEC) == -1)
212 		{
213 			int error = errno;
214 
215 			(void) close(fd);
216 			errno = error;
217 			return -1;
218 		}
219 #endif
220 #ifndef O_EXLOCK
221 		if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
222 			int error = errno;
223 
224 			(void) close(fd);
225 			if (error != EAGAIN) {
226 				errno = error;
227 				return -1;
228 			}
229 			fd = -1;
230 		}
231 #endif
232 
233 return_pid:
234 		if (fd == -1) {
235 			pid_t pid;
236 
237 			if (errno == EAGAIN) {
238 				/* The pidfile is locked, return the process ID
239 				 * it contains.
240 				 * If sucessful, set errno to EEXIST. */
241 				if ((pid = pidfile_read(path)) != -1)
242 					errno = EEXIST;
243 			} else
244 				pid = -1;
245 
246 			return pid;
247 		}
248 		pidfile_fd = fd;
249 		strlcpy(pidfile_path, path, sizeof(pidfile_path));
250 	}
251 
252 	pidfile_pid = getpid();
253 
254 	/* Truncate the file, as we could be re-writing it.
255 	 * Then write the process ID. */
256 	if (ftruncate(pidfile_fd, 0) == -1 ||
257 	    lseek(pidfile_fd, 0, SEEK_SET) == -1 ||
258 	    dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1)
259 	{
260 		int error = errno;
261 
262 		pidfile_cleanup();
263 		errno = error;
264 		return -1;
265 	}
266 
267 	/* Hold the fd open to persist the lock. */
268 	return 0;
269 }
270