#include "lwatch.h"
Go to the source code of this file.
Classes | |
struct | path_watch_t |
When a watch is established on a filesystem directory this struct is used to track it. More... | |
Defines | |
#define | INOTIFY_WATCH_ERROR_BASE -2500 |
Base error code (minimum value) for inotify watch errors. | |
#define | INOTIFY_WATCH_ERROR_LIMIT (INOTIFY_WATCH_ERROR_BASE + 50) |
Maximum error code for inotify errors. | |
#define | IS_INOTIFY_WATCH_ERROR(error) (((error) >= INOTIFY_WATCH_ERROR_BASE) && ((error) < INOTIFY_WATCH_ERROR_LIMIT)) |
Return true if error code is within the range for inotify watch errors. | |
#define | INOTIFY_WATCH_ERROR_NULL_WATCH (INOTIFY_WATCH_ERROR_BASE + 1) |
watch invalid, was NULL | |
#define | INOTIFY_WATCH_ERROR_WATCH_ID_NOT_FOUND (INOTIFY_WATCH_ERROR_BASE + 2) |
could not look up watch id | |
#define | INOTIFY_WATCH_ALREADY_WATCHING (INOTIFY_WATCH_ERROR_BASE + 3) |
already being watched | |
#define | INOTIFY_WATCH_NOT_WATCHED (INOTIFY_WATCH_ERROR_BASE + 4) |
not being watched | |
Functions | |
int | inotify_watch_init (void) |
Initialize the inotify monitoring back end. | |
int | inotify_watch_fini (void) |
Close the inotify monitoring back end, stop all monitoring, free all resources. | |
bool | is_watch_target (const char *path) |
Return true if path parameter is a watch target. | |
bool | is_path_watched (const char *path) |
Return true if the given path is being watched by inotify. | |
const char * | inotify_watch_error_string (int error) |
Translate error code to string description. | |
char * | inotify_mask_string (unsigned int mask, const char *separator) |
Given an inotify event bitmask return a string of all the enabled flags. | |
char * | path_watch_string (struct path_watch_t *pw) |
Render a path_watch_t struct as a string. | |
struct path_watch_t * | find_watch_by_path (const char *path) |
Given a path lookup the path watch object associated with it. | |
struct path_watch_t * | find_watch_by_watch_id (int watch_id) |
Given an inotify watch id lookup the path watch object associated with it. | |
int | inotify_start_monitoring (const char *path) |
Start monitoring the target path with inotify. | |
int | inotify_stop_monitoring (const char *path) |
Stop monitoring the target path with inotify. | |
int | read_events (void) |
Loop used to read inotify events and dispatch them. | |
int | get_inotify_fd (void) |
Return the file descriptor used by the inotify back end. | |
lwatch_event_callback_t | register_lwatch_event_callback (lwatch_event_callback_t callback) |
Register a callback to receive log watch events. | |
Variables | |
bool | keep_watching |
Flag used to indicate if the read_events() loop should terminate or continue. |
Definition in file inotify_watch.h.
#define IS_INOTIFY_WATCH_ERROR | ( | error | ) | (((error) >= INOTIFY_WATCH_ERROR_BASE) && ((error) < INOTIFY_WATCH_ERROR_LIMIT)) |
Return true if error code is within the range for inotify watch errors.
[in] | error | The error code to test if it's a inotify watch error code. |
Definition at line 38 of file inotify_watch.h.
Referenced by error_string().
struct path_watch_t* find_watch_by_path | ( | const char * | path | ) | [read] |
Given a path lookup the path watch object associated with it.
[in] | path | The path whose matching watch object is desired. |
Definition at line 1407 of file inotify_watch.c.
Referenced by inotify_start_monitoring().
01408 { 01409 int error; 01410 hash_key_t key; 01411 hash_value_t value; 01412 struct path_watch_t *pw; 01413 01414 key.type = HASH_KEY_STRING; 01415 key.str = path; 01416 01417 if ((error = hash_lookup(watch_path_table, &key, &value)) != HASH_SUCCESS) { 01418 log_msg(LOG_VERBOSE_DEBUG, _("cannot find pathname \"%s\"\n"), path); 01419 return NULL; 01420 } 01421 01422 pw = (struct path_watch_t *) value.ptr; 01423 return pw; 01424 }
struct path_watch_t* find_watch_by_watch_id | ( | int | watch_id | ) | [read] |
Given an inotify watch id lookup the path watch object associated with it.
[in] | watch_id | The watch_id assigned by inotify to identify the watch |
Definition at line 1431 of file inotify_watch.c.
Referenced by process_event().
01432 { 01433 int error; 01434 hash_key_t key; 01435 hash_value_t value; 01436 struct path_watch_t *pw; 01437 01438 key.type = HASH_KEY_ULONG; 01439 key.ul = watch_id; 01440 01441 if ((error = hash_lookup(watch_id_table, &key, &value)) != HASH_SUCCESS) { 01442 log_msg(LOG_DEBUG, _("cannot find watch_id %d\n"), watch_id); 01443 return NULL; 01444 } 01445 01446 pw = (struct path_watch_t *) value.ptr; 01447 return pw; 01448 }
int get_inotify_fd | ( | void | ) |
Return the file descriptor used by the inotify back end.
Definition at line 1627 of file inotify_watch.c.
Referenced by main_loop().
01628 { 01629 return inotify_fd; 01630 }
char* inotify_mask_string | ( | unsigned int | mask, | |
const char * | separator | |||
) |
Given an inotify event bitmask return a string of all the enabled flags.
[in] | mask | Bitmask of inotify event flags |
[in] | separator | String used to separate individual flags |
Definition at line 1305 of file inotify_watch.c.
Referenced by process_event().
01306 { 01307 static __thread char buf[1024]; /* thread local static buffer */ 01308 char *p, *buf_end; 01309 size_t separator_len; 01310 unsigned int unknown; 01311 01312 p = buf; /* current position in buffer */ 01313 buf_end = &buf[sizeof(buf)]; /* non-inclusive end of buffer */ 01314 separator_len = strlen(separator); 01315 *p = 0; 01316 01317 /* validate there is nothing in the event mask we don't know about */ 01318 unknown = mask & ~(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | 01319 IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO | 01320 IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | 01321 IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | 01322 IN_DONT_FOLLOW | IN_MASK_ADD | IN_ISDIR | IN_ONESHOT); 01323 01324 if (unknown) p += snprintf(p, buf_end - p, "unknown bits=0x%x%s", unknown, separator); 01325 if (p >= buf_end) goto fail; 01326 01327 if (mask & IN_ACCESS) p += snprintf(p, buf_end - p, "ACCESS%s", separator); 01328 if (p >= buf_end) goto fail; 01329 if (mask & IN_MODIFY) p += snprintf(p, buf_end - p, "MODIFY%s", separator); 01330 if (p >= buf_end) goto fail; 01331 if (mask & IN_ATTRIB) p += snprintf(p, buf_end - p, "ATTRIB%s", separator); 01332 if (p >= buf_end) goto fail; 01333 if (mask & IN_CLOSE_WRITE) p += snprintf(p, buf_end - p, "CLOSE_WRITE%s", separator); 01334 if (p >= buf_end) goto fail; 01335 if (mask & IN_CLOSE_NOWRITE) p += snprintf(p, buf_end - p, "CLOSE_NOWRITE%s", separator); 01336 if (p >= buf_end) goto fail; 01337 if (mask & IN_OPEN) p += snprintf(p, buf_end - p, "OPEN%s", separator); 01338 if (p >= buf_end) goto fail; 01339 if (mask & IN_MOVED_FROM) p += snprintf(p, buf_end - p, "MOVED_FROM%s", separator); 01340 if (p >= buf_end) goto fail; 01341 if (mask & IN_MOVED_TO) p += snprintf(p, buf_end - p, "MOVED_TO%s", separator); 01342 if (p >= buf_end) goto fail; 01343 if (mask & IN_CREATE) p += snprintf(p, buf_end - p, "CREATE%s", separator); 01344 if (p >= buf_end) goto fail; 01345 if (mask & IN_DELETE) p += snprintf(p, buf_end - p, "DELETE%s", separator); 01346 if (p >= buf_end) goto fail; 01347 if (mask & IN_DELETE_SELF) p += snprintf(p, buf_end - p, "DELETE_SELF%s", separator); 01348 if (p >= buf_end) goto fail; 01349 if (mask & IN_MOVE_SELF) p += snprintf(p, buf_end - p, "MOVE_SELF%s", separator); 01350 if (p >= buf_end) goto fail; 01351 if (mask & IN_UNMOUNT) p += snprintf(p, buf_end - p, "UNMOUNT%s", separator); 01352 if (p >= buf_end) goto fail; 01353 if (mask & IN_Q_OVERFLOW) p += snprintf(p, buf_end - p, "Q_OVERFLOW%s", separator); 01354 if (p >= buf_end) goto fail; 01355 if (mask & IN_IGNORED) p += snprintf(p, buf_end - p, "IGNORED%s", separator); 01356 if (p >= buf_end) goto fail; 01357 if (mask & IN_ONLYDIR) p += snprintf(p, buf_end - p, "ONLYDIR%s", separator); 01358 if (p >= buf_end) goto fail; 01359 if (mask & IN_DONT_FOLLOW) p += snprintf(p, buf_end - p, "DONT_FOLLOW%s", separator); 01360 if (p >= buf_end) goto fail; 01361 if (mask & IN_MASK_ADD) p += snprintf(p, buf_end - p, "MASK_ADD%s", separator); 01362 if (p >= buf_end) goto fail; 01363 if (mask & IN_ISDIR) p += snprintf(p, buf_end - p, "ISDIR%s", separator); 01364 if (p >= buf_end) goto fail; 01365 if (mask & IN_ONESHOT) p += snprintf(p, buf_end - p, "ONESHOT%s", separator); 01366 if (p >= buf_end) goto fail; 01367 01368 if (p > buf) p[-separator_len] = 0; 01369 01370 return buf; 01371 fail: 01372 /* exhausted the buffer; NULL terminate then return truncated string */ 01373 buf_end[-1] = 0; /* should already be NULL terminated, but be safe */ 01374 return buf; 01375 }
int inotify_start_monitoring | ( | const char * | path_arg | ) |
Start monitoring the target path with inotify.
[in] | path_arg | The pathname to monitor, it will be normalized and made absolute |
Definition at line 1455 of file inotify_watch.c.
Referenced by process_file_rename(), relocate_watches(), start_monitoring(), and validate_backups_in_database().
01456 { 01457 int error; 01458 char path[PATH_MAX]; 01459 hash_key_t key; 01460 hash_value_t value; 01461 char existing_directory_ancestor[PATH_MAX]; 01462 struct path_watch_t *pw; 01463 01464 /* Normalize the path passed to us */ 01465 if ((error = make_normalized_absolute_path(path, sizeof(path), path_arg)) != SUCCESS) { 01466 log_msg(LOG_ERROR, _("failed make_normalized_absolute_path from \"%s\" (%s)\n"), 01467 path_arg, error_string(error)); 01468 return error; 01469 } 01470 01471 /* Assure we're not already watching this path */ 01472 if (is_watch_target(path)) { 01473 error = INOTIFY_WATCH_ALREADY_WATCHING; 01474 log_msg(LOG_ERROR, _("path is already being watched \"%s\"\n"), path); 01475 return error; 01476 } 01477 01478 log_msg(LOG_INFO, _("watching target \"%s\"\n"), path); 01479 01480 /* 01481 * We watch the directory which contains the path, not the path itself. To 01482 * establish a watch on a directory the directory must exist in the 01483 * filesystem so we search for the nearest directory ancestor containing the 01484 * path which exists in the file system. This is where the watch will be 01485 * established. 01486 * 01487 * If the directory containing the path does not yet exist we watch the 01488 * closest directory to the final path component and will move the watch to 01489 * a closer directory when the missing directory component is later created 01490 * in the directory which was chosen to watch. 01491 * 01492 * FIXME 01493 * WARNING: Race Condition. If the missing directory is created before the 01494 * watch is established we won't see the directory creation event which 01495 * tells us we can move the watch directory closer to the target. The only 01496 * way to protect against this is to periodically walk all the watch targets 01497 * and and adjust their watch directories. 01498 */ 01499 if ((error = find_existing_directory_ancestor(existing_directory_ancestor, 01500 sizeof(existing_directory_ancestor), 01501 path) != SUCCESS)) { 01502 log_msg(LOG_ERROR, _("failed find_existing_directory_ancestor \"%s\" (%s)\n"), 01503 path, error_string(error)); 01504 return error; 01505 } 01506 01507 /* 01508 * If we're already watching the selected directory get the watch object 01509 * previously created for watching that directory, otherwise create a new 01510 * watch object for the directory. 01511 */ 01512 if ((pw = find_watch_by_path(existing_directory_ancestor)) == NULL) { 01513 if ((error = new_path_watch(existing_directory_ancestor, &pw)) != SUCCESS) { 01514 log_msg(LOG_ERROR, _("could not allocate new watch for \"%s\" (%s)\n"), 01515 existing_directory_ancestor, error_string(error)); 01516 return error; 01517 } 01518 } 01519 01520 /* Do some basic bookkeeping */ 01521 01522 /* Add the path to our list of targets. */ 01523 if ((error = add_path_to_target_list(path)) != SUCCESS) { 01524 log_msg(LOG_ERROR, _("could not insert path into target table \"%s\" (%s)\n"), 01525 pw->path, error_string(error)); 01526 inotify_rm_watch(inotify_fd, pw->watch_id); 01527 free_path_watch(&pw); 01528 return error; 01529 } 01530 01531 /* Add the watch object to our list of directories we're watching. */ 01532 if ((error = track_path_watch(pw)) != SUCCESS) { 01533 log_msg(LOG_ERROR, _("could not insert watch into tables for \"%s\" (%s)\n"), 01534 pw->path, error_string(error)); 01535 inotify_rm_watch(inotify_fd, pw->watch_id); 01536 free_path_watch(&pw); 01537 return error; 01538 } 01539 01540 log_msg(LOG_DEBUG, _("adding path \"%s\" to descendant_path_table for directory \"%s\"\n"), 01541 path, pw->path); 01542 01543 /* Add the path to the list of targets the watch object is responsible for watching (e.g. descendants). */ 01544 key.type = HASH_KEY_STRING; 01545 key.str = path; 01546 value.type = HASH_VALUE_UNDEF; 01547 01548 if ((error = hash_enter(pw->descendant_path_table, &key, &value)) != HASH_SUCCESS) { 01549 log_msg(LOG_ERROR, _("cannot add path \"%s\" to path_watch_t descendant_path_table \"%s\" (%s)\n"), 01550 path, pw->path, error_string(error)); 01551 return error; 01552 } 01553 01554 return SUCCESS; 01555 }
int inotify_stop_monitoring | ( | const char * | path_arg | ) |
Stop monitoring the target path with inotify.
[in] | path_arg | The pathname cease monitoring, it will be normalized and made absolute |
Definition at line 1562 of file inotify_watch.c.
01563 { 01564 int error; 01565 char path[PATH_MAX]; 01566 unsigned long i, count; 01567 hash_key_t key; 01568 hash_entry_t *entries, *entry; 01569 struct path_watch_t *pw; 01570 int path_count; 01571 01572 if ((error = make_normalized_absolute_path(path, sizeof(path), path_arg)) != SUCCESS) { 01573 log_msg(LOG_ERROR, _("failed make_normalized_absolute_path from \"%s\" (%s)\n"), 01574 path_arg, error_string(error)); 01575 return error; 01576 } 01577 01578 if (!is_watch_target(path)) { 01579 error = INOTIFY_WATCH_NOT_WATCHED; 01580 log_msg(LOG_ERROR, _("path was not being watched \"%s\"\n"), path); 01581 return error; 01582 } 01583 01584 log_msg(LOG_INFO, _("removing watch target \"%s\"\n"), path); 01585 01586 key.type = HASH_KEY_STRING; 01587 key.str = path; 01588 01589 path_count = 0; 01590 hash_entries(watch_id_table, &count, &entries); 01591 for (i = 0, entry = entries; i < count; i++, entry++) { 01592 pw = entry->value.ptr; 01593 01594 if (hash_has_key(pw->descendant_path_table, &key)) { 01595 path_count++; 01596 if ((error = hash_delete(pw->descendant_path_table, &key)) != HASH_SUCCESS) { 01597 log_msg(LOG_ERROR, _("cannot delete path \"%s\" from path_watch_t descendant_path_table \"%s\" (%s)\n"), 01598 path, pw->path, error_string(error)); 01599 } 01600 if ((error = destroy_path_watch_if_empty(&pw)) != SUCCESS) { 01601 log_msg(LOG_ERROR, _("destroy_path_watch_if_empty failed (%s)\n"), error_string(error)); 01602 } 01603 } 01604 } 01605 free(entries); 01606 01607 if (path_count != 1) { 01608 log_msg(LOG_ERROR, _("found %d instances of \"%s\" but expected exactly one\n"), 01609 path_count, path); 01610 } 01611 01612 if ((error = remove_path_from_target_list(path)) != SUCCESS) { 01613 log_msg(LOG_ERROR, _("could not remove path from target table \"%s\" (%s)\n"), 01614 path, error_string(error)); 01615 } 01616 01617 return error; 01618 }
const char* inotify_watch_error_string | ( | int | error | ) |
Translate error code to string description.
[in] | error | Error code whose string description is sought. |
Definition at line 1287 of file inotify_watch.c.
Referenced by error_string().
01288 { 01289 switch(error) { 01290 case SUCCESS: return _("success"); 01291 case INOTIFY_WATCH_ERROR_NULL_WATCH: return _("watch invalid, was NULL"); 01292 case INOTIFY_WATCH_ERROR_WATCH_ID_NOT_FOUND: return _("could not look up watch id"); 01293 case INOTIFY_WATCH_ALREADY_WATCHING: return _("already being watched"); 01294 case INOTIFY_WATCH_NOT_WATCHED: return _("not being watched"); 01295 } 01296 return NULL; 01297 }
int inotify_watch_fini | ( | void | ) |
Close the inotify monitoring back end, stop all monitoring, free all resources.
Definition at line 1197 of file inotify_watch.c.
Referenced by lwatch_fini().
01198 { 01199 unsigned long i, count; 01200 hash_entry_t *entries, *entry; 01201 struct path_watch_t *pw; 01202 01203 flush_inotify_cookie_cache(); 01204 01205 hash_entries(watch_id_table, &count, &entries); 01206 for (i = 0, entry = entries; i < count; i++, entry++) { 01207 pw = entry->value.ptr; 01208 log_msg(LOG_VERBOSE_DEBUG, _("destroying [%d] \"%s\"\n"), pw->watch_id, pw->path); 01209 destroy_path_watch(&pw); 01210 } 01211 free(entries); 01212 01213 if (hash_count(watch_path_table) != 0) { 01214 log_msg(LOG_ERROR, _("watch_path_table not empty (%d) after removing all watches\n"), 01215 hash_count(watch_path_table)); 01216 } 01217 01218 if (hash_count(watch_id_table) != 0) { 01219 log_msg(LOG_ERROR, _("watch_id_table not empty (%d) after removing all watches\n"), 01220 hash_count(watch_id_table)); 01221 } 01222 01223 hash_destroy(watch_path_table); 01224 hash_destroy(watch_id_table); 01225 hash_destroy(target_table); 01226 close(inotify_fd); 01227 01228 return SUCCESS; 01229 }
int inotify_watch_init | ( | void | ) |
Initialize the inotify monitoring back end.
Definition at line 1162 of file inotify_watch.c.
Referenced by lwatch_init().
01163 { 01164 int error; 01165 01166 inotify_lost_cookie_callback = lost_rename_callback; 01167 01168 if ((inotify_fd = inotify_init()) < 0) { 01169 error = errno; 01170 log_msg(LOG_ERROR, _("inotify_init() failed (%s)\n"), error_string(error)); 01171 return error; 01172 } 01173 01174 01175 if ((error = hash_create(INITIAL_HASH_TABLE_SIZE, &watch_path_table, NULL)) != HASH_SUCCESS) { 01176 log_msg(LOG_ERROR, _("inotify_watch_init: cannot create pathname hash table\n")); 01177 return error; 01178 } 01179 01180 if ((error = hash_create(INITIAL_HASH_TABLE_SIZE, &watch_id_table, NULL)) != HASH_SUCCESS) { 01181 log_msg(LOG_ERROR, _("inotify_watch_init: cannot create watch id hash table\n")); 01182 return error; 01183 } 01184 01185 if ((error = hash_create(INITIAL_HASH_TABLE_SIZE, &target_table, NULL)) != HASH_SUCCESS) { 01186 log_msg(LOG_ERROR, _("%s: cannot create target hash table\n")); 01187 return error; 01188 } 01189 01190 return SUCCESS; 01191 }
bool is_path_watched | ( | const char * | path | ) |
Return true if the given path is being watched by inotify.
[in] | path | The path to check for inotify watching |
Definition at line 1267 of file inotify_watch.c.
01268 { 01269 hash_key_t key; 01270 01271 key.type = HASH_KEY_STRING; 01272 key.str = path; 01273 01274 return hash_has_key(watch_path_table, &key); 01275 }
bool is_watch_target | ( | const char * | path | ) |
Return true if path parameter is a watch target.
[in] | path | The path we're testing to see if it's a watch target |
Definition at line 1245 of file inotify_watch.c.
Referenced by inotify_start_monitoring(), inotify_stop_monitoring(), process_event(), and start_monitoring().
01246 { 01247 hash_key_t key; 01248 01249 key.type = HASH_KEY_STRING; 01250 key.str = path; 01251 01252 return hash_has_key(target_table, &key); 01253 }
char* path_watch_string | ( | struct path_watch_t * | pw | ) |
Render a path_watch_t struct as a string.
[in] | pw | The path watch object to render as a string |
Definition at line 1382 of file inotify_watch.c.
Referenced by process_event(), and watch_path_table_string().
01383 { 01384 char *files; 01385 char *fw_string; 01386 int len; 01387 01388 if (!pw) return strdup("(null)"); 01389 01390 files = keys_string(pw->descendant_path_table, " ", "\n"); 01391 01392 len = asprintf(&fw_string, "[%d] \"%s\" n_files=%ld%s%s", 01393 pw->watch_id, pw->path, 01394 hash_count(pw->descendant_path_table), 01395 hash_count(pw->descendant_path_table) ? "\n" : "", files); 01396 01397 free(files); 01398 return fw_string; 01399 }
int read_events | ( | void | ) |
Loop used to read inotify events and dispatch them.
Definition at line 1636 of file inotify_watch.c.
Referenced by main_loop().
01637 { 01638 char buf[1024 * (INOTIFY_EVENT_SIZE + 16)]; /* reasonable guess as to size of 1024 events */ 01639 int len, error, i; 01640 bool done; 01641 01642 /* 01643 * The behavior when the buffer given to read(2) is too small to return information 01644 * about the next event depends on the kernel version: in kernels before 2.6.21, 01645 * read(2) returns 0; since kernel 2.6.21, read(2) fails with the error EINVAL. 01646 */ 01647 01648 done = false; 01649 while (!done) { 01650 len = read (inotify_fd, buf, sizeof(buf)); 01651 i = 0; 01652 if (len <= 0) { 01653 error = errno; 01654 if (error == EINTR || error == EAGAIN) { /* need to reissue system call */ 01655 continue; 01656 } 01657 else if (error == EINVAL || len == 0) { 01658 log_msg(LOG_ERROR, _("failed, buffer too small (%s)\n"), 01659 error_string(error)); 01660 return error; 01661 } else { 01662 log_msg(LOG_ERROR, _("failed (%s)\n"), 01663 error_string(error)); 01664 return error; 01665 } 01666 } 01667 while (i < len) { 01668 struct inotify_event *event; 01669 01670 event = (struct inotify_event *) &buf[i]; 01671 01672 process_event(event); 01673 i += INOTIFY_EVENT_SIZE + event->len; 01674 } 01675 done = true; 01676 } 01677 return SUCCESS; 01678 }
lwatch_event_callback_t register_lwatch_event_callback | ( | lwatch_event_callback_t | callback | ) |
Register a callback to receive log watch events.
[in] | callback | The callback which will be invoked to send a log watch event. |
Definition at line 1685 of file inotify_watch.c.
Referenced by lwatch_init().
01686 { 01687 lwatch_event_callback_t prev_callback = lwatch_event_callback; 01688 lwatch_event_callback = callback; 01689 return prev_callback; 01690 }