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 50static pid_t pidfile_pid; 51static char pidfile_path[PATH_MAX]; 52static int pidfile_fd = -1; 53 54/* Closes pidfile resources. 55 * 56 * Returns 0 on success, otherwise -1. */ 57static int 58pidfile_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. */ 75int 76pidfile_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 */ 104static void 105pidfile_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. */ 116static int 117pidfile_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. */ 134pid_t 135pidfile_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. */ 175pid_t 176pidfile_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. */ 244int 245pidfile(const char *path) 246{ 247 pid_t pid; 248 249 pid = pidfile_lock(path); 250 return pid == 0 ? 0 : -1; 251} 252