Пример #1
0
  void fsevents_monitor::run()
  {
    if (stream) return;

    // parsing paths
    vector<CFStringRef> dirs;

    for (string path : paths)
    {
      dirs.push_back(CFStringCreateWithCString(NULL,
                                               path.c_str(),
                                               kCFStringEncodingUTF8));
    }

    if (dirs.size() == 0) return;

    CFArrayRef pathsToWatch =
      CFArrayCreate(NULL,
                    reinterpret_cast<const void **> (&dirs[0]),
                    dirs.size(),
                    &kCFTypeArrayCallBacks);

    FSEventStreamContext *context = new FSEventStreamContext();
    context->version = 0;
    context->info = this;
    context->retain = nullptr;
    context->release = nullptr;
    context->copyDescription = nullptr;

    FSW_ELOG(_("Creating FSEvent stream...\n"));
    unique_lock<mutex> run_loop_lock(run_mutex);
    stream = FSEventStreamCreate(NULL,
                                 &fsevents_monitor::fsevents_callback,
                                 context,
                                 pathsToWatch,
                                 kFSEventStreamEventIdSinceNow,
                                 latency,
                                 kFSEventStreamCreateFlagFileEvents);

    if (!stream) throw libfsw_exception(_("Event stream could not be created."));

    run_loop = CFRunLoopGetCurrent();
    run_loop_lock.unlock();

    FSW_ELOG(_("Scheduling stream with run loop...\n"));
    FSEventStreamScheduleWithRunLoop(stream,
                                     run_loop,
                                     kCFRunLoopDefaultMode);

    FSW_ELOG(_("Starting event stream...\n"));
    FSEventStreamStart(stream);

    FSW_ELOG(_("Starting run loop...\n"));
    CFRunLoopRun();
  }
Пример #2
0
  void fsevents_monitor::on_stop()
  {
    lock_guard<mutex> run_loop_lock(run_mutex);
    if (!run_loop) throw libfsw_exception(_("run loop is null"));

    FSW_ELOG(_("Stopping event stream...\n"));
    FSEventStreamStop(stream);
    stream = nullptr;

    FSW_ELOG(_("Stopping run loop...\n"));
    CFRunLoopStop(run_loop);
    run_loop = nullptr;
  }
Пример #3
0
  void inotify_monitor::process_pending_events()
  {
    // Remove watches.
    auto wtd = impl->watches_to_remove.begin();

    while (wtd != impl->watches_to_remove.end())
    {
      if (inotify_rm_watch(impl->inotify_monitor_handle, *wtd) != 0)
      {
        perror("inotify_rm_watch");
      }
      else
      {
        ostringstream log;
        log << _("Removed: ") << *wtd << "\n";
        FSW_ELOG(log.str().c_str());
      }

      impl->watches_to_remove.erase(wtd++);
    }

    // Clean up descriptors.
    auto fd = impl->descriptors_to_remove.begin();

    while (fd != impl->descriptors_to_remove.end())
    {
      const string & curr_path = impl->wd_to_path[*fd];
      impl->path_to_wd.erase(curr_path);
      impl->wd_to_path.erase(*fd);
      impl->watched_descriptors.erase(*fd);

      impl->descriptors_to_remove.erase(fd++);
    }
  }
Пример #4
0
  bool inotify_monitor::add_watch(const string &path,
                                  const struct stat &fd_stat)
  {
    // TODO: Consider optionally adding the IN_EXCL_UNLINK flag.
    int inotify_desc = inotify_add_watch(impl->inotify_monitor_handle,
                                         path.c_str(),
                                         IN_ALL_EVENTS);

    if (inotify_desc == -1)
    {
      perror("inotify_add_watch");
    }
    else
    {
      impl->watched_descriptors.insert(inotify_desc);
      impl->wd_to_path[inotify_desc] = path;
      impl->path_to_wd[path] = inotify_desc;

      ostringstream log;
      log << _("Added: ") << path << "\n";
      FSW_ELOG(log.str().c_str());
    }

    return (inotify_desc != -1);
  }
Пример #5
0
  fsevents_monitor::~fsevents_monitor()
  {
    if (stream)
    {
      FSW_ELOG(_("Stopping event stream...\n"));
      FSEventStreamStop(stream);

      FSW_ELOG(_("Invalidating event stream...\n"));
      FSEventStreamInvalidate(stream);

      FSW_ELOG(_("Releasing event stream...\n"));
      FSEventStreamRelease(stream);
    }

    stream = nullptr;
  }
Пример #6
0
  void poll_monitor::run()
  {
    collect_initial_data();

    for (;;)
    {
#ifdef HAVE_CXX_MUTEX
      unique_lock<mutex> run_guard(run_mutex);
      if (should_stop) break;
      run_guard.unlock();
#endif

      FSW_ELOG(_("Done scanning.\n"));

      sleep(latency < MIN_POLL_LATENCY ? MIN_POLL_LATENCY : latency);

      time(&curr_time);

      collect_data();

      if (events.size())
      {
        notify_events(events);
        events.clear();
      }
    }
  }
