ewmscp  ..
inotify_watch.cpp
Go to the documentation of this file.
1 /*
2  ewmscp: copy program with extended functionality
3  Copyright (C) 2018 Juergen Hannappel
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program 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
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18 
19 #include <chrono>
20 #include <csignal>
21 #include <dirent.h>
22 #include <fstream>
23 #include <iostream>
24 #include <list>
25 #include <map>
26 #include <poll.h>
27 #include <regex>
28 #include <atomic>
29 #include <scoped.h>
30 #include <string.h>
31 #include <sys/inotify.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <syslogstream.h>
35 #include <throwcall.h>
36 #include <unistd.h>
37 
58 #include <Options.h>
59 #include <OptionsRegex.h>
60 typedef std::chrono::system_clock clock_type;
61 
62 std::ostream* errStream(nullptr);
63 options::single<std::string> errPrefix('\0', "errPrefix", "prefix for error messages");
64 
65 
66 class maskName {
67  const uint32_t bits;
68  const std::string name;
69  maskName(uint32_t aBits,const std::string& aName): bits(aBits), name(aName){};
70  static const std::array<maskName,16> maskNames() {
71  static const std::array<maskName,16> maskNamesArray {{{IN_ACCESS, "IN_ACCESS"},
72  {IN_ATTRIB, "IN_ATTRIB"},
73  {IN_CLOSE_WRITE, "IN_CLOSE_WRITE"},
74  {IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"},
75  {IN_CREATE, "IN_CREATE"},
76  {IN_DELETE, "IN_DELETE"},
77  {IN_DELETE_SELF, "IN_DELETE_SELF"},
78  {IN_MODIFY, "IN_MODIFY"},
79  {IN_MOVE_SELF, "IN_MOVE_SELF"},
80  {IN_MOVED_FROM, "IN_MOVED_FROM"},
81  {IN_MOVED_TO, "IN_MOVED_TO"},
82  {IN_OPEN, "IN_OPEN"},
83  {IN_IGNORED, "IN_IGNORED"},
84  {IN_ISDIR, "IN_ISDIR"},
85  {IN_Q_OVERFLOW, "IN_Q_OVERFLOW"},
86  {IN_UNMOUNT, "IN_UNMOUNT"}
87  }};
88  return(maskNamesArray);
89  }
90 public:
91  static void print(std::ostream& out, uint32_t mask) {
92  bool needComma = false;
93 
94  for (const auto &item : maskNames()) {
95  if (mask & item.bits) {
96  if (needComma) {
97  out << ",";
98  }
99  out << item.name;
100  needComma = true;
101  }
102  }
103  };
104  static uint32_t maskFromList(const options::container<std::string>& list) {
105  uint32_t mask = 0;
106  for (const auto& listItem : list) {
107  for (const auto& item : maskNames()) {
108  if (item.name == listItem) {
109  mask |= item.bits;
110  }
111  }
112  }
113  return mask;
114  };
116  for (const auto& item : maskNames()) {
117  opt.fAddToRange(item.name);
118  }
119  };
120 };
121 
122 
133  inotify_event data;
134  public:
135  inotify_event_wrapper() = delete; // we only get "objects" of this type by reinterpret_casts
136  ~inotify_event_wrapper() = delete;
137  inline int wd() const {
138  return data.wd;
139  }
140  inline uint32_t mask() const {
141  return data.mask;
142  }
143  inline uint32_t cookie() const {
144  return data.cookie;
145  }
146  inline uint32_t len() const {
147  return data.len;
148  }
149  inline const char *name() const {
150  return data.name;
151  }
152  const inotify_event_wrapper* next() const {
153  return reinterpret_cast<const inotify_event_wrapper*>(&data.name[0] + len());
154  }
155  size_t size() const {
156  return sizeof(inotify_event) + len();
157  }
158  static const inotify_event_wrapper* fromChar(char *pointer) {
159  return reinterpret_cast<const inotify_event_wrapper*>(pointer);
160  }
161  friend std::ostream& operator<<(std::ostream& out, const inotify_event_wrapper& aEvent) {
162  out << " mask: ";
163  maskName::print(out, aEvent.mask());
164  out << "(" << aEvent.mask() << ")";
165  return out;
166  }
167 };
168 
178  protected:
180  static std::map<uint32_t, inotify_event_copy>& getRenameEvents() {
181  static std::remove_reference<decltype(getRenameEvents())>::type renameEvents;
182  return renameEvents;
183  }
185  static bool cmp(const inotify_event_copy* lhs, const inotify_event_copy* rhs) {
186  return lhs->then < rhs->then;
187  }
189  static std::multiset<inotify_event_copy*, decltype(cmp)*>& getSingleEvents() {
190  static std::remove_reference<decltype(getSingleEvents())>::type singleEvents(&cmp);
191  return singleEvents;
192  }
193  std::remove_reference<decltype(getSingleEvents())>::type::iterator singleEventIterator;
195  clock_type::time_point then;
196  bool isCopy;
197 
199  void doCopyIfNeeded() {
200  if (!isCopy) {
201  auto theEvent = getEvent();
202  void* newEvent = malloc(theEvent->size());
203  memcpy(newEvent, theEvent, theEvent->size());
204  event = reinterpret_cast<inotify_event_wrapper*>(newEvent);
205  isCopy = true;
206  singleEventIterator = getSingleEvents().emplace(this);
207  }
208  }
209 
210  public:
212  singleEventIterator(getSingleEvents().end()), event(aEvent), isCopy(false) {
213  }
215  if (isCopy) {
216  free(reinterpret_cast<void*>(const_cast<inotify_event_wrapper*>(event)));
217  }
218  }
219  clock_type::time_point timeStamp() const {
220  return then;
221  }
223  return event;
224  }
225 
226  static void doCopyLeftovers() {
227  for (auto& it : getRenameEvents()) {
228  it.second.doCopyIfNeeded();
229  }
230  }
231 
238  auto it = getSingleEvents().begin();
239 
240  if (it != getSingleEvents().end() &&
241  (*it)->then < clock_type::now() - std::chrono::seconds(5)) {
242  auto retval = *it;
243  return retval;
244  }
245 
246  return nullptr;
247  }
254  static void forget(inotify_event_copy* what) {
255  if (what->singleEventIterator != getSingleEvents().end()) {
256  getSingleEvents().erase(what->singleEventIterator);
257  }
258 
259  getRenameEvents().erase(what->getEvent()->cookie());
260  }
261 
268  clock_type::time_point then) {
269  auto result = getRenameEvents().emplace(event->cookie(), event);
270 
271  if (result.second) {
272  result.first->second.then = then;
273  return nullptr;
274  } else {
275  return &(result.first->second);
276  }
277  }
278 };
279 
286  static std::map<int, watchedDirType*>& getWatchedDirs() {
287  static std::remove_reference<decltype(getWatchedDirs())>::type watchedDirs;
288  return watchedDirs;
289  }
290  static int extraEventMask;
291 
292  std::string name;
294  int fd;
295  int id;
296  std::map<std::string, watchedDirType*> children;
297  decltype(getWatchedDirs().begin()) watchIterator;
298  decltype(children.begin()) childIterator;
299 
300 
306  parent = aParent;
307 
308  if (parent) {
309  auto result = parent->children.emplace(name, this);
310 
311  if (result.second == false) {
312  result.first->second->parent = nullptr; // orphanize old child
313  result.first->second = this; // overwrite old child
314  }
315 
316  childIterator = result.first;
317  }
318  }
319 
320  public:
321  static void dump(std::ostream& out) {
322  out << errPrefix << "begin list of watched directories (" << getWatchedDirs().size() << "):\n";
323  for (const auto& item: getWatchedDirs()) {
324  std::string path;
325  item.second->buildPath(path);
326  out << errPrefix << item.first << ": " << path << "\n";
327  }
328  out << errPrefix << "end list of watched directories.\n";
329  }
330 
331 
333  void buildPath(std::string& path) {
334  if (parent) {
335  parent->buildPath(path);
336  path += "/";
337  }
338 
339  path += name;
340  }
342  void buildPath(std::string& path,
343  const std::string& aName) {
344  buildPath(path);
345  path += "/";
346  path += aName;
347  }
349  std::string getPath() {
350  std::string path;
351  buildPath(path);
352  return path;
353  }
354 
356  static watchedDirType* get(int aId) {
357  return getWatchedDirs()[aId];
358  }
360  static bool empty() {
361  return getWatchedDirs().empty();
362  }
364  static size_t size() {
365  return getWatchedDirs().size();
366  }
367 
368  static void setExtraEventMask(int extraMask) {
369  extraEventMask = extraMask;
370  }
371 
379  watchedDirType(const std::string &aName,
380  int aFd,
381  watchedDirType* aParent = nullptr):
382  name(aName),
383  parent(aParent),
384  fd(aFd) {
385  std::string path;
386  buildPath(path);
387  watchIterator = getWatchedDirs().end();
388  id = inotify_add_watch(fd, path.c_str(),
389  IN_CLOSE_WRITE
390  | IN_DELETE
391  | IN_MOVED_TO | IN_MOVED_FROM
392  | IN_CREATE
393  | IN_DELETE_SELF
394  | IN_EXCL_UNLINK
395  | IN_DONT_FOLLOW
396  | IN_ONLYDIR
397  | extraEventMask);
398 
399  if (id == -1 && (errno == ENOENT || errno == ENOTDIR)) { // we fell prey to a race
400  parent = nullptr;
401  return;
402  }
403 
404  throwcall::badval(id, -1, "inotify_add_watch failed on ", path);
405 
406  auto result = getWatchedDirs().emplace(id, this);
407 
408  if (result.second == false) { // watch for this object already exists, declare this as invalid
409  parent = nullptr;
410  id = -1;
411  return;
412  } else {
413  watchIterator = result.first;
414  }
415 
416  becomeChildOfMyParent(aParent);
417  }
418 
425  if (id != -1) {
426  auto retval = inotify_rm_watch(fd, id);
427 
428  if (retval && errno == EINVAL) {
429  *errStream << errPrefix << "oops " << getPath() << " id " << id << "could not be unwatched" << std::endl;
430  } else {
431  throwcall::good0(retval, "can't unwatch ", getPath());
432  }
433  }
434 
435  if (watchIterator != getWatchedDirs().end()) {
436  getWatchedDirs().erase(watchIterator);
437  }
438 
439  for (auto& child : children) {
440  *errStream << errPrefix << "oops " << getPath() << " still has child " << child.second->getPath() << ", deleting it" << std::endl;
441  delete child.second;
442  }
443 
444  if (parent) {
445  parent->children.erase(childIterator);
446  }
447  }
448  void invalidateId() {
449  id = -1;
450  }
451  int getId() const {
452  return id;
453  }
454  watchedDirType* getChild(const std::string& aName) {
455  auto it = children.find(aName);
456 
457  if (it == children.end()) {
458  return nullptr;
459  } else {
460  return it->second;
461  }
462  }
467  void getAdoptedBy(const std::string& newName,
468  watchedDirType *newParent) {
469  if (parent) {
470  parent->children.erase(childIterator);
471  }
472 
473  name = newName;
474  becomeChildOfMyParent(newParent);
475  }
476 };
477 
480 
482  vanished,
483  islink,
484  ignore,
485  iswatched
486 };
495  const std::string& name,
496  watchedDirType* parent = nullptr) {
497  std::string path;
498 
499  if (parent) {
500  parent->buildPath(path, name);
501  } else {
502  path = name;
503  }
504 
505  {
506  struct stat statbuf;
507  auto retval = lstat(path.c_str(), &statbuf);
508 
509  if (retval && (errno == ENOENT || errno == ENOTDIR)) {
510  return watchDirectoryReturnType::vanished; // happens for very short lived files
511  }
512 
513  throwcall::good0(retval, "can't lstat ", path);
514 
515  if (!S_ISDIR(statbuf.st_mode)) {
516  return S_ISLNK(statbuf.st_mode) ? watchDirectoryReturnType::islink : watchDirectoryReturnType::ignore;
517  }
518  }
519 
520  auto watchedDir = new watchedDirType(name, fd, parent);
521 
522  if (watchedDir->getId() == -1) { // vanmished or already watched
523  delete watchedDir;
525  }
526 
527  // with the subdir list we have only one open dir at a time
528  std::list<std::string> subdirs;
529  {
530  auto result = opendir(path.c_str());
531 
532  if (result == nullptr && (errno == ENOENT || errno == ENOTDIR)) {
533  delete watchedDir;
534  return watchDirectoryReturnType::vanished; // dir vanished before we could check the content
535  }
536 
537  scoped::Dir dir(path, result);
538 
539  while (auto entry = readdir(dir)) {
540  if (entry->d_name[entry->d_name[0] != '.' ? 0 : entry->d_name[1] != '.' ? 1 : 2] == '\0') {
541  continue; // skip . .. and empty strings
542  }
543  if (entry->d_type == DT_DIR) {
544  subdirs.emplace_back(entry->d_name);
545  }
546  }
547  }
548 
549  for (auto& subname : subdirs) {
550  watchDirectory(fd, subname, watchedDir);
551  }
552 
554 }
556 
557 
558 std::ostream& operator<< (std::ostream& stream,
559  std::chrono::system_clock::time_point t) {
560  stream << std::chrono::duration_cast<std::chrono::duration<double>>(t.time_since_epoch()).count();
561  return stream;
562 }
563 
564 
565 
566 
567 static options::single<std::string> pidFileName('p', "pidFile",
568  "name of PID file");
569 
570 static void pidFileRemover() {
571  if (pidFileName != "") {
572  throwcall::good0(unlink(pidFileName.c_str()), "can't unlink pid file ", pidFileName);
573  }
574 }
575 static void sigTermHandler(int) {
576  std::exit(EXIT_SUCCESS);
577 }
578 static void sigPipeHandler(int) {
579  std::exit(EXIT_FAILURE);
580 }
581 static std::atomic<bool> dumpRequested(false);
582 static void sigUsrHandler(int) {
583  dumpRequested = true;
584 }
585 
594  char delimiter;
595  clock_type::time_point lastSendTime;
596  bool needFlush;
597  public:
599  delimiter = nullDelimiter ? '\0' : '\n';
600  std::cout << std::fixed; // proper format for time stamps
601  }
602  void sendCommand(const char *command, std::chrono::system_clock::time_point when,
603  const std::string& path) {
604  std::cout << command << ' ' << when << ' ' << path << delimiter;
605  lastSendTime = when;
606  needFlush = true;
607  }
608  void sendCommand(const char *command, std::chrono::system_clock::time_point when,
609  const std::string& path1, const std::string& path2) {
610  sendCommand(command, when, path1);
611  std::cout << path2 << delimiter;
612  }
613  void flush() {
614  std::cout.flush();
615  }
616  void keepalive() {
617  auto now = clock_type::now();
618  if (now - lastSendTime > std::chrono::seconds(keepaliveTimeout)) {
619  sendCommand("ig", now, "keepalive");
620  flush();
621  }
622  }
625  class flushDomain {
627  public:
628  flushDomain(commandSender& aSender): sender(aSender) {
629  sender.needFlush = false;
630  };
632  if (sender.needFlush) {
633  sender.flush();
634  }
635  }
636  };
637 };
639  "use NUL as delimiter between requests", false);
641  "keepalive timeout (seconds)", 120);
642 
643 
649 int main(int argc, const char* argv[]) {
650  std::ios::sync_with_stdio(false);
651  options::parser parser("inotify_watch", "", {});
652  options::positional<options::container<std::string>>directories(10, "directory",
653  "directories to be watched");
654  options::single<std::string> workDir('\0', "workDir",
655  "use DIRECTORY as work dir");
656  options::single<std::regex> ignore('i', "ignore",
657  "regexp for files to be ignored for IN_CLOSE_WRITE");
658  options::single<bool> verbose('v', "verbose",
659  "be verbose", false);
660  options::single<std::string> errorStream('\0', "errors",
661  "file to write errors to");
662 
663  options::container<std::string> extraEvents('e', "extraEvents",
664  "extra events to listen to");
665  maskName::addToRange(extraEvents);
666  options::container<std::string> copyTriggers('c', "copyTrigger",
667  "extra events beside IN_CLOSE_NOWRITE hat trigger a copy");
668  maskName::addToRange(copyTriggers);
669  parser.fParse(argc, argv);
670 
671  logstream::provider errStreamBase(errorStream, std::cerr);
672  errStream = &(errStreamBase.getStream());
673 
675 
676  uint32_t copyTriggerBits = IN_CLOSE_WRITE | maskName::maskFromList(copyTriggers);
677  if (verbose) {
678  std::cerr << "events of type ";
679  maskName::print(std::cerr, copyTriggerBits);
680  std::cerr << " (" << std::hex << copyTriggerBits << std::dec << ") will trigger a copy event\n";
681  }
682  try {
683 
684  if (pidFileName != "") {
685  std::ofstream pidFile(pidFileName);
686  pidFile << getpid() << "\n";
687  throwcall::good0(std::atexit(pidFileRemover), "can't register pidFileRemover");
688  std::signal(SIGTERM, sigTermHandler);
689  }
690 
691  std::signal(SIGPIPE, sigPipeHandler);
692  std::signal(SIGUSR1, sigUsrHandler);
693 
694  auto fd = throwcall::badval(inotify_init(), -1, "inotify_init");
695 
696  if (workDir.fIsSet()) {
697  throwcall::good0(chdir(workDir.c_str()), "can't change work dir to ", workDir);
698  }
699 
700  for (const auto& directory : directories) {
701  watchDirectory(fd, directory);
702  }
703 
704  *errStream << errPrefix << "watching " << watchedDirType::size() << " directories" << std::endl;
705 
706  *errStream << std::fixed;
707  char buffer[4096 * 256] __attribute__ ((aligned(__alignof__(struct inotify_event))));
708  struct pollfd pfd;
709  pfd.fd = fd;
710  pfd.events = POLLIN;
711 
712  commandSender sender;
713 
714  while (true) {
715  if (dumpRequested) {
716  dumpRequested = false;
718  }
719  {
720  {
721  // process single events that took forever to get a partner
722  commandSender::flushDomain flusher(sender);
723  while (auto copy = inotify_event_copy::getStaleSingles()) {
724  auto event = copy->getEvent();
725  std::string path;
726  watchedDirType::get(event->wd())->buildPath(path, event->name());
727 
728  if (event->mask() & IN_MOVED_TO) {
729  auto result = watchDirectory(fd, event->name(), watchedDirType::get(event->wd()));
730 
731  if (result == watchDirectoryReturnType::islink) {
732  sender.sendCommand("ln", copy->timeStamp(), path);
733  } else if (result == watchDirectoryReturnType::ignore) {
734  sender.sendCommand("cp", copy->timeStamp(), path);
735  }
736 
737  if (verbose && result == watchDirectoryReturnType::iswatched) {
738  *errStream << errPrefix << "watching " << watchedDirType::size() << " directories (added subtree)" << std::endl;
739  }
740  } else {
741  auto parent = watchedDirType::get(event->wd());
742  auto meAsDirChild = parent->getChild(event->name());
743 
744  if (meAsDirChild) {
745  delete meAsDirChild;
746  }
747 
748  sender.sendCommand("rm", copy->timeStamp(), path);
749  }
750 
752  }
753  }
754  auto result = poll(&pfd, 1, 1000);
755 
756  if (result == -1) {
757  if (errno == EINTR) {
758  continue;
759  }
760 
761  throwcall::badval(result, -1, "poll failed");
762  }
763 
764  if (result == 0) { // timeout, check continued presence of initial dirs
765  for (const auto& directory : directories) {
766  struct stat statbuf;
767  auto retval = stat(directory.c_str(), &statbuf);
768 
769  if (retval != 0 && errno == ENOENT) {
770  return 0;
771  } else {
772  throwcall::good0(retval, "can't stat ", directory);
773  }
774  }
775  sender.keepalive();
776  continue;
777  }
778  }
779  auto bytes = throwcall::badval(read(fd, buffer, sizeof(buffer)),
780  -1, "read inotification");
781  auto now = clock_type::now();
782 
783  if (verbose) {
784  *errStream << errPrefix << now << " got " << bytes << " bytes of notification" << std::endl;
785  }
786 
787  commandSender::flushDomain flusher(sender);
788 
789  for (auto event = inotify_event_wrapper::fromChar(buffer);
790  event < inotify_event_wrapper::fromChar(buffer + bytes);
791  event = event->next()) {
792  if (verbose) {
793  *errStream << errPrefix << "watch id " << event->wd() << " @ " << (void*)event << " with name " << event->name() << *event << std::endl;
794  }
795 
796  if (event->mask() & IN_IGNORED) {
797  continue;
798  }
799 
800  if (event->mask() & IN_Q_OVERFLOW) {
801  *errStream << errPrefix << "watch queue overflow" << std::endl;
802  continue; // we cannot do more than lopg this
803  }
804 
805  std::string path;
806  auto watchedDir = watchedDirType::get(event->wd());
807 
808  if (watchedDir == nullptr) {
809  *errStream << errPrefix << "unknown watch id " << event->wd() << " @ " << (void*)event << " with name " << event->name() << *event << std::endl;
810  continue;
811  }
812 
813  watchedDir->buildPath(path, event->name());
814 
815  if (event->mask() & (IN_MOVED_TO | IN_MOVED_FROM)) {
816  auto partner = inotify_event_copy::getPartner(event, now);
817 
818  if (partner) { // we have a partner event
819  std::string partnerPath;
820  watchedDirType::get(partner->getEvent()->wd())->buildPath(partnerPath, partner->getEvent()->name());
821  const inotify_event_wrapper* from;
822  const inotify_event_wrapper* to;
823  const std::string* fromPath;
824  const std::string* toPath;
825 
826  if (event->mask() & IN_MOVED_TO) {
827  from = partner->getEvent();
828  to = event;
829  fromPath = &partnerPath;
830  toPath = &path;
831  } else {
832  from = event;
833  to = partner->getEvent();
834  fromPath = &path;
835  toPath = &partnerPath;
836  }
837 
838  auto fromAsDirChild = watchedDirType::get(from->wd())->getChild(from->name());
839 
840  if (fromAsDirChild) {
841  fromAsDirChild->getAdoptedBy(to->name(), watchedDirType::get(to->wd()));
842  }
843 
844  sender.sendCommand("mv", now, *fromPath, *toPath);
846  }
847  }
848 
849  if (event->mask() & copyTriggerBits) {
850  if (!(ignore.fIsSet() && regex_match(path, ignore))) {
851  sender.sendCommand("cp", now, path);
852  }
853  }
854 
855  if (event->mask() & IN_CREATE) { // watch new directories also
856  auto result = watchDirectory(fd, event->name(), watchedDir);
857 
858  if (result == watchDirectoryReturnType::islink) {
859  sender.sendCommand("ln", now, path);
860  }
861 
862  if (verbose && result == watchDirectoryReturnType::iswatched) {
863  *errStream << errPrefix << "watching " << watchedDirType::size() << " directories (added subtree)" << std::endl;
864  }
865  }
866 
867  if (event->mask() & IN_DELETE_SELF) { // a watched directory is deleted
868  watchedDir->invalidateId();
869  delete watchedDir;
870 
871  if (verbose) {
872  *errStream << errPrefix << "watching " << watchedDirType::size() << " directories (removed subtree)" << std::endl;
873  }
874  }
875 
876  if (event->mask() & IN_DELETE) {
877  if (!(ignore.fIsSet() && regex_match(path, ignore))) {
878  sender.sendCommand("rm", now, path);
879  }
880  }
881  }
882  // make copies of all leftover half move events befor re-using buffer
884 
885  if (watchedDirType::empty()) {
886  throwcall::good0(close(fd), "can't close inotify fd");
887 
888  *errStream << errPrefix << "no more stuff to watch, finishing...\n";
889 
890  return EXIT_SUCCESS;
891  }
892  }
893  } catch (const std::exception& e) {
894  *errStream << logstream::level::crit << errPrefix << " ended badly: " << e.what() << std::endl;
895  return EXIT_FAILURE;
896  }
897 
898  return EXIT_SUCCESS;
899 }
inotify_event_copy::getSingleEvents
static std::multiset< inotify_event_copy *, decltype(cmp) * > & getSingleEvents()
return a multiset containing single inotify_event_copys ordered by time
Definition: inotify_watch.cpp:189
inotify_event_wrapper::len
uint32_t len() const
Definition: inotify_watch.cpp:146
options::parser
class that contains the parser, i.e. does that option handling
Definition: Options.h:363
watchedDirType
represents a watched directory inside a directory tree.
Definition: inotify_watch.cpp:285
inotify_event_copy::doCopyLeftovers
static void doCopyLeftovers()
Definition: inotify_watch.cpp:226
verbose
options::single< bool > verbose
options::single< std::string >
watchedDirType::getWatchedDirs
static std::map< int, watchedDirType * > & getWatchedDirs()
Definition: inotify_watch.cpp:286
maskName::bits
const uint32_t bits
Definition: inotify_watch.cpp:67
watchedDirType::buildPath
void buildPath(std::string &path, const std::string &aName)
fill path with the full path to a file in this directory
Definition: inotify_watch.cpp:342
watchedDirType::name
std::string name
basename of the directory, i.e. not the full path
Definition: inotify_watch.cpp:292
workDir
options::single< std::string > workDir
inotify_event_copy::getPartner
static inotify_event_copy * getPartner(const inotify_event_wrapper *event, clock_type::time_point then)
get partner of a half rename event.
Definition: inotify_watch.cpp:267
commandSender::flushDomain::~flushDomain
~flushDomain()
Definition: inotify_watch.cpp:631
options::single< bool >
class specialisation for options of type bool
Definition: Options.h:595
maskName::maskName
maskName(uint32_t aBits, const std::string &aName)
Definition: inotify_watch.cpp:69
inotify_event_wrapper::mask
uint32_t mask() const
Definition: inotify_watch.cpp:140
inotify_event_copy::then
clock_type::time_point then
the time the event was recorded
Definition: inotify_watch.cpp:195
options::internal::typed_base::fAddToRange
virtual void fAddToRange(rangeValueType aValue)
add a value to the range of allowed values
Definition: Options.h:458
throwcall::badval
T badval(T call, t badvalue, const Args &... args)
template function to wrap system calls that return a special bad value on failure
Definition: throwcall.h:54
watchedDirType::buildPath
void buildPath(std::string &path)
fill path with the fill path to this directory
Definition: inotify_watch.cpp:333
commandSender::flushDomain::flushDomain
flushDomain(commandSender &aSender)
Definition: inotify_watch.cpp:628
watchedDirType::id
int id
the watch id
Definition: inotify_watch.cpp:295
inotify_event_wrapper::name
const char * name() const
Definition: inotify_watch.cpp:149
inotify_event_copy
smart copy class for inotify_event.
Definition: inotify_watch.cpp:177
inotify_event_wrapper::operator<<
friend std::ostream & operator<<(std::ostream &out, const inotify_event_wrapper &aEvent)
Definition: inotify_watch.cpp:161
inotify_event_wrapper::wd
int wd() const
Definition: inotify_watch.cpp:137
inotify_event_copy::singleEventIterator
std::remove_reference< decltype(getSingleEvents())>::type::iterator singleEventIterator
iterator into the multiset containing the single events
Definition: inotify_watch.cpp:193
commandSender::sendCommand
void sendCommand(const char *command, std::chrono::system_clock::time_point when, const std::string &path1, const std::string &path2)
Definition: inotify_watch.cpp:608
watchedDirType::size
static size_t size()
return number of watched directories
Definition: inotify_watch.cpp:364
watchedDirType::childIterator
decltype(children.begin()) childIterator
iterator in parent's child map that holds this object
Definition: inotify_watch.cpp:298
inotify_event_copy::~inotify_event_copy
~inotify_event_copy()
Definition: inotify_watch.cpp:214
options::base::fIsSet
virtual bool fIsSet() const
check if this option was set, regardless of from command line or config file
Definition: Options.h:263
options::single< std::regex >
Definition: OptionsRegex.h:8
inotify_event_wrapper
wrapper for inotify_event with nicer interface.
Definition: inotify_watch.cpp:132
maskName::name
const std::string name
Definition: inotify_watch.cpp:68
dumpRequested
static std::atomic< bool > dumpRequested(false)
errStream
std::ostream * errStream(nullptr)
watchDirectoryReturnType::islink
@ islink
inotify_event_copy::forget
static void forget(inotify_event_copy *what)
deletes a copy.
Definition: inotify_watch.cpp:254
watchedDirType::getChild
watchedDirType * getChild(const std::string &aName)
Definition: inotify_watch.cpp:454
watchDirectoryReturnType::iswatched
@ iswatched
commandSender::nullDelimiter
static options::single< bool > nullDelimiter
Definition: inotify_watch.cpp:592
commandSender::commandSender
commandSender()
Definition: inotify_watch.cpp:598
scoped.h
errPrefix
options::single< std::string > errPrefix('\0', "errPrefix", "prefix for error messages")
Options.h
logstream::provider
Definition: syslogstream.h:100
clock_type
std::chrono::system_clock clock_type
Definition: inotify_watch.cpp:60
watchedDirType::getAdoptedBy
void getAdoptedBy(const std::string &newName, watchedDirType *newParent)
move to a new place in the directory tree.
Definition: inotify_watch.cpp:467
watchedDirType::watchIterator
decltype(getWatchedDirs().begin()) watchIterator
iterator in the map that holds this object
Definition: inotify_watch.cpp:297
inotify_event_wrapper::~inotify_event_wrapper
~inotify_event_wrapper()=delete
logstream::provider::getStream
std::ostream & getStream()
Definition: syslogstream.h:125
commandSender::sendCommand
void sendCommand(const char *command, std::chrono::system_clock::time_point when, const std::string &path)
Definition: inotify_watch.cpp:602
watchedDirType::watchedDirType
watchedDirType(const std::string &aName, int aFd, watchedDirType *aParent=nullptr)
construct a new watchedDir.
Definition: inotify_watch.cpp:379
sigUsrHandler
static void sigUsrHandler(int)
Definition: inotify_watch.cpp:582
watchDirectoryReturnType
watchDirectoryReturnType
special enum for the return values of watchDirectory.
Definition: inotify_watch.cpp:481
options::container< std::string >
pidFileRemover
static void pidFileRemover()
Definition: inotify_watch.cpp:570
maskName::print
static void print(std::ostream &out, uint32_t mask)
Definition: inotify_watch.cpp:91
inotify_event_wrapper::size
size_t size() const
Definition: inotify_watch.cpp:155
watchedDirType::becomeChildOfMyParent
void becomeChildOfMyParent(watchedDirType *aParent)
set object a child of the parent.
Definition: inotify_watch.cpp:305
commandSender::flushDomain::sender
commandSender & sender
Definition: inotify_watch.cpp:626
watchedDirType::~watchedDirType
~watchedDirType()
destruct watchedDir.
Definition: inotify_watch.cpp:424
throwcall.h
commandSender::flushDomain
class to flush output on scope basis.
Definition: inotify_watch.cpp:625
watchedDirType::invalidateId
void invalidateId()
Definition: inotify_watch.cpp:448
inotify_event_wrapper::next
const inotify_event_wrapper * next() const
Definition: inotify_watch.cpp:152
logstream::level::crit
@ crit
inotify_event_copy::doCopyIfNeeded
void doCopyIfNeeded()
make a real copy of this inotify_event.
Definition: inotify_watch.cpp:199
commandSender::flush
void flush()
Definition: inotify_watch.cpp:613
commandSender::keepalive
void keepalive()
Definition: inotify_watch.cpp:616
pidFileName
static options::single< std::string > pidFileName('p', "pidFile", "name of PID file")
watchDirectory
watchDirectoryReturnType watchDirectory(int fd, const std::string &name, watchedDirType *parent=nullptr)
watch a directory.
Definition: inotify_watch.cpp:494
maskName::maskNames
static const std::array< maskName, 16 > maskNames()
Definition: inotify_watch.cpp:70
maskName::addToRange
static void addToRange(options::container< std::string > &opt)
Definition: inotify_watch.cpp:115
maskName
Definition: inotify_watch.cpp:66
main
int main(int argc, const char *argv[])
main inotify_watch function.
Definition: inotify_watch.cpp:649
maskName::maskFromList
static uint32_t maskFromList(const options::container< std::string > &list)
Definition: inotify_watch.cpp:104
watchedDirType::setExtraEventMask
static void setExtraEventMask(int extraMask)
Definition: inotify_watch.cpp:368
inotify_event_wrapper::inotify_event_wrapper
inotify_event_wrapper()=delete
watchedDirType::empty
static bool empty()
test if any directories are watched
Definition: inotify_watch.cpp:360
watchedDirType::getPath
std::string getPath()
return full path to this dir, for use in error messages only
Definition: inotify_watch.cpp:349
inotify_event_copy::timeStamp
clock_type::time_point timeStamp() const
Definition: inotify_watch.cpp:219
commandSender::keepaliveTimeout
static options::single< unsigned > keepaliveTimeout
Definition: inotify_watch.cpp:593
inotify_event_copy::getStaleSingles
static inotify_event_copy * getStaleSingles()
get the next old inotify_event.
Definition: inotify_watch.cpp:237
watchDirectoryReturnType::vanished
@ vanished
commandSender::needFlush
bool needFlush
Definition: inotify_watch.cpp:596
scoped::Dir
Definition: scoped.h:71
inotify_event_copy::getRenameEvents
static std::map< uint32_t, inotify_event_copy > & getRenameEvents()
return a map which contains the inotify_event_copys
Definition: inotify_watch.cpp:180
inotify_event_wrapper::data
inotify_event data
Definition: inotify_watch.cpp:133
inotify_event_wrapper::cookie
uint32_t cookie() const
Definition: inotify_watch.cpp:143
options::positional
Definition: Options.h:876
commandSender::lastSendTime
clock_type::time_point lastSendTime
Definition: inotify_watch.cpp:595
watchedDirType::dump
static void dump(std::ostream &out)
Definition: inotify_watch.cpp:321
inotify_event_copy::event
const inotify_event_wrapper * event
pointer to the event, either the original or a copy
Definition: inotify_watch.cpp:194
inotify_event_copy::inotify_event_copy
inotify_event_copy(const inotify_event_wrapper *aEvent)
Definition: inotify_watch.cpp:211
inotify_event_copy::getEvent
const inotify_event_wrapper * getEvent() const
Definition: inotify_watch.cpp:222
sigPipeHandler
static void sigPipeHandler(int)
Definition: inotify_watch.cpp:578
watchedDirType::parent
watchedDirType * parent
Definition: inotify_watch.cpp:293
watchedDirType::fd
int fd
fd for the inotify instance, needed for destructor
Definition: inotify_watch.cpp:294
inotify_event_copy::cmp
static bool cmp(const inotify_event_copy *lhs, const inotify_event_copy *rhs)
function to order inotify_event_copys by time
Definition: inotify_watch.cpp:185
watchedDirType::children
std::map< std::string, watchedDirType * > children
Definition: inotify_watch.cpp:296
throwcall::good0
void good0(T call, const Args &... args)
template function to wrap system calls that return 0 on success
Definition: throwcall.h:40
commandSender::delimiter
char delimiter
Definition: inotify_watch.cpp:594
watchedDirType::get
static watchedDirType * get(int aId)
get a watcheDir by the inotify id
Definition: inotify_watch.cpp:356
operator<<
std::ostream & operator<<(std::ostream &stream, std::chrono::system_clock::time_point t)
Definition: inotify_watch.cpp:558
watchDirectoryReturnType::ignore
@ ignore
inotify_event_wrapper::fromChar
static const inotify_event_wrapper * fromChar(char *pointer)
Definition: inotify_watch.cpp:158
sigTermHandler
static void sigTermHandler(int)
Definition: inotify_watch.cpp:575
syslogstream.h
commandSender
class used to send messages via stdout.
Definition: inotify_watch.cpp:591
inotify_event_copy::isCopy
bool isCopy
true if it's a copy. We can't use singleEventIterator as this might be getSingleEvents()....
Definition: inotify_watch.cpp:196
OptionsRegex.h
watchedDirType::getId
int getId() const
Definition: inotify_watch.cpp:451
watchedDirType::extraEventMask
static int extraEventMask
Definition: inotify_watch.cpp:290