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) {
206 #if defined F_SETFD && defined FD_CLOEXEC 207 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC |
O_CLOEXEC, 0666);
209 int lockfd =
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 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;
269 if (
rare(lockfd < 2)) {
275 int lockfd_dup = fcntl(lockfd, F_DUPFD, 2);
278 if (lockfd_dup < 0) {
279 return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
285 int lockfd_dup = dup(lockfd);
286 if (
rare(lockfd_dup < 2)) {
288 if (lockfd_dup < 0) {
292 int lockfd_dup2 = dup(lockfd);
293 if (lockfd_dup2 < 0) {
298 lockfd = lockfd_dup2;
301 return ((eno == EMFILE || eno == ENFILE) ? FDLIMIT : UNKNOWN);
311 if (socketpair(AF_UNIX, SOCK_STREAM|
SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
313 explanation.assign(
"Couldn't create socketpair: ");
315 reason why = ((errno == EMFILE || errno == ENFILE) ? FDLIMIT : UNKNOWN);
320 pid_t child = fork();
326 #if defined F_SETFD && defined FD_CLOEXEC 336 (void)fcntl(fds[1], F_SETFD, 0);
338 (void)fcntl(lockfd, F_SETFD, 0);
349 for (
int i = 2; i < lockfd; ++i) {
352 while (
close(i) < 0 && errno == EINTR) { }
360 fl.l_whence = SEEK_SET;
363 while (fcntl(lockfd, wait ? F_SETLKW : F_SETLK, &fl) == -1) {
364 if (errno != EINTR) {
367 if (errno == EACCES || errno == EAGAIN) {
369 }
else if (errno == ENOLCK) {
381 char ch =
static_cast<char>(why);
382 while (write(1, &ch, 1) < 0) {
387 if (errno != EINTR) _exit(1);
389 if (why != SUCCESS) _exit(0);
394 if (chdir(
"/") < 0) {
404 execl(
"/bin/cat",
"/bin/cat", static_cast<void*>(NULL));
407 while (read(0, &ch, 1) != 0) {
418 explanation.assign(
"Couldn't fork: ");
429 ssize_t n = read(fds[0], &ch, 1);
431 why =
static_cast<reason>(ch);
432 if (why != SUCCESS)
break;
440 explanation.assign(
"Got EOF reading from child process");
443 if (errno != EINTR) {
445 explanation.assign(
"Error reading from child process: ");
454 while (waitpid(child, &status, 0) < 0) {
455 if (errno != EINTR)
break;
464 #if defined __CYGWIN__ || defined __WIN32__ 465 if (hFile == INVALID_HANDLE_VALUE)
return;
467 hFile = INVALID_HANDLE_VALUE;
468 #elif defined FLINTLOCK_USE_FLOCK 477 if (pid == 0)
return;
489 if (kill(pid, SIGKILL) == 0) {
491 while (waitpid(pid, &status, 0) < 0) {
492 if (errno != EINTR)
break;
500 const string & db_dir,
501 const string & explanation)
const 503 string msg(
"Unable to get write lock on ");
506 msg +=
": already locked";
508 msg +=
": locking probably not supported by this FS";
510 msg +=
": too many open files";
512 if (!explanation.empty())
513 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.