Пример #7
0
  void monitor::notify_events(const std::vector<event>& events) const
  {
    FSW_MONITOR_NOTIFY_GUARD;

    // Update the last notification timestamp
#ifdef HAVE_INACTIVITY_CALLBACK
    milliseconds now =
      duration_cast<milliseconds>(
        system_clock::now().time_since_epoch());
    last_notification.store(now);
#endif

    std::vector<event> filtered_events;

    for (auto const& event : events)
    {
      // Filter flags
      std::vector<fsw_event_flag> filtered_flags = filter_flags(event);

      if (filtered_flags.empty()) continue;
      if (!accept_path(event.get_path())) continue;

      filtered_events.emplace_back(event.get_path(),
                                   event.get_time(),
                                   filtered_flags);
    }

    if (!filtered_events.empty())
    {
      FSW_ELOG(string_utils::string_from_format(_("Notifying events #: %d.\n"),
                                                filtered_events.size()).c_str());

      callback(filtered_events, context);
    }
  }
Пример #8
0
  void monitor::start()
  {
    FSW_MONITOR_RUN_GUARD;
    if (this->running) return;

    this->running = true;
    FSW_MONITOR_RUN_GUARD_UNLOCK;

    // Fire the inactivity thread
    std::unique_ptr<std::thread> inactivity_thread;
#ifdef HAVE_INACTIVITY_CALLBACK
    if (fire_idle_event)
      inactivity_thread.reset(
        new std::thread(monitor::inactivity_callback, this));
#endif

    // Fire the monitor run loop.
    this->run();

    // Join the inactivity thread and wait until it stops.
    FSW_ELOG(_("Inactivity notification thread: joining\n"));
    if (inactivity_thread) inactivity_thread->join();

    FSW_MONITOR_RUN_GUARD_LOCK;
    this->running = false;
    this->should_stop = false;
    FSW_MONITOR_RUN_GUARD_UNLOCK;
  }
Пример #9
0
  void monitor::stop()
  {
    // Stopping a monitor is a cooperative task: the caller request a task to
    // stop and it's responsibility of each monitor to check for this flag and
    // timely stop the processing loop.
    FSW_MONITOR_RUN_GUARD;
    if (!this->running || this->should_stop) return;

    FSW_ELOG(_("Stopping the monitor.\n"));
    this->should_stop = true;
    on_stop();
  }
Пример #10
0
  void monitor::inactivity_callback(monitor *mon)
  {
    if (!mon) throw libfsw_exception(_("Callback argument cannot be null."));

    FSW_ELOG(_("Inactivity notification thread: starting\n"));

    for (;;)
    {
      std::unique_lock<std::mutex> run_guard(mon->run_mutex);
      if (mon->should_stop) break;
      run_guard.unlock();

      milliseconds elapsed =
        duration_cast<milliseconds>(system_clock::now().time_since_epoch())
        - mon->last_notification.load();

      // Sleep and loop again if sufficient time has not elapsed yet.
      if (elapsed < mon->get_latency_ms())
      {
        milliseconds to_sleep = mon->get_latency_ms() - elapsed;
        seconds max_sleep_time(2);

        std::this_thread::sleep_for(
          to_sleep > max_sleep_time ? max_sleep_time : to_sleep);
        continue;
      }

      // Build a fake event.
      time_t curr_time;
      time(&curr_time);

      std::vector<event> events;
      events.push_back({"", curr_time, {NoOp}});

      mon->notify_events(events);
    }

    FSW_ELOG(_("Inactivity notification thread: exiting\n"));
  }
Пример #11
0
  void poll_monitor::run()
  {
    collect_initial_data();

    while (true)
    {
      FSW_ELOG(_("Done scanning.\n"));

      sleep(latency < MIN_POLL_LATENCY ? MIN_POLL_LATENCY : latency);

      time(&curr_time);

      collect_data();

      if (events.size())
      {
        notify_events(events);
        events.clear();
      }
    }
  }
Пример #12
0
  inotify_monitor::~inotify_monitor()
  {
    // close inotify watchers
    for (auto inotify_desc_pair : impl->watched_descriptors)
    {
      ostringstream log;
      log << _("Removing: ") << inotify_desc_pair << "\n";
      FSW_ELOG(log.str().c_str());

      if (inotify_rm_watch(impl->inotify_monitor_handle, inotify_desc_pair))
      {
        perror("inotify_rm_watch");
      }
    }

    // close inotify
    if (impl->inotify_monitor_handle > 0)
    {
      close(impl->inotify_monitor_handle);
    }

    delete impl;
  }
