1/*** 2 This file is part of libdaemon. 3 4 Copyright 2003-2008 Lennart Poettering 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 SOFTWARE. 23 24***/ 25 26#ifdef HAVE_CONFIG_H 27#include <config.h> 28#endif 29 30#include <limits.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <signal.h> 34#include <string.h> 35#include <unistd.h> 36#include <errno.h> 37#include <sys/types.h> 38#include <sys/stat.h> 39#include <time.h> 40#include <sys/select.h> 41#include <fcntl.h> 42#include <stddef.h> 43#include <sys/time.h> 44 45#include "dpid.h" 46#include "dlog.h" 47 48#ifndef ETIME 49#define ETIME ETIMEDOUT /* For FreeBSD */ 50#endif 51 52#ifndef PATH_MAX 53#define PATH_MAX 512 54#endif 55 56#define VARRUN LOCALSTATEDIR "/run" 57 58const char *daemon_pid_file_ident = NULL; 59daemon_pid_file_proc_t daemon_pid_file_proc = daemon_pid_file_proc_default; 60 61const char *daemon_pid_file_proc_default(void) { 62#ifdef HAVE_ASPRINTF 63 static char *fn = NULL; 64 free(fn); 65 asprintf(&fn, "%s/%s.pid", VARRUN, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); 66#else 67 static char fn[PATH_MAX]; 68 snprintf(fn, sizeof(fn), "%s/%s.pid", VARRUN, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); 69#endif 70 71 return fn; 72} 73 74static int lock_file(int fd, int enable) { 75 struct flock f; 76 77 memset(&f, 0, sizeof(f)); 78 f.l_type = enable ? F_WRLCK : F_UNLCK; 79 f.l_whence = SEEK_SET; 80 f.l_start = 0; 81 f.l_len = 0; 82 83 if (fcntl(fd, F_SETLKW, &f) < 0) { 84 85 if (enable && errno == EBADF) { 86 f.l_type = F_RDLCK; 87 88 if (fcntl(fd, F_SETLKW, &f) >= 0) 89 return 0; 90 } 91 92 daemon_log(LOG_WARNING, "fcntl(F_SETLKW) failed: %s", strerror(errno)); 93 return -1; 94 } 95 96 return 0; 97} 98 99pid_t daemon_pid_file_is_running(void) { 100 const char *fn; 101 static char txt[256]; 102 int fd = -1, locked = -1; 103 pid_t ret = (pid_t) -1, pid; 104 ssize_t l; 105 long lpid; 106 char *e = NULL; 107 108 if (!(fn = daemon_pid_file_proc())) { 109 errno = EINVAL; 110 goto finish; 111 } 112 113 if ((fd = open(fn, O_RDWR, 0644)) < 0) { 114 if ((fd = open(fn, O_RDONLY, 0644)) < 0) { 115 if (errno != ENOENT) 116 daemon_log(LOG_WARNING, "Failed to open PID file: %s", strerror(errno)); 117 118 goto finish; 119 } 120 } 121 122 if ((locked = lock_file(fd, 1)) < 0) 123 goto finish; 124 125 if ((l = read(fd, txt, sizeof(txt)-1)) < 0) { 126 int saved_errno = errno; 127 daemon_log(LOG_WARNING, "read(): %s", strerror(errno)); 128 unlink(fn); 129 errno = saved_errno; 130 goto finish; 131 } 132 133 txt[l] = 0; 134 txt[strcspn(txt, "\r\n")] = 0; 135 136 errno = 0; 137 lpid = strtol(txt, &e, 10); 138 pid = (pid_t) lpid; 139 140 if (errno != 0 || !e || *e || (long) pid != lpid) { 141 daemon_log(LOG_WARNING, "PID file corrupt, removing. (%s)", fn); 142 unlink(fn); 143 errno = EINVAL; 144 goto finish; 145 } 146 147 if (kill(pid, 0) != 0 && errno != EPERM) { 148 int saved_errno = errno; 149 daemon_log(LOG_WARNING, "Process %lu died: %s; trying to remove PID file. (%s)", (unsigned long) pid, strerror(errno), fn); 150 unlink(fn); 151 errno = saved_errno; 152 goto finish; 153 } 154 155 ret = pid; 156 157finish: 158 159 if (fd >= 0) { 160 int saved_errno = errno; 161 if (locked >= 0) 162 lock_file(fd, 0); 163 close(fd); 164 errno = saved_errno; 165 } 166 167 return ret; 168} 169 170int daemon_pid_file_kill(int s) { 171 pid_t pid; 172 173 if ((pid = daemon_pid_file_is_running()) == (pid_t) -1) 174 return -1; 175 176 if (kill(pid, s) < 0) 177 return -1; 178 179 return 0; 180} 181 182int daemon_pid_file_kill_wait(int s, int m) { 183 pid_t pid; 184 time_t t; 185 186 if ((pid = daemon_pid_file_is_running()) < 0) 187 return -1; 188 189 if (kill(pid, s) < 0) 190 return -1; 191 192 t = time(NULL) + m; 193 194 for (;;) { 195 int r; 196 struct timeval tv = { 0, 100000 }; 197 198 if (time(NULL) > t) { 199 errno = ETIME; 200 return -1; 201 } 202 203 if ((r = kill(pid, 0)) < 0 && errno != ESRCH) 204 return -1; 205 206 if (r) 207 return 0; 208 209 if (select(0, NULL, NULL, NULL, &tv) < 0) 210 return -1; 211 } 212} 213 214int daemon_pid_file_create(void) { 215 const char *fn; 216 int fd = -1; 217 int ret = -1; 218 int locked = -1; 219 char t[64]; 220 ssize_t l; 221 mode_t u; 222 223 u = umask(022); 224 225 if (!(fn = daemon_pid_file_proc())) { 226 errno = EINVAL; 227 goto finish; 228 } 229 230 if ((fd = open(fn, O_CREAT|O_RDWR|O_EXCL, 0644)) < 0) { 231 daemon_log(LOG_ERR, "open(%s): %s", fn, strerror(errno)); 232 goto finish; 233 } 234 235 if ((locked = lock_file(fd, 1)) < 0) { 236 int saved_errno = errno; 237 unlink(fn); 238 errno = saved_errno; 239 goto finish; 240 } 241 242 snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid()); 243 244 l = strlen(t); 245 if (write(fd, t, l) != l) { 246 int saved_errno = errno; 247 daemon_log(LOG_WARNING, "write(): %s", strerror(errno)); 248 unlink(fn); 249 errno = saved_errno; 250 goto finish; 251 } 252 253 ret = 0; 254 255finish: 256 257 if (fd >= 0) { 258 int saved_errno = errno; 259 260 if (locked >= 0) 261 lock_file(fd, 0); 262 263 close(fd); 264 errno = saved_errno; 265 } 266 267 umask(u); 268 269 return ret; 270} 271 272int daemon_pid_file_remove(void) { 273 const char *fn; 274 275 if (!(fn = daemon_pid_file_proc())) { 276 errno = EINVAL; 277 return -1; 278 } 279 280 return unlink(fn); 281} 282