Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * fam.h - File Alteration Monitor 00004 * 00005 * Created: Fri May 23 11:38:41 2008 00006 * Copyright 2006-2008 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU Library General Public License for more details. 00019 * 00020 * Read the full text in the LICENSE.GPL file in the doc directory. 00021 */ 00022 00023 #include <utils/system/fam.h> 00024 #include <core/exception.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 /* Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. */ 00040 /** File was accessed. */ 00041 const unsigned int FamListener::FAM_ACCESS = 0x00000001; 00042 /** File was modified. */ 00043 const unsigned int FamListener::FAM_MODIFY = 0x00000002; 00044 /** Metadata changed. */ 00045 const unsigned int FamListener::FAM_ATTRIB = 0x00000004; 00046 /** Writtable file was closed. */ 00047 const unsigned int FamListener::FAM_CLOSE_WRITE = 0x00000008; 00048 /** Unwrittable file closed. */ 00049 const unsigned int FamListener::FAM_CLOSE_NOWRITE = 0x00000010; 00050 /** Close. */ 00051 const unsigned int FamListener::FAM_CLOSE = (FAM_CLOSE_WRITE | FAM_CLOSE_NOWRITE); 00052 /** File was opened. */ 00053 const unsigned int FamListener::FAM_OPEN = 0x00000020; 00054 /** File was moved from X. */ 00055 const unsigned int FamListener::FAM_MOVED_FROM = 0x00000040; 00056 /** File was moved to Y. */ 00057 const unsigned int FamListener::FAM_MOVED_TO = 0x00000080; 00058 /** Moves. */ 00059 const unsigned int FamListener::FAM_MOVE = (FAM_MOVED_FROM | FAM_MOVED_TO); 00060 /** Subfile was created. */ 00061 const unsigned int FamListener::FAM_CREATE = 0x00000100; 00062 /** Subfile was deleted. */ 00063 const unsigned int FamListener::FAM_DELETE = 0x00000200; 00064 /** Self was deleted. */ 00065 const unsigned int FamListener::FAM_DELETE_SELF = 0x00000400; 00066 /** Self was moved. */ 00067 const unsigned int FamListener::FAM_MOVE_SELF = 0x00000800; 00068 00069 /* Events sent by the kernel. */ 00070 /** Backing fs was unmounted. */ 00071 const unsigned int FamListener::FAM_UNMOUNT = 0x00002000; 00072 /** Event queued overflowed. */ 00073 const unsigned int FamListener::FAM_Q_OVERFLOW = 0x00004000; 00074 /** File was ignored. */ 00075 const unsigned int FamListener::FAM_IGNORED = 0x00008000; 00076 00077 /* Special flags. */ 00078 /** Only watch the path if it is a directory. */ 00079 const unsigned int FamListener::FAM_ONLYDIR = 0x01000000; 00080 /** Do not follow a sym link. */ 00081 const unsigned int FamListener::FAM_DONT_FOLLOW = 0x02000000; 00082 /** Add to the mask of an already existing watch. */ 00083 const unsigned int FamListener::FAM_MASK_ADD = 0x20000000; 00084 /** Event occurred against dir. */ 00085 const unsigned int FamListener::FAM_ISDIR = 0x40000000; 00086 /** Only send event once. */ 00087 const unsigned int FamListener::FAM_ONESHOT = 0x80000000; 00088 00089 /** All events which a program can wait on. */ 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 /** @class FileAlterationMonitor <utils/system/fam.h> 00097 * Monitors files for changes. 00098 * This is a wrapper around inotify. It will watch directories and files 00099 * for modifications. If a modifiacation, removal or addition of a file 00100 * is detected one or more listeners are called. The files which trigger 00101 * the event can be constrained with regular expressions. 00102 * @author Tim Niemueller 00103 */ 00104 00105 /** Constructor. 00106 * Opens the inotify context. 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 // from http://www.linuxjournal.com/article/8478 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 /** Destructor. */ 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 /** Watch a directory. 00149 * This adds the given directory recursively to this FAM. 00150 * @param dirpath path to directory to add 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 //LibLogger::log_debug("FileAlterationMonitor", "Adding watch for %s", dirpath); 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 //} else { 00181 //LibLogger::log_debug("SkillerExecutionThread", "Skipping file %s", fp.c_str()); 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(errno, "FileAlterationMonitor: cannot add watch for %s", dirpath); 00191 } 00192 00193 closedir(d); 00194 #endif 00195 } 00196 00197 /** Watch a file. 00198 * This adds the given fileto this FAM. 00199 * @param filepath path to file to add 00200 */ 00201 void 00202 FileAlterationMonitor::watch_file(const char *filepath) 00203 { 00204 #ifdef HAVE_INOTIFY 00205 uint32_t mask = IN_MODIFY | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF; 00206 int iw; 00207 00208 //LibLogger::log_debug("FileAlterationMonitor", "Adding watch for %s", dirpath); 00209 if ( (iw = inotify_add_watch(__inotify_fd, filepath, mask)) >= 0) { 00210 __inotify_watches[iw] = filepath; 00211 } else { 00212 throw Exception("FileAlterationMonitor: cannot add watch for file %s", filepath); 00213 } 00214 #endif 00215 } 00216 00217 00218 /** Add a filter. 00219 * Filters are applied to path names that triggered an event. All 00220 * pathnames are checked against this regex and if any does not match 00221 * the event is not posted to listeners. 00222 * An example regular expression is 00223 * @code 00224 * ^[^.].*\\.lua$ 00225 * @endcode 00226 * This regular expression matches to all files that does not start with 00227 * a dot and have an .lua ending. 00228 * @param regex regular expression to add 00229 */ 00230 void 00231 FileAlterationMonitor::add_filter(const char *regex) 00232 { 00233 int regerr = 0; 00234 regex_t *rx = (regex_t *)malloc(sizeof(regex_t)); 00235 if ( (regerr = regcomp(rx, regex, REG_EXTENDED)) != 0 ) { 00236 char errtmp[1024]; 00237 regerror(regerr, rx, errtmp, sizeof(errtmp)); 00238 free(rx); 00239 throw Exception("Failed to compile lua file regex: %s", errtmp); 00240 } 00241 __regexes.push_back_locked(rx); 00242 } 00243 00244 00245 /** Add a listener. 00246 * @param listener listener to add 00247 */ 00248 void 00249 FileAlterationMonitor::add_listener(FamListener *listener) 00250 { 00251 __listeners.push_back_locked(listener); 00252 } 00253 00254 00255 /** Remove a listener. 00256 * @param listener listener to remove 00257 */ 00258 void 00259 FileAlterationMonitor::remove_listener(FamListener *listener) 00260 { 00261 __listeners.remove_locked(listener); 00262 } 00263 00264 00265 /** Process events. 00266 * Call this when you want file events to be processed. 00267 * @param timeout timeout in milliseconds to wait for an event, 0 to just check 00268 * and no wait, -1 to wait forever until an event is received 00269 */ 00270 void 00271 FileAlterationMonitor::process_events(int timeout) 00272 { 00273 #ifdef HAVE_INOTIFY 00274 // Check for inotify events 00275 __interrupted = false; 00276 pollfd ipfd[2]; 00277 ipfd[0].fd = __inotify_fd; 00278 ipfd[0].events = POLLIN; 00279 ipfd[0].revents = 0; 00280 ipfd[1].fd = __pipe_fds[0]; 00281 ipfd[1].events = POLLIN; 00282 ipfd[1].revents = 0; 00283 int prv = poll(ipfd, 2, timeout); 00284 if ( prv == -1 ) { 00285 if ( errno != EINTR ) { 00286 //LibLogger::log_error("FileAlterationMonitor", 00287 // "inotify poll failed: %s (%i)", 00288 // strerror(errno), errno); 00289 } else { 00290 __interrupted = true; 00291 } 00292 } else while ( !__interrupted && (prv > 0) ) { 00293 // Our fd has an event, we can read 00294 if ( ipfd[0].revents & POLLERR ) { 00295 //LibLogger::log_error("FileAlterationMonitor", "inotify poll error"); 00296 } else if (__interrupted) { 00297 // interrupted 00298 return; 00299 } else { 00300 // must be POLLIN 00301 int bytes = 0, i = 0; 00302 if ((bytes = read(__inotify_fd, __inotify_buf, __inotify_bufsize)) != -1) { 00303 while (!__interrupted && (i < bytes)) { 00304 struct inotify_event *event = (struct inotify_event *) &__inotify_buf[i]; 00305 00306 bool valid = true; 00307 if (! (event->mask & IN_ISDIR)) { 00308 for (__rxit = __regexes.begin(); __rxit != __regexes.end(); ++__rxit) { 00309 if (regexec(*__rxit, event->name, 0, NULL, 0) == REG_NOMATCH ) { 00310 //LibLogger::log_debug("FileAlterationMonitor", "A regex did not match for %s", event->name); 00311 valid = false; 00312 break; 00313 } 00314 } 00315 } 00316 00317 /* 00318 if (event->mask & IN_MODIFY) { 00319 LibLogger::log_debug("FileAlterationMonitor", "%s has been modified", event->name); 00320 } 00321 if (event->mask & IN_MOVE) { 00322 LibLogger::log_debug("FileAlterationMonitor", "%s has been moved", event->name); 00323 } 00324 if (event->mask & IN_DELETE) { 00325 LibLogger::log_debug("FileAlterationMonitor", "%s has been deleted", event->name); 00326 } 00327 if (event->mask & IN_CREATE) { 00328 LibLogger::log_debug("FileAlterationMonitor", "%s has been created", event->name); 00329 } 00330 */ 00331 00332 if ( valid ) { 00333 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) { 00334 (*__lit)->fam_event(event->name, event->mask); 00335 } 00336 } 00337 00338 if (event->mask & IN_DELETE_SELF) { 00339 //LibLogger::log_debug("FileAlterationMonitor", "Watched %s has been deleted", event->name); 00340 __inotify_watches.erase(event->wd); 00341 inotify_rm_watch(__inotify_fd, event->wd); 00342 } 00343 00344 if (event->mask & IN_CREATE) { 00345 // Check if it is a directory, if it is, watch it 00346 std::string fp = __inotify_watches[event->wd] + "/" + event->name; 00347 if ( (event->mask & IN_ISDIR) && (event->name[0] != '.') ) { 00348 /* 00349 LibLogger::log_debug("FileAlterationMonitor", 00350 "Directory %s has been created, " 00351 "adding to watch list", event->name); 00352 */ 00353 try { 00354 watch_dir(fp.c_str()); 00355 } catch (Exception &e) { 00356 //LibLogger::log_warn("FileAlterationMonitor", "Adding watch for %s failed, ignoring.", fp.c_str()); 00357 //LibLogger::log_warn("FileAlterationMonitor", e); 00358 } 00359 } 00360 } 00361 00362 i += sizeof(struct inotify_event) + event->len; 00363 } 00364 } else { 00365 //LibLogger::log_error("FileAlterationMonitor", "inotify failed to read any bytes"); 00366 } 00367 } 00368 00369 prv = poll(ipfd, 2, 0); 00370 } 00371 #else 00372 //LibLogger::log_error("FileAlterationMonitor", 00373 // "inotify support not available, but " 00374 // "process_events() was called. Ignoring."); 00375 throw Exception("FileAlterationMonitor: inotify support not available, " 00376 "but process_events() was called."); 00377 #endif 00378 } 00379 00380 00381 /** Interrupt a running process_events(). 00382 * This method will interrupt e.g. a running inifinetly blocking call of 00383 * process_events(). 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 /** @class FamListener <utils/system/fam.h> 00402 * File Alteration Monitor Listener. 00403 * Listener called by FileAlterationMonitor for events. 00404 * @author Tim Niemueller 00405 * 00406 * @fn FamListener::fam_event(const char *filename, unsigned int mask) 00407 * Event has been raised. 00408 * @param filename name of the file that triggered the event 00409 * @param mask mask indicating the event. Currently inotify event flags 00410 * are used, see inotify.h. 00411 * 00412 */ 00413 00414 /** Virtual empty destructor. */ 00415 FamListener::~FamListener() 00416 { 00417 } 00418 00419 } // end of namespace fawkes