Пример #13
0
  void inotify_monitor::run()
  {
    char buffer[BUFFER_SIZE];

    while (true)
    {
      process_pending_events();

      scan_root_paths();

      // If no files can be watched, sleep and repeat the loop.
      if (!impl->watched_descriptors.size())
      {
        sleep(latency);
        continue;
      }

      // Use select to timeout on file descriptor read the amount specified by
      // the monitor latency.  This way, the monitor has a chance to update its
      // watches with at least the periodicity expected by the user.
      fd_set set;
      struct timeval timeout;

      FD_ZERO(&set);
      FD_SET(impl->inotify_monitor_handle, &set);
      double sec;
      double frac = modf(this->latency, &sec);
      timeout.tv_sec = sec;
      timeout.tv_usec = 1000 * 1000 * frac;

      int rv = select(impl->inotify_monitor_handle + 1,
                      &set,
                      nullptr,
                      nullptr,
                      &timeout);

      if (rv == -1)
      {
        throw libfsw_exception(_("::select() on inotify descriptor encountered an error."));
      }

      // In case of read timeout just repeat the loop.
      if (rv == 0)
      {
        continue;
      }

      ssize_t record_num = read(impl->inotify_monitor_handle,
                                buffer,
                                BUFFER_SIZE);

      {
        ostringstream log;
        log << _("Number of records: ") << record_num << "\n";
        FSW_ELOG(log.str().c_str());
      }

      if (!record_num)
      {
        throw libfsw_exception(_("read() on inotify descriptor read 0 records."));
      }

      if (record_num == -1)
      {
        perror("read()");
        throw libfsw_exception(_("read() on inotify descriptor returned -1."));
      }

      time(&impl->curr_time);

      for (char *p = buffer; p < buffer + record_num;)
      {
        struct inotify_event * event = reinterpret_cast<struct inotify_event *> (p);

        preprocess_event(event);

        p += (sizeof (struct inotify_event)) + event->len;
      }

      if (impl->events.size())
      {
        notify_events(impl->events);

        impl->events.clear();
      }

      sleep(latency);
    }
  }
Пример #14
0
  void inotify_monitor::preprocess_node_event(struct inotify_event * event)
  {
    vector<fsw_event_flag> flags;

    if (event->mask & IN_ACCESS) flags.push_back(fsw_event_flag::PlatformSpecific);
    if (event->mask & IN_ATTRIB) flags.push_back(fsw_event_flag::AttributeModified);
    if (event->mask & IN_CLOSE_NOWRITE) flags.push_back(fsw_event_flag::PlatformSpecific);
    if (event->mask & IN_CLOSE_WRITE) flags.push_back(fsw_event_flag::Updated);
    if (event->mask & IN_CREATE) flags.push_back(fsw_event_flag::Created);
    if (event->mask & IN_DELETE) flags.push_back(fsw_event_flag::Removed);
    if (event->mask & IN_MODIFY) flags.push_back(fsw_event_flag::Updated);
    if (event->mask & IN_MOVED_FROM)
    {
      flags.push_back(fsw_event_flag::Removed);
      flags.push_back(fsw_event_flag::MovedFrom);
    }
    if (event->mask & IN_MOVED_TO)
    {
      flags.push_back(fsw_event_flag::Created);
      flags.push_back(fsw_event_flag::MovedTo);
    }
    if (event->mask & IN_OPEN) flags.push_back(fsw_event_flag::PlatformSpecific);

    // Build the file name.
    ostringstream filename_stream;
    filename_stream << impl->wd_to_path[event->wd];

    if (event->len > 1)
    {
      filename_stream << "/";
      filename_stream << event->name;
    }

    if (flags.size())
    {
      impl->events.push_back({filename_stream.str(), impl->curr_time, flags});
    }

    {
      ostringstream log;
      log << _("Generic event: ") << event->wd << "::" << filename_stream.str() << "\n";
      FSW_ELOG(log.str().c_str());
    }

    /*
     * inotify automatically removes the watch of a watched item that has been
     * removed and posts an IN_IGNORED event after an IN_DELETE_SELF.
     */
    if (event->mask & IN_IGNORED)
    {
      ostringstream log;
      log << "IN_IGNORED: " << event->wd << "::" << filename_stream.str() << "\n";
      FSW_ELOG(log.str().c_str());

      impl->descriptors_to_remove.insert(event->wd);
    }

    /*
     * inotify sends an IN_MOVE_SELF event when a watched object is moved into
     * the same filesystem and keeps watching it.  Since its path has changed,
     * we remove the watch so that recreation is attempted at the next
     * iteration.
     *
     * Beware that a race condition exists which may result in events go
     * unnoticed when a watched file x is removed and a new file named x is
     * created thereafter.  In this case, fswatch could be blocked on read and
     * it would not have any chance to create a new watch descriptor for x until
     *  an event is received and read unblocks.
     */
    if (event->mask & IN_MOVE_SELF)
    {
      ostringstream log;
      log << "IN_MOVE_SELF: " << event->wd << "::" << filename_stream.str() << "\n";
      FSW_ELOG(log.str().c_str());

      impl->watches_to_remove.insert(event->wd);
      impl->descriptors_to_remove.insert(event->wd);
    }

    /*
     * An file could be moved to a path which is being observed.  The clobbered
     * file is handled by the corresponding IN_DELETE_SELF event.
     */

    /*
     * inotify automatically removes the watch of the object the IN_DELETE_SELF
     * event is related to.
     */
    if (event->mask & IN_DELETE_SELF)
    {
      ostringstream log;
      log << "IN_DELETE_SELF: " << event->wd << "::" << filename_stream.str() << "\n";
      FSW_ELOG(log.str().c_str());

      impl->descriptors_to_remove.insert(event->wd);
    }
  }