1*aa772005SRobert Watson /*- 2*aa772005SRobert Watson * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3*aa772005SRobert Watson * All rights reserved. 4*aa772005SRobert Watson * 5*aa772005SRobert Watson * Redistribution and use in source and binary forms, with or without 6*aa772005SRobert Watson * modification, are permitted provided that the following conditions 7*aa772005SRobert Watson * are met: 8*aa772005SRobert Watson * 1. Redistributions of source code must retain the above copyright 9*aa772005SRobert Watson * notice, this list of conditions and the following disclaimer. 10*aa772005SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 11*aa772005SRobert Watson * notice, this list of conditions and the following disclaimer in the 12*aa772005SRobert Watson * documentation and/or other materials provided with the distribution. 13*aa772005SRobert Watson * 14*aa772005SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15*aa772005SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*aa772005SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*aa772005SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18*aa772005SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*aa772005SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*aa772005SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*aa772005SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*aa772005SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*aa772005SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*aa772005SRobert Watson * SUCH DAMAGE. 25*aa772005SRobert Watson * 26*aa772005SRobert Watson * Derived from FreeBSD head/lib/libutil/pidfile.c r231938 27*aa772005SRobert Watson * $P4: //depot/projects/trustedbsd/openbsm/compat/pidfile.h#1 $ 28*aa772005SRobert Watson */ 29*aa772005SRobert Watson 30*aa772005SRobert Watson #include <sys/param.h> 31*aa772005SRobert Watson #include <sys/file.h> 32*aa772005SRobert Watson #include <sys/stat.h> 33*aa772005SRobert Watson 34*aa772005SRobert Watson #include <stdio.h> 35*aa772005SRobert Watson #include <stdlib.h> 36*aa772005SRobert Watson #include <unistd.h> 37*aa772005SRobert Watson #include <fcntl.h> 38*aa772005SRobert Watson #include <string.h> 39*aa772005SRobert Watson #include <time.h> 40*aa772005SRobert Watson #include <err.h> 41*aa772005SRobert Watson #include <errno.h> 42*aa772005SRobert Watson 43*aa772005SRobert Watson #include "flopen.h" 44*aa772005SRobert Watson 45*aa772005SRobert Watson struct pidfh { 46*aa772005SRobert Watson int pf_fd; 47*aa772005SRobert Watson char pf_path[MAXPATHLEN + 1]; 48*aa772005SRobert Watson dev_t pf_dev; 49*aa772005SRobert Watson ino_t pf_ino; 50*aa772005SRobert Watson }; 51*aa772005SRobert Watson 52*aa772005SRobert Watson static int _pidfile_remove(struct pidfh *pfh, int freeit); 53*aa772005SRobert Watson 54*aa772005SRobert Watson static int 55*aa772005SRobert Watson pidfile_verify(const struct pidfh *pfh) 56*aa772005SRobert Watson { 57*aa772005SRobert Watson struct stat sb; 58*aa772005SRobert Watson 59*aa772005SRobert Watson if (pfh == NULL || pfh->pf_fd == -1) 60*aa772005SRobert Watson return (EINVAL); 61*aa772005SRobert Watson /* 62*aa772005SRobert Watson * Check remembered descriptor. 63*aa772005SRobert Watson */ 64*aa772005SRobert Watson if (fstat(pfh->pf_fd, &sb) == -1) 65*aa772005SRobert Watson return (errno); 66*aa772005SRobert Watson if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino) 67*aa772005SRobert Watson return (EINVAL); 68*aa772005SRobert Watson return (0); 69*aa772005SRobert Watson } 70*aa772005SRobert Watson 71*aa772005SRobert Watson static int 72*aa772005SRobert Watson pidfile_read(const char *path, pid_t *pidptr) 73*aa772005SRobert Watson { 74*aa772005SRobert Watson char buf[16], *endptr; 75*aa772005SRobert Watson int error, fd, i; 76*aa772005SRobert Watson 77*aa772005SRobert Watson fd = open(path, O_RDONLY); 78*aa772005SRobert Watson if (fd == -1) 79*aa772005SRobert Watson return (errno); 80*aa772005SRobert Watson 81*aa772005SRobert Watson i = read(fd, buf, sizeof(buf) - 1); 82*aa772005SRobert Watson error = errno; /* Remember errno in case close() wants to change it. */ 83*aa772005SRobert Watson close(fd); 84*aa772005SRobert Watson if (i == -1) 85*aa772005SRobert Watson return (error); 86*aa772005SRobert Watson else if (i == 0) 87*aa772005SRobert Watson return (EAGAIN); 88*aa772005SRobert Watson buf[i] = '\0'; 89*aa772005SRobert Watson 90*aa772005SRobert Watson *pidptr = strtol(buf, &endptr, 10); 91*aa772005SRobert Watson if (endptr != &buf[i]) 92*aa772005SRobert Watson return (EINVAL); 93*aa772005SRobert Watson 94*aa772005SRobert Watson return (0); 95*aa772005SRobert Watson } 96*aa772005SRobert Watson 97*aa772005SRobert Watson static struct pidfh * 98*aa772005SRobert Watson pidfile_open(const char *path, mode_t mode, pid_t *pidptr) 99*aa772005SRobert Watson { 100*aa772005SRobert Watson struct pidfh *pfh; 101*aa772005SRobert Watson struct stat sb; 102*aa772005SRobert Watson int error, fd, len, count; 103*aa772005SRobert Watson struct timespec rqtp; 104*aa772005SRobert Watson 105*aa772005SRobert Watson if (pidptr != NULL) 106*aa772005SRobert Watson *pidptr = -1; 107*aa772005SRobert Watson 108*aa772005SRobert Watson if (path == NULL) 109*aa772005SRobert Watson return (NULL); 110*aa772005SRobert Watson 111*aa772005SRobert Watson pfh = malloc(sizeof(*pfh)); 112*aa772005SRobert Watson if (pfh == NULL) 113*aa772005SRobert Watson return (NULL); 114*aa772005SRobert Watson 115*aa772005SRobert Watson len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), 116*aa772005SRobert Watson "%s", path); 117*aa772005SRobert Watson if (len >= (int)sizeof(pfh->pf_path)) { 118*aa772005SRobert Watson free(pfh); 119*aa772005SRobert Watson errno = ENAMETOOLONG; 120*aa772005SRobert Watson return (NULL); 121*aa772005SRobert Watson } 122*aa772005SRobert Watson 123*aa772005SRobert Watson /* 124*aa772005SRobert Watson * Open the PID file and obtain exclusive lock. 125*aa772005SRobert Watson * We truncate PID file here only to remove old PID immediatelly, 126*aa772005SRobert Watson * PID file will be truncated again in pidfile_write(), so 127*aa772005SRobert Watson * pidfile_write() can be called multiple times. 128*aa772005SRobert Watson */ 129*aa772005SRobert Watson fd = flopen(pfh->pf_path, 130*aa772005SRobert Watson #ifdef O_CLOEXEC 131*aa772005SRobert Watson O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK | O_CLOEXEC, mode); 132*aa772005SRobert Watson #else 133*aa772005SRobert Watson O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode); 134*aa772005SRobert Watson #endif 135*aa772005SRobert Watson if (fd == -1) { 136*aa772005SRobert Watson if (errno == EWOULDBLOCK && pidptr != NULL) { 137*aa772005SRobert Watson count = 20; 138*aa772005SRobert Watson rqtp.tv_sec = 0; 139*aa772005SRobert Watson rqtp.tv_nsec = 5000000; 140*aa772005SRobert Watson for (;;) { 141*aa772005SRobert Watson errno = pidfile_read(pfh->pf_path, pidptr); 142*aa772005SRobert Watson if (errno != EAGAIN || --count == 0) 143*aa772005SRobert Watson break; 144*aa772005SRobert Watson nanosleep(&rqtp, 0); 145*aa772005SRobert Watson } 146*aa772005SRobert Watson if (errno == EAGAIN) 147*aa772005SRobert Watson *pidptr = -1; 148*aa772005SRobert Watson if (errno == 0 || errno == EAGAIN) 149*aa772005SRobert Watson errno = EEXIST; 150*aa772005SRobert Watson } 151*aa772005SRobert Watson free(pfh); 152*aa772005SRobert Watson return (NULL); 153*aa772005SRobert Watson } 154*aa772005SRobert Watson 155*aa772005SRobert Watson #ifndef O_CLOEXEC 156*aa772005SRobert Watson if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { 157*aa772005SRobert Watson error = errno; 158*aa772005SRobert Watson unlink(pfh->pf_path); 159*aa772005SRobert Watson close(fd); 160*aa772005SRobert Watson free(pfh); 161*aa772005SRobert Watson errno = error; 162*aa772005SRobert Watson return (NULL); 163*aa772005SRobert Watson } 164*aa772005SRobert Watson #endif 165*aa772005SRobert Watson 166*aa772005SRobert Watson /* 167*aa772005SRobert Watson * Remember file information, so in pidfile_write() we are sure we write 168*aa772005SRobert Watson * to the proper descriptor. 169*aa772005SRobert Watson */ 170*aa772005SRobert Watson if (fstat(fd, &sb) == -1) { 171*aa772005SRobert Watson error = errno; 172*aa772005SRobert Watson unlink(pfh->pf_path); 173*aa772005SRobert Watson close(fd); 174*aa772005SRobert Watson free(pfh); 175*aa772005SRobert Watson errno = error; 176*aa772005SRobert Watson return (NULL); 177*aa772005SRobert Watson } 178*aa772005SRobert Watson 179*aa772005SRobert Watson pfh->pf_fd = fd; 180*aa772005SRobert Watson pfh->pf_dev = sb.st_dev; 181*aa772005SRobert Watson pfh->pf_ino = sb.st_ino; 182*aa772005SRobert Watson 183*aa772005SRobert Watson return (pfh); 184*aa772005SRobert Watson } 185*aa772005SRobert Watson 186*aa772005SRobert Watson static int 187*aa772005SRobert Watson pidfile_write(struct pidfh *pfh) 188*aa772005SRobert Watson { 189*aa772005SRobert Watson char pidstr[16]; 190*aa772005SRobert Watson int error, fd; 191*aa772005SRobert Watson 192*aa772005SRobert Watson /* 193*aa772005SRobert Watson * Check remembered descriptor, so we don't overwrite some other 194*aa772005SRobert Watson * file if pidfile was closed and descriptor reused. 195*aa772005SRobert Watson */ 196*aa772005SRobert Watson errno = pidfile_verify(pfh); 197*aa772005SRobert Watson if (errno != 0) { 198*aa772005SRobert Watson /* 199*aa772005SRobert Watson * Don't close descriptor, because we are not sure if it's ours. 200*aa772005SRobert Watson */ 201*aa772005SRobert Watson return (-1); 202*aa772005SRobert Watson } 203*aa772005SRobert Watson fd = pfh->pf_fd; 204*aa772005SRobert Watson 205*aa772005SRobert Watson /* 206*aa772005SRobert Watson * Truncate PID file, so multiple calls of pidfile_write() are allowed. 207*aa772005SRobert Watson */ 208*aa772005SRobert Watson if (ftruncate(fd, 0) == -1) { 209*aa772005SRobert Watson error = errno; 210*aa772005SRobert Watson _pidfile_remove(pfh, 0); 211*aa772005SRobert Watson errno = error; 212*aa772005SRobert Watson return (-1); 213*aa772005SRobert Watson } 214*aa772005SRobert Watson 215*aa772005SRobert Watson snprintf(pidstr, sizeof(pidstr), "%u", getpid()); 216*aa772005SRobert Watson if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { 217*aa772005SRobert Watson error = errno; 218*aa772005SRobert Watson _pidfile_remove(pfh, 0); 219*aa772005SRobert Watson errno = error; 220*aa772005SRobert Watson return (-1); 221*aa772005SRobert Watson } 222*aa772005SRobert Watson 223*aa772005SRobert Watson return (0); 224*aa772005SRobert Watson } 225*aa772005SRobert Watson 226*aa772005SRobert Watson static int 227*aa772005SRobert Watson pidfile_close(struct pidfh *pfh) 228*aa772005SRobert Watson { 229*aa772005SRobert Watson int error; 230*aa772005SRobert Watson 231*aa772005SRobert Watson error = pidfile_verify(pfh); 232*aa772005SRobert Watson if (error != 0) { 233*aa772005SRobert Watson errno = error; 234*aa772005SRobert Watson return (-1); 235*aa772005SRobert Watson } 236*aa772005SRobert Watson 237*aa772005SRobert Watson if (close(pfh->pf_fd) == -1) 238*aa772005SRobert Watson error = errno; 239*aa772005SRobert Watson free(pfh); 240*aa772005SRobert Watson if (error != 0) { 241*aa772005SRobert Watson errno = error; 242*aa772005SRobert Watson return (-1); 243*aa772005SRobert Watson } 244*aa772005SRobert Watson return (0); 245*aa772005SRobert Watson } 246*aa772005SRobert Watson 247*aa772005SRobert Watson static int 248*aa772005SRobert Watson _pidfile_remove(struct pidfh *pfh, int freeit) 249*aa772005SRobert Watson { 250*aa772005SRobert Watson int error; 251*aa772005SRobert Watson 252*aa772005SRobert Watson error = pidfile_verify(pfh); 253*aa772005SRobert Watson if (error != 0) { 254*aa772005SRobert Watson errno = error; 255*aa772005SRobert Watson return (-1); 256*aa772005SRobert Watson } 257*aa772005SRobert Watson 258*aa772005SRobert Watson if (unlink(pfh->pf_path) == -1) 259*aa772005SRobert Watson error = errno; 260*aa772005SRobert Watson if (close(pfh->pf_fd) == -1) { 261*aa772005SRobert Watson if (error == 0) 262*aa772005SRobert Watson error = errno; 263*aa772005SRobert Watson } 264*aa772005SRobert Watson if (freeit) 265*aa772005SRobert Watson free(pfh); 266*aa772005SRobert Watson else 267*aa772005SRobert Watson pfh->pf_fd = -1; 268*aa772005SRobert Watson if (error != 0) { 269*aa772005SRobert Watson errno = error; 270*aa772005SRobert Watson return (-1); 271*aa772005SRobert Watson } 272*aa772005SRobert Watson return (0); 273*aa772005SRobert Watson } 274*aa772005SRobert Watson 275*aa772005SRobert Watson static int 276*aa772005SRobert Watson pidfile_remove(struct pidfh *pfh) 277*aa772005SRobert Watson { 278*aa772005SRobert Watson 279*aa772005SRobert Watson return (_pidfile_remove(pfh, 1)); 280*aa772005SRobert Watson } 281*aa772005SRobert Watson 282*aa772005SRobert Watson #if 0 283*aa772005SRobert Watson static int 284*aa772005SRobert Watson pidfile_fileno(const struct pidfh *pfh) 285*aa772005SRobert Watson { 286*aa772005SRobert Watson 287*aa772005SRobert Watson if (pfh == NULL || pfh->pf_fd == -1) { 288*aa772005SRobert Watson errno = EINVAL; 289*aa772005SRobert Watson return (-1); 290*aa772005SRobert Watson } 291*aa772005SRobert Watson return (pfh->pf_fd); 292*aa772005SRobert Watson } 293*aa772005SRobert Watson #endif 294