00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <utils/system/fam.h>
00024 #include <utils/logging/liblogger.h>
00025
00026 #ifdef HAVE_INOTIFY
00027 # include <sys/inotify.h>
00028 # include <sys/stat.h>
00029 # include <poll.h>
00030 # include <dirent.h>
00031 # include <unistd.h>
00032 # include <cstring>
00033 #endif
00034 #include <cerrno>
00035 #include <cstdlib>
00036
00037 namespace fawkes {
00038
00039
00040
00041 const unsigned int FamListener::FAM_ACCESS = 0x00000001;
00042
00043 const unsigned int FamListener::FAM_MODIFY = 0x00000002;
00044
00045 const unsigned int FamListener::FAM_ATTRIB = 0x00000004;
00046
00047 const unsigned int FamListener::FAM_CLOSE_WRITE = 0x00000008;
00048
00049 const unsigned int FamListener::FAM_CLOSE_NOWRITE = 0x00000010;
00050
00051 const unsigned int FamListener::FAM_CLOSE = (FAM_CLOSE_WRITE | FAM_CLOSE_NOWRITE);
00052
00053 const unsigned int FamListener::FAM_OPEN = 0x00000020;
00054
00055 const unsigned int FamListener::FAM_MOVED_FROM = 0x00000040;
00056
00057 const unsigned int FamListener::FAM_MOVED_TO = 0x00000080;
00058
00059 const unsigned int FamListener::FAM_MOVE = (FAM_MOVED_FROM | FAM_MOVED_TO);
00060
00061 const unsigned int FamListener::FAM_CREATE = 0x00000100;
00062
00063 const unsigned int FamListener::FAM_DELETE = 0x00000200;
00064
00065 const unsigned int FamListener::FAM_DELETE_SELF = 0x00000400;
00066
00067 const unsigned int FamListener::FAM_MOVE_SELF = 0x00000800;
00068
00069
00070
00071 const unsigned int FamListener::FAM_UNMOUNT = 0x00002000;
00072
00073 const unsigned int FamListener::FAM_Q_OVERFLOW = 0x00004000;
00074
00075 const unsigned int FamListener::FAM_IGNORED = 0x00008000;
00076
00077
00078
00079 const unsigned int FamListener::FAM_ONLYDIR = 0x01000000;
00080
00081 const unsigned int FamListener::FAM_DONT_FOLLOW = 0x02000000;
00082
00083 const unsigned int FamListener::FAM_MASK_ADD = 0x20000000;
00084
00085 const unsigned int FamListener::FAM_ISDIR = 0x40000000;
00086
00087 const unsigned int FamListener::FAM_ONESHOT = 0x80000000;
00088
00089
00090 const unsigned int FamListener::FAM_ALL_EVENTS = (FAM_ACCESS | FAM_MODIFY | FAM_ATTRIB | FAM_CLOSE_WRITE \
00091 | FAM_CLOSE_NOWRITE | FAM_OPEN | FAM_MOVED_FROM \
00092 | FAM_MOVED_TO | FAM_CREATE | FAM_DELETE \
00093 | FAM_DELETE_SELF | FAM_MOVE_SELF);
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108 FileAlterationMonitor::FileAlterationMonitor()
00109 {
00110 #ifdef HAVE_INOTIFY
00111 if ( (__inotify_fd = inotify_init()) == -1 ) {
00112 throw Exception(errno, "Failed to initialize inotify");
00113 }
00114
00115
00116 __inotify_bufsize = 1024 * (sizeof(struct inotify_event) + 16);
00117 __inotify_buf = (char *)malloc(__inotify_bufsize);
00118 #endif
00119
00120 __interrupted = false;
00121 __interruptible = (pipe(__pipe_fds) == 0);
00122
00123 __regexes.clear();
00124 }
00125
00126
00127
00128 FileAlterationMonitor::~FileAlterationMonitor()
00129 {
00130 for (__rxit = __regexes.begin(); __rxit != __regexes.end(); ++__rxit) {
00131 regfree(*__rxit);
00132 free(*__rxit);
00133 }
00134
00135 #ifdef HAVE_INOTIFY
00136 for (__inotify_wit = __inotify_watches.begin(); __inotify_wit != __inotify_watches.end(); ++__inotify_wit) {
00137 inotify_rm_watch(__inotify_fd, __inotify_wit->first);
00138 }
00139 close(__inotify_fd);
00140 if ( __inotify_buf ) {
00141 free(__inotify_buf);
00142 __inotify_buf = NULL;
00143 }
00144 #endif
00145 }
00146
00147
00148
00149
00150
00151
00152 void
00153 FileAlterationMonitor::watch_dir(const char *dirpath)
00154 {
00155 #ifdef HAVE_INOTIFY
00156 DIR *d = opendir(dirpath);
00157 if ( d == NULL ) {
00158 throw Exception(errno, "Failed to open dir %s", dirpath);
00159 }
00160
00161 uint32_t mask = IN_MODIFY | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF;
00162 int iw;
00163
00164
00165 if ( (iw = inotify_add_watch(__inotify_fd, dirpath, mask)) >= 0) {
00166 __inotify_watches[iw] = dirpath;
00167
00168 dirent de, *res;
00169 while ( (readdir_r(d, &de, &res) == 0) && (res != NULL) ) {
00170 std::string fp = std::string(dirpath) + "/" + de.d_name;
00171 struct stat st;
00172 if ( stat(fp.c_str(), &st) == 0 ) {
00173 if ( (de.d_name[0] != '.') && S_ISDIR(st.st_mode) ) {
00174 try {
00175 watch_dir(fp.c_str());
00176 } catch (Exception &e) {
00177 closedir(d);
00178 throw;
00179 }
00180
00181
00182 }
00183 } else {
00184 LibLogger::log_debug("FileAlterationMonitor",
00185 "Skipping watch on %s, cannot stat (%s)",
00186 fp.c_str(), strerror(errno));
00187 }
00188 }
00189 } else {
00190 throw Exception("FileAlterationMonitor",
00191 "Cannot add watch for %s", dirpath);
00192 }
00193
00194 closedir(d);
00195 #endif
00196 }
00197
00198
00199
00200
00201
00202 void
00203 FileAlterationMonitor::watch_file(const char *filepath)
00204 {
00205 #ifdef HAVE_INOTIFY
00206 uint32_t mask = IN_MODIFY | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF;
00207 int iw;
00208
00209
00210 if ( (iw = inotify_add_watch(__inotify_fd, filepath, mask)) >= 0) {
00211 __inotify_watches[iw] = filepath;
00212 } else {
00213 throw Exception("FileAlterationMonitor",
00214 "Cannot add watch for file %s", filepath);
00215 }
00216 #endif
00217 }
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232 void
00233 FileAlterationMonitor::add_filter(const char *regex)
00234 {
00235 int regerr = 0;
00236 regex_t *rx = (regex_t *)malloc(sizeof(regex_t));
00237 if ( (regerr = regcomp(rx, regex, REG_EXTENDED)) != 0 ) {
00238 char errtmp[1024];
00239 regerror(regerr, rx, errtmp, sizeof(errtmp));
00240 free(rx);
00241 throw Exception("Failed to compile lua file regex: %s", errtmp);
00242 }
00243 __regexes.push_back_locked(rx);
00244 }
00245
00246
00247
00248
00249
00250 void
00251 FileAlterationMonitor::add_listener(FamListener *listener)
00252 {
00253 __listeners.push_back_locked(listener);
00254 }
00255
00256
00257
00258
00259
00260 void
00261 FileAlterationMonitor::remove_listener(FamListener *listener)
00262 {
00263 __listeners.remove_locked(listener);
00264 }
00265
00266
00267
00268
00269
00270
00271
00272 void
00273 FileAlterationMonitor::process_events(int timeout)
00274 {
00275 #ifdef HAVE_INOTIFY
00276
00277 __interrupted = false;
00278 pollfd ipfd[2];
00279 ipfd[0].fd = __inotify_fd;
00280 ipfd[0].events = POLLIN;
00281 ipfd[0].revents = 0;
00282 ipfd[1].fd = __pipe_fds[0];
00283 ipfd[1].events = POLLIN;
00284 ipfd[1].revents = 0;
00285 int prv = poll(ipfd, 2, timeout);
00286 if ( prv == -1 ) {
00287 if ( errno != EINTR ) {
00288 LibLogger::log_error("FileAlterationMonitor",
00289 "inotify poll failed: %s (%i)",
00290 strerror(errno), errno);
00291 } else {
00292 __interrupted = true;
00293 }
00294 } else while ( !__interrupted && (prv > 0) ) {
00295
00296 if ( ipfd[0].revents & POLLERR ) {
00297 LibLogger::log_error("FileAlterationMonitor", "inotify poll error");
00298 } else if (__interrupted) {
00299
00300 return;
00301 } else {
00302
00303 int bytes = 0, i = 0;
00304 if ((bytes = read(__inotify_fd, __inotify_buf, __inotify_bufsize)) != -1) {
00305 while (!__interrupted && (i < bytes)) {
00306 struct inotify_event *event = (struct inotify_event *) &__inotify_buf[i];
00307
00308 bool valid = true;
00309 if (! (event->mask & IN_ISDIR)) {
00310 for (__rxit = __regexes.begin(); __rxit != __regexes.end(); ++__rxit) {
00311 if (regexec(*__rxit, event->name, 0, NULL, 0) == REG_NOMATCH ) {
00312
00313 valid = false;
00314 break;
00315 }
00316 }
00317 }
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334 if ( valid ) {
00335 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) {
00336 (*__lit)->fam_event(event->name, event->mask);
00337 }
00338 }
00339
00340 if (event->mask & IN_DELETE_SELF) {
00341
00342 __inotify_watches.erase(event->wd);
00343 inotify_rm_watch(__inotify_fd, event->wd);
00344 }
00345
00346 if (event->mask & IN_CREATE) {
00347
00348 std::string fp = __inotify_watches[event->wd] + "/" + event->name;
00349 if ( (event->mask & IN_ISDIR) && (event->name[0] != '.') ) {
00350
00351
00352
00353
00354
00355 try {
00356 watch_dir(fp.c_str());
00357 } catch (Exception &e) {
00358 LibLogger::log_warn("FileAlterationMonitor", "Adding watch for %s failed, ignoring.", fp.c_str());
00359 LibLogger::log_warn("FileAlterationMonitor", e);
00360 }
00361 }
00362 }
00363
00364 i += sizeof(struct inotify_event) + event->len;
00365 }
00366 } else {
00367 LibLogger::log_error("FileAlterationMonitor", "inotify failed to read any bytes");
00368 }
00369 }
00370
00371 prv = poll(ipfd, 2, 0);
00372 }
00373 #else
00374 LibLogger::log_error("FileAlterationMonitor",
00375 "inotify support not available, but "
00376 "process_events() was called. Ignoring.");
00377 #endif
00378 }
00379
00380
00381
00382
00383
00384
00385 void
00386 FileAlterationMonitor::interrupt()
00387 {
00388 if (__interruptible) {
00389 __interrupted = true;
00390 char tmp = 0;
00391 if (write(__pipe_fds[1], &tmp, 1) != 1) {
00392 throw Exception(errno, "Failed to interrupt file alteration monitor,"
00393 " failed to write to pipe");
00394 }
00395 } else {
00396 throw Exception("Currently not interruptible");
00397 }
00398 }
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415 FamListener::~FamListener()
00416 {
00417 }
00418
00419 }