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