backends/flint/flint_lock.cc

Go to the documentation of this file.
00001 /* flint_lock.cc: database locking for flint backend.
00002  *
00003  * Copyright (C) 2005,2006,2007 Olly Betts
00004  *
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License as
00007  * published by the Free Software Foundation; either version 2 of the
00008  * License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00018  * USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include "flint_lock.h"
00024 
00025 #ifndef __WIN32__
00026 #include "safeerrno.h"
00027 
00028 #include "safefcntl.h"
00029 #include <unistd.h>
00030 #include <stdlib.h>
00031 #include <sys/types.h>
00032 // FIXME:1.1: It's unclear why this workaround is needed here, yet not needed
00033 // in configure or the other files which include sys/socket.h and use
00034 // SOCKLEN_T.  The commit comment doesn't help, and there's no obvious related
00035 // email thread.  I think the way forward is to drop this in 1.1.0, and if it
00036 // is required, we can work out why this file is different and either fix that
00037 // or add a "safesyssocket.h" header to replace <sys/socket.h> uses with.
00038 #ifdef _NEWLIB_VERSION
00039 // Workaround bug in newlib (at least some versions) - socklen_t doesn't
00040 // get defined if you just "#include <sys/socket.h>".
00041 #include <netinet/in.h>
00042 #endif
00043 #include <sys/socket.h>
00044 #include <sys/wait.h>
00045 #include <signal.h>
00046 #endif
00047 
00048 #include "omassert.h"
00049 
00050 #ifdef __CYGWIN__
00051 #include <sys/cygwin.h>
00052 #endif
00053 
00054 FlintLock::reason
00055 FlintLock::lock(bool exclusive) {
00056     // Currently we only support exclusive locks.
00057     (void)exclusive;
00058     Assert(exclusive);
00059 #if defined __CYGWIN__ || defined __WIN32__
00060     Assert(hFile == INVALID_HANDLE_VALUE);
00061 #ifdef __CYGWIN__
00062     char fnm[MAX_PATH];
00063     cygwin_conv_to_win32_path(filename.c_str(), fnm);
00064 #else
00065     const char *fnm = filename.c_str();
00066 #endif
00067     hFile = CreateFile(fnm, GENERIC_WRITE, FILE_SHARE_READ,
00068                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
00069     if (hFile != INVALID_HANDLE_VALUE) return SUCCESS;
00070     if (GetLastError() == ERROR_ALREADY_EXISTS) return INUSE;
00071     return UNKNOWN;
00072 #elif defined __EMX__
00073     APIRET rc;
00074     ULONG ulAction;
00075     rc = DosOpen((PCSZ)filename.c_str(), &hFile, &ulAction, 0, FILE_NORMAL,
00076                  OPEN_ACTION_OPEN_IF_EXISTS  | OPEN_ACTION_CREATE_IF_NEW,
00077                  OPEN_SHARE_DENYWRITE | OPEN_ACCESS_WRITEONLY,
00078                  NULL);
00079     if (rc == NO_ERROR) return SUCCESS;
00080     if (rc == ERROR_ACCESS_DENIED) return INUSE;
00081     return UNKNOWN;
00082 #else
00083     Assert(fd == -1);
00084     int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
00085     if (lockfd < 0) return UNKNOWN; // Couldn't open lockfile.
00086 
00087     int fds[2];
00088     if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) < 0) {
00089         // Couldn't create socketpair.
00090         close(lockfd);
00091         return UNKNOWN;
00092     }
00093 
00094     pid_t child = fork();
00095     if (child == 0) {
00096         // Child process.
00097         close(fds[0]);
00098 
00099         reason why = SUCCESS;
00100         {
00101             struct flock fl;
00102             fl.l_type = F_WRLCK;
00103             fl.l_whence = SEEK_SET;
00104             fl.l_start = 0;
00105             fl.l_len = 1;
00106             while (fcntl(lockfd, F_SETLK, &fl) == -1) {
00107                 if (errno != EINTR) {
00108                     // Lock failed - translate known errno values into a reason
00109                     // code.
00110                     if (errno == EACCES || errno == EAGAIN) {
00111                         why = INUSE;
00112                     } else if (errno == ENOLCK) {
00113                         why = UNSUPPORTED;
00114                     } else {
00115                         _exit(0);
00116                     }
00117                     break;
00118                 }
00119             }
00120         }
00121 
00122         {
00123             // Tell the parent if we got the lock, and if not, why not.
00124             char ch = static_cast<char>(why);
00125             while (write(fds[1], &ch, 1) < 0) {
00126                 // EINTR means a signal interrupted us, so retry.
00127                 // Otherwise we're DOOMED!  The best we can do is just exit
00128                 // and the parent process should get EOF and know the lock
00129                 // failed.
00130                 if (errno != EINTR) _exit(1);
00131             }
00132             if (why != SUCCESS) _exit(0);
00133         }
00134 
00135         //shutdown(fds[1], 1); // Disable further sends.
00136         // Connect pipe to stdin.
00137         dup2(fds[1], 0);
00138         // FIXME: use special statically linked helper instead of cat.
00139         execl("/bin/cat", "/bin/cat", (void*)NULL);
00140         // Emulate cat ourselves (we try to avoid this to reduce VM overhead).
00141         char ch;
00142         while (read(0, &ch, 1) != 0) { /* Do nothing */ }
00143         _exit(0);
00144     }
00145 
00146     close(lockfd);
00147 
00148     if (child == -1) {
00149         // Couldn't fork.
00150         close(fds[0]);
00151         close(fds[1]);
00152         return UNKNOWN;
00153     }
00154 
00155     // Parent process.
00156     close(fds[1]);
00157     while (true) {
00158         char ch;
00159         int n = read(fds[0], &ch, 1);
00160         if (n == 1) {
00161             reason why = static_cast<reason>(ch);
00162             if (why == SUCCESS) break; // Got the lock.
00163             close(fds[0]);
00164             return why;
00165         }
00166         if (n == 0 || errno != EINTR) {
00167             // EOF means the lock failed; we also treat unexpected errors from
00168             // read() the same way.
00169             close(fds[0]);
00170             return UNKNOWN;
00171         }
00172     }
00173     //shutdown(fds[0], 0); // Disable further receives.
00174     fd = fds[0];
00175     pid = child;
00176     return SUCCESS;
00177 #endif
00178 }
00179 
00180 void
00181 FlintLock::release() {
00182 #if defined __CYGWIN__ || defined __WIN32__
00183     if (hFile == INVALID_HANDLE_VALUE) return;
00184     CloseHandle(hFile);
00185     hFile = INVALID_HANDLE_VALUE;
00186 #elif defined __EMX__
00187     if (hFile == NULLHANDLE) return;
00188     DosClose(hFile);
00189     hFile = NULLHANDLE;
00190 #else
00191     if (fd < 0) return;
00192     close(fd);
00193     fd = -1;
00194     // The only likely error from kill is ESRCH.  The other possibilities
00195     // (according to the Linux man page) are EINVAL (invalid signal) and EPERM
00196     // (don't have permission to SIGHUP the process) but in none of the cases
00197     // does calling waitpid do us any good!
00198     if (kill(pid, SIGHUP) == 0) {
00199         int status;
00200         while (waitpid(pid, &status, 0) < 0) {
00201             if (errno != EINTR) break;
00202         }
00203     }
00204 #endif
00205 }

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.