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 #ifndef HAVE_PLEDGE /* Avoid a pledge violating segfault. */ 90 (void)unlink(pidfile_path); 91 #endif 92 error = 0; 93 } 94 95 (void) pidfile_close(); 96 97 if (error != 0) { 98 errno = error; 99 return -1; 100 } 101 return 0; 102 } 103 104 /* atexit shim for pidfile_clean */ 105 static void 106 pidfile_cleanup(void) 107 { 108 109 pidfile_clean(); 110 } 111 112 /* Constructs a name for a pidfile in the default location (/var/run). 113 * If 'bname' is NULL, uses the name of the current program for the name of 114 * the pidfile. 115 * 116 * Returns 0 on success, otherwise -1. */ 117 static int 118 pidfile_varrun_path(char *path, size_t len, const char *bname) 119 { 120 121 if (bname == NULL) 122 bname = PACKAGE; 123 124 /* _PATH_VARRUN includes trailing / */ 125 if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len) 126 { 127 errno = ENAMETOOLONG; 128 return -1; 129 } 130 return 0; 131 } 132 133 /* Returns the process ID inside path on success, otherwise -1. 134 * If no path is given, use the last pidfile path, othewise the default one. */ 135 pid_t 136 pidfile_read(const char *path) 137 { 138 char dpath[PATH_MAX], buf[16], *eptr; 139 int fd, error; 140 ssize_t n; 141 pid_t pid; 142 143 if (path == NULL && pidfile_path[0] != '\0') 144 path = pidfile_path; 145 if (path == NULL || strchr(path, '/') == NULL) { 146 if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1) 147 return -1; 148 path = dpath; 149 } 150 151 if ((fd = open(path, O_RDONLY | O_NONBLOCK)) == -1) 152 return -1; 153 n = read(fd, buf, sizeof(buf) - 1); 154 error = errno; 155 (void) close(fd); 156 if (n == -1) { 157 errno = error; 158 return -1; 159 } 160 buf[n] = '\0'; 161 pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error); 162 if (error && !(error == ENOTSUP && *eptr == '\n')) { 163 errno = error; 164 return -1; 165 } 166 return pid; 167 } 168 169 /* Locks the pidfile specified by path and writes the process pid to it. 170 * The new pidfile is "registered" in the global variables pidfile_fd, 171 * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3) 172 * can check if we are recreating the same file or a new one. 173 * 174 * Returns 0 on success, otherwise the pid of the process who owns the 175 * lock if it can be read, otherwise -1. */ 176 pid_t 177 pidfile_lock(const char *path) 178 { 179 char dpath[PATH_MAX]; 180 static bool registered_atexit = false; 181 182 /* Register for cleanup with atexit. */ 183 if (!registered_atexit) { 184 if (atexit(pidfile_cleanup) == -1) 185 return -1; 186 registered_atexit = true; 187 } 188 189 if (path == NULL || strchr(path, '/') == NULL) { 190 if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1) 191 return -1; 192 path = dpath; 193 } 194 195 /* If path has changed (no good reason), clean up the old pidfile. */ 196 if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0) 197 pidfile_clean(); 198 199 if (pidfile_fd == -1) { 200 int fd, opts; 201 202 opts = O_WRONLY | O_CREAT | O_NONBLOCK; 203 #ifdef O_CLOEXEC 204 opts |= O_CLOEXEC; 205 #endif 206 #ifdef O_EXLOCK 207 opts |= O_EXLOCK; 208 #endif 209 if ((fd = open(path, opts, 0644)) == -1) 210 goto return_pid; 211 #ifndef O_CLOEXEC 212 if ((opts = fcntl(fd, F_GETFD)) == -1 || 213 fcntl(fd, F_SETFL, opts | FD_CLOEXEC) == -1) 214 { 215 int error = errno; 216 217 (void) close(fd); 218 errno = error; 219 return -1; 220 } 221 #endif 222 #ifndef O_EXLOCK 223 if (flock(fd, LOCK_EX | LOCK_NB) == -1) { 224 int error = errno; 225 226 (void) close(fd); 227 if (error != EAGAIN) { 228 errno = error; 229 return -1; 230 } 231 fd = -1; 232 } 233 #endif 234 235 return_pid: 236 if (fd == -1) { 237 pid_t pid; 238 239 if (errno == EAGAIN) { 240 /* The pidfile is locked, return the process ID 241 * it contains. 242 * If sucessful, set errno to EEXIST. */ 243 if ((pid = pidfile_read(path)) != -1) 244 errno = EEXIST; 245 } else 246 pid = -1; 247 248 return pid; 249 } 250 pidfile_fd = fd; 251 strlcpy(pidfile_path, path, sizeof(pidfile_path)); 252 } 253 254 pidfile_pid = getpid(); 255 256 /* Truncate the file, as we could be re-writing it. 257 * Then write the process ID. */ 258 if (ftruncate(pidfile_fd, 0) == -1 || 259 lseek(pidfile_fd, 0, SEEK_SET) == -1 || 260 dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1) 261 { 262 int error = errno; 263 264 pidfile_cleanup(); 265 errno = error; 266 return -1; 267 } 268 269 /* Hold the fd open to persist the lock. */ 270 return 0; 271 } 272