LCOV - code coverage report
Current view: top level - backends - flint_lock.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 7822d31adece Lines: 84 141 59.6 %
Date: 2019-05-23 11:15:29 Functions: 4 5 80.0 %
Branches: 69 211 32.7 %

           Branch data     Line data    Source code
       1                 :            : /** @file flint_lock.cc
       2                 :            :  * @brief Flint-compatible database locking.
       3                 :            :  */
       4                 :            : /* Copyright (C) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017 Olly Betts
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or
       7                 :            :  * modify it under the terms of the GNU General Public License as
       8                 :            :  * published by the Free Software Foundation; either version 2 of the
       9                 :            :  * License, or (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with this program; if not, write to the Free Software
      18                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
      19                 :            :  * USA
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : 
      24                 :            : #include "flint_lock.h"
      25                 :            : 
      26                 :            : #ifndef __WIN32__
      27                 :            : #include <cerrno>
      28                 :            : 
      29                 :            : #include "safefcntl.h"
      30                 :            : #include <unistd.h>
      31                 :            : #include <cstdlib>
      32                 :            : #include <sys/types.h>
      33                 :            : #include "safesyssocket.h"
      34                 :            : #include <sys/wait.h>
      35                 :            : #include <signal.h>
      36                 :            : #include <cstring>
      37                 :            : #endif
      38                 :            : 
      39                 :            : #include "closefrom.h"
      40                 :            : #include "errno_to_string.h"
      41                 :            : #include "omassert.h"
      42                 :            : 
      43                 :            : #ifdef __CYGWIN__
      44                 :            : # include <cygwin/version.h>
      45                 :            : # include <sys/cygwin.h>
      46                 :            : #endif
      47                 :            : 
      48                 :            : #ifdef FLINTLOCK_USE_FLOCK
      49                 :            : # include <sys/file.h>
      50                 :            : #endif
      51                 :            : 
      52                 :            : #include "xapian/error.h"
      53                 :            : 
      54                 :            : using namespace std;
      55                 :            : 
      56                 :            : #ifndef F_OFD_SETLK
      57                 :            : # ifdef __linux__
      58                 :            : // Apparently defining _GNU_SOURCE should get us F_OFD_SETLK, etc, but that
      59                 :            : // doesn't actually seem to work, so hard-code the known values.
      60                 :            : #  define F_OFD_GETLK   36
      61                 :            : #  define F_OFD_SETLK   37
      62                 :            : #  define F_OFD_SETLKW  38
      63                 :            : # endif
      64                 :            : #endif
      65                 :            : 
      66                 :            : [[noreturn]]
      67                 :            : static void
      68                 :          0 : throw_cannot_test_lock()
      69                 :            : {
      70                 :            :     throw Xapian::FeatureUnavailableError("Can't test lock without trying to "
      71 [ #  # ][ #  # ]:          0 :                                           "take it");
                 [ #  # ]
      72                 :            : }
      73                 :            : 
      74                 :            : bool
      75                 :         11 : FlintLock::test() const
      76                 :            : {
      77                 :            :     // A database which doesn't support update can't be locked for update.
      78         [ +  + ]:         11 :     if (filename.empty()) return false;
      79                 :            : 
      80                 :            : #if defined __CYGWIN__ || defined __WIN32__
      81                 :            :     if (hFile != INVALID_HANDLE_VALUE) return true;
      82                 :            :     // Doesn't seem to be possible to check if the lock is held without briefly
      83                 :            :     // taking the lock.
      84                 :            :     throw_cannot_test_lock();
      85                 :            : #elif defined FLINTLOCK_USE_FLOCK
      86                 :            :     if (fd != -1) return true;
      87                 :            :     // Doesn't seem to be possible to check if the lock is held without briefly
      88                 :            :     // taking the lock.
      89                 :            :     throw_cannot_test_lock();
      90                 :            : #else
      91         [ +  + ]:         10 :     if (fd != -1) return true;
      92         [ +  - ]:          8 :     int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
      93         [ -  + ]:          8 :     if (lockfd < 0) {
      94                 :            :         // Couldn't open lockfile.
      95 [ #  # ][ #  # ]:          0 :         reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
      96         [ #  # ]:          0 :         throw_databaselockerror(why, filename, "Testing lock");
      97                 :            :     }
      98                 :            : 
      99                 :            :     struct flock fl;
     100                 :          8 :     fl.l_type = F_WRLCK;
     101                 :          8 :     fl.l_whence = SEEK_SET;
     102                 :          8 :     fl.l_start = 0;
     103                 :          8 :     fl.l_len = 1;
     104                 :          8 :     fl.l_pid = 0;
     105 [ +  - ][ -  + ]:          8 :     while (fcntl(lockfd, F_GETLK, &fl) == -1) {
     106         [ #  # ]:          0 :         if (errno != EINTR) {
     107                 :            :             // Translate known errno values into a reason code.
     108                 :          0 :             int e = errno;
     109         [ #  # ]:          0 :             close(lockfd);
     110         [ #  # ]:          0 :             if (e == ENOSYS) {
     111                 :            :                 // F_GETLK isn't implemented by GNU Hurd, and always fails with
     112                 :            :                 // ENOSYS: https://bugs.debian.org/190367
     113                 :          0 :                 throw_cannot_test_lock();
     114                 :            :             }
     115         [ #  # ]:          0 :             reason why = (e == ENOLCK ? UNSUPPORTED : UNKNOWN);
     116         [ #  # ]:          0 :             throw_databaselockerror(why, filename, "Testing lock");
     117                 :            :         }
     118                 :            :     }
     119         [ +  - ]:          8 :     close(lockfd);
     120                 :         11 :     return fl.l_type != F_UNLCK;
     121                 :            : #endif
     122                 :            : }
     123                 :            : 
     124                 :            : FlintLock::reason
     125                 :       1873 : FlintLock::lock(bool exclusive, bool wait, string & explanation) {
     126                 :            :     // Currently we only support exclusive locks.
     127                 :            :     (void)exclusive;
     128                 :            :     Assert(exclusive);
     129                 :            : #if defined __CYGWIN__ || defined __WIN32__
     130                 :            :     Assert(hFile == INVALID_HANDLE_VALUE);
     131                 :            : #ifdef __CYGWIN__
     132                 :            :     char fnm[MAX_PATH];
     133                 :            : #if CYGWIN_VERSION_API_MAJOR == 0 && CYGWIN_VERSION_API_MINOR < 181
     134                 :            :     cygwin_conv_to_win32_path(filename.c_str(), fnm);
     135                 :            : #else
     136                 :            :     if (cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, filename.c_str(),
     137                 :            :                          fnm, MAX_PATH) < 0) {
     138                 :            :         explanation.assign("cygwin_conv_path failed: ");
     139                 :            :         errno_to_string(errno, explanation);
     140                 :            :         return UNKNOWN;
     141                 :            :     }
     142                 :            : #endif
     143                 :            : #else
     144                 :            :     const char *fnm = filename.c_str();
     145                 :            : #endif
     146                 :            : retry:
     147                 :            :     // FIXME: Use LockFileEx() for locking, which would allow proper blocking
     148                 :            :     // and also byte-range locking for when we implement MVCC.  But is there a
     149                 :            :     // way to interwork with the CreateFile()-based locking while doing so?
     150                 :            :     hFile = CreateFile(fnm, GENERIC_WRITE, FILE_SHARE_READ,
     151                 :            :                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     152                 :            :     if (hFile != INVALID_HANDLE_VALUE) return SUCCESS;
     153                 :            :     if (GetLastError() == ERROR_ALREADY_EXISTS) {
     154                 :            :         if (wait) {
     155                 :            :             Sleep(1000);
     156                 :            :             goto retry;
     157                 :            :         }
     158                 :            :         return INUSE;
     159                 :            :     }
     160                 :            :     explanation = string();
     161                 :            :     return UNKNOWN;
     162                 :            : #elif defined FLINTLOCK_USE_FLOCK
     163                 :            :     // This is much simpler than using fcntl() due to saner semantics around
     164                 :            :     // releasing locks when closing other descriptors on the same file (at
     165                 :            :     // least on platforms where flock() isn't just a compatibility wrapper
     166                 :            :     // around fcntl()).  We can't simply switch to this without breaking
     167                 :            :     // locking compatibility with previous releases, though it might be useful
     168                 :            :     // for porting to platforms without fcntl() locking.
     169                 :            :     //
     170                 :            :     // Also, flock() is problematic over NFS at least on Linux - it's been
     171                 :            :     // supported since Linux 2.6.12 but it's actually emulated by taking out an
     172                 :            :     // fcntl() byte-range lock on the entire file, which means that a process
     173                 :            :     // on the NFS server can get a (genuine) flock() lock on the same file a
     174                 :            :     // process on an NFS client has locked by flock() emulated as an fcntl()
     175                 :            :     // lock.
     176                 :            :     Assert(fd == -1);
     177                 :            :     int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
     178                 :            :     if (lockfd < 0) {
     179                 :            :         // Couldn't open lockfile.
     180                 :            :         explanation.assign("Couldn't open lockfile: ");
     181                 :            :         errno_to_string(errno, explanation);
     182                 :            :         return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
     183                 :            :     }
     184                 :            : 
     185                 :            :     int op = LOCK_EX;
     186                 :            :     if (!wait) op |= LOCK_NB;
     187                 :            :     while (flock(lockfd, op) == -1) {
     188                 :            :         if (errno != EINTR) {
     189                 :            :             // Lock failed - translate known errno values into a reason code.
     190                 :            :             close(lockfd);
     191                 :            :             switch (errno) {
     192                 :            :                 case EWOULDBLOCK:
     193                 :            :                     return INUSE;
     194                 :            :                 case ENOLCK:
     195                 :            :                     return UNSUPPORTED; // FIXME: what do we get for NFS?
     196                 :            :                 default:
     197                 :            :                     return UNKNOWN;
     198                 :            :             }
     199                 :            :         }
     200                 :            :     }
     201                 :            : 
     202                 :            :     fd = lockfd;
     203                 :            :     return SUCCESS;
     204                 :            : #else
     205                 :            :     Assert(fd == -1);
     206                 :            : #if defined F_SETFD && defined FD_CLOEXEC
     207         [ +  - ]:       1873 :     int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0666);
     208                 :            : #else
     209                 :            :     int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
     210                 :            : #endif
     211         [ +  + ]:       1873 :     if (lockfd < 0) {
     212                 :            :         // Couldn't open lockfile.
     213         [ +  - ]:          2 :         explanation.assign("Couldn't open lockfile: ");
     214         [ +  - ]:          2 :         errno_to_string(errno, explanation);
     215 [ +  - ][ -  + ]:          2 :         return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
     216                 :            :     }
     217                 :            : 
     218                 :            : #ifdef F_OFD_SETLK
     219                 :            :     // F_OFD_SETLK has exactly the semantics we want, so use it if it's
     220                 :            :     // available.  Support was added in Linux 3.15, and there's work on
     221                 :            :     // getting it standardised via POSIX:
     222                 :            :     // http://austingroupbugs.net/view.php?id=768
     223                 :            : 
     224                 :            :     // Use a static flag so we don't repeatedly try F_OFD_SETLK when
     225                 :            :     // the kernel in use doesn't support it.  This should be safe in a
     226                 :            :     // threaded context - at worst multiple threads might end up trying
     227                 :            :     // F_OFD_SETLK and then setting f_ofd_setlk_fails to true.
     228                 :            :     static bool f_ofd_setlk_fails = false;
     229         [ +  + ]:       1871 :     if (!f_ofd_setlk_fails) {
     230                 :            :         struct flock fl;
     231                 :        175 :         fl.l_type = F_WRLCK;
     232                 :        175 :         fl.l_whence = SEEK_SET;
     233                 :        175 :         fl.l_start = 0;
     234                 :        175 :         fl.l_len = 1;
     235                 :        175 :         fl.l_pid = 0;
     236 [ -  + ][ +  - ]:        175 :         while (fcntl(lockfd, wait ? F_OFD_SETLKW : F_OFD_SETLK, &fl) == -1) {
                 [ +  - ]
     237         [ +  - ]:        175 :             if (errno != EINTR) {
     238         [ +  - ]:        175 :                 if (errno == EINVAL) {
     239                 :            :                     // F_OFD_SETLK not supported by this kernel.
     240                 :        175 :                     goto no_ofd_support;
     241                 :            :                 }
     242                 :            :                 // Lock failed - translate known errno values into a reason
     243                 :            :                 // code.
     244                 :          0 :                 int e = errno;
     245         [ #  # ]:          0 :                 close(lockfd);
     246      [ #  #  # ]:          0 :                 switch (e) {
     247                 :            :                     case EACCES: case EAGAIN:
     248                 :          0 :                         return INUSE;
     249                 :            :                     case ENOLCK:
     250                 :          0 :                         return UNSUPPORTED;
     251                 :            :                     default:
     252                 :          0 :                         return UNKNOWN;
     253                 :            :                 }
     254                 :            :             }
     255                 :            :         }
     256                 :          0 :         fd = lockfd;
     257                 :          0 :         pid = 0;
     258                 :          0 :         return SUCCESS;
     259                 :            : no_ofd_support:
     260                 :        175 :         f_ofd_setlk_fails = true;
     261                 :            :     }
     262                 :            : #endif
     263                 :            : 
     264                 :            :     // If stdin and/or stdout have been closed, it is possible that lockfd could
     265                 :            :     // be 0 or 1.  We need fds 0 and 1 to be available in the child process to
     266                 :            :     // be stdin and stdout, and we can't use dup() on lockfd after locking it,
     267                 :            :     // as the lock won't be transferred, so we handle this corner case here by
     268                 :            :     // using F_DUPFD or by calling dup() once or twice so that lockfd >= 2.
     269         [ +  + ]:       1871 :     if (rare(lockfd < 2)) {
     270                 :            :         // Note this temporarily requires one or two spare fds to work, but
     271                 :            :         // then we need two spare for socketpair() to succeed below anyway.
     272                 :            : #ifdef F_DUPFD
     273                 :            :         // Where available, F_DUPFD allows us to directly get the first unused
     274                 :            :         // fd which is at least 2.
     275         [ +  - ]:          5 :         int lockfd_dup = fcntl(lockfd, F_DUPFD, 2);
     276                 :          5 :         int eno = errno;
     277         [ +  - ]:          5 :         close(lockfd);
     278         [ -  + ]:          5 :         if (lockfd_dup < 0) {
     279 [ #  # ][ #  # ]:          0 :             return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
     280                 :            :         }
     281                 :          5 :         lockfd = lockfd_dup;
     282                 :            : #else
     283                 :            :         // Otherwise we have to call dup() until we get one, though at least
     284                 :            :         // that's only at most twice.
     285                 :            :         int lockfd_dup = dup(lockfd);
     286                 :            :         if (rare(lockfd_dup < 2)) {
     287                 :            :             int eno = 0;
     288                 :            :             if (lockfd_dup < 0) {
     289                 :            :                 eno = errno;
     290                 :            :                 close(lockfd);
     291                 :            :             } else {
     292                 :            :                 int lockfd_dup2 = dup(lockfd);
     293                 :            :                 if (lockfd_dup2 < 0) {
     294                 :            :                     eno = errno;
     295                 :            :                 }
     296                 :            :                 close(lockfd);
     297                 :            :                 close(lockfd_dup);
     298                 :            :                 lockfd = lockfd_dup2;
     299                 :            :             }
     300                 :            :             if (eno) {
     301                 :            :                 return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
     302                 :            :             }
     303                 :            :         } else {
     304                 :            :             close(lockfd);
     305                 :            :             lockfd = lockfd_dup;
     306                 :            :         }
     307                 :            : #endif
     308                 :            :     }
     309                 :            : 
     310                 :            :     int fds[2];
     311         [ -  + ]:       1871 :     if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
     312                 :            :         // Couldn't create socketpair.
     313         [ #  # ]:          0 :         explanation.assign("Couldn't create socketpair: ");
     314         [ #  # ]:          0 :         errno_to_string(errno, explanation);
     315 [ #  # ][ #  # ]:          0 :         reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
     316         [ #  # ]:          0 :         (void)close(lockfd);
     317                 :          0 :         return why;
     318                 :            :     }
     319                 :            : 
     320                 :       1871 :     pid_t child = fork();
     321                 :            : 
     322         [ +  + ]:       2561 :     if (child == 0) {
     323                 :            :         // Child process.
     324         [ +  - ]:        692 :         close(fds[0]);
     325                 :            : 
     326                 :            : #if defined F_SETFD && defined FD_CLOEXEC
     327                 :            :         // Clear close-on-exec flag, if we set it when we called socketpair().
     328                 :            :         // Clearing it here means there's no window where another thread in the
     329                 :            :         // parent process could fork()+exec() and end up with this fd still
     330                 :            :         // open (assuming close-on-exec is supported).
     331                 :            :         //
     332                 :            :         // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as
     333                 :            :         // on Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC
     334                 :            :         // SOCK_CLOEXEC' to allow '#ifdef SOCK_CLOEXEC' to work.
     335                 :            :         if (SOCK_CLOEXEC != 0)
     336         [ +  - ]:        692 :             (void)fcntl(fds[1], F_SETFD, 0);
     337                 :            :         if (O_CLOEXEC != 0)
     338         [ +  - ]:        692 :             (void)fcntl(lockfd, F_SETFD, 0);
     339                 :            : #endif
     340                 :            :         // Connect pipe to stdin and stdout.
     341                 :        692 :         dup2(fds[1], 0);
     342                 :        692 :         dup2(fds[1], 1);
     343                 :            : 
     344                 :            :         // Make sure we don't hang on to open files which may get deleted but
     345                 :            :         // not have their disk space released until we exit.  Close these
     346                 :            :         // before we try to get the lock because if one of them is open on
     347                 :            :         // the lock file then closing it after obtaining the lock would release
     348                 :            :         // the lock, which would be really bad.
     349         [ +  + ]:       3036 :         for (int i = 2; i < lockfd; ++i) {
     350                 :            :             // Retry on EINTR; just ignore other errors (we'll get
     351                 :            :             // EBADF if the fd isn't open so that's OK).
     352 [ +  - ][ -  + ]:       2344 :             while (close(i) < 0 && errno == EINTR) { }
         [ #  # ][ -  + ]
     353                 :            :         }
     354         [ +  - ]:        692 :         closefrom(lockfd + 1);
     355                 :            : 
     356                 :        692 :         reason why = SUCCESS;
     357                 :            :         {
     358                 :            :             struct flock fl;
     359                 :        692 :             fl.l_type = F_WRLCK;
     360                 :        692 :             fl.l_whence = SEEK_SET;
     361                 :        692 :             fl.l_start = 0;
     362                 :        692 :             fl.l_len = 1;
     363 [ +  + ][ +  - ]:        692 :             while (fcntl(lockfd, wait ? F_SETLKW : F_SETLK, &fl) == -1) {
                 [ -  + ]
     364         [ #  # ]:          0 :                 if (errno != EINTR) {
     365                 :            :                     // Lock failed - translate known errno values into a reason
     366                 :            :                     // code.
     367 [ #  # ][ #  # ]:          0 :                     if (errno == EACCES || errno == EAGAIN) {
     368                 :          0 :                         why = INUSE;
     369         [ #  # ]:          0 :                     } else if (errno == ENOLCK) {
     370                 :          0 :                         why = UNSUPPORTED;
     371                 :            :                     } else {
     372                 :          0 :                         _exit(0);
     373                 :            :                     }
     374                 :          0 :                     break;
     375                 :            :                 }
     376                 :            :             }
     377                 :            :         }
     378                 :            : 
     379                 :            :         {
     380                 :            :             // Tell the parent if we got the lock, and if not, why not.
     381                 :        692 :             char ch = static_cast<char>(why);
     382 [ +  - ][ -  + ]:        692 :             while (write(1, &ch, 1) < 0) {
     383                 :            :                 // EINTR means a signal interrupted us, so retry.
     384                 :            :                 // Otherwise we're DOOMED!  The best we can do is just exit
     385                 :            :                 // and the parent process should get EOF and know the lock
     386                 :            :                 // failed.
     387         [ #  # ]:          0 :                 if (errno != EINTR) _exit(1);
     388                 :            :             }
     389         [ -  + ]:        692 :             if (why != SUCCESS) _exit(0);
     390                 :            :         }
     391                 :            : 
     392                 :            :         // Make sure we don't block unmount of partition holding the current
     393                 :            :         // directory.
     394                 :        692 :         if (chdir("/") < 0) {
     395                 :            :             // We can't usefully do anything in response to an error, so just
     396                 :            :             // ignore it - the worst harm it can do is make it impossible to
     397                 :            :             // unmount a partition.
     398                 :            :             //
     399                 :            :             // We need the if statement because glibc's _FORTIFY_SOURCE mode
     400                 :            :             // gives a warning even if we cast the result to void.
     401                 :            :         }
     402                 :            : 
     403                 :            :         // FIXME: use special statically linked helper instead of cat.
     404                 :          0 :         execl("/bin/cat", "/bin/cat", static_cast<void*>(NULL));
     405                 :            :         // Emulate cat ourselves (we try to avoid this to reduce VM overhead).
     406                 :            :         char ch;
     407 [ #  # ][ #  # ]:          0 :         while (read(0, &ch, 1) != 0) {
     408                 :            :             /* Do nothing */
     409                 :            :         }
     410                 :          0 :         _exit(0);
     411                 :            :     }
     412                 :            : 
     413         [ +  - ]:       1869 :     close(lockfd);
     414         [ +  - ]:       1869 :     close(fds[1]);
     415                 :            : 
     416         [ -  + ]:       1869 :     if (child == -1) {
     417                 :            :         // Couldn't fork.
     418         [ #  # ]:          0 :         explanation.assign("Couldn't fork: ");
     419         [ #  # ]:          0 :         errno_to_string(errno, explanation);
     420         [ #  # ]:          0 :         close(fds[0]);
     421                 :          0 :         return UNKNOWN;
     422                 :            :     }
     423                 :            : 
     424                 :       1869 :     reason why = UNKNOWN;
     425                 :            : 
     426                 :            :     // Parent process.
     427                 :            :     while (true) {
     428                 :            :         char ch;
     429         [ +  - ]:       1869 :         ssize_t n = read(fds[0], &ch, 1);
     430         [ +  - ]:       1869 :         if (n == 1) {
     431                 :       1869 :             why = static_cast<reason>(ch);
     432         [ +  + ]:       1869 :             if (why != SUCCESS) break;
     433                 :            :             // Got the lock.
     434                 :       1860 :             fd = fds[0];
     435                 :       1860 :             pid = child;
     436                 :       1860 :             return SUCCESS;
     437                 :            :         }
     438         [ #  # ]:          0 :         if (n == 0) {
     439                 :            :             // EOF means the lock failed.
     440         [ #  # ]:          0 :             explanation.assign("Got EOF reading from child process");
     441                 :          0 :             break;
     442                 :            :         }
     443         [ #  # ]:          0 :         if (errno != EINTR) {
     444                 :            :             // Treat unexpected errors from read() as failure to get the lock.
     445         [ #  # ]:          0 :             explanation.assign("Error reading from child process: ");
     446         [ #  # ]:          0 :             errno_to_string(errno, explanation);
     447                 :          0 :             break;
     448                 :            :         }
     449                 :            :     }
     450                 :            : 
     451         [ +  - ]:          9 :     close(fds[0]);
     452                 :            : 
     453                 :            :     int status;
     454 [ +  - ][ -  + ]:          9 :     while (waitpid(child, &status, 0) < 0) {
     455         [ #  # ]:          0 :         if (errno != EINTR) break;
     456                 :            :     }
     457                 :            : 
     458                 :       1871 :     return why;
     459                 :            : #endif
     460                 :            : }
     461                 :            : 
     462                 :            : void
     463                 :       5353 : FlintLock::release() {
     464                 :            : #if defined __CYGWIN__ || defined __WIN32__
     465                 :            :     if (hFile == INVALID_HANDLE_VALUE) return;
     466                 :            :     CloseHandle(hFile);
     467                 :            :     hFile = INVALID_HANDLE_VALUE;
     468                 :            : #elif defined FLINTLOCK_USE_FLOCK
     469                 :            :     if (fd < 0) return;
     470                 :            :     close(fd);
     471                 :            :     fd = -1;
     472                 :            : #else
     473         [ +  + ]:       5353 :     if (fd < 0) return;
     474                 :       1859 :     close(fd);
     475                 :       1859 :     fd = -1;
     476                 :            : #ifdef F_OFD_SETLK
     477         [ -  + ]:       1859 :     if (pid == 0) return;
     478                 :            : #endif
     479                 :            :     // Kill the child process which is holding the lock.  Use SIGKILL since
     480                 :            :     // that can't be caught or ignored (we used to use SIGHUP, but if the
     481                 :            :     // application has set that to SIG_IGN, the child process inherits that
     482                 :            :     // setting, which sometimes results in the child process not exiting -
     483                 :            :     // noted on Linux).
     484                 :            :     //
     485                 :            :     // The only likely error from kill is ESRCH (pid doesn't exist).  The other
     486                 :            :     // possibilities (according to the Linux man page) are EINVAL (invalid
     487                 :            :     // signal) and EPERM (don't have permission to SIGKILL the process) but in
     488                 :            :     // none of the cases does calling waitpid do us any good!
     489         [ +  - ]:       1859 :     if (kill(pid, SIGKILL) == 0) {
     490                 :            :         int status;
     491 [ +  - ][ -  + ]:       5353 :         while (waitpid(pid, &status, 0) < 0) {
     492         [ #  # ]:          0 :             if (errno != EINTR) break;
     493                 :            :         }
     494                 :            :     }
     495                 :            : #endif
     496                 :            : }
     497                 :            : 
     498                 :            : void
     499                 :          9 : FlintLock::throw_databaselockerror(FlintLock::reason why,
     500                 :            :                                    const string & db_dir,
     501                 :            :                                    const string & explanation) const
     502                 :            : {
     503         [ +  - ]:          9 :     string msg("Unable to get write lock on ");
     504         [ +  - ]:          9 :     msg += db_dir;
     505         [ +  - ]:          9 :     if (why == FlintLock::INUSE) {
     506         [ +  - ]:          9 :         msg += ": already locked";
     507         [ #  # ]:          0 :     } else if (why == FlintLock::UNSUPPORTED) {
     508         [ #  # ]:          0 :         msg += ": locking probably not supported by this FS";
     509         [ #  # ]:          0 :     } else if (why == FlintLock::FDLIMIT) {
     510         [ #  # ]:          0 :         msg += ": too many open files";
     511         [ #  # ]:          0 :     } else if (why == FlintLock::UNKNOWN) {
     512         [ #  # ]:          0 :         if (!explanation.empty())
     513 [ #  # ][ #  # ]:          0 :             msg += ": " + explanation;
     514                 :            :     }
     515 [ +  - ][ +  - ]:          9 :     throw Xapian::DatabaseLockError(msg);
     516                 :            : }

Generated by: LCOV version 1.11