00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <config.h>
00023
00024 #include "flint_lock.h"
00025
00026 #ifndef __WIN32__
00027 #include "safeerrno.h"
00028
00029 #include "safefcntl.h"
00030 #include <unistd.h>
00031 #include <cstdlib>
00032 #include <sys/types.h>
00033 #include <sys/socket.h>
00034 #include <sys/wait.h>
00035 #include <signal.h>
00036 #include <cstring>
00037 #endif
00038
00039 #include "closefrom.h"
00040 #include "omassert.h"
00041
00042 #ifdef __CYGWIN__
00043 #include <sys/cygwin.h>
00044 #endif
00045
00046 #include "xapian/error.h"
00047
00048 using namespace std;
00049
00050 FlintLock::reason
00051 FlintLock::lock(bool exclusive, string & explanation) {
00052
00053 (void)exclusive;
00054 Assert(exclusive);
00055 #if defined __CYGWIN__ || defined __WIN32__
00056 Assert(hFile == INVALID_HANDLE_VALUE);
00057 #ifdef __CYGWIN__
00058 char fnm[MAX_PATH];
00059 cygwin_conv_to_win32_path(filename.c_str(), fnm);
00060 #else
00061 const char *fnm = filename.c_str();
00062 #endif
00063 hFile = CreateFile(fnm, GENERIC_WRITE, FILE_SHARE_READ,
00064 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
00065 if (hFile != INVALID_HANDLE_VALUE) return SUCCESS;
00066 if (GetLastError() == ERROR_ALREADY_EXISTS) return INUSE;
00067 explanation = string();
00068 return UNKNOWN;
00069 #elif defined __EMX__
00070 APIRET rc;
00071 ULONG ulAction;
00072 rc = DosOpen((PCSZ)filename.c_str(), &hFile, &ulAction, 0, FILE_NORMAL,
00073 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
00074 OPEN_SHARE_DENYWRITE | OPEN_ACCESS_WRITEONLY,
00075 NULL);
00076 if (rc == NO_ERROR) return SUCCESS;
00077 if (rc == ERROR_ACCESS_DENIED) return INUSE;
00078 explanation = string();
00079 return UNKNOWN;
00080 #else
00081 Assert(fd == -1);
00082 int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
00083 if (lockfd < 0) {
00084
00085 explanation = string("Couldn't open lockfile: ") + strerror(errno);
00086 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
00087 }
00088
00089
00090
00091
00092
00093
00094 if (rare(lockfd < 2)) {
00095
00096
00097 int lockfd_dup = dup(lockfd);
00098 if (rare(lockfd_dup < 2)) {
00099 int eno = 0;
00100 if (lockfd_dup < 0) {
00101 eno = errno;
00102 close(lockfd);
00103 } else {
00104 int lockfd_dup2 = dup(lockfd);
00105 if (lockfd_dup2 < 0) {
00106 eno = errno;
00107 }
00108 close(lockfd);
00109 close(lockfd_dup);
00110 lockfd = lockfd_dup2;
00111 }
00112 if (eno) {
00113 return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
00114 }
00115 } else {
00116 close(lockfd);
00117 lockfd = lockfd_dup;
00118 }
00119 }
00120
00121 int fds[2];
00122 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) < 0) {
00123
00124 explanation = string("Couldn't create socketpair: ") + strerror(errno);
00125 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
00126 (void)close(lockfd);
00127 return why;
00128 }
00129
00130 pid_t child = fork();
00131
00132 if (child == 0) {
00133
00134 close(fds[0]);
00135
00136 reason why = SUCCESS;
00137 {
00138 struct flock fl;
00139 fl.l_type = F_WRLCK;
00140 fl.l_whence = SEEK_SET;
00141 fl.l_start = 0;
00142 fl.l_len = 1;
00143 while (fcntl(lockfd, F_SETLK, &fl) == -1) {
00144 if (errno != EINTR) {
00145
00146
00147 if (errno == EACCES || errno == EAGAIN) {
00148 why = INUSE;
00149 } else if (errno == ENOLCK) {
00150 why = UNSUPPORTED;
00151 } else {
00152 _exit(0);
00153 }
00154 break;
00155 }
00156 }
00157 }
00158
00159 {
00160
00161 char ch = static_cast<char>(why);
00162 while (write(fds[1], &ch, 1) < 0) {
00163
00164
00165
00166
00167 if (errno != EINTR) _exit(1);
00168 }
00169 if (why != SUCCESS) _exit(0);
00170 }
00171
00172
00173 dup2(fds[1], 0);
00174 dup2(fds[1], 1);
00175
00176
00177
00178 if (chdir("/") < 0) {
00179
00180
00181
00182
00183
00184
00185 }
00186
00187
00188
00189 for (int i = 2; i < lockfd; ++i) {
00190
00191
00192 while (close(i) < 0 && errno == EINTR) { }
00193 }
00194 closefrom(lockfd + 1);
00195
00196
00197 execl("/bin/cat", "/bin/cat", static_cast<void*>(NULL));
00198
00199 char ch;
00200 while (read(0, &ch, 1) != 0) { }
00201 _exit(0);
00202 }
00203
00204 close(lockfd);
00205 close(fds[1]);
00206
00207 if (child == -1) {
00208
00209 explanation = string("Couldn't fork: ") + strerror(errno);
00210 close(fds[0]);
00211 return UNKNOWN;
00212 }
00213
00214 reason why = UNKNOWN;
00215
00216
00217 while (true) {
00218 char ch;
00219 ssize_t n = read(fds[0], &ch, 1);
00220 if (n == 1) {
00221 why = static_cast<reason>(ch);
00222 if (why != SUCCESS) break;
00223
00224 fd = fds[0];
00225 pid = child;
00226 return SUCCESS;
00227 }
00228 if (n == 0) {
00229
00230 explanation.assign("Got EOF reading from child process");
00231 break;
00232 }
00233 if (errno != EINTR) {
00234
00235 explanation = string("Error reading from child process: ") + strerror(errno);
00236 break;
00237 }
00238 }
00239
00240 close(fds[0]);
00241
00242 int status;
00243 while (waitpid(child, &status, 0) < 0) {
00244 if (errno != EINTR) break;
00245 }
00246
00247 return why;
00248 #endif
00249 }
00250
00251 void
00252 FlintLock::release() {
00253 #if defined __CYGWIN__ || defined __WIN32__
00254 if (hFile == INVALID_HANDLE_VALUE) return;
00255 CloseHandle(hFile);
00256 hFile = INVALID_HANDLE_VALUE;
00257 #elif defined __EMX__
00258 if (hFile == NULLHANDLE) return;
00259 DosClose(hFile);
00260 hFile = NULLHANDLE;
00261 #else
00262 if (fd < 0) return;
00263 close(fd);
00264 fd = -1;
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275 if (kill(pid, SIGKILL) == 0) {
00276 int status;
00277 while (waitpid(pid, &status, 0) < 0) {
00278 if (errno != EINTR) break;
00279 }
00280 }
00281 #endif
00282 }
00283
00284 void
00285 FlintLock::throw_databaselockerror(FlintLock::reason why,
00286 const string & db_dir,
00287 const string & explanation)
00288 {
00289 string msg("Unable to get write lock on ");
00290 msg += db_dir;
00291 if (why == FlintLock::INUSE) {
00292 msg += ": already locked";
00293 } else if (why == FlintLock::UNSUPPORTED) {
00294 msg += ": locking probably not supported by this FS";
00295 } else if (why == FlintLock::FDLIMIT) {
00296 msg += ": too many open files";
00297 } else if (why == FlintLock::UNKNOWN) {
00298 if (!explanation.empty())
00299 msg += ": " + explanation;
00300 }
00301 throw Xapian::DatabaseLockError(msg);
00302 }