$search
00001 00002 /*************************************************************************** 00003 * fam.h - File Alteration Monitor 00004 * 00005 * Created: Fri May 23 11:38:41 2008 00006 * Copyright 2006-2010 Tim Niemueller [www.niemueller.de] 00007 * 2010 Carnegie Mellon University 00008 * 2010 Intel Labs Pittsburgh 00009 ****************************************************************************/ 00010 00011 /* This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL file in the doc directory. 00022 */ 00023 00024 #include <lua_utils/fam.h> 00025 00026 #ifndef USE_ROS 00027 # include <core/exception.h> 00028 #else 00029 # include <ros/common.h> 00030 # if ROS_VERSION_MAJOR > 1 || ROS_VERSION_MAJOR == 1 && ROS_VERSION_MINOR >= 2 00031 # include <ros/exception.h> 00032 # else 00033 # include <ros/exceptions.h> 00034 # endif 00035 using ros::Exception; 00036 #endif 00037 00038 #ifdef HAVE_INOTIFY 00039 # include <sys/inotify.h> 00040 # include <sys/stat.h> 00041 # include <poll.h> 00042 # include <dirent.h> 00043 # include <unistd.h> 00044 # include <cstring> 00045 #endif 00046 #include <cerrno> 00047 #include <cstdlib> 00048 #include <cstdio> 00049 00050 namespace fawkes { 00051 00052 /* Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. */ 00054 const unsigned int FamListener::FAM_ACCESS = 0x00000001; 00056 const unsigned int FamListener::FAM_MODIFY = 0x00000002; 00058 const unsigned int FamListener::FAM_ATTRIB = 0x00000004; 00060 const unsigned int FamListener::FAM_CLOSE_WRITE = 0x00000008; 00062 const unsigned int FamListener::FAM_CLOSE_NOWRITE = 0x00000010; 00064 const unsigned int FamListener::FAM_CLOSE = (FAM_CLOSE_WRITE | FAM_CLOSE_NOWRITE); 00066 const unsigned int FamListener::FAM_OPEN = 0x00000020; 00068 const unsigned int FamListener::FAM_MOVED_FROM = 0x00000040; 00070 const unsigned int FamListener::FAM_MOVED_TO = 0x00000080; 00072 const unsigned int FamListener::FAM_MOVE = (FAM_MOVED_FROM | FAM_MOVED_TO); 00074 const unsigned int FamListener::FAM_CREATE = 0x00000100; 00076 const unsigned int FamListener::FAM_DELETE = 0x00000200; 00078 const unsigned int FamListener::FAM_DELETE_SELF = 0x00000400; 00080 const unsigned int FamListener::FAM_MOVE_SELF = 0x00000800; 00081 00082 /* Events sent by the kernel. */ 00084 const unsigned int FamListener::FAM_UNMOUNT = 0x00002000; 00086 const unsigned int FamListener::FAM_Q_OVERFLOW = 0x00004000; 00088 const unsigned int FamListener::FAM_IGNORED = 0x00008000; 00089 00090 /* Special flags. */ 00092 const unsigned int FamListener::FAM_ONLYDIR = 0x01000000; 00094 const unsigned int FamListener::FAM_DONT_FOLLOW = 0x02000000; 00096 const unsigned int FamListener::FAM_MASK_ADD = 0x20000000; 00098 const unsigned int FamListener::FAM_ISDIR = 0x40000000; 00100 const unsigned int FamListener::FAM_ONESHOT = 0x80000000; 00101 00103 const unsigned int FamListener::FAM_ALL_EVENTS = (FAM_ACCESS | FAM_MODIFY | FAM_ATTRIB | FAM_CLOSE_WRITE \ 00104 | FAM_CLOSE_NOWRITE | FAM_OPEN | FAM_MOVED_FROM \ 00105 | FAM_MOVED_TO | FAM_CREATE | FAM_DELETE \ 00106 | FAM_DELETE_SELF | FAM_MOVE_SELF); 00107 00108 00121 FileAlterationMonitor::FileAlterationMonitor() 00122 { 00123 #ifdef HAVE_INOTIFY 00124 if ( (__inotify_fd = inotify_init()) == -1 ) { 00125 throw Exception("Failed to initialize inotify"); 00126 } 00127 00128 // from http://www.linuxjournal.com/article/8478 00129 __inotify_bufsize = 1024 * (sizeof(struct inotify_event) + 16); 00130 __inotify_buf = (char *)malloc(__inotify_bufsize); 00131 #endif 00132 00133 __interrupted = false; 00134 __interruptible = (pipe(__pipe_fds) == 0); 00135 00136 __regexes.clear(); 00137 } 00138 00139 00141 FileAlterationMonitor::~FileAlterationMonitor() 00142 { 00143 for (__rxit = __regexes.begin(); __rxit != __regexes.end(); ++__rxit) { 00144 regfree(*__rxit); 00145 free(*__rxit); 00146 } 00147 00148 #ifdef HAVE_INOTIFY 00149 for (__inotify_wit = __inotify_watches.begin(); __inotify_wit != __inotify_watches.end(); ++__inotify_wit) { 00150 inotify_rm_watch(__inotify_fd, __inotify_wit->first); 00151 } 00152 close(__inotify_fd); 00153 if ( __inotify_buf ) { 00154 free(__inotify_buf); 00155 __inotify_buf = NULL; 00156 } 00157 #endif 00158 } 00159 00160 00165 void 00166 FileAlterationMonitor::watch_dir(const char *dirpath) 00167 { 00168 #ifdef HAVE_INOTIFY 00169 DIR *d = opendir(dirpath); 00170 if ( d == NULL ) { 00171 throw Exception(std::string("Failed to open dir ") + dirpath); 00172 } 00173 00174 uint32_t mask = IN_MODIFY | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF; 00175 int iw; 00176 00177 //LibLogger::log_debug("FileAlterationMonitor", "Adding watch for %s", dirpath); 00178 if ( (iw = inotify_add_watch(__inotify_fd, dirpath, mask)) >= 0) { 00179 __inotify_watches[iw] = dirpath; 00180 00181 dirent de, *res; 00182 while ( (readdir_r(d, &de, &res) == 0) && (res != NULL) ) { 00183 std::string fp = std::string(dirpath) + "/" + de.d_name; 00184 struct stat st; 00185 if ( stat(fp.c_str(), &st) == 0 ) { 00186 if ( (de.d_name[0] != '.') && S_ISDIR(st.st_mode) ) { 00187 try { 00188 watch_dir(fp.c_str()); 00189 } catch (Exception &e) { 00190 closedir(d); 00191 throw; 00192 } 00193 //} else { 00194 //LibLogger::log_debug("SkillerExecutionThread", "Skipping file %s", fp.c_str()); 00195 } 00196 } else { 00197 //LibLogger::log_debug("FileAlterationMonitor", 00198 // "Skipping watch on %s, cannot stat (%s)", 00199 // fp.c_str(), strerror(errno)); 00200 } 00201 } 00202 } else { 00203 throw Exception(std::string("Cannot add watch for ") + dirpath); 00204 } 00205 00206 closedir(d); 00207 #endif 00208 } 00209 00214 void 00215 FileAlterationMonitor::watch_file(const char *filepath) 00216 { 00217 #ifdef HAVE_INOTIFY 00218 uint32_t mask = IN_MODIFY | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF; 00219 int iw; 00220 00221 //LibLogger::log_debug("FileAlterationMonitor", "Adding watch for %s", dirpath); 00222 if ( (iw = inotify_add_watch(__inotify_fd, filepath, mask)) >= 0) { 00223 __inotify_watches[iw] = filepath; 00224 } else { 00225 throw Exception(std::string("Cannot add watch for file ") + filepath); 00226 } 00227 #endif 00228 } 00229 00230 00243 void 00244 FileAlterationMonitor::add_filter(const char *regex) 00245 { 00246 int regerr = 0; 00247 regex_t *rx = (regex_t *)malloc(sizeof(regex_t)); 00248 if ( (regerr = regcomp(rx, regex, REG_EXTENDED)) != 0 ) { 00249 char errtmp[1024]; 00250 regerror(regerr, rx, errtmp, sizeof(errtmp)); 00251 free(rx); 00252 throw Exception(std::string("Failed to compile lua file regex: ") + errtmp); 00253 } 00254 __regexes.push_back(rx); 00255 } 00256 00257 00261 void 00262 FileAlterationMonitor::add_listener(FamListener *listener) 00263 { 00264 __listeners.push_back(listener); 00265 } 00266 00267 00271 void 00272 FileAlterationMonitor::remove_listener(FamListener *listener) 00273 { 00274 __listeners.remove(listener); 00275 } 00276 00277 00283 void 00284 FileAlterationMonitor::process_events(int timeout) 00285 { 00286 #ifdef HAVE_INOTIFY 00287 // Check for inotify events 00288 __interrupted = false; 00289 pollfd ipfd[2]; 00290 ipfd[0].fd = __inotify_fd; 00291 ipfd[0].events = POLLIN; 00292 ipfd[0].revents = 0; 00293 ipfd[1].fd = __pipe_fds[0]; 00294 ipfd[1].events = POLLIN; 00295 ipfd[1].revents = 0; 00296 int prv = poll(ipfd, 2, timeout); 00297 if ( prv == -1 ) { 00298 if ( errno != EINTR ) { 00299 #ifndef USE_ROS 00300 LibLogger::log_error("FileAlterationMonitor", 00301 "inotify poll failed: %s (%i)", 00302 strerror(errno), errno); 00303 #else 00304 printf("FileAlterationMonitor: inotify poll failed: %s (%i)\n", 00305 strerror(errno), errno); 00306 #endif 00307 } else { 00308 __interrupted = true; 00309 } 00310 } else while ( !__interrupted && (prv > 0) ) { 00311 // Our fd has an event, we can read 00312 if ( ipfd[0].revents & POLLERR ) { 00313 //LibLogger::log_error("FileAlterationMonitor", "inotify poll error"); 00314 } else if (__interrupted) { 00315 // interrupted 00316 return; 00317 } else { 00318 // must be POLLIN 00319 int bytes = 0, i = 0; 00320 if ((bytes = read(__inotify_fd, __inotify_buf, __inotify_bufsize)) != -1) { 00321 while (!__interrupted && (i < bytes)) { 00322 struct inotify_event *event = (struct inotify_event *) &__inotify_buf[i]; 00323 00324 bool valid = true; 00325 if (! (event->mask & IN_ISDIR)) { 00326 for (__rxit = __regexes.begin(); __rxit != __regexes.end(); ++__rxit) { 00327 if (event->len > 0 && regexec(*__rxit, event->name, 0, NULL, 0) == REG_NOMATCH ) { 00328 //LibLogger::log_debug("FileAlterationMonitor", "A regex did not match for %s", event->name); 00329 valid = false; 00330 break; 00331 } 00332 } 00333 } 00334 00335 /* 00336 if (event->mask & IN_MODIFY) { 00337 printf("FileAlterationMonitor: %s has been modified\n", event->len > 0 ? event->name : "?"); 00338 } 00339 if (event->mask & IN_MOVE) { 00340 printf("FileAlterationMonitor: %s has been moved\n", event->len > 0 ? event->name : "?"); 00341 } 00342 if (event->mask & IN_DELETE) { 00343 printf("FileAlterationMonitor: %s has been deleted\n", event->len > 0 ? event->name : "?"); 00344 } 00345 if (event->mask & IN_CREATE) { 00346 printf("FileAlterationMonitor: %s has been created\n", event->len > 0 ? event->name : "?"); 00347 } 00348 */ 00349 00350 if ( valid ) { 00351 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) { 00352 (*__lit)->fam_event(event->len > 0 ? event->name : "?", event->mask); 00353 } 00354 } 00355 00356 if (event->mask & IN_DELETE_SELF) { 00357 //LibLogger::log_debug("FileAlterationMonitor", "Watched %s has been deleted", event->name); 00358 __inotify_watches.erase(event->wd); 00359 inotify_rm_watch(__inotify_fd, event->wd); 00360 } 00361 00362 if (event->mask & IN_CREATE && event->len > 0) { 00363 // Check if it is a directory, if it is, watch it 00364 std::string fp = __inotify_watches[event->wd] + "/" + event->name; 00365 if ( (event->mask & IN_ISDIR) && (event->name[0] != '.') ) { 00366 /* 00367 LibLogger::log_debug("FileAlterationMonitor", 00368 "Directory %s has been created, " 00369 "adding to watch list", event->name); 00370 */ 00371 try { 00372 watch_dir(fp.c_str()); 00373 } catch (Exception &e) { 00374 //LibLogger::log_warn("FileAlterationMonitor", "Adding watch for %s failed, ignoring.", fp.c_str()); 00375 //LibLogger::log_warn("FileAlterationMonitor", e); 00376 } 00377 } 00378 } 00379 00380 i += sizeof(struct inotify_event) + event->len; 00381 } 00382 } else { 00383 //LibLogger::log_error("FileAlterationMonitor", "inotify failed to read any bytes"); 00384 } 00385 } 00386 00387 prv = poll(ipfd, 2, 0); 00388 } 00389 #else 00390 //LibLogger::log_error("FileAlterationMonitor", 00391 // "inotify support not available, but " 00392 // "process_events() was called. Ignoring."); 00393 #endif 00394 } 00395 00396 00401 void 00402 FileAlterationMonitor::interrupt() 00403 { 00404 if (__interruptible) { 00405 __interrupted = true; 00406 char tmp = 0; 00407 if (write(__pipe_fds[1], &tmp, 1) != 1) { 00408 throw Exception("Failed to interrupt file alteration monitor," 00409 " failed to write to pipe"); 00410 } 00411 } else { 00412 throw Exception("Currently not interruptible"); 00413 } 00414 } 00415 00416 00431 FamListener::~FamListener() 00432 { 00433 } 00434 00435 } // end of namespace fawkes