31 #include <sys/types.h>
43 # include <cygwin/version.h>
44 # include <sys/cygwin.h>
47 #ifdef FLINTLOCK_USE_FLOCK
48 # include <sys/file.h>
59 # define F_OFD_GETLK 36
60 # define F_OFD_SETLK 37
61 # define F_OFD_SETLKW 38
77 if (filename.empty())
return false;
79 #if defined __CYGWIN__ || defined __WIN32__
80 if (hFile != INVALID_HANDLE_VALUE)
return true;
84 #elif defined FLINTLOCK_USE_FLOCK
85 if (fd != -1)
return true;
90 if (fd != -1)
return true;
91 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
94 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
95 throw_databaselockerror(why, filename,
"Testing lock");
100 fl.l_whence = SEEK_SET;
104 while (fcntl(lockfd, F_GETLK, &fl) == -1) {
105 if (errno != EINTR) {
114 reason why = (e == ENOLCK ? UNSUPPORTED : UNKNOWN);
115 throw_databaselockerror(why, filename,
"Testing lock");
119 return fl.l_type != F_UNLCK;
128 #if defined __CYGWIN__ || defined __WIN32__
129 Assert(hFile == INVALID_HANDLE_VALUE);
132 #if CYGWIN_VERSION_API_MAJOR == 0 && CYGWIN_VERSION_API_MINOR < 181
133 cygwin_conv_to_win32_path(filename.c_str(), fnm);
135 if (cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, filename.c_str(),
136 fnm, MAX_PATH) < 0) {
137 explanation.assign(
"cygwin_conv_path failed: ");
143 const char *fnm = filename.c_str();
149 hFile = CreateFile(fnm, GENERIC_WRITE, FILE_SHARE_READ,
150 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
151 if (hFile != INVALID_HANDLE_VALUE)
return SUCCESS;
152 if (GetLastError() == ERROR_ALREADY_EXISTS) {
159 explanation = string();
161 #elif defined FLINTLOCK_USE_FLOCK
176 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
179 explanation.assign(
"Couldn't open lockfile: ");
181 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
185 if (!wait) op |= LOCK_NB;
186 while (flock(lockfd, op) == -1) {
187 if (errno != EINTR) {
209 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
212 explanation.assign(
"Couldn't open lockfile: ");
214 return ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
227 static bool f_ofd_setlk_fails =
false;
228 if (!f_ofd_setlk_fails) {
231 fl.l_whence = SEEK_SET;
235 while (fcntl(lockfd, wait ? F_OFD_SETLKW : F_OFD_SETLK, &fl) == -1) {
236 if (errno != EINTR) {
237 if (errno == EINVAL) {
246 case EACCES:
case EAGAIN:
259 f_ofd_setlk_fails =
true;
264 if (socketpair(AF_UNIX, SOCK_STREAM|
SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
266 explanation.assign(
"Couldn't create socketpair: ");
268 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
275 if (
rare(fds[1] == 2)) swap(fds[0], fds[1]);
277 pid_t child = fork();
288 int parentfd = fds[1];
307 bool lockfd_cloexec_cleared =
false;
308 int dup_parent_to_first = parentfd == 0 ? 1 : 0;
309 if (
rare(lockfd < 2)) {
310 int oldlockfd = lockfd;
313 lockfd = dup2(lockfd, 2);
314 if (
rare(lockfd < 0))
goto report_dup_failure;
315 lockfd_cloexec_cleared =
true;
317 dup_parent_to_first = oldlockfd;
324 if (
rare(dup2(parentfd, dup_parent_to_first) < 0)) {
326 _exit((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
329 if (
rare(dup2(dup_parent_to_first, dup_parent_to_first ^ 1) < 0))
330 goto report_dup_failure;
335 lockfd = dup2(lockfd, 2);
336 if (
rare(lockfd < 0))
goto report_dup_failure;
337 }
else if (!lockfd_cloexec_cleared &&
O_CLOEXEC != 0) {
338 #if defined F_SETFD && defined FD_CLOEXEC
339 (void)fcntl(lockfd, F_SETFD, 0);
343 if (
rare(dup2(lockfd, 3) < 0 || dup2(3, lockfd) < 0))
344 goto report_dup_failure;
353 fl.l_whence = SEEK_SET;
356 while (fcntl(lockfd, wait ? F_SETLKW : F_SETLK, &fl) == -1) {
357 if (errno != EINTR) {
360 if (errno == EACCES || errno == EAGAIN) {
362 }
else if (errno == ENOLCK) {
374 while (write(1,
"", 1) < 0) {
380 if (errno != EINTR) _exit(UNKNOWN);
386 if (chdir(
"/") < 0) {
396 execl(
"/bin/cat",
"/bin/cat",
static_cast<void*
>(NULL));
399 while (read(0, &ch, 1) != 0) {
410 explanation.assign(
"Couldn't fork: ");
419 ssize_t n = read(fds[0], &ch, 1);
431 if (errno != EINTR) {
433 explanation.assign(
"Error reading from child process: ");
442 while (waitpid(child, &status, 0) < 0) {
443 if (errno != EINTR)
return UNKNOWN;
447 if (WIFEXITED(status)) {
448 int exit_status = WEXITSTATUS(status);
449 if (
usual(exit_status > 0 && exit_status <= UNKNOWN))
450 why =
static_cast<reason>(exit_status);
459 #if defined __CYGWIN__ || defined __WIN32__
460 if (hFile == INVALID_HANDLE_VALUE)
return;
462 hFile = INVALID_HANDLE_VALUE;
463 #elif defined FLINTLOCK_USE_FLOCK
472 if (pid == 0)
return;
484 if (kill(pid, SIGKILL) == 0) {
486 while (waitpid(pid, &status, 0) < 0) {
487 if (errno != EINTR)
break;
495 const string & db_dir,
496 const string & explanation)
const
498 string msg(
"Unable to get write lock on ");
501 msg +=
": already locked";
503 msg +=
": locking probably not supported by this FS";
505 msg +=
": too many open files";
507 if (!explanation.empty())
508 msg +=
": " + explanation;
void release()
Release the lock.
reason lock(bool exclusive, bool wait, std::string &explanation)
Attempt to obtain the lock.
bool test() const
Test if the lock is held.
void throw_databaselockerror(FlintLock::reason why, const std::string &db_dir, const std::string &explanation) const
Throw Xapian::DatabaseLockError.
DatabaseLockError indicates failure to lock a database.
Indicates an attempt to use a feature which is unavailable.
Implementation of closefrom() function.
void errno_to_string(int e, string &s)
Convert errno value to std::string, thread-safe if possible.
Hierarchy of classes which Xapian can throw as exceptions.
static void throw_cannot_test_lock()
Flint-compatible database locking.
Database open(std::string_view host, unsigned int port, unsigned timeout=10000, unsigned connect_timeout=10000)
Construct a Database object for read-only access to a remote database accessed via a TCP connection.
Various assertion macros.
include <fcntl.h>, but working around broken platforms.
include <sys/socket.h> with portability workarounds.