32 #include <sys/types.h> 44 # include <cygwin/version.h> 45 # include <sys/cygwin.h> 48 #ifdef FLINTLOCK_USE_FLOCK 49 # include <sys/file.h> 60 # define F_OFD_GETLK 36 61 # define F_OFD_SETLK 37 62 # define F_OFD_SETLKW 38 78 if (filename.empty())
return false;
80 #if defined __CYGWIN__ || defined __WIN32__ 81 if (hFile != INVALID_HANDLE_VALUE)
return true;
85 #elif defined FLINTLOCK_USE_FLOCK 86 if (fd != -1)
return true;
91 if (fd != -1)
return true;
92 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
95 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
96 throw_databaselockerror(why, filename,
"Testing lock");
101 fl.l_whence = SEEK_SET;
105 while (fcntl(lockfd, F_GETLK, &fl) == -1) {
106 if (errno != EINTR) {
115 reason why = (e == ENOLCK ? UNSUPPORTED : UNKNOWN);
116 throw_databaselockerror(why, filename,
"Testing lock");
120 return fl.l_type != F_UNLCK;
129 #if defined __CYGWIN__ || defined __WIN32__ 130 Assert(hFile == INVALID_HANDLE_VALUE);
133 #if CYGWIN_VERSION_API_MAJOR == 0 && CYGWIN_VERSION_API_MINOR < 181 134 cygwin_conv_to_win32_path(filename.c_str(), fnm);
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: ");
144 const char *fnm = filename.c_str();
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) {
160 explanation = string();
162 #elif defined FLINTLOCK_USE_FLOCK 177 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
180 explanation.assign(
"Couldn't open lockfile: ");
182 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
186 if (!wait) op |= LOCK_NB;
187 while (flock(lockfd, op) == -1) {
188 if (errno != EINTR) {
210 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
213 explanation.assign(
"Couldn't open lockfile: ");
215 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
228 static bool f_ofd_setlk_fails =
false;
229 if (!f_ofd_setlk_fails) {
232 fl.l_whence = SEEK_SET;
236 while (fcntl(lockfd, wait ? F_OFD_SETLKW : F_OFD_SETLK, &fl) == -1) {
237 if (errno != EINTR) {
238 if (errno == EINVAL) {
247 case EACCES:
case EAGAIN:
260 f_ofd_setlk_fails =
true;
265 if (socketpair(AF_UNIX, SOCK_STREAM|
SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
267 explanation.assign(
"Couldn't create socketpair: ");
269 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
276 if (
rare(fds[1] == 2)) swap(fds[0], fds[1]);
278 pid_t child = fork();
289 int parentfd = fds[1];
308 bool lockfd_cloexec_cleared =
false;
309 int dup_parent_to_first = parentfd == 0 ? 1 : 0;
310 if (
rare(lockfd < 2)) {
311 int oldlockfd = lockfd;
314 lockfd = dup2(lockfd, 2);
315 if (
rare(lockfd < 0))
goto report_dup_failure;
316 lockfd_cloexec_cleared =
true;
318 dup_parent_to_first = oldlockfd;
325 if (
rare(dup2(parentfd, dup_parent_to_first) < 0)) {
327 _exit((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
330 if (
rare(dup2(dup_parent_to_first, dup_parent_to_first ^ 1) < 0))
331 goto report_dup_failure;
336 lockfd = dup2(lockfd, 2);
337 if (
rare(lockfd < 0))
goto report_dup_failure;
338 }
else if (!lockfd_cloexec_cleared &&
O_CLOEXEC != 0) {
339 #if defined F_SETFD && defined FD_CLOEXEC 340 (void)fcntl(lockfd, F_SETFD, 0);
344 if (
rare(dup2(lockfd, 3) < 0 || dup2(3, lockfd) < 0))
345 goto report_dup_failure;
354 fl.l_whence = SEEK_SET;
357 while (fcntl(lockfd, wait ? F_SETLKW : F_SETLK, &fl) == -1) {
358 if (errno != EINTR) {
361 if (errno == EACCES || errno == EAGAIN) {
363 }
else if (errno == ENOLCK) {
375 while (write(1,
"", 1) < 0) {
381 if (errno != EINTR) _exit(UNKNOWN);
387 if (chdir(
"/") < 0) {
397 execl(
"/bin/cat",
"/bin/cat", static_cast<void*>(NULL));
400 while (read(0, &ch, 1) != 0) {
411 explanation.assign(
"Couldn't fork: ");
420 ssize_t n = read(fds[0], &ch, 1);
432 if (errno != EINTR) {
434 explanation.assign(
"Error reading from child process: ");
443 while (waitpid(child, &status, 0) < 0) {
444 if (errno != EINTR)
return UNKNOWN;
448 if (WIFEXITED(status)) {
449 int exit_status = WEXITSTATUS(status);
450 if (
usual(exit_status > 0 && exit_status <= UNKNOWN))
451 why =
static_cast<reason>(exit_status);
460 #if defined __CYGWIN__ || defined __WIN32__ 461 if (hFile == INVALID_HANDLE_VALUE)
return;
463 hFile = INVALID_HANDLE_VALUE;
464 #elif defined FLINTLOCK_USE_FLOCK 473 if (pid == 0)
return;
485 if (kill(pid, SIGKILL) == 0) {
487 while (waitpid(pid, &status, 0) < 0) {
488 if (errno != EINTR)
break;
496 const string & db_dir,
497 const string & explanation)
const 499 string msg(
"Unable to get write lock on ");
502 msg +=
": already locked";
504 msg +=
": locking probably not supported by this FS";
506 msg +=
": too many open files";
508 if (!explanation.empty())
509 msg +=
": " + explanation;
void throw_databaselockerror(FlintLock::reason why, const std::string &db_dir, const std::string &explanation) const
Throw Xapian::DatabaseLockError.
void release()
Release the lock.
include <sys/socket.h> with portability workarounds.
Convert errno value to std::string, thread-safe if possible.
Implementation of closefrom() function.
WritableDatabase open()
Construct a WritableDatabase object for a new, empty InMemory database.
Flint-compatible database locking.
bool test() const
Test if the lock is held.
Hierarchy of classes which Xapian can throw as exceptions.
DatabaseLockError indicates failure to lock a database.
void errno_to_string(int e, string &s)
Indicates an attempt to use a feature which is unavailable.
static void throw_cannot_test_lock()
reason lock(bool exclusive, bool wait, std::string &explanation)
Attempt to obtain the lock.
Various assertion macros.
include <fcntl.h>, but working around broken platforms.