Merge branch 'master' of https://framagit.org/medoc92/recoll
This commit is contained in:
commit
a777995946
@ -452,6 +452,8 @@ bool startMonitor(RclConfig *conf, int opts)
|
||||
auxinterval = dfltauxinterval;
|
||||
if (!conf->getConfParam("monixinterval", &ixinterval))
|
||||
ixinterval = dfltixinterval;
|
||||
bool doweb{false};
|
||||
conf->getConfParam("processwebqueue", &doweb);
|
||||
|
||||
rclEQ.setConfig(conf);
|
||||
rclEQ.setopts(opts);
|
||||
@ -471,11 +473,13 @@ bool startMonitor(RclConfig *conf, int opts)
|
||||
|
||||
while (true) {
|
||||
time_t now = time(0);
|
||||
if (now - lastmovetime > ixinterval) {
|
||||
#ifndef DISABLE_WEB_INDEXER
|
||||
if (doweb && (now - lastmovetime > ixinterval)) {
|
||||
lastmovetime = now;
|
||||
runWebFilesMoverScript(conf);
|
||||
}
|
||||
|
||||
#endif // DISABLE_WEB_INDEXER
|
||||
|
||||
{
|
||||
// Wait for event or timeout.
|
||||
// Set a relatively short timeout for better monitoring of
|
||||
@ -572,6 +576,7 @@ bool startMonitor(RclConfig *conf, int opts)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Check for a config change
|
||||
if (!(opts & RCLMON_NOCONFCHECK) && o_reexec && conf->sourceChanged()) {
|
||||
LOGDEB("Rclmonprc: config changed, reexecuting myself\n" );
|
||||
@ -581,6 +586,7 @@ bool startMonitor(RclConfig *conf, int opts)
|
||||
o_reexec->removeArg("-n");
|
||||
o_reexec->reexec();
|
||||
}
|
||||
#endif // ! _WIN32
|
||||
}
|
||||
LOGDEB("Rclmonprc: calling queue setTerminate\n" );
|
||||
rclEQ.setTerminate();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "autoconfig.h"
|
||||
#ifdef RCL_MONITOR
|
||||
/* Copyright (C) 2006 J.F.Dockes
|
||||
/* Copyright (C) 2006-2021 J.F.Dockes
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
@ -16,6 +16,35 @@
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* The code for the Win32 version of the monitor was largely copied from efsw:
|
||||
* https://github.com/SpartanJ/efsw
|
||||
* LICENSE for the original WIN32 code:
|
||||
* Copyright (c) 2020 Martín Lucas Golini
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com)
|
||||
* http://code.google.com/p/simplefilewatcher/ also MIT licensed.
|
||||
*/
|
||||
|
||||
|
||||
#include "autoconfig.h"
|
||||
|
||||
#include <errno.h>
|
||||
@ -72,8 +101,8 @@ public:
|
||||
|
||||
virtual FsTreeWalker::Status processone(
|
||||
const string &fn, const struct PathStat *st, FsTreeWalker::CbFlag flg) {
|
||||
MONDEB("rclMonRcvRun: processone " << fn << " m_mon " << m_mon <<
|
||||
" m_mon->ok " << (m_mon ? m_mon->ok() : false) << std::endl);
|
||||
MONDEB("walkerCB: processone " << fn << " m_mon " << m_mon <<
|
||||
" m_mon->ok " << (m_mon ? m_mon->ok() : false) << "\n");
|
||||
|
||||
if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) {
|
||||
m_config->setKeyDir(fn);
|
||||
@ -90,7 +119,7 @@ public:
|
||||
if (ev.m_etyp != RclMonEvent::RCLEVT_NONE)
|
||||
m_queue->pushEvent(ev);
|
||||
} else {
|
||||
MONDEB("rclMonRcvRun: no event pending\n");
|
||||
MONDEB("walkerCB: no event pending\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -99,8 +128,10 @@ public:
|
||||
// We do nothing special if addWatch fails for a reasonable reason
|
||||
if (!m_mon->addWatch(fn, true)) {
|
||||
if (m_mon->saved_errno != EACCES &&
|
||||
m_mon->saved_errno != ENOENT)
|
||||
m_mon->saved_errno != ENOENT) {
|
||||
LOGINF("walkerCB: addWatch failed\n");
|
||||
return FsTreeWalker::FtwError;
|
||||
}
|
||||
}
|
||||
} else if (!m_mon->generatesExist() && flg == FsTreeWalker::FtwRegular) {
|
||||
// Have to synthetize events for regular files existence
|
||||
@ -164,8 +195,8 @@ void *rclMonRcvRun(void *q)
|
||||
FsTreeWalker walker;
|
||||
walker.setSkippedPaths(lconfig.getDaemSkippedPaths());
|
||||
WalkCB walkcb(&lconfig, mon, queue, walker);
|
||||
for (auto it = tdl.begin(); it != tdl.end(); it++) {
|
||||
lconfig.setKeyDir(*it);
|
||||
for (const auto& dir : tdl) {
|
||||
lconfig.setKeyDir(dir);
|
||||
// Adjust the follow symlinks options
|
||||
bool follow;
|
||||
if (lconfig.getConfParam("followLinks", &follow) &&
|
||||
@ -176,20 +207,18 @@ void *rclMonRcvRun(void *q)
|
||||
}
|
||||
// We have to special-case regular files which are part of the topdirs
|
||||
// list because we the tree walker only adds watches for directories
|
||||
if (path_isdir(*it, follow)) {
|
||||
LOGDEB("rclMonRcvRun: walking " << *it << "\n");
|
||||
if (walker.walk(*it, walkcb) != FsTreeWalker::FtwOk) {
|
||||
if (path_isdir(dir, follow)) {
|
||||
LOGDEB("rclMonRcvRun: walking " << dir << "\n");
|
||||
if (walker.walk(dir, walkcb) != FsTreeWalker::FtwOk) {
|
||||
LOGERR("rclMonRcvRun: tree walk failed\n");
|
||||
goto terminate;
|
||||
}
|
||||
if (walker.getErrCnt() > 0) {
|
||||
LOGINFO("rclMonRcvRun: fs walker errors: " <<
|
||||
walker.getReason() << "\n");
|
||||
LOGINFO("rclMonRcvRun: fs walker errors: " << walker.getReason() << "\n");
|
||||
}
|
||||
} else {
|
||||
if (!mon->addWatch(*it, false)) {
|
||||
LOGERR("rclMonRcvRun: addWatch failed for " << *it <<
|
||||
" errno " << mon->saved_errno << std::endl);
|
||||
if (!mon->addWatch(dir, false)) {
|
||||
LOGSYSERR("rclMonRcvRun", "addWatch", dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,8 +237,7 @@ void *rclMonRcvRun(void *q)
|
||||
}
|
||||
|
||||
// Forever wait for monitoring events and add them to queue:
|
||||
MONDEB("rclMonRcvRun: waiting for events. q->ok(): " << queue->ok() <<
|
||||
std::endl);
|
||||
MONDEB("rclMonRcvRun: waiting for events. q->ok(): " << queue->ok() << "\n");
|
||||
while (queue->ok() && mon->ok()) {
|
||||
RclMonEvent ev;
|
||||
// Note: I could find no way to get the select
|
||||
@ -245,8 +273,7 @@ void *rclMonRcvRun(void *q)
|
||||
goto terminate;
|
||||
}
|
||||
if (walker.getErrCnt() > 0) {
|
||||
LOGINFO("rclMonRcvRun: fs walker errors: " <<
|
||||
walker.getReason() << "\n");
|
||||
LOGINFO("rclMonRcvRun: fs walker errors: " << walker.getReason() << "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,7 +391,7 @@ bool RclFAM::addWatch(const string& path, bool isdir)
|
||||
return false;
|
||||
bool ret = false;
|
||||
|
||||
MONDEB("RclFAM::addWatch: adding " << path << std::endl);
|
||||
MONDEB("RclFAM::addWatch: adding " << path << "\n");
|
||||
|
||||
// It happens that the following call block forever.
|
||||
// We'd like to be able to at least terminate on a signal here, but
|
||||
@ -410,7 +437,7 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fam_fd, &readfds);
|
||||
|
||||
MONDEB("RclFAM::getEvent: select. fam_fd is " << fam_fd << std::endl);
|
||||
MONDEB("RclFAM::getEvent: select. fam_fd is " << fam_fd << "\n");
|
||||
// Fam / gamin is sometimes a bit slow to send events. Always add
|
||||
// a little timeout, because if we fail to retrieve enough events,
|
||||
// we risk deadlocking in addwatch()
|
||||
@ -432,7 +459,7 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
|
||||
return false;
|
||||
}
|
||||
|
||||
MONDEB("RclFAM::getEvent: select returned " << ret << std::endl);
|
||||
MONDEB("RclFAM::getEvent: select returned " << ret << "\n");
|
||||
|
||||
if (!FD_ISSET(fam_fd, &readfds))
|
||||
return false;
|
||||
@ -464,8 +491,7 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
|
||||
ev.m_path = fe.filename;
|
||||
}
|
||||
|
||||
MONDEB("RclFAM::getEvent: " << event_name(fe.code) < " " <<
|
||||
ev.m_path << std::endl);
|
||||
MONDEB("RclFAM::getEvent: " << event_name(fe.code) < " " << ev.m_path << "\n");
|
||||
|
||||
switch (fe.code) {
|
||||
case FAMCreated:
|
||||
@ -517,18 +543,16 @@ bool RclFAM::getEvent(RclMonEvent& ev, int msecs)
|
||||
class RclIntf : public RclMonitor {
|
||||
public:
|
||||
RclIntf()
|
||||
: m_ok(false), m_fd(-1), m_evp(0), m_ep(0)
|
||||
{
|
||||
if ((m_fd = inotify_init()) < 0) {
|
||||
LOGERR("RclIntf:: inotify_init failed, errno " << errno << "\n");
|
||||
return;
|
||||
}
|
||||
m_ok = true;
|
||||
}
|
||||
virtual ~RclIntf()
|
||||
{
|
||||
close();
|
||||
: m_ok(false), m_fd(-1), m_evp(0), m_ep(0) {
|
||||
if ((m_fd = inotify_init()) < 0) {
|
||||
LOGERR("RclIntf:: inotify_init failed, errno " << errno << "\n");
|
||||
return;
|
||||
}
|
||||
m_ok = true;
|
||||
}
|
||||
virtual ~RclIntf() {
|
||||
close();
|
||||
}
|
||||
|
||||
virtual bool addWatch(const string& path, bool isdir);
|
||||
virtual bool getEvent(RclMonEvent& ev, int msecs = -1);
|
||||
@ -586,7 +610,7 @@ bool RclIntf::addWatch(const string& path, bool)
|
||||
{
|
||||
if (!ok())
|
||||
return false;
|
||||
MONDEB("RclIntf::addWatch: adding " << path << std::endl);
|
||||
MONDEB("RclIntf::addWatch: adding " << path << "\n");
|
||||
// CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs
|
||||
uint32_t mask = IN_MODIFY | IN_CREATE
|
||||
| IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE
|
||||
@ -636,9 +660,8 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
|
||||
}
|
||||
int ret;
|
||||
MONDEB("RclIntf::getEvent: select\n");
|
||||
if ((ret = select(m_fd + 1, &readfds, 0, 0, msecs >= 0 ? &timeout : 0))
|
||||
< 0) {
|
||||
LOGERR("RclIntf::getEvent: select failed, errno " << errno << "\n");
|
||||
if ((ret = select(m_fd + 1, &readfds, 0, 0, msecs >= 0 ? &timeout : 0)) < 0) {
|
||||
LOGSYSERR("RclIntf::getEvent", "select", "");
|
||||
close();
|
||||
return false;
|
||||
} else if (ret == 0) {
|
||||
@ -652,8 +675,7 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
|
||||
return false;
|
||||
int rret;
|
||||
if ((rret=read(m_fd, m_evbuf, sizeof(m_evbuf))) <= 0) {
|
||||
LOGERR("RclIntf::getEvent: read failed, " << sizeof(m_evbuf) <<
|
||||
"->" << rret << " errno " << errno << "\n");
|
||||
LOGSYSERR("RclIntf::getEvent", "read", sizeof(m_evbuf));
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
@ -679,8 +701,7 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
|
||||
ev.m_path = path_cat(ev.m_path, evp->name);
|
||||
}
|
||||
|
||||
MONDEB("RclIntf::getEvent: " << event_name(evp->mask) << " " <<
|
||||
ev.m_path << std::endl);
|
||||
MONDEB("RclIntf::getEvent: " << event_name(evp->mask) << " " << ev.m_path << "\n");
|
||||
|
||||
if ((evp->mask & IN_MOVED_FROM) && (evp->mask & IN_ISDIR)) {
|
||||
// We get this when a directory is renamed. Erase the subtree
|
||||
@ -725,20 +746,477 @@ bool RclIntf::getEvent(RclMonEvent& ev, int msecs)
|
||||
#endif // RCL_USE_INOTIFY
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
|
||||
/*
|
||||
* WIN32 VERSION ISSUES:
|
||||
*
|
||||
* - It appears that watching a subdirectory of a given directory
|
||||
* prevents renaming the top directory, Windows says: can't rename
|
||||
* because open or a file in it is open. This is a major issue of
|
||||
* course. Check if this can be solved by using a recursive watch
|
||||
* instead of setting watches on all subdirs. Would need a code
|
||||
* changes in the "generic" part of course.
|
||||
* - In general, directory renames need more studying.
|
||||
* - Otherwise appears to more or less work...
|
||||
*/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
|
||||
#include "safewindows.h"
|
||||
|
||||
typedef long WatchID;
|
||||
class WatcherWin32;
|
||||
class RclFSWatchWin32;
|
||||
|
||||
enum class Action {Add = 1, Delete = 2, Modify = 3, Move = 4};
|
||||
|
||||
class FileWatchListener {
|
||||
public:
|
||||
virtual ~FileWatchListener() {}
|
||||
|
||||
/// Handles the action file action
|
||||
/// @param watchid The watch id for the directory
|
||||
/// @param dir The directory
|
||||
/// @param filename The filename that was accessed (not full path)
|
||||
/// @param action Action that was performed
|
||||
/// @param oldFilename The name of the file or directory moved
|
||||
virtual void handleFileAction(WatchID watchid, const std::string& dir, const std::string& fn,
|
||||
Action action, bool isdir, std::string oldfn = "" ) = 0;
|
||||
};
|
||||
|
||||
// Internal watch data
|
||||
struct WatcherStructWin32
|
||||
{
|
||||
OVERLAPPED Overlapped;
|
||||
WatcherWin32 *Watch;
|
||||
};
|
||||
|
||||
class WatcherWin32 {
|
||||
public:
|
||||
WatcherWin32() {}
|
||||
|
||||
WatchID ID;
|
||||
FileWatchListener *Listener;
|
||||
bool Recursive;
|
||||
std::string DirName;
|
||||
std::string OldFileName;
|
||||
|
||||
HANDLE DirHandle{nullptr};
|
||||
// do NOT make this bigger than 64K because it will fail if the folder being watched is on the
|
||||
// network! (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
|
||||
BYTE Buffer[8 * 1024];
|
||||
DWORD NotifyFilter{0};
|
||||
bool StopNow{false};
|
||||
RclFSWatchWin32 *Watch{nullptr};
|
||||
};
|
||||
|
||||
class RclFSWatchWin32 {
|
||||
public:
|
||||
RclFSWatchWin32();
|
||||
|
||||
virtual ~RclFSWatchWin32();
|
||||
|
||||
// Add a directory watch
|
||||
// On error returns -1
|
||||
WatchID addWatch(const std::string& directory, FileWatchListener *watcher, bool recursive);
|
||||
|
||||
// 2nd stage of action processing (after the static handler which just reads the data)
|
||||
void handleAction(WatcherWin32 *watch, const std::string& fn, unsigned long action);
|
||||
|
||||
bool ok() const {
|
||||
return mInitOK;
|
||||
}
|
||||
|
||||
// Fetch events, with msecs timeout if there are no more
|
||||
void run(DWORD msecs);
|
||||
|
||||
private:
|
||||
HANDLE mIOCP;
|
||||
// Using a vector because we don't remove watches. Change to list if needed.
|
||||
std::vector<WatcherStructWin32*> mWatches;
|
||||
bool mInitOK{false};
|
||||
WatchID mLastWatchID{0};
|
||||
|
||||
std::mutex mWatchesLock;
|
||||
|
||||
bool pathInWatches(const std::string& path);
|
||||
/// Remove all directory watches.
|
||||
void removeAllWatches();
|
||||
};
|
||||
|
||||
class RclMonitorWin32 : public RclMonitor, public FileWatchListener {
|
||||
public:
|
||||
RclMonitorWin32() {
|
||||
MONDEB("RclMonitorWin32::RclMonitorWin32\n");
|
||||
}
|
||||
virtual ~RclMonitorWin32() {}
|
||||
|
||||
virtual bool addWatch(const string& path, bool /*isDir*/) override {
|
||||
MONDEB("RclMonitorWin32::addWatch: " << path << "\n");
|
||||
return m_fswatcher.addWatch(path, this, false) != -1;
|
||||
}
|
||||
|
||||
virtual bool getEvent(RclMonEvent& ev, int msecs = -1) {
|
||||
PRETEND_USE(msecs);
|
||||
if (!m_events.empty()) {
|
||||
ev = m_events.front();
|
||||
m_events.pop();
|
||||
return true;
|
||||
}
|
||||
m_fswatcher.run(msecs);
|
||||
if (!m_events.empty()) {
|
||||
ev = m_events.front();
|
||||
m_events.pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool ok() const override {
|
||||
return m_fswatcher.ok();
|
||||
}
|
||||
// Does this monitor generate 'exist' events at startup?
|
||||
virtual bool generatesExist() const override {
|
||||
return false;
|
||||
}
|
||||
virtual void handleFileAction(WatchID watchid, const std::string& dir, const std::string& fn,
|
||||
Action action, bool isdir, std::string oldfn = "") {
|
||||
MONDEB("RclMonitorWin32::handleFileAction: dir [" << dir << "] fn [" << fn << "] act " <<
|
||||
int(action) << " isdir " << isdir << " oldfn [" << oldfn << "]\n");
|
||||
RclMonEvent event;
|
||||
switch (action) {
|
||||
case Action::Move:
|
||||
case Action::Add: event.m_etyp = isdir ?
|
||||
RclMonEvent::RCLEVT_DIRCREATE : RclMonEvent::RCLEVT_MODIFY; break;
|
||||
case Action::Delete:
|
||||
event.m_etyp = RclMonEvent::RCLEVT_DELETE;
|
||||
if (isdir) {
|
||||
event.m_etyp |= RclMonEvent::RCLEVT_ISDIR;
|
||||
}
|
||||
break;
|
||||
case Action::Modify: event.m_etyp = RclMonEvent::RCLEVT_MODIFY; break;
|
||||
}
|
||||
event.m_path = path_cat(dir, fn);
|
||||
m_events.push(event);
|
||||
}
|
||||
|
||||
// Save significant errno after monitor calls
|
||||
int saved_errno{0};
|
||||
private:
|
||||
std::queue<RclMonEvent> m_events;
|
||||
RclFSWatchWin32 m_fswatcher;
|
||||
};
|
||||
|
||||
|
||||
/// Stops monitoring a directory.
|
||||
void DestroyWatch(WatcherStructWin32 *pWatch)
|
||||
{
|
||||
if (pWatch) {
|
||||
WatcherWin32 *ww32 = pWatch->Watch;
|
||||
ww32->StopNow = true;
|
||||
CancelIoEx(ww32->DirHandle, &pWatch->Overlapped);
|
||||
CloseHandle(ww32->DirHandle);
|
||||
delete ww32;
|
||||
// Shouldn't we call heapfree on the parameter here ??
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes the directory monitoring.
|
||||
bool RefreshWatch(WatcherStructWin32 *pWatch)
|
||||
{
|
||||
WatcherWin32 *ww32 = pWatch->Watch;
|
||||
return ReadDirectoryChangesW(
|
||||
ww32->DirHandle,
|
||||
ww32->Buffer,
|
||||
sizeof(ww32->Buffer),
|
||||
ww32->Recursive,
|
||||
ww32->NotifyFilter,
|
||||
NULL,
|
||||
&pWatch->Overlapped,
|
||||
NULL
|
||||
) != 0;
|
||||
}
|
||||
|
||||
/// Starts monitoring a directory.
|
||||
WatcherStructWin32 *CreateWatch(LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, HANDLE iocp)
|
||||
{
|
||||
WatcherStructWin32 *wsw32;
|
||||
size_t ptrsize = sizeof(*wsw32);
|
||||
wsw32 =static_cast<WatcherStructWin32*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize));
|
||||
|
||||
WatcherWin32 *ww32 = new WatcherWin32();
|
||||
wsw32->Watch = ww32;
|
||||
|
||||
ww32->DirHandle = CreateFileW(
|
||||
szDirectory,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (ww32->DirHandle != INVALID_HANDLE_VALUE &&
|
||||
CreateIoCompletionPort(ww32->DirHandle, iocp, 0, 1)) {
|
||||
ww32->NotifyFilter = NotifyFilter;
|
||||
ww32->Recursive = recursive;
|
||||
|
||||
if (RefreshWatch(wsw32)) {
|
||||
return wsw32;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(ww32->DirHandle);
|
||||
delete ww32;
|
||||
HeapFree(GetProcessHeap(), 0, wsw32);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
RclFSWatchWin32::RclFSWatchWin32()
|
||||
: mLastWatchID(0)
|
||||
{
|
||||
mIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
||||
if (mIOCP && mIOCP != INVALID_HANDLE_VALUE)
|
||||
mInitOK = true;
|
||||
}
|
||||
|
||||
RclFSWatchWin32::~RclFSWatchWin32()
|
||||
{
|
||||
mInitOK = false;
|
||||
|
||||
if (mIOCP && mIOCP != INVALID_HANDLE_VALUE) {
|
||||
PostQueuedCompletionStatus(mIOCP, 0, reinterpret_cast<ULONG_PTR>(this), NULL);
|
||||
}
|
||||
|
||||
// delete mThread ??
|
||||
|
||||
removeAllWatches();
|
||||
|
||||
CloseHandle(mIOCP);
|
||||
}
|
||||
|
||||
WatchID RclFSWatchWin32::addWatch(const std::string& _dir,FileWatchListener *watcher,bool recursive)
|
||||
{
|
||||
LOGDEB("RclFSWatchWin32::addWatch: " << _dir << " recursive " << recursive << "\n");
|
||||
std::string dir(_dir);
|
||||
path_slashize(dir);
|
||||
if (!path_isdir(dir)) {
|
||||
LOGDEB("RclFSWatchWin32::addWatch: not a directory: " << dir << "\n");
|
||||
return -1;
|
||||
}
|
||||
if (!path_readable(dir)) {
|
||||
LOGINF("RclFSWatchWin32::addWatch: not readable: " << dir << "\n");
|
||||
return 0;
|
||||
}
|
||||
path_catslash(dir);
|
||||
auto wdir = utf8towchar(dir);
|
||||
|
||||
std::unique_lock<std::mutex> lock(mWatchesLock);
|
||||
|
||||
if (pathInWatches(dir)) {
|
||||
MONDEB("RclFSWatchWin32::addWatch: already in watches: " << dir << "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
WatchID watchid = ++mLastWatchID;
|
||||
|
||||
WatcherStructWin32 *watch = CreateWatch(
|
||||
wdir.get(), recursive,
|
||||
FILE_NOTIFY_CHANGE_CREATION |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_SIZE,
|
||||
mIOCP
|
||||
);
|
||||
|
||||
if (nullptr == watch) {
|
||||
LOGINF("RclFSWatchWin32::addWatch: CreateWatch failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add the handle to the handles vector
|
||||
watch->Watch->ID = watchid;
|
||||
watch->Watch->Watch = this;
|
||||
watch->Watch->Listener = watcher;
|
||||
watch->Watch->DirName = dir;
|
||||
|
||||
mWatches.push_back(watch);
|
||||
|
||||
return watchid;
|
||||
}
|
||||
|
||||
void RclFSWatchWin32::removeAllWatches()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mWatchesLock);
|
||||
for( auto& watchp : mWatches) {
|
||||
DestroyWatch(watchp);
|
||||
}
|
||||
mWatches.clear();
|
||||
}
|
||||
|
||||
/// Unpacks events and passes them to the event processor
|
||||
void CALLBACK WatchCallback(DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
|
||||
{
|
||||
if (dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
WatcherStructWin32 *wsw32 = (WatcherStructWin32*)lpOverlapped;
|
||||
WatcherWin32 *ww32 = wsw32->Watch;
|
||||
|
||||
PFILE_NOTIFY_INFORMATION pNotify;
|
||||
size_t offset = 0;
|
||||
do {
|
||||
pNotify = (PFILE_NOTIFY_INFORMATION) &ww32->Buffer[offset];
|
||||
offset += pNotify->NextEntryOffset;
|
||||
|
||||
std::string sfn;
|
||||
wchartoutf8(pNotify->FileName, sfn, pNotify->FileNameLength / sizeof(WCHAR));
|
||||
ww32->Watch->handleAction(ww32, sfn, pNotify->Action);
|
||||
} while (pNotify->NextEntryOffset != 0);
|
||||
|
||||
if (!ww32->StopNow) {
|
||||
RefreshWatch(wsw32);
|
||||
}
|
||||
}
|
||||
|
||||
void RclFSWatchWin32::run(DWORD msecs)
|
||||
{
|
||||
if (!mWatches.empty()) {
|
||||
DWORD numOfBytes = 0;
|
||||
OVERLAPPED* ov = NULL;
|
||||
ULONG_PTR compKey = 0;
|
||||
BOOL res = FALSE;
|
||||
DWORD ms = msecs == -1 ? INFINITE : msecs;
|
||||
while ((res = GetQueuedCompletionStatus(mIOCP, &numOfBytes, &compKey, &ov, ms))) {
|
||||
if (compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>(this)) {
|
||||
// Called from ~RclFSWatchWin32. Must exit.
|
||||
MONDEB("RclFSWatchWin32::run: queuedcompletion said need exit\n");
|
||||
return;
|
||||
} else {
|
||||
std::unique_lock<std::mutex> lock(mWatchesLock);
|
||||
WatchCallback(numOfBytes, ov);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No watches yet.
|
||||
MONDEB("RclFSWatchWin32::run: no watches yet\n");
|
||||
DWORD ms = msecs == -1 ? 1000 : msecs;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
}
|
||||
|
||||
void RclFSWatchWin32::handleAction(WatcherWin32 *watch, const std::string& fn, unsigned long action)
|
||||
{
|
||||
Action fwAction;
|
||||
MONDEB("handleAction: fn [" << fn << "] action " << action << "\n");
|
||||
|
||||
// In case fn is not a simple name but a relative path (probably
|
||||
// possible/common if recursive is set ?), sort out the directory
|
||||
// path and simple file name.
|
||||
std::string newpath = path_cat(watch->DirName, fn);
|
||||
bool isdir = path_isdir(newpath);
|
||||
std::string simplefn = path_getsimple(newpath);
|
||||
std::string folderPath = path_getfather(newpath);
|
||||
|
||||
switch (action) {
|
||||
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||
watch->OldFileName = fn;
|
||||
/* FALLTHROUGH */
|
||||
case FILE_ACTION_REMOVED:
|
||||
fwAction = Action::Delete;
|
||||
// The system does not tell us if this was a directory, but we
|
||||
// need the info. Check if it was in the watches.
|
||||
// TBD: for a delete, we should delete all watches on the subtree !
|
||||
path_catslash(newpath);
|
||||
for (auto& watchp : mWatches) {
|
||||
if (watchp->Watch->DirName == newpath) {
|
||||
isdir = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FILE_ACTION_ADDED:
|
||||
fwAction = Action::Add;
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED:
|
||||
fwAction = Action::Modify;
|
||||
break;
|
||||
case FILE_ACTION_RENAMED_NEW_NAME: {
|
||||
fwAction = Action::Move;
|
||||
|
||||
// If this is a directory, possibly update the watches.
|
||||
// TBD: this seems wrong because we should process the whole subtree ?
|
||||
if (isdir) {
|
||||
// Update the new directory path
|
||||
std::string oldpath = path_cat(watch->DirName, watch->OldFileName);
|
||||
path_catslash(oldpath);
|
||||
for (auto& watchp : mWatches) {
|
||||
if (watchp->Watch->DirName == oldpath) {
|
||||
watchp->Watch->DirName = newpath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string oldFolderPath = watch->DirName +
|
||||
watch->OldFileName.substr(0, watch->OldFileName.find_last_of("/\\"));
|
||||
|
||||
if (folderPath == oldFolderPath) {
|
||||
watch->Listener->handleFileAction(watch->ID, folderPath, simplefn, fwAction, isdir,
|
||||
path_getsimple(watch->OldFileName));
|
||||
} else {
|
||||
// Calling the client with non-simple paths??
|
||||
watch->Listener->handleFileAction(watch->ID, watch->DirName, fn, fwAction, isdir,
|
||||
watch->OldFileName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
||||
watch->Listener->handleFileAction(watch->ID, folderPath, simplefn, fwAction, isdir);
|
||||
}
|
||||
|
||||
bool RclFSWatchWin32::pathInWatches(const std::string& path)
|
||||
{
|
||||
for (const auto& wsw32 : mWatches) {
|
||||
if (wsw32->Watch->DirName == path ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// The monitor 'factory'
|
||||
static RclMonitor *makeMonitor()
|
||||
{
|
||||
#ifdef RCL_USE_INOTIFY
|
||||
#ifdef _WIN32
|
||||
return new RclMonitorWin32;
|
||||
#else
|
||||
# ifdef RCL_USE_INOTIFY
|
||||
return new RclIntf;
|
||||
#endif
|
||||
#ifndef RCL_USE_INOTIFY
|
||||
#ifdef RCL_USE_FAM
|
||||
# elif defined(RCL_USE_FAM)
|
||||
return new RclFAM;
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
LOGINFO("RclMonitor: neither Inotify nor Fam was compiled as file system "
|
||||
"change notification interface\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // RCL_MONITOR
|
||||
|
||||
@ -103,7 +103,9 @@ static struct option long_options[] = {
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
ReExec *o_reexec;
|
||||
#endif
|
||||
|
||||
// Globals for atexit cleanup
|
||||
static ConfIndexer *confindexer;
|
||||
@ -867,11 +869,13 @@ int main(int argc, char *argv[])
|
||||
LOGDEB("recollindex: sleeping " << sleepsecs << "\n");
|
||||
for (int i = 0; i < sleepsecs; i++) {
|
||||
sleep(1);
|
||||
#ifndef _WIN32
|
||||
// Check that x11 did not go away while we were sleeping.
|
||||
if (!(op_flags & OPT_x) && !x11IsAlive()) {
|
||||
LOGDEB("X11 session went away during initial sleep period\n");
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user