GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/file_watcher_inotify.cc Lines: 57 70 81.4 %
Date: 2019-02-03 02:48:13 Branches: 28 44 63.6 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 */
4
5
#include <errno.h>
6
#include <fcntl.h>
7
#include <linux/limits.h>
8
#include <poll.h>
9
#include <sys/inotify.h>
10
#include <sys/time.h>
11
#include <sys/types.h>
12
#include <unistd.h>
13
14
#include <cassert>
15
#include <string>
16
#include <vector>
17
18
#include "file_watcher_inotify.h"
19
#include "logging.h"
20
#include "util/posix.h"
21
22
namespace file_watcher {
23
24
4
FileWatcherInotify::FileWatcherInotify() {}
25
26
8
FileWatcherInotify::~FileWatcherInotify() {}
27
28
4
bool FileWatcherInotify::RunEventLoop(const FileWatcher::HandlerMap& handlers,
29
                                      int read_pipe, int write_pipe) {
30
4
  inotify_fd_ = inotify_init1(IN_NONBLOCK);
31
4
  assert(inotify_fd_ >= 0);
32
33
8
  for (FileWatcher::HandlerMap::const_iterator it = handlers.begin();
34
       it != handlers.end(); ++it) {
35
4
    RegisterFilter(it->first, it->second);
36
  }
37
38
  // Use the control pipe to signal readiness to the main thread,
39
  // before continuing with the event loop.
40
4
  WritePipe(write_pipe, "s", 1);
41
42
  struct pollfd poll_set[2];
43
44
4
  poll_set[0].fd = read_pipe;
45
4
  poll_set[0].events = POLLHUP | POLLIN;
46
4
  poll_set[0].revents = 0;
47
4
  poll_set[1].fd = inotify_fd_;
48
4
  poll_set[1].events = POLLIN;
49
4
  poll_set[1].revents = 0;
50
51
4
  bool stop = false;
52
17
  while (!stop) {
53
9
    int ready = poll(poll_set, 2, -1);
54
9
    if (ready == -1) {
55
      if (errno == EINTR) {
56
        continue;
57
      }
58
      LogCvmfs(kLogCvmfs, kLogSyslogErr,
59
               "FileWatcherInotify - Could not poll events. Errno: %d", errno);
60
      return false;
61
    }
62
9
    if (ready == 0) {
63
      continue;
64
    }
65
66
9
    if (poll_set[0].revents & POLLHUP) {
67
      LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n");
68
      stop = true;
69
      continue;
70
    }
71
9
    if (poll_set[0].revents & POLLIN) {
72
      char buffer[1];
73
4
      ReadPipe(read_pipe, &buffer, 1);
74
4
      LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n");
75
4
      stop = true;
76
4
      continue;
77
    }
78
79
5
    const size_t event_size = sizeof(struct inotify_event);
80
    // We need a large enough buffer for an event with the largest path name
81
5
    const size_t buffer_size = event_size + PATH_MAX + 1;
82
    char buffer[buffer_size];
83
5
    if (poll_set[1].revents & POLLIN) {
84
5
      int len = read(inotify_fd_, buffer, buffer_size);
85
5
      assert(len > 0);
86
5
      int i = 0;
87
18
      while (i < len) {
88
        struct inotify_event* inotify_event =
89
8
            reinterpret_cast<struct inotify_event*>(&buffer[i]);
90
        std::map<int, WatchRecord>::const_iterator it =
91
8
            watch_records_.find(inotify_event->wd);
92
8
        if (it != watch_records_.end()) {
93
6
          WatchRecord current_record = it->second;
94
6
          file_watcher::Event event = file_watcher::kInvalid;
95
6
          if (inotify_event->mask & IN_DELETE_SELF) {
96
2
            event = file_watcher::kDeleted;
97
4
          } else if (inotify_event->mask & IN_MODIFY) {
98
            // Modified
99
2
            event = file_watcher::kModified;
100
2
          } else if (inotify_event->mask & IN_MOVE_SELF) {
101
            // Renamed
102
            event = file_watcher::kRenamed;
103
2
          } else if (inotify_event->mask & IN_ATTRIB) {
104
            // Attributes
105
2
            event = file_watcher::kAttributes;
106
          } else if (inotify_event->mask & IN_IGNORED) {
107
            // An IN_IGNORED event is generated after a file is deleted and the
108
            // watch is removed
109
            event = file_watcher::kIgnored;
110
          }
111
6
          bool clear_handler = true;
112

12
          if (event != file_watcher::kInvalid &&
113
              event != file_watcher::kIgnored) {
114
            current_record.handler_->Handle(current_record.file_path_, event,
115
6
                                            &clear_handler);
116
          } else {
117
            LogCvmfs(kLogCvmfs, kLogDebug,
118
                     "FileWatcherInotify - Unknown event 0x%x\n",
119
                     inotify_event->mask);
120
          }
121
122
          // Perform post-handling actions (i.e. remove, reset filter)
123
6
          if (event == file_watcher::kDeleted) {
124
2
            watch_records_.erase(inotify_event->wd);
125
2
            if (!clear_handler) {
126
              RegisterFilter(current_record.file_path_,
127
                             current_record.handler_);
128
            }
129
          }
130
        } else {
131
          LogCvmfs(kLogCvmfs, kLogDebug,
132
                   "FileWatcherInotify - Unknown event ident: %ld",
133
2
                   inotify_event->wd);
134
        }
135
136
8
        i += event_size + inotify_event->len;
137
      }
138
    }
139
  }
140
141
4
  watch_records_.clear();
142
143
4
  close(inotify_fd_);
144
145
4
  return true;
146
}
147
148
4
int FileWatcherInotify::TryRegisterFilter(const std::string& file_path) {
149
  return inotify_add_watch(
150
      inotify_fd_, file_path.c_str(),
151
4
      IN_ATTRIB | IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
152
}
153
154
}  // namespace file_watcher