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