PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/strigi-0.7.7/strigidaemon/bin/daemon/eventlistener/famlistener.cpp

#
C++ | 494 lines | 384 code | 81 blank | 29 comment | 75 complexity | 4cee5f067baa9794d524ac9f6da12946 MD5 | raw file
Possible License(s): LGPL-2.0
  1. /* This file is part of Strigi Desktop Search
  2. *
  3. * Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Library General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Library General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Library General Public License
  16. * along with this library; see the file COPYING.LIB. If not, write to
  17. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18. * Boston, MA 02110-1301, USA.
  19. */
  20. #include "famlistener.h"
  21. #include <strigi/analyzerconfiguration.h>
  22. #include "combinedindexmanager.h"
  23. #include "event.h"
  24. #include "eventlistenerqueue.h"
  25. #include <strigi/filelister.h>
  26. #include <strigi/indexreader.h>
  27. #include "../strigilogging.h"
  28. #include <cerrno>
  29. #include <cstring>
  30. #include <sys/resource.h>
  31. #include <sys/select.h>
  32. #include <sys/types.h>
  33. #include <vector>
  34. #include <algorithm>
  35. using namespace std;
  36. using namespace Strigi;
  37. class MatchString {
  38. string m_fixed_val;
  39. public:
  40. MatchString (string fixed_val) {m_fixed_val = fixed_val;}
  41. bool operator() (pair<FAMRequest,string> val) {
  42. return (m_fixed_val.compare(val.second) == 0 ? true : false);
  43. }
  44. };
  45. bool operator< (const FAMRequest& f1, const FAMRequest& f2)
  46. {
  47. return f1.reqnum < f2.reqnum;
  48. }
  49. FamEvent::FamEvent (const string& watchName, FAMEvent event)
  50. : FsEvent (watchName, event.filename),
  51. m_event(event),
  52. m_watchName (watchName)
  53. {
  54. bool doStat = true;
  55. switch (event.code)
  56. {
  57. case FAMChanged:
  58. m_type = UPDATE;
  59. break;
  60. case FAMMoved:
  61. case FAMDeleted:
  62. m_type = DELETE;
  63. break;
  64. case FAMCreated:
  65. m_type = CREATE;
  66. break;
  67. default:
  68. STRIGI_LOG_DEBUG ("strigi.FamEvent.FamEvent",
  69. "unuseful event");
  70. doStat = false;
  71. break;
  72. }
  73. if (!doStat)
  74. return;
  75. struct stat status;
  76. string path = m_watchName;
  77. if (path[path.length() - 1] != '/')
  78. path += '/';
  79. path += event.filename;
  80. if ( stat( path.c_str(), &status) == 0) {
  81. if (S_ISDIR(status.st_mode))
  82. m_regardsDir = true;
  83. else
  84. m_regardsDir = false;
  85. }
  86. else {
  87. string msg;
  88. msg = "unable to execute stat() on FAMEvent.filename because of: ";
  89. msg += strerror (errno);
  90. STRIGI_LOG_ERROR ("strigi.FamEvent", msg)
  91. }
  92. }
  93. char* FamEvent::name()
  94. {
  95. return m_event.filename;
  96. }
  97. const string FamEvent::description()
  98. {
  99. string message;
  100. switch (m_event.code)
  101. {
  102. case FAMChanged:
  103. message += "FAMChanged";
  104. break;
  105. case FAMMoved:
  106. message += "FAMMoved";
  107. break;
  108. case FAMDeleted:
  109. message += "FAMDeleted";
  110. break;
  111. case FAMCreated:
  112. message += "FAMCreated";
  113. break;
  114. case FAMExists:
  115. message += "FAMExists";
  116. break;
  117. case FAMEndExist:
  118. message += "FAMEndExist";
  119. break;
  120. case FAMAcknowledge:
  121. message += "FAMAcknowledge";
  122. break;
  123. case FAMStartExecuting:
  124. message += "FAMStartExecuting";
  125. break;
  126. case FAMStopExecuting:
  127. message += "FAMStopExecuting";
  128. break;
  129. }
  130. message += " event regarding ";
  131. message += (m_regardsDir) ? "dir " : "file ";
  132. message += m_event.filename;
  133. message += " ; associated watch description: " + m_watchName;
  134. return message;
  135. }
  136. bool FamEvent::regardsDir()
  137. {
  138. return m_regardsDir;
  139. }
  140. class FamListener::Private
  141. {
  142. public:
  143. Private();
  144. virtual ~Private();
  145. bool init();
  146. void stopMonitoring();
  147. bool isEventInteresting (FsEvent * event);
  148. bool isEventValid(FsEvent* event);
  149. // event methods
  150. void pendingEvent(vector<FsEvent*>& events, unsigned int& counter);
  151. // dir methods
  152. void dirRemoved (string dir);
  153. // watches methods
  154. bool addWatch (const std::string& path);
  155. void addWatches (const std::set<std::string>& watches);
  156. void rmWatch(FAMRequest& famRequest, std::string path);
  157. void rmWatches(std::map<FAMRequest, std::string>& watchesToRemove);
  158. void rmWatches(std::set<std::string>& watchesToRemove);
  159. void clearWatches();
  160. private:
  161. FAMConnection m_famConnection;
  162. std::map<FAMRequest, std::string> m_watches; //!< map containing all inotify watches added by FamListener. Key is watch descriptor, value is dir path
  163. };
  164. bool FamListener::Private::init()
  165. {
  166. if (FAMOpen(&m_famConnection) == -1)
  167. return false;
  168. return true;
  169. }
  170. FamListener::Private::Private()
  171. {
  172. }
  173. FamListener::Private::~Private()
  174. {
  175. clearWatches();
  176. if (FAMClose (&m_famConnection) == -1)
  177. STRIGI_LOG_ERROR ("strigi.FamListener",
  178. "Error during FAM close procedure");
  179. }
  180. void FamListener::Private::pendingEvent(vector<FsEvent*>& events,
  181. unsigned int& counter)
  182. {
  183. sleep (1);
  184. if (FAMPending(&m_famConnection)) {
  185. FAMEvent event;
  186. if (FAMNextEvent (&m_famConnection, &event) == -1) {
  187. STRIGI_LOG_ERROR ("strigi.FamListener.pendingEvent",
  188. "Fam event retrieval failed");
  189. return;
  190. }
  191. if ((event.code == FAMChanged) || (event.code == FAMMoved) ||
  192. (event.code == FAMDeleted) || (event.code == FAMCreated))
  193. {
  194. map<FAMRequest, string>::iterator match;
  195. match = m_watches.find (event.fr);
  196. if (match != m_watches.end()) {
  197. events.push_back (new FamEvent (match->second, event));
  198. counter++;
  199. }
  200. }
  201. }
  202. }
  203. bool FamListener::Private::isEventValid(FsEvent* event)
  204. {
  205. FamEvent* famEvent = dynamic_cast<FamEvent*> (event);
  206. if (famEvent == 0)
  207. return false;
  208. map<FAMRequest, string>::iterator match = m_watches.find(famEvent->fr());
  209. return (match != m_watches.end());
  210. }
  211. bool FamListener::Private::addWatch (const string& path)
  212. {
  213. map<FAMRequest, string>::iterator iter;
  214. for (iter = m_watches.begin(); iter != m_watches.end(); ++iter) {
  215. if ((iter->second).compare (path) == 0) // dir is already watched
  216. return true;
  217. }
  218. FAMRequest famRequest;
  219. if (FAMMonitorDirectory (&m_famConnection, path.c_str(), &famRequest, 0) == 0) {
  220. m_watches.insert(make_pair(famRequest, path));
  221. return true;
  222. }
  223. else
  224. return false;
  225. }
  226. void FamListener::Private::rmWatch(FAMRequest& famRequest, string path)
  227. {
  228. if (FAMCancelMonitor (&m_famConnection, &famRequest) == -1)
  229. STRIGI_LOG_ERROR ("strigi.FamListener.Private.rmWatch",
  230. string("Error removing watch associated to path: ") + path)
  231. else
  232. STRIGI_LOG_DEBUG ("strigi.FamListener.Private.rmWatch",
  233. string("Removed watch associated to path: ") + path)
  234. map<FAMRequest, string>::iterator match = m_watches.find(famRequest);
  235. if (match != m_watches.end() && (path.compare(match->second) == 0))
  236. m_watches.erase (match);
  237. else
  238. STRIGI_LOG_ERROR ("strigi.FamListener.Private.rmWatch",
  239. "unable to remove internal watch reference for " + path)
  240. }
  241. void FamListener::Private::rmWatches(map<FAMRequest, string>& watchesToRemove)
  242. {
  243. for (map<FAMRequest,string>::iterator it = watchesToRemove.begin();
  244. it != watchesToRemove.end(); ++it)
  245. {
  246. map<FAMRequest,string>::iterator match = m_watches.find (it->first);
  247. if (match != m_watches.end()) {
  248. FAMRequest famRequest = it->first;
  249. rmWatch ( famRequest, it->second);
  250. }
  251. else
  252. STRIGI_LOG_WARNING ("strigi.FamListener.Private.rmWatches",
  253. "unable to remove watch associated to " + it->second);
  254. }
  255. }
  256. void FamListener::Private::rmWatches(set<string>& watchesToRemove)
  257. {
  258. map<FAMRequest, string> removedWatches;
  259. // find all pairs <watch-id, watch-name> that have to be removed
  260. for (set<string>::iterator it = watchesToRemove.begin();
  261. it != watchesToRemove.end(); ++it)
  262. {
  263. MatchString finder (*it);
  264. map<FAMRequest, string>::iterator match;
  265. match = find_if (m_watches.begin(), m_watches.end(), finder);
  266. if (match != m_watches.end())
  267. removedWatches.insert (make_pair (match->first, match->second));
  268. else
  269. STRIGI_LOG_WARNING ("strigi.FamListener.Private.rmWatches",
  270. "unable to find the watch associated to " + *it)
  271. }
  272. rmWatches (removedWatches);
  273. }
  274. void FamListener::Private::clearWatches ()
  275. {
  276. map<FAMRequest, string>::iterator iter;
  277. for (iter = m_watches.begin(); iter != m_watches.end(); ++iter) {
  278. FAMRequest famRequest = iter->first;
  279. if (FAMCancelMonitor (&m_famConnection, &famRequest) == -1)
  280. STRIGI_LOG_ERROR ("strigi.FamListener.rmWatch",
  281. string("Error removing watch associated to path: ") + iter->second)
  282. else
  283. STRIGI_LOG_DEBUG ("strigi.FamListener.rmWatch",
  284. string("Removed watch associated to path: ") + iter->second)
  285. }
  286. m_watches.clear();
  287. }
  288. void FamListener::Private::dirRemoved (string dir)
  289. {
  290. map<FAMRequest, string> watchesToRemove;
  291. // remove inotify watches over no more indexed dirs
  292. for (map<FAMRequest, string>::iterator mi = m_watches.begin();
  293. mi != m_watches.end(); ++mi)
  294. {
  295. if ((mi->second).find (dir,0) == 0)
  296. watchesToRemove.insert (make_pair (mi->first, mi->second));
  297. }
  298. rmWatches (watchesToRemove);
  299. }
  300. // END FamListener::Private
  301. FamListener::FamListener(set<string>& indexedDirs)
  302. :FsListener("FamListener", indexedDirs)
  303. {
  304. p = new Private();
  305. }
  306. FamListener::~FamListener()
  307. {
  308. delete p;
  309. }
  310. bool FamListener::init()
  311. {
  312. if (!p->init()) {
  313. STRIGI_LOG_ERROR ("strigi.FamListener.init",
  314. "Error during FAM initialization");
  315. return false;
  316. }
  317. m_bInitialized = true;
  318. STRIGI_LOG_DEBUG ("strigi.FamListener.init", "successfully initialized");
  319. return true;
  320. }
  321. FsEvent* FamListener:: retrieveEvent()
  322. {
  323. if (m_events.empty())
  324. return 0;
  325. vector<FsEvent*>::iterator iter = m_events.begin();
  326. FsEvent* event = *iter;
  327. m_events.erase(iter);
  328. return event;
  329. }
  330. bool FamListener::pendingEvent()
  331. {
  332. unsigned int counter = 0;
  333. p->pendingEvent (m_events, counter);
  334. if (counter > 0) {
  335. char buff [20];
  336. snprintf(buff, 20 * sizeof (char), "%i", counter);
  337. string message = "Caught ";
  338. message += buff;
  339. message += " FAM event(s)";
  340. STRIGI_LOG_DEBUG ("strigi.FamListener.pendingEvent", message)
  341. }
  342. return !m_events.empty();
  343. }
  344. bool FamListener::isEventInteresting (FsEvent* event)
  345. {
  346. FamEvent* famEvent = dynamic_cast<FamEvent*> (event);
  347. if (famEvent == 0)
  348. return false;
  349. // ignore directories starting with '.'
  350. if ((famEvent->regardsDir()) &&
  351. (strlen (famEvent->name()) > 0) && (famEvent->name())[0] == '.')
  352. return false;
  353. if (m_pAnalyzerConfiguration == NULL) {
  354. STRIGI_LOG_WARNING ("strigi.FamListener.isEventInteresting",
  355. "AnalyzerConfiguration == NULL")
  356. return true;
  357. }
  358. string path = famEvent->watchName();
  359. if (path[path.length() - 1] != '/')
  360. path += '/';
  361. path += famEvent->name();
  362. if (famEvent->regardsDir())
  363. return m_pAnalyzerConfiguration->indexDir( path.c_str(),
  364. famEvent->name());
  365. else
  366. return m_pAnalyzerConfiguration->indexFile( path.c_str(),
  367. famEvent->name());
  368. }
  369. bool FamListener::isEventValid(FsEvent* event)
  370. {
  371. return p->isEventValid ( event);
  372. }
  373. bool FamListener::addWatch (const string& path)
  374. {
  375. if (p->addWatch (path)) {
  376. STRIGI_LOG_INFO ("strigi.FamListener.addWatch",
  377. "added watch for " + path);
  378. return true;
  379. }
  380. else {
  381. STRIGI_LOG_ERROR ("strigi.FamListener.addWatch",
  382. "Failed to watch " + path)
  383. return false;
  384. }
  385. }
  386. void FamListener::clearWatches ()
  387. {
  388. p->clearWatches();
  389. }
  390. void FamListener::dirRemoved (string dir, vector<Event*>& events)
  391. {
  392. map<FAMRequest, string> watchesToRemove;
  393. STRIGI_LOG_DEBUG ("strigi.FamListener.dirRemoved", dir +
  394. " is no longer watched, removing all indexed files contained")
  395. // we've to de-index all files contained into the deleted/moved directory
  396. if (m_pManager)
  397. {
  398. // all indexed files contained into directory
  399. map<string, time_t> indexedFiles;
  400. m_pManager->indexReader()->getChildren(dir, indexedFiles);
  401. // remove all entries that were contained into the removed directory
  402. for (map<string, time_t>::iterator it = indexedFiles.begin();
  403. it != indexedFiles.end(); ++it)
  404. {
  405. Event* event = new Event (Event::DELETED, it->first);
  406. events.push_back (event);
  407. }
  408. }
  409. else
  410. STRIGI_LOG_WARNING ("strigi.FamListener.dirRemoved",
  411. "m_pManager == NULL!");
  412. p->dirRemoved (dir);
  413. }