"Fossies" - the Fresh Open Source Software Archive

Member "leafnode-1.12.0/lockfile.c" (28 Dec 2021, 8794 Bytes) of package /linux/misc/leafnode-1.12.0.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. For more information about "lockfile.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.11.12_vs_1.12.0.

    1 /** \file lockfile.c 
    2  *  library module to safely create a lock file.
    3  *  \author Matthias Andree
    4  *  \date 2001 - 2006
    5  *
    6  *  Copyright (C) 2001 - 2006  Matthias Andree <matthias.andree@gmx.de>
    7  *
    8  *  This library is free software; you can redistribute it and/or modify
    9  *  it under the terms of the GNU Lesser General Public License as
   10  *  published by the Free Software Foundation; either version 2 of the
   11  *  License, or (at your option) any later version.
   12  *
   13  *  This library is distributed in the hope that it will be useful, but
   14  *  WITHOUT ANY WARRANTY; without even the implied warranty of
   15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   16  *  Lesser General Public License for more details.
   17  *
   18  *  You should have received a copy of the GNU Lesser General Public
   19  *  License along with this library; if not, write to the Free Software
   20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   21  *  USA 
   22  */
   23 
   24 #include "leafnode.h"
   25 #include "ln_log.h"
   26 #include "critmem.h"
   27 #include "validatefqdn.h"
   28 
   29 #include <stdlib.h>
   30 #include <string.h>
   31 #include <stdio.h>
   32 
   33 #include <sys/types.h>
   34 #include <sys/stat.h>
   35 #include <unistd.h>
   36 #include <fcntl.h>
   37 
   38 #ifdef WITH_DMALLOC
   39 #include <dmalloc.h>
   40 #endif
   41 
   42 #include <signal.h>     /* for kill */
   43 
   44 /** get hard link count of open file pointed to by filedes.
   45  * uses fstat(2)
   46  * \return 0 in case of trouble
   47  * (which is also logged), the count of hard links otherwise
   48  *
   49  * NOTE: you cannot distinguish "fstat error" from "deleted file".
   50  */
   51 static nlink_t
   52 fd_st_nlink(const int fd /** open file descriptor */ )
   53 {
   54     struct stat st;
   55 
   56     if (fstat(fd, &st)) {
   57     ln_log(LNLOG_SERR, LNLOG_CTOP, "Cannot fstat %d: %m", fd);
   58     return 0;
   59     }
   60 
   61     return st.st_nlink;
   62 }
   63 
   64 /** Check if the lock file given by "name" is stale and if so, erase it.
   65  * A lock is stale when the process to which it belongs, is dead.
   66  *
   67  * \bug cannot detect if lock files held by other hosts are stale.
   68  *
   69  * \return 
   70  *         - -1 for error "failure"
   71  *         - 0 if lock file is still in use or held by another host "failure"
   72  *         - 1 if lock file is stale and has been erased "success" */
   73 static int
   74 lock_is_stale(
   75 /** file name of lock file */
   76          const char *const name,
   77 /** quiet flag */
   78          const int quiet)
   79 {
   80     char buf[512];
   81     int fd;
   82     int len;
   83     char *pid;
   84     char *host;
   85     char *tmp;
   86     unsigned long npid;
   87 
   88     fd = open(name, O_RDONLY, 0);
   89     if (fd < 0) {
   90     if (errno == ENOENT) {
   91         /* file has just disappeared, thus it's not stale */
   92         return 0;
   93     } else {
   94         ln_log(LNLOG_SERR, LNLOG_CTOP,
   95            "cannot open %s for reading: %m", name);
   96         return -1;
   97     }
   98     }
   99 
  100     if ((len = read(fd, buf, sizeof(buf) - 1)) < 0) {
  101     ln_log(LNLOG_SERR, LNLOG_CTOP, "read error on %s: %m", name);
  102     (void)close(fd);
  103     return -1;
  104     }
  105 
  106     if (close(fd) < 0) {
  107     ln_log(LNLOG_SERR, LNLOG_CTOP, "read error on %s: %m", name);
  108     return -1;
  109     }
  110 
  111     /* read pid and host */
  112     buf[len - 1] = '\0';
  113     pid = host = buf;
  114     /* we expect a single \n here */
  115     host += strcspn(host, "\n");
  116     *(host++) = '\0';
  117 
  118     /* kill trailing \n */
  119     tmp = host;
  120     tmp += strcspn(tmp, "\n");
  121     *tmp = '\0';
  122 
  123     npid = strtoul(pid, 0, 10);
  124     if (npid == ULONG_MAX && errno == ERANGE) {
  125     /* overflow error, should not happen, bail out */
  126     ln_log(LNLOG_SERR, LNLOG_CTOP, "bogus pid in %s: %m", name);
  127     return -1;
  128     }
  129 
  130     if (strcasecmp(host, fqdn)) {
  131     if (!quiet)
  132         ln_log(LNLOG_SERR, LNLOG_CTOP,
  133            "lockfile held by pid %lu on host %s, we're %s",
  134            npid, host, fqdn);
  135     return 0;       /* other host holds the lock */
  136     }
  137 
  138     /* okay, we can see if there's still a process with that pid active */
  139     if (kill((pid_t)npid, 0) && errno == ESRCH) {
  140     /* no such process, good */
  141     if (!unlink(name)) {
  142         ln_log(LNLOG_SNOTICE, LNLOG_CTOP,
  143            "erased stale pid %lu host %s lockfile %s",
  144            npid, host, name);
  145         return 1;
  146     } else {
  147         if (!quiet)
  148         ln_log(LNLOG_SERR, LNLOG_CTOP,
  149                "unable to erase stale pid %lu host %s lockfile %s",
  150                npid, host, name);
  151         return 0;
  152     }
  153     }
  154 
  155     /* there is a process active */
  156     return 0;
  157 }
  158 
  159 /** Safe mkstemp replacement.  
  160  * Ensures the file is only read- and writable by its owner; some systems
  161  * create these with 0777 or 0666 permissions. */
  162 int
  163 safe_mkstemp(
  164 /** template to build filename upon, as for mkstemp */
  165         char *templ)
  166 {
  167     mode_t oldmask;
  168     int ret;
  169 
  170     oldmask = umask(077);
  171     ret = mkstemp(templ);
  172     (void)umask(oldmask);
  173 
  174     return ret;
  175 }
  176 
  177 
  178 /**
  179  * Try to set a lockfile, blocking or non-blocking.
  180  * Checks if the lockfile exists and is active.
  181  *
  182  * requires: atomic link(2) syscall.
  183  *
  184  * features: 
  185  *  - NFS safe (but leafnode does not work distributed)
  186  *  - stale detection by PID in lock file (may be fooled)
  187  *
  188  * \bug Cannot remove stale lock on other machine.
  189  *
  190  * \bug Stale detection may be fooled if another process has been
  191  * assigned the PID that the last caller had.
  192  *
  193  * \return
  194  * - 0: if locking succeeded
  195  * - 1: if locking failed because the lock is held by someone else and
  196  *     isn't stale
  197  * - -1: for other errors
  198  */
  199 int
  200 try_lock(
  201 /** Timeout, if nonzero, wait at most this many seconds. */
  202            unsigned long timeout)
  203 {
  204     const int block = 1;
  205     char *l2, *pid;
  206     int fd;
  207     int have_lock = 0;
  208     int quiet = 0;
  209     const char *const append = ".XXXXXXXXXX";
  210     const int have_timeout = (timeout != 0);
  211 
  212     if (debugmode)
  213     syslog(LOG_DEBUG,
  214            "try_lock(timeout=%lu), fqdn=\"%s\"",
  215            timeout, fqdn);
  216     if (verbose)
  217     printf("try_lock(timeout=%lu), fqdn=\"%s\"\n",
  218         timeout, fqdn);
  219 
  220     /* kill bogus fqdn */
  221     if (!is_validfqdn(fqdn)) {
  222     ln_log(LNLOG_SCRIT, LNLOG_CTOP,
  223            "Internal error: "
  224            "must not try to lock with local host name \"%s\"", fqdn);
  225     return -1;
  226     }
  227 
  228     l2 = (char *)critmalloc(strlen(lockfile) + strlen(append) + 1,
  229                 "try_lock");
  230     pid = (char *)critmalloc(strlen(fqdn) + sizeof(unsigned long) * 4 + 4,
  231                  "try_lock");
  232 
  233     strcpy(l2, lockfile); /* RATS: ignore */
  234     strcat(l2, append); /* RATS: ignore */
  235 
  236     /* make a temporary file */
  237     fd = safe_mkstemp(l2);
  238     if (fd < 0) {
  239     ln_log(LNLOG_SERR, LNLOG_CTOP, "mkstemp(%s) failed: %m", l2);
  240     free(l2);
  241     free(pid);
  242     return -1;
  243     }
  244 
  245     /* write our PID and host into it (stale detection) */
  246     sprintf(pid, "%lu\n%s\n", (unsigned long)getpid(), fqdn);
  247     /* safe, see malloc above */
  248     if (writes(fd, pid) < 0 || fsync(fd) < 0) {
  249     ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot write to %s: %m", l2);
  250     if (unlink(l2))
  251         ln_log(LNLOG_SERR, LNLOG_CTOP,
  252            "Cannot remove lock helper file %s: %m", l2);
  253     free(l2);
  254     free(pid);
  255     return -1;
  256     }
  257 
  258     /* and try to finally lock */
  259     while (!have_lock) {
  260     if (0 == link(l2, lockfile)) {
  261         /* link succeeded. good. */
  262         have_lock = 1;
  263         break;
  264     } else {
  265         if (2 == fd_st_nlink(fd)) {
  266         /* link failed, but st_nlink has increased to 2, good. */
  267         have_lock = 1;
  268         } else {
  269         int stale;
  270         struct timeval tv = { 1, 0 };
  271 
  272         /* Could not create link. Check if the lock file is stale. */
  273         stale = lock_is_stale(lockfile, quiet);
  274 
  275         /* if we have a stale file, it's just been removed.
  276            retry, don't care for what block says */
  277         if (stale == 1)
  278             continue;
  279 
  280         quiet = 1;
  281 
  282         /* if we have a problem with stale detection, or
  283            if we are in non-blocking mode, abort */
  284         if (stale == -1 || !block)
  285             break;
  286 
  287         if (have_timeout) {
  288             if (timeout == 0)
  289             break;
  290 
  291             --timeout;
  292         }
  293 
  294         /* retry after a second, select does not interfere w/ alarm */
  295         if (select(0, NULL, NULL, NULL, &tv) < 0) {
  296             /* must not happen */
  297             ln_log(LNLOG_SERR, LNLOG_CTOP,
  298                "try_lock: select failed: %m");
  299             break;
  300         }
  301         }
  302     }
  303     }
  304 
  305     if (close(fd) < 0) {
  306     ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot write to %s: %m", l2);
  307     have_lock = 0;
  308     }
  309 
  310     /* unlink l2, but just log if unable to unlink, ignore otherwise */
  311     if (unlink(l2))
  312     ln_log(LNLOG_SERR, LNLOG_CTOP,
  313            "Cannot remove lock helper file %s: %m", l2);
  314 
  315     /* clean up */
  316     free(l2);
  317     free(pid);
  318 
  319     /* mind the return logic */
  320     return have_lock ? 0 : 1;
  321 }
  322 
  323 /** Tries to hand over lock to the process with the given pid. \return
  324  * 0 for success, nonzero for failure -- check errno for details in
  325  * case of error.
  326  */
  327 int handover_lock(pid_t pid) {
  328   int fd = open(lockfile, O_RDWR|O_TRUNC, (mode_t)0600);
  329   char *buf = (char *)critmalloc(strlen(fqdn) + sizeof(unsigned long) * 4 + 4,
  330                  "handover_lock");
  331   if (fd < 0) { free(buf); return fd; }
  332   sprintf(buf, "%lu\n%s\n", (unsigned long)pid, fqdn);
  333   if (-1 == writes(fd, buf)) goto close_bail;
  334   if (-1 == fsync(fd)) goto close_bail;
  335   if (-1 == close(fd)) goto close_bail;
  336   free(buf);
  337   return 0;
  338 
  339  close_bail:
  340   (void)close(fd); 
  341   free(buf);
  342   return -1;
  343 }