From: Roy Marples Date: Sun, 10 Apr 2016 20:15:36 +0000 (+0000) Subject: Sync pidfile.c from NetBSD. X-Git-Tag: v6.10.3~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c18fd8ddbcfc194620172bec93d0182c96069fed;p=thirdparty%2Fdhcpcd.git Sync pidfile.c from NetBSD. --- diff --git a/compat/pidfile.c b/compat/pidfile.c index 8adc1a0f..b301a574 100644 --- a/compat/pidfile.c +++ b/compat/pidfile.c @@ -1,8 +1,12 @@ -/* - * pidfile_lock and pidfile_read - * Copyright (c) 2016 Roy Marples - * All rights reserved +/* $NetBSD: pidfile.c,v 1.12 2016/04/10 19:05:50 roy Exp $ */ +/*- + * Copyright (c) 1999, 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,25 +16,30 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: pidfile.c,v 1.11 2015/01/22 19:04:28 christos Exp $"); +#endif + #include +#include #include #include #include -#include #include #include #include @@ -38,48 +47,112 @@ #include #include -#include "pidfile.h" #include "../config.h" +#include "../defs.h" static pid_t pidfile_pid; -static char *pidfile_path; +static char pidfile_path[PATH_MAX]; static int pidfile_fd = -1; -/* Close and optionally remove an existent pidfile, - * if it was created by this process. +/* Closes pidfile resources. * * Returns 0 on success, otherwise -1. */ -static void -pidfile_cleanup(void) +static int +pidfile_close(void) { + int error; + + pidfile_pid = 0; + error = close(pidfile_fd); + pidfile_fd = -1; + pidfile_path[0] = '\0'; + return error; +} - if (pidfile_fd != -1) { - close(pidfile_fd); - pidfile_fd = -1; +/* Truncate, close and unlink an existent pidfile, + * if and only if it was created by this process. + * The pidfile is truncated because we may have dropped permissions + * or entered a chroot and thus unable to unlink it. + * + * Returns 0 on truncation success, otherwise -1. */ +int +pidfile_clean(void) +{ + int error; + + if (pidfile_fd == -1) { + errno = EBADF; + return -1; } - if (pidfile_path != NULL) { - if (pidfile_pid == getpid()) - unlink(pidfile_path); - free(pidfile_path); - pidfile_path = NULL; + + if (pidfile_pid != getpid()) + error = EPERM; + else if (ftruncate(pidfile_fd, 0) == -1) + error = errno; + else { + (void) unlink(pidfile_path); + error = 0; } - pidfile_pid = 0; + (void) pidfile_close(); + + if (error != 0) { + errno = error; + return -1; + } + return 0; +} + +/* atexit shim for pidfile_clean */ +static void +pidfile_cleanup(void) +{ + + pidfile_clean(); } -/* Returns the pid inside path on success, otherwise -1. - * If no path is give, use the last pidfile path, othewise the default one. */ +/* Constructs a name for a pidfile in the default location (/var/run). + * If 'bname' is NULL, uses the name of the current program for the name of + * the pidfile. + * + * Returns 0 on success, otherwise -1. */ +static int +pidfile_varrun_path(char *path, size_t len, const char *bname) +{ + + if (bname == NULL) + bname = PACKAGE; + + /* _PATH_VARRUN includes trailing / */ + if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len) + { + errno = ENAMETOOLONG; + return -1; + } + return 0; +} + +/* Returns the process ID inside path on success, otherwise -1. + * If no path is given, use the last pidfile path, othewise the default one. */ pid_t pidfile_read(const char *path) { - char buf[16], *eptr; - int fd, error, e; + char dpath[PATH_MAX], buf[16], *eptr; + int fd, error; ssize_t n; pid_t pid; - if ((fd = open(path, O_RDONLY)) == -1) - return -1; + if (path == NULL) { + if (pidfile_path[0] != '\0') + path = pidfile_path; + else if (pidfile_varrun_path(dpath, sizeof(dpath), NULL) == -1) + return -1; + else + path = dpath; + } + if ((fd = open(path, O_RDONLY | O_NONBLOCK)) == -1) + return -1; n = read(fd, buf, sizeof(buf) - 1); error = errno; (void) close(fd); @@ -88,87 +161,101 @@ pidfile_read(const char *path) return -1; } buf[n] = '\0'; - pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &e); - if (e && !(e == ENOTSUP && *eptr == '\n')) { - errno = e; + pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error); + if (error && !(error == ENOTSUP && *eptr == '\n')) { + errno = error; return -1; } return pid; } /* Locks the pidfile specified by path and writes the process pid to it. + * The new pidfile is "registered" in the global variables pidfile_fd, + * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3) + * can check if we are recreating the same file or a new one. * * Returns 0 on success, otherwise the pid of the process who owns the * lock if it can be read, otherwise -1. */ pid_t pidfile_lock(const char *path) { - static bool reg_atexit = false; + char dpath[PATH_MAX]; + static bool registered_atexit = false; - if (!reg_atexit) { + /* Register for cleanup with atexit. */ + if (!registered_atexit) { if (atexit(pidfile_cleanup) == -1) return -1; - reg_atexit = true; + registered_atexit = true; + } + + if (path == NULL || strchr(path, '/') == NULL) { + if (pidfile_varrun_path(dpath, sizeof(dpath), NULL) == -1) + return -1; + path = dpath; } /* If path has changed (no good reason), clean up the old pidfile. */ - if (pidfile_path != NULL && strcmp(pidfile_path, path) != 0) + if (strcmp(pidfile_path, path) != 0) pidfile_cleanup(); if (pidfile_fd == -1) { - pid_t pid = -1; - int opt; + int fd, opts; - opt = O_WRONLY | O_CREAT | O_NONBLOCK; + opts = O_WRONLY | O_CREAT | O_NONBLOCK; #ifdef O_CLOEXEC - opt |= O_CLOEXEC; + opts |= O_CLOEXEC; #endif #ifdef O_EXLOCK - opt |= O_EXLOCK; + opts |= O_EXLOCK; #endif - /* Grab an fd to ensure pidfile is created. */ - if ((pidfile_fd = open(path, opt, 0666)) == -1) { - if (errno == EAGAIN) - pid = pidfile_read(path); - } else if ((pidfile_path = strdup(path)) == NULL) { + if ((fd = open(path, opts, 0644)) == -1) + return -1; +#ifndef O_CLOEXEC + if ((opts = fcntl(fd, F_GETFD)) == -1 || + fctnl(fd, F_SETFL, opts | FD_CLOEXEC) == -1) + { int error = errno; - (void) close(pidfile_fd); - (void) unlink(path); + (void) close(fd); errno = error; + return -1; } - if (pidfile_fd == -1) - return pid; - +#endif #ifndef O_EXLOCK - if (flock(pidfile_fd, LOCK_EX | LOCK_NB) == -1) { + if (flock(fd, LOCK_EX | LOCK_NB) == -1) { int error = errno; - (void) close(pidfile_fd); - pidfile_fd = -1; - /* Don't unlink, other process has lock. */ - errno = error; - return errno == EAGAIN ? pidfile_read(path) : -1; + (void) close(fd); + if (error != EAGAIN) { + errno = error; + return -1; + } + fd = -1; } #endif -#ifndef O_CLOEXEC - if ((opt = fcntl(pidfile_fd, F_GETFD)) == -1 || - fcntl(pidfile_fd, F_SETFD, opt | FD_CLOEXEC) == -1) - { - int error = errno; + if (fd == -1) { + pid_t pid; - (void) close(fd); - (void) unlink(path); - errno = error; - return -1; + if (errno == EAGAIN) { + /* The pidfile is locked, return the process ID + * it contains. + * If sucessful, set errno to EEXIST. */ + if ((pid = pidfile_read(path)) != -1) + errno = EEXIST; + } else + pid = -1; + + return pid; } -#endif + pidfile_fd = fd; + strlcpy(pidfile_path, path, sizeof(pidfile_path)); } pidfile_pid = getpid(); - /* Truncate the file, as we could be re-writing it after forking. - * Then write the pidfile. */ + /* Truncate the file, as we could be re-writing it. + * Then write the process ID. */ if (ftruncate(pidfile_fd, 0) == -1 || lseek(pidfile_fd, 0, SEEK_SET) == -1 || dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1) diff --git a/compat/pidfile.h b/compat/pidfile.h index 7a4393e8..89ebad81 100644 --- a/compat/pidfile.h +++ b/compat/pidfile.h @@ -30,7 +30,8 @@ #include -pid_t pidfile_read(const char *); +int pidfile_clean(void); pid_t pidfile_lock(const char *); +pid_t pidfile_read(const char *); #endif