diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index 947f0043..fa702dde 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -1377,6 +1377,10 @@ string RclConfig::getPidfile() const { return path_cat(getCacheDir(), "index.pid"); } +string RclConfig::getIdxStopFile() const +{ + return path_cat(getCacheDir(), "index.stop"); +} /* Eliminate the common leaf part of file paths p1 and p2. Example: * /mnt1/common/part /mnt2/common/part -> /mnt1 /mnt2. This is used diff --git a/src/common/rclconfig.h b/src/common/rclconfig.h index d289f5cf..0c503019 100644 --- a/src/common/rclconfig.h +++ b/src/common/rclconfig.h @@ -203,6 +203,7 @@ class RclConfig { string getPidfile() const; /** Get indexing status file name */ string getIdxStatusFile() const; + string getIdxStopFile() const; /** Do path translation according to the ptrans table */ void urlrewrite(const string& dbdir, string& url) const; ConfSimple *getPTrans() { diff --git a/src/index/recollindex.cpp b/src/index/recollindex.cpp index 390fcb8a..a2a080a3 100644 --- a/src/index/recollindex.cpp +++ b/src/index/recollindex.cpp @@ -111,6 +111,7 @@ class MyUpdater : public DbIxStatusUpdater { public: MyUpdater(const RclConfig *config) : m_file(config->getIdxStatusFile().c_str()), + m_stopfilename(config->getIdxStopFile()), m_prevphase(DbIxStatus::DBIXS_NONE) { // The total number of files included in the index is actually // difficult to compute from the index itself. For display @@ -145,7 +146,10 @@ class MyUpdater : public DbIxStatusUpdater { m_file.set("hasmonitor", status.hasmonitor); m_file.holdWrites(false); } - + if (path_exists(m_stopfilename)) { + unlink(m_stopfilename.c_str()); + stopindexing = true; + } if (stopindexing) { return false; } @@ -166,6 +170,7 @@ class MyUpdater : public DbIxStatusUpdater { private: ConfSimple m_file; + string m_stopfilename; Chrono m_chron; DbIxStatus::Phase m_prevphase; }; diff --git a/src/qtgui/rclm_idx.cpp b/src/qtgui/rclm_idx.cpp index 19ace869..6b71d42b 100644 --- a/src/qtgui/rclm_idx.cpp +++ b/src/qtgui/rclm_idx.cpp @@ -246,13 +246,6 @@ void RclMain::toggleIndexing() break; case IXST_RUNNINGNOTMINE: { -#ifdef _WIN32 - QMessageBox::warning(0, tr("Warning"), - tr("The current indexing process " - "was not started from this " - "interface, can't kill it"), - QMessageBox::Ok, QMessageBox::NoButton); -#else int rep = QMessageBox::information( 0, tr("Warning"), @@ -261,12 +254,16 @@ void RclMain::toggleIndexing() "anyway, or Cancel to leave it alone"), QMessageBox::Ok, QMessageBox::Cancel, QMessageBox::NoButton); if (rep == QMessageBox::Ok) { +#ifdef _WIN32 + // No simple way to signal the process. Use the stop file + ::close(::creat(theconfig->getIdxStopFile().c_str(), 0666)); +#else Pidfile pidfile(theconfig->getPidfile()); pid_t pid = pidfile.open(); if (pid > 0) kill(pid, SIGTERM); +#endif // !_WIN32 } -#endif } break; case IXST_NOTRUNNING: diff --git a/src/utils/pathut.cpp b/src/utils/pathut.cpp index f6a4350d..84643605 100644 --- a/src/utils/pathut.cpp +++ b/src/utils/pathut.cpp @@ -13,6 +13,30 @@ * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * flock emulation: + * Emulate flock on platforms that lack it, primarily Windows and MinGW. + * + * This is derived from sqlite3 sources. + * https://www.sqlite.org/src/finfo?name=src/os_win.c + * https://www.sqlite.org/copyright.html + * + * Written by Richard W.M. Jones + * + * Copyright (C) 2008-2019 Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ #ifdef BUILDING_RECOLL @@ -105,7 +129,144 @@ static bool path_isdriveabs(const string& s) } return false; } -#endif + +/* Operations for the 'flock' call (same as Linux kernel constants). */ +# define LOCK_SH 1 /* Shared lock. */ +# define LOCK_EX 2 /* Exclusive lock. */ +# define LOCK_UN 8 /* Unlock. */ + +/* Can be OR'd in to one of the above. */ +# define LOCK_NB 4 /* Don't block when locking. */ + +#include + +/* Determine the current size of a file. Because the other braindead + * APIs we'll call need lower/upper 32 bit pairs, keep the file size + * like that too. + */ +static BOOL +file_size (HANDLE h, DWORD * lower, DWORD * upper) +{ + *lower = GetFileSize (h, upper); + /* It appears that we can't lock an empty file, a lock is always + over a data section. But we seem to be able to set a lock + beyond the current file size, which is enough to get Pidfile + working */ + if (*lower == 0 && *upper == 0) { + *lower = 100; + } + return 1; +} + +/* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */ +# ifndef LOCKFILE_FAIL_IMMEDIATELY +# define LOCKFILE_FAIL_IMMEDIATELY 1 +# endif + +/* Acquire a lock. */ +static BOOL +do_lock (HANDLE h, int non_blocking, int exclusive) +{ + BOOL res; + DWORD size_lower, size_upper; + OVERLAPPED ovlp; + int flags = 0; + + /* We're going to lock the whole file, so get the file size. */ + res = file_size (h, &size_lower, &size_upper); + if (!res) + return 0; + + /* Start offset is 0, and also zero the remaining members of this struct. */ + memset (&ovlp, 0, sizeof ovlp); + + if (non_blocking) + flags |= LOCKFILE_FAIL_IMMEDIATELY; + if (exclusive) + flags |= LOCKFILE_EXCLUSIVE_LOCK; + + return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); +} + +/* Unlock reader or exclusive lock. */ +static BOOL +do_unlock (HANDLE h) +{ + int res; + DWORD size_lower, size_upper; + + res = file_size (h, &size_lower, &size_upper); + if (!res) + return 0; + + return UnlockFile (h, 0, 0, size_lower, size_upper); +} + +/* Now our BSD-like flock operation. */ +int +flock (int fd, int operation) +{ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD res; + int non_blocking; + + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + non_blocking = operation & LOCK_NB; + operation &= ~LOCK_NB; + + switch (operation) { + case LOCK_SH: + res = do_lock (h, non_blocking, 0); + break; + case LOCK_EX: + res = do_lock (h, non_blocking, 1); + break; + case LOCK_UN: + res = do_unlock (h); + break; + default: + errno = EINVAL; + return -1; + } + + /* Map Windows errors into Unix errnos. As usual MSDN fails to + * document the permissible error codes. + */ + if (!res) { + DWORD err = GetLastError (); + switch (err){ + /* This means someone else is holding a lock. */ + case ERROR_LOCK_VIOLATION: + errno = EAGAIN; + break; + + /* Out of memory. */ + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + + case ERROR_BAD_COMMAND: + errno = EINVAL; + break; + + /* Unlikely to be other errors, but at least don't lose the + * error code. + */ + default: + errno = err; + } + + return -1; + } + + return 0; +} + +#endif // Win32 only section bool fsocc(const string& path, int *pc, long long *avmbs) { @@ -931,14 +1092,14 @@ pid_t Pidfile::read_pid() { int fd = ::open(m_path.c_str(), O_RDONLY); if (fd == -1) { - return (pid_t) - 1; + return (pid_t) -1; } char buf[16]; int i = read(fd, buf, sizeof(buf) - 1); ::close(fd); if (i <= 0) { - return (pid_t) - 1; + return (pid_t) -1; } buf[i] = '\0'; char *endptr; @@ -967,12 +1128,9 @@ int Pidfile::flopen() int serrno = errno; this->close() errno = serrno; - m_reason = "fcntl lock failed"; + m_reason = "fcntl lock failed"; return -1; } -#else -#ifdef _WIN32 - return 0; #else int operation = LOCK_EX | LOCK_NB; if (flock(m_fd, operation) == -1) { @@ -982,7 +1140,6 @@ int Pidfile::flopen() m_reason = "flock failed"; return -1; } -#endif // ! win32 #endif // ! sun if (ftruncate(m_fd, 0) != 0) {