"Fossies" - the Fresh Open Source Software Archive

Member "dhcpcd-9.4.1/compat/pidfile.c" (22 Oct 2021, 6699 Bytes) of package /linux/misc/dhcpcd-9.4.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 #ifndef HAVE_PLEDGE /* Avoid a pledge violating segfault. */
   90         (void)unlink(pidfile_path);
   91 #endif
   92         error = 0;
   93     }
   94 
   95     (void) pidfile_close();
   96 
   97     if (error != 0) {
   98         errno = error;
   99         return -1;
  100     }
  101     return 0;
  102 }
  103 
  104 /* atexit shim for pidfile_clean */
  105 static void
  106 pidfile_cleanup(void)
  107 {
  108 
  109     pidfile_clean();
  110 }
  111 
  112 /* Constructs a name for a pidfile in the default location (/var/run).
  113  * If 'bname' is NULL, uses the name of the current program for the name of
  114  * the pidfile.
  115  *
  116  * Returns 0 on success, otherwise -1. */
  117 static int
  118 pidfile_varrun_path(char *path, size_t len, const char *bname)
  119 {
  120 
  121     if (bname == NULL)
  122         bname = PACKAGE;
  123 
  124     /* _PATH_VARRUN includes trailing / */
  125     if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len)
  126     {
  127         errno = ENAMETOOLONG;
  128         return -1;
  129     }
  130     return 0;
  131 }
  132 
  133 /* Returns the process ID inside path on success, otherwise -1.
  134  * If no path is given, use the last pidfile path, othewise the default one. */
  135 pid_t
  136 pidfile_read(const char *path)
  137 {
  138     char dpath[PATH_MAX], buf[16], *eptr;
  139     int fd, error;
  140     ssize_t n;
  141     pid_t pid;
  142 
  143     if (path == NULL && pidfile_path[0] != '\0')
  144         path = pidfile_path;
  145     if (path == NULL || strchr(path, '/') == NULL) {
  146         if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
  147             return -1;
  148         path = dpath;
  149     }
  150 
  151     if ((fd = open(path, O_RDONLY | O_NONBLOCK)) == -1)
  152         return  -1;
  153     n = read(fd, buf, sizeof(buf) - 1);
  154     error = errno;
  155     (void) close(fd);
  156     if (n == -1) {
  157         errno = error;
  158         return -1;
  159     }
  160     buf[n] = '\0';
  161     pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);
  162     if (error && !(error == ENOTSUP && *eptr == '\n')) {
  163         errno = error;
  164         return -1;
  165     }
  166     return pid;
  167 }
  168 
  169 /* Locks the pidfile specified by path and writes the process pid to it.
  170  * The new pidfile is "registered" in the global variables pidfile_fd,
  171  * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3)
  172  * can check if we are recreating the same file or a new one.
  173  *
  174  * Returns 0 on success, otherwise the pid of the process who owns the
  175  * lock if it can be read, otherwise -1. */
  176 pid_t
  177 pidfile_lock(const char *path)
  178 {
  179     char dpath[PATH_MAX];
  180     static bool registered_atexit = false;
  181 
  182     /* Register for cleanup with atexit. */
  183     if (!registered_atexit) {
  184         if (atexit(pidfile_cleanup) == -1)
  185             return -1;
  186         registered_atexit = true;
  187     }
  188 
  189     if (path == NULL || strchr(path, '/') == NULL) {
  190         if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)
  191             return -1;
  192         path = dpath;
  193     }
  194 
  195     /* If path has changed (no good reason), clean up the old pidfile. */
  196     if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0)
  197         pidfile_clean();
  198 
  199     if (pidfile_fd == -1) {
  200         int fd, opts;
  201 
  202         opts = O_WRONLY | O_CREAT | O_NONBLOCK;
  203 #ifdef O_CLOEXEC
  204         opts |= O_CLOEXEC;
  205 #endif
  206 #ifdef O_EXLOCK
  207         opts |= O_EXLOCK;
  208 #endif
  209         if ((fd = open(path, opts, 0644)) == -1)
  210             goto return_pid;
  211 #ifndef O_CLOEXEC
  212         if ((opts = fcntl(fd, F_GETFD)) == -1 ||
  213             fcntl(fd, F_SETFL, opts | FD_CLOEXEC) == -1)
  214         {
  215             int error = errno;
  216 
  217             (void) close(fd);
  218             errno = error;
  219             return -1;
  220         }
  221 #endif
  222 #ifndef O_EXLOCK
  223         if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
  224             int error = errno;
  225 
  226             (void) close(fd);
  227             if (error != EAGAIN) {
  228                 errno = error;
  229                 return -1;
  230             }
  231             fd = -1;
  232         }
  233 #endif
  234 
  235 return_pid:
  236         if (fd == -1) {
  237             pid_t pid;
  238 
  239             if (errno == EAGAIN) {
  240                 /* The pidfile is locked, return the process ID
  241                  * it contains.
  242                  * If sucessful, set errno to EEXIST. */
  243                 if ((pid = pidfile_read(path)) != -1)
  244                     errno = EEXIST;
  245             } else
  246                 pid = -1;
  247 
  248             return pid;
  249         }
  250         pidfile_fd = fd;
  251         strlcpy(pidfile_path, path, sizeof(pidfile_path));
  252     }
  253 
  254     pidfile_pid = getpid();
  255 
  256     /* Truncate the file, as we could be re-writing it.
  257      * Then write the process ID. */
  258     if (ftruncate(pidfile_fd, 0) == -1 ||
  259         lseek(pidfile_fd, 0, SEEK_SET) == -1 ||
  260         dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1)
  261     {
  262         int error = errno;
  263 
  264         pidfile_cleanup();
  265         errno = error;
  266         return -1;
  267     }
  268 
  269     /* Hold the fd open to persist the lock. */
  270     return 0;
  271 }