From 4e710f239a7ccc4fcb7a4775b2725cf7483ae351 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Thu, 13 Jan 2022 10:17:59 +0000 Subject: [PATCH] Initial draft of file system monitoring for windows. Still has issues, dnot build by default --- src/index/rclmonprc.cpp | 10 +- src/index/rclmonrcv.cpp | 574 ++++++++++++++++++++++++++++++++++---- src/index/recollindex.cpp | 4 + 3 files changed, 538 insertions(+), 50 deletions(-) diff --git a/src/index/rclmonprc.cpp b/src/index/rclmonprc.cpp index a2e54662..56251456 100644 --- a/src/index/rclmonprc.cpp +++ b/src/index/rclmonprc.cpp @@ -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(); diff --git a/src/index/rclmonrcv.cpp b/src/index/rclmonrcv.cpp index e3fc0df1..80a1f7e4 100644 --- a/src/index/rclmonrcv.cpp +++ b/src/index/rclmonrcv.cpp @@ -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 @@ -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 +#include +#include +#include +#include + +#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 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 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(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(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 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 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(this)) { + // Called from ~RclFSWatchWin32. Must exit. + MONDEB("RclFSWatchWin32::run: queuedcompletion said need exit\n"); + return; + } else { + std::unique_lock 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 diff --git a/src/index/recollindex.cpp b/src/index/recollindex.cpp index d9b8810e..28d3f5b1 100644 --- a/src/index/recollindex.cpp +++ b/src/index/recollindex.cpp @@ -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 } }