#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/inotify.h>
#include <signal.h>
#include "dhash.h"
#include "path_utils.h"
#include "logging.h"
#include "inotify_watch.h"
#include "util.h"
#include "lwatch.h"
Go to the source code of this file.
Classes | |
struct | inotify_cookie_cache_entry_t |
Struct used to track inotifiy MOVE operations (e.g. More... | |
struct | path_and_result_t |
Convenience struct used in callbacks to test a path and get return a boolean result. More... | |
Defines | |
#define | _GNU_SOURCE |
Turn on GNU extensions. | |
#define | INOTIFY_EVENT_SIZE (sizeof (struct inotify_event)) |
size of the inotify event structure, not counting name | |
#define | INOTIFY_COOKIE_CACHE_SIZE 10 |
How many pending rename operations we'll track. | |
#define | INOTIFY_EVENTS |
The inotify events we need to be informed of. | |
#define | INITIAL_HASH_TABLE_SIZE 16 |
How many entries to initially reserve in global hash tables. | |
#define | INITIAL_DESCENDANT_HASH_TABLE_SIZE 2 |
How many entries to initially reserve in the path watch object descendant hash table. | |
Typedefs | |
typedef void(* | inotify_lost_cookie_callback_t )(struct inotify_cookie_cache_entry_t *move) |
Definition of callback signature used when interested party wants to be informed that a rename operation never saw both the previous and new names, only either the previous or the new name was ever seen. | |
Functions | |
static bool | visit_descendants_and_test_for_ancestor (hash_entry_t *item, void *user_data) |
Hash iteration callback used to determine if subject path is an ancestor of any descendant. | |
static bool | is_path_an_ancestor_of_any_watch_descendants (struct path_watch_t *pw, const char *subject_path) |
Return true if a portion of the path could be one of the descendants being watched. | |
static int | free_path_watch (struct path_watch_t **ppw) |
Free a watch object when no longer needed. | |
static int | new_path_watch (const char *path, struct path_watch_t **pw_return) |
Create and intialize a new path watch object, add it's path to the watch subsystem. | |
static int | destroy_path_watch (struct path_watch_t **ppw) |
Destroy the path watch object; remove it's path from watch subsystem, then free it's memory and resources, set the variable pointed to by ppw to NULL;. | |
static int | destroy_path_watch_if_empty (struct path_watch_t **ppw) |
If the path watch object has no descendant targets to watch then destroy it. | |
static int | add_path_to_target_list (const char *path) |
Add this path to list of watch targets. | |
static int | remove_path_from_target_list (const char *path) |
Removes a path from the list of watch targets. | |
static int | track_path_watch (struct path_watch_t *pw) |
Do the bookkeeping to track a path watch object. | |
static int | untrack_path_watch (struct path_watch_t *pw) |
Remove a path watch object from our tracking data structures. | |
static void | lost_rename_callback (struct inotify_cookie_cache_entry_t *move) |
A directory or file was renamed but we don't have both names, invoke this to emit a rename event. | |
static int | process_event (struct inotify_event *event) |
Given an inotify event, process it and transform it into a generic lwatch_event. | |
static int | relocate_watches (struct path_watch_t *pw) |
Move each descendant watch target of this directory to it's optimal watch directory. | |
static struct inotify_cookie_cache_entry_t * | inotify_cookie_cache_operator (struct inotify_event *event, const char *path) |
Process an inotify MOVE (i.e. | |
static void | flush_inotify_cookie_cache (void) |
Flush any pending inotify MOVE (rename) events from the cache. | |
static void | log_watch_path_table (const char *fmt,...) |
Write the watch path table to our log with formatted message prepended. | |
static void | log_target_table (const char *fmt,...) |
Write the target table to our log with formatted message prepended. | |
int | inotify_watch_init () |
Initialize the inotify monitoring back end. | |
int | inotify_watch_fini () |
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_arg) |
Start monitoring the target path with inotify. | |
int | inotify_stop_monitoring (const char *path_arg) |
Stop monitoring the target path with inotify. | |
int | get_inotify_fd () |
Return the file descriptor used by the inotify back end. | |
int | read_events () |
Loop used to read inotify events and dispatch them. | |
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 = true |
Flag used to indicate if the read_events() loop should terminate or continue. | |
static inotify_lost_cookie_callback_t | inotify_lost_cookie_callback = NULL |
User callback invoked when an inotify move has a "lost" old or new name as part of a rename (MOVE) event. | |
static struct inotify_cookie_cache_entry_t | inotify_cookie_cache [INOTIFY_COOKIE_CACHE_SIZE] |
Cache of incomplete inotify rename (MOVE) events. | |
static int | n_cookies = 0 |
Number of entries in the cache of incomplete inotify rename (MOVE) events. | |
static hash_table_t * | watch_path_table = NULL |
Hash table which maps from a path name being watched by inotify to a path watch object. | |
static hash_table_t * | watch_id_table = NULL |
Hash table which maps from an id provided by inotify to a path watch object. | |
static hash_table_t * | target_table = NULL |
Hash table of watch targets. | |
static int | inotify_fd = -1 |
The file descriptor provided by inotify to read inotify events from. | |
static lwatch_event_callback_t | lwatch_event_callback = NULL |
User callback for sending log watch events from the inotify back end to the log watcher. |
Definition in file inotify_watch.c.
#define INOTIFY_COOKIE_CACHE_SIZE 10 |
How many pending rename operations we'll track.
See How inotify rename cookies are managed..
Definition at line 172 of file inotify_watch.c.
Referenced by inotify_cookie_cache_operator().
#define INOTIFY_EVENTS |
Value:
(IN_MOVE | IN_CREATE | IN_ISDIR | IN_MODIFY | IN_OPEN | \ IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | \ IN_IGNORED | IN_MOVED_FROM | IN_MOVED_TO)
Definition at line 177 of file inotify_watch.c.
Referenced by new_path_watch().
typedef void(* inotify_lost_cookie_callback_t)(struct inotify_cookie_cache_entry_t *move) |
Definition of callback signature used when interested party wants to be informed that a rename operation never saw both the previous and new names, only either the previous or the new name was ever seen.
See How inotify rename cookies are managed..
Definition at line 224 of file inotify_watch.c.
static int add_path_to_target_list | ( | const char * | path | ) | [static] |
Add this path to list of watch targets.
[in] | path | The path to add to the list of watch targets |
Definition at line 504 of file inotify_watch.c.
Referenced by inotify_start_monitoring().
00505 { 00506 int error; 00507 hash_key_t key; 00508 hash_value_t value; 00509 00510 key.type = HASH_KEY_STRING; 00511 key.str = path; 00512 value.type = HASH_VALUE_UNDEF; 00513 00514 if ((error = hash_enter(target_table, &key, &value)) != HASH_SUCCESS) { 00515 log_msg(LOG_ERROR, _("cannot add to target table \"%s\" (%s)\n"), 00516 path, error_string(error)); 00517 return error; 00518 } 00519 return SUCCESS; 00520 }
static int destroy_path_watch | ( | struct path_watch_t ** | ppw | ) | [static] |
Destroy the path watch object; remove it's path from watch subsystem, then free it's memory and resources, set the variable pointed to by ppw to NULL;.
[out] | ppw | Pointer to the path watch object to destroy, will be set to NULL on SUCCESS. |
Definition at line 463 of file inotify_watch.c.
Referenced by destroy_path_watch_if_empty(), and inotify_watch_fini().
00464 { 00465 struct path_watch_t *pw = *ppw; 00466 int error; 00467 00468 log_msg(LOG_DEBUG, _("destroying watch for [%d] \"%s\"\n"), pw->watch_id, pw->path); 00469 inotify_rm_watch(inotify_fd, pw->watch_id); 00470 if ((error = untrack_path_watch(pw)) != SUCCESS) { 00471 log_msg(LOG_ERROR, _("Could not untrack_path_watch for \"%s\" (%s)\n"), 00472 pw->path, error_string(error)); 00473 } 00474 free_path_watch(ppw); 00475 return SUCCESS; 00476 }
static int destroy_path_watch_if_empty | ( | struct path_watch_t ** | ppw | ) | [static] |
If the path watch object has no descendant targets to watch then destroy it.
If the path watch is destroyed the variable pointed to by ppw will be set to NULL.
[out] | ppw | Pointer to the path watch object to destroy, will be set to NULL if destroyed. |
Definition at line 484 of file inotify_watch.c.
Referenced by inotify_stop_monitoring(), and relocate_watches().
00485 { 00486 struct path_watch_t *pw = *ppw; 00487 int error; 00488 00489 error = SUCCESS; 00490 if (hash_count(pw->descendant_path_table) == 0) { 00491 log_msg(LOG_DEBUG, _("destroying empty watch for [%d] \"%s\"\n"), pw->watch_id, pw->path); 00492 if ((error = destroy_path_watch(ppw)) != SUCCESS) { 00493 log_msg(LOG_ERROR, _("destroy_path_watch() failed (%s)\n"), error_string(error)); 00494 } 00495 } 00496 return error; 00497 }
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 }
static void flush_inotify_cookie_cache | ( | void | ) | [static] |
Flush any pending inotify MOVE (rename) events from the cache.
See How inotify rename cookies are managed..
Definition at line 1095 of file inotify_watch.c.
Referenced by inotify_watch_fini().
01096 { 01097 int i; 01098 struct inotify_cookie_cache_entry_t *entry; 01099 01100 for (i = 0; i < n_cookies; i++) { 01101 entry = &inotify_cookie_cache[i]; 01102 if (inotify_lost_cookie_callback) 01103 inotify_lost_cookie_callback(entry); 01104 } 01105 n_cookies = 0; 01106 }
static int free_path_watch | ( | struct path_watch_t ** | ppw | ) | [static] |
Free a watch object when no longer needed.
[out] | ppw | Pointer to the path watch object to free, will be set to NULL |
Definition at line 390 of file inotify_watch.c.
Referenced by destroy_path_watch(), inotify_start_monitoring(), new_path_watch(), and process_event().
00391 { 00392 struct path_watch_t *pw = *ppw; 00393 int error = SUCCESS; 00394 00395 if (pw->descendant_path_table) { 00396 if ((error = hash_destroy(pw->descendant_path_table)) != HASH_SUCCESS) { 00397 log_msg(LOG_ERROR, _("could not free file hash table error=%d\n"), error); 00398 } 00399 } 00400 00401 free(pw); 00402 *ppw = NULL; 00403 00404 return error; 00405 }
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 }
static struct inotify_cookie_cache_entry_t * inotify_cookie_cache_operator | ( | struct inotify_event * | event, | |
const char * | path | |||
) | [static, read] |
Process an inotify MOVE (i.e.
rename) event which come in pairs.
[in] | event | An inotify MOVE event to process |
[in] | path | The path name associated with the event |
See How inotify rename cookies are managed..
Definition at line 1017 of file inotify_watch.c.
Referenced by process_event().
01018 { 01019 int error; 01020 int i; 01021 struct inotify_cookie_cache_entry_t *entry; 01022 static struct inotify_cookie_cache_entry_t result; /* FIXME */ 01023 01024 /* see if the cookie is present, if so remove it and return True */ 01025 for (i = 0; i < n_cookies; i++) { 01026 if (inotify_cookie_cache[i].cookie == event->cookie) { /* Found, delete the entry */ 01027 entry = &inotify_cookie_cache[i]; 01028 if (event->mask & IN_MOVED_FROM) { 01029 entry->prev_watch_id = event->wd; 01030 if ((error = copy_path(entry->prev_path, path, sizeof(entry->prev_path))) != SUCCESS) { 01031 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), path, error_string(error)); 01032 } 01033 } else if (event->mask & IN_MOVED_TO) { 01034 entry->new_watch_id = event->wd; 01035 if ((error = copy_path(entry->new_path, path, sizeof(entry->new_path))) != SUCCESS) { 01036 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), path, error_string(error)); 01037 } 01038 } 01039 result = *entry; 01040 01041 /* fill gap of deleted entry by shifting all entries above the gap downward */ 01042 for (i++; i < n_cookies; i++) { 01043 inotify_cookie_cache[i - 1] = inotify_cookie_cache[i]; 01044 } 01045 n_cookies--; 01046 return &result; 01047 } 01048 } 01049 /* Not Found */ 01050 if (n_cookies == INOTIFY_COOKIE_CACHE_SIZE) { 01051 /* Cache full, remove oldest by shifting all entries down, 01052 * provide notification if requested. */ 01053 if (inotify_lost_cookie_callback) 01054 inotify_lost_cookie_callback(&inotify_cookie_cache[0]); 01055 n_cookies--; 01056 for (i = 0; i < n_cookies; i++) { 01057 inotify_cookie_cache[i] = inotify_cookie_cache[i + 1]; 01058 } 01059 } 01060 entry = &inotify_cookie_cache[n_cookies]; 01061 entry->cookie = event->cookie; 01062 if (event->mask & IN_MOVED_FROM) { 01063 entry->prev_watch_id = event->wd; 01064 entry->new_watch_id = -1; 01065 if ((error = copy_path(entry->prev_path, path, sizeof(entry->prev_path))) != SUCCESS) { 01066 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), path, error_string(error)); 01067 } 01068 entry->new_path[0] = 0; 01069 } else if (event->mask & IN_MOVED_TO) { 01070 entry->prev_watch_id = -1; 01071 entry->new_watch_id = event->wd; 01072 entry->prev_path[0] = 0; 01073 if ((error = copy_path(entry->new_path, path, sizeof(entry->new_path))) != SUCCESS) { 01074 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), path, error_string(error)); 01075 } 01076 } 01077 n_cookies++; 01078 return NULL; 01079 }
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 }
static bool is_path_an_ancestor_of_any_watch_descendants | ( | struct path_watch_t * | pw, | |
const char * | subject_path | |||
) | [static] |
Return true if a portion of the path could be one of the descendants being watched.
[in] | pw | Path watch object |
[in] | subject_path | The path being tested |
Example:
The watch is established on "/a/b"
The watch on "/a/b" has a descendant "/a/b/c/d" it's responsible for watching for.
The subject path being tested is "/a/b/c"
In this instance the subject "/a/b/c" is an ancestor of "/a/b/c/d" and the function would return true.
This test is typically done when a new directory is added to the directory being watched. So lets say directory "c" was created in "/a/b" forming a new path of "a/b/c". We call this function asking if "/a/b/c" belongs to any of the descendants being watched. In our example one of the descendants being watched by "/a/b" is "/a/b/c/d" (we're waiting for "/a/b/c" to be created which is why "/a/b/c/d" a descendant watch of "a/b"). The function returns true indicating that the newly formed directory "/a/b/c" is indeed part of what this directory is responsible watching for and as a consequence the descendant watch for "/a/b/c/d" will be moved from "/a/b" to one of the newly created descendants of "/a/b", i.e. it will be moved to "/a/b/c" (or further down the list of path components if we discover more path components exist at the time we move the watch.
Definition at line 373 of file inotify_watch.c.
Referenced by process_event().
00374 { 00375 struct path_and_result_t data; 00376 00377 data.path = subject_path; 00378 data.result = false; 00379 00380 hash_iterate(pw->descendant_path_table, visit_descendants_and_test_for_ancestor, &data); 00381 return data.result; 00382 }
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 }
static void log_target_table | ( | const char * | fmt, | |
... | ||||
) | [static] |
Write the target table to our log with formatted message prepended.
[in] | fmt | printf style format followed by optional arguments used to identify the table. |
Definition at line 1137 of file inotify_watch.c.
Referenced by process_event().
01138 { 01139 char buf[1024]; 01140 va_list ap; 01141 char *targets = keys_string(target_table, " ", "\n"); 01142 01143 va_start(ap, fmt); 01144 if (fmt) { 01145 vsnprintf(buf, sizeof(buf), fmt, ap); 01146 } else { 01147 *buf = 0; 01148 } 01149 01150 log_msg(LOG_DEBUG, _("target table: %s\n%s\n"), buf, targets); 01151 free(targets); 01152 }
static void log_watch_path_table | ( | const char * | fmt, | |
... | ||||
) | [static] |
Write the watch path table to our log with formatted message prepended.
[in] | fmt | printf style format followed by optional arguments used to identify the table. |
Definition at line 1114 of file inotify_watch.c.
Referenced by process_event(), track_path_watch(), and untrack_path_watch().
01115 { 01116 char buf[1024]; 01117 va_list ap; 01118 char *watch_path_table_str = watch_path_table_string(watch_path_table); 01119 01120 va_start(ap, fmt); 01121 if (fmt) { 01122 vsnprintf(buf, sizeof(buf), fmt, ap); 01123 } else { 01124 *buf = 0; 01125 } 01126 01127 log_msg(LOG_DEBUG, _("pathname table: %s\n%s\n"), buf, watch_path_table_str); 01128 free(watch_path_table_str); 01129 }
static void lost_rename_callback | ( | struct inotify_cookie_cache_entry_t * | move | ) | [static] |
A directory or file was renamed but we don't have both names, invoke this to emit a rename event.
[in] | move | an entry in inotify cookie cache which is being flushed even though incomplete |
Definition at line 626 of file inotify_watch.c.
Referenced by inotify_watch_init().
00627 { 00628 int error; 00629 00630 if (lwatch_event_callback) { 00631 struct lwatch_event_t event; 00632 00633 event.event_type = LWATCH_EVENT_RENAME; 00634 if ((error = copy_path(event.rename.prev_path, move->prev_path, sizeof(event.rename.prev_path))) != SUCCESS) { 00635 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), move->prev_path, error_string(error)); 00636 } 00637 if ((error = copy_path(event.rename.new_path, move->new_path, sizeof(event.rename.new_path))) != SUCCESS) { 00638 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), move->new_path, error_string(error)); 00639 } 00640 lwatch_event_callback(&event); 00641 } 00642 }
int new_path_watch | ( | const char * | path, | |
struct path_watch_t ** | pw_return | |||
) | [static] |
Create and intialize a new path watch object, add it's path to the watch subsystem.
[in] | path | The path being watched by this watch object |
[out] | pw_return | The newly created path watch object is assigned to this variable |
Definition at line 414 of file inotify_watch.c.
Referenced by inotify_start_monitoring().
00415 { 00416 struct path_watch_t *pw; 00417 int error; 00418 00419 *pw_return = NULL; 00420 00421 if ((pw = (struct path_watch_t *)malloc(sizeof(struct path_watch_t))) == NULL) { 00422 error = ENOMEM; 00423 log_msg(LOG_ERROR, _("\"%s\" (%s)\n"), path, error_string(error)); 00424 return error; 00425 } 00426 00427 if ((error = copy_path(pw->path, path, sizeof(pw->path))) != SUCCESS) { 00428 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), path, error_string(error)); 00429 free_path_watch(&pw); 00430 return error; 00431 } 00432 00433 pw->watch_id = -1; 00434 pw->descendant_path_table = NULL; 00435 00436 if ((error = hash_create(INITIAL_DESCENDANT_HASH_TABLE_SIZE, 00437 &pw->descendant_path_table, NULL)) != HASH_SUCCESS) { 00438 log_msg(LOG_ERROR, _("cannot create file hash table in path_watch_t object for path \"%s\"\n"), path); 00439 free_path_watch(&pw); 00440 return error; 00441 } 00442 00443 log_msg(LOG_DEBUG, _("adding directory watch for \"%s\"\n"), path); 00444 if ((pw->watch_id = inotify_add_watch(inotify_fd, path, INOTIFY_EVENTS)) < 0) { 00445 error = errno; 00446 log_msg(LOG_ERROR, _("could not add inotify watch for \"%s\" (%s)\n"), 00447 path, error_string(error)); 00448 free_path_watch(&pw); 00449 return error; 00450 } 00451 log_msg(LOG_DEBUG, _("newly watching [%d] \"%s\"\n"), pw->watch_id, path); 00452 00453 *pw_return = pw; 00454 return SUCCESS; 00455 }
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 }
static int process_event | ( | struct inotify_event * | event | ) | [static] |
Given an inotify event, process it and transform it into a generic lwatch_event.
[in] | event | The inotify event to process. |
To isolate the log watcher code from specific monitoring backends a generic monitoring event (lwatch_event_t) is defined. Each monitoring backend is responsible for translating to the generic lwatch_event_t event and emiting an event of that type.
This routine takes an inotify_event, processes it, and then emits a generic lwatch_event_t event which the log watcher code then processes.
See Independent backends for watching. for an explantion of how inotify serves as a backend for the log watching code.
Definition at line 667 of file inotify_watch.c.
Referenced by read_events().
00668 { 00669 int error; /* FIXME: need to check error on all calls */ 00670 struct path_watch_t *pw; 00671 char event_path[PATH_MAX]; 00672 00673 /* Lookup the watch info associated with this event in our hash table */ 00674 if (!(pw = find_watch_by_watch_id(event->wd))) { 00675 log_msg(LOG_ERROR, _("could not look up watch id %d\n"), event->wd); 00676 return INOTIFY_WATCH_ERROR_WATCH_ID_NOT_FOUND; 00677 } 00678 00679 /* If the event has a name associated with it, form it's complete path */ 00680 if (event->len) { 00681 if ((error = path_concat(event_path, sizeof(event_path), pw->path, event->name)) != SUCCESS) { 00682 log_msg(LOG_ERROR, _("failed to concatenate path \"%s\" + \"%s\" (%s)\n"), 00683 pw->path, event->name, error_string(error)); 00684 return error; 00685 } 00686 } else { 00687 if ((error = copy_path(event_path, pw->path, sizeof(event_path))) != SUCCESS) { 00688 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), pw->path, error_string(error)); 00689 return error; 00690 } 00691 } 00692 00693 log_msg(LOG_DEBUG, _("wd=%d mask=[%s] cookie=%u len=%u name=\"%s\" path=\"%s\"\n"), 00694 event->wd, inotify_mask_string(event->mask, ","), 00695 event->cookie, event->len, event->len ? event->name : NULL, 00696 event->len ? event_path : pw->path); 00697 00698 /* 00699 * Extra debug information. Print the watch info associated with this event 00700 * and the full contents of the watch table, i.e. the watch info for all existing 00701 * watches. Print the target table. 00702 * Generating these strings is expensive, only do if debugging. 00703 */ 00704 if (debug) { 00705 char *fw_string = path_watch_string(pw); 00706 00707 log_msg(LOG_DEBUG, _("[%s] pw=%s\n"), 00708 inotify_mask_string(event->mask, ","), fw_string); 00709 00710 log_watch_path_table("On entry to: %s", __FUNCTION__); 00711 log_target_table("On entry to: %s", __FUNCTION__); 00712 free(fw_string); 00713 } 00714 00715 /* 00716 * A file or directory in this watched directory was renamed. 00717 */ 00718 if (event->mask & IN_MOVE) { 00719 struct inotify_cookie_cache_entry_t *move; 00720 00721 if ((move = inotify_cookie_cache_operator(event, event_path))) { 00722 log_msg(LOG_DEBUG, _("renamed \"%s\" --> \"%s\"\n"), 00723 move->prev_path, move->new_path); 00724 00725 if (is_watch_target(move->prev_path) || is_watch_target(move->new_path)) { 00726 if (lwatch_event_callback) { 00727 struct lwatch_event_t lw_event; 00728 00729 lw_event.event_type = LWATCH_EVENT_RENAME; 00730 if ((error = copy_path(lw_event.rename.prev_path, move->prev_path, 00731 sizeof(lw_event.rename.prev_path))) != SUCCESS) { 00732 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), 00733 move->prev_path, error_string(error)); 00734 return error; 00735 } 00736 if ((error = copy_path(lw_event.rename.new_path, move->new_path, 00737 sizeof(lw_event.rename.new_path))) != SUCCESS) { 00738 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), 00739 move->new_path, error_string(error)); 00740 return error; 00741 } 00742 lwatch_event_callback(&lw_event); 00743 } 00744 } 00745 } 00746 /* 00747 * A file or directory was created in this watched directory. 00748 */ 00749 } else if (event->mask & IN_CREATE) { 00750 char *new_path = event_path; 00751 00752 if (is_path_an_ancestor_of_any_watch_descendants(pw, new_path)) { 00753 log_msg(LOG_DEBUG, _("new path %s \"%s\" is a watch target\n"), 00754 event->mask & IN_ISDIR ? "directory":"file", new_path); 00755 00756 if ((error = relocate_watches(pw)) != SUCCESS) { 00757 log_msg(LOG_ERROR, _("could not relocate_watches for \"%s\" (%s)\n"), 00758 new_path, error_string(error)); 00759 } 00760 00761 if (lwatch_event_callback) { 00762 struct lwatch_event_t lw_event; 00763 00764 lw_event.event_type = LWATCH_EVENT_CREATE; 00765 if ((error = copy_path(lw_event.path, new_path, sizeof(lw_event.path))) != SUCCESS) { 00766 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), new_path, error_string(error)); 00767 return error; 00768 } 00769 lwatch_event_callback(&lw_event); 00770 } 00771 } else { 00772 log_msg(LOG_DEBUG, _("new path %s \"%s\" is not within a watch target\n"), 00773 event->mask & IN_ISDIR ? "directory":"file", new_path); 00774 } 00775 /* 00776 * A file or directory in this watched directory was modifed. 00777 */ 00778 } else if (event->mask & IN_MODIFY) { 00779 char *modified_path = event_path; 00780 00781 log_msg(LOG_DEBUG, _("modified \"%s\"\n"), modified_path); 00782 00783 if (is_watch_target(modified_path)) { 00784 log_msg(LOG_DEBUG, _("watched file is modified \"%s\"\n"), modified_path); 00785 if (lwatch_event_callback) { 00786 struct lwatch_event_t lw_event; 00787 00788 lw_event.event_type = LWATCH_EVENT_MODIFY; 00789 if ((error = copy_path(lw_event.path, modified_path, sizeof(lw_event.path))) != SUCCESS) { 00790 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), modified_path, error_string(error)); 00791 return error; 00792 } 00793 lwatch_event_callback(&lw_event); 00794 } 00795 } 00796 /* 00797 * A file or directory in this watched directory was opened. 00798 */ 00799 } else if (event->mask & IN_OPEN) { 00800 char *opened_path = event_path; 00801 00802 if (is_watch_target(opened_path)) { 00803 log_msg(LOG_DEBUG, _("opened watched \"%s\"\n"), opened_path); 00804 if (lwatch_event_callback) { 00805 struct lwatch_event_t lw_event; 00806 00807 lw_event.event_type = LWATCH_EVENT_OPEN; 00808 if ((error = copy_path(lw_event.path, opened_path, sizeof(lw_event.path))) != SUCCESS) { 00809 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), opened_path, error_string(error)); 00810 return error; 00811 } 00812 lwatch_event_callback(&lw_event); 00813 } 00814 } else { 00815 log_msg(LOG_DEBUG, _("opened unwatched \"%s\"\n"), opened_path); 00816 } 00817 /* 00818 * A file or directory in this watched directory was closed. 00819 */ 00820 } else if (event->mask & IN_CLOSE_WRITE) { 00821 char *closed_path = event_path; 00822 00823 if (is_watch_target(closed_path)) { 00824 log_msg(LOG_DEBUG, _("closed watched \"%s\"\n"), closed_path); 00825 if (lwatch_event_callback) { 00826 struct lwatch_event_t lw_event; 00827 00828 lw_event.event_type = LWATCH_EVENT_CLOSE; 00829 if ((error = copy_path(lw_event.path, closed_path, sizeof(lw_event.path))) != SUCCESS) { 00830 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), closed_path, error_string(error)); 00831 return error; 00832 } 00833 lwatch_event_callback(&lw_event); 00834 } 00835 } else { 00836 log_msg(LOG_DEBUG, _("closed unwatched \"%s\"\n"), closed_path); 00837 } 00838 /* 00839 * A file or directory in this watched directory was deleted. 00840 */ 00841 } else if (event->mask & IN_DELETE) { 00842 char *deleted_path = event_path; 00843 00844 if (is_watch_target(deleted_path)) { 00845 log_msg(LOG_DEBUG, _("deleted watched \"%s\"\n"), deleted_path); 00846 if (lwatch_event_callback) { 00847 struct lwatch_event_t lw_event; 00848 00849 lw_event.event_type = LWATCH_EVENT_DELETE; 00850 if ((error = copy_path(lw_event.path, deleted_path, sizeof(lw_event.path))) != SUCCESS) { 00851 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), deleted_path, error_string(error)); 00852 return error; 00853 } 00854 lwatch_event_callback(&lw_event); 00855 } 00856 } else { 00857 log_msg(LOG_DEBUG, _("deleted unwatched \"%s\"\n"), deleted_path); 00858 } 00859 00860 /* 00861 * This file or directory on which a watch was established was deleted. 00862 */ 00863 } else if (event->mask & IN_DELETE_SELF) { 00864 char deleted_path[PATH_MAX]; 00865 00866 if ((error = copy_path(deleted_path, pw->path, sizeof(deleted_path))) != SUCCESS) { 00867 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), pw->path, error_string(error)); 00868 return error; 00869 } 00870 if ((error = relocate_watches(pw)) != SUCCESS) { 00871 log_msg(LOG_ERROR, _("IN_DELETE_SELF could not relocate_watches for \"%s\" (%s)\n"), 00872 deleted_path, error_string(error)); 00873 } 00874 /* 00875 * This watch has been removed. 00876 */ 00877 } else if (event->mask & IN_IGNORED) { 00878 if ((error = untrack_path_watch(pw)) != SUCCESS) { 00879 log_msg(LOG_ERROR, _("IN_IGNORED could not untrack_path_watch for \"%s\" (%s)\n"), 00880 pw->path, error_string(error)); 00881 } 00882 if ((error = free_path_watch(&pw)) != SUCCESS) { 00883 log_msg(LOG_ERROR, _("IN_IGNORED could not free_path_watch (%s)\n"), error_string(error)); 00884 } 00885 00886 } 00887 return SUCCESS; 00888 }
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 }
static int relocate_watches | ( | struct path_watch_t * | pw | ) | [static] |
Move each descendant watch target of this directory to it's optimal watch directory.
[in] | pw | The path watch object to track in our internal data structures |
A path watch object on a directory owns the responsibility for watching for a set of watch targets. It's targets may be in it's own directory or they may be further down a path hierarchy branching from this directory (in other words the basename component of the path watch appears somewhere in the chain of path components leading to the target). One consequence of this restriction is that every watch target belonging to a path watch object is a descendant of this directory, hence the name "descendent watch".
WARNING: Race Condition. There is an inherent race condition. We receive notifications of modifications to the filesystem asynchronously which triggers the processing performed here. We must assume the filesystem is changing underneath us all the time. To minimize effects of this we defer as long as possible the determination of existing directories and the computation of the optimal watch directory for a given watch target. That responsibility is given to the inotify_start_monitoring() function.
Definition at line 924 of file inotify_watch.c.
Referenced by process_event().
00925 { 00926 int error; 00927 hash_key_t *descendant, *descendant_keys; 00928 unsigned long i, count; 00929 char existing_directory_ancestor[PATH_MAX]; 00930 char descendant_path[PATH_MAX]; 00931 00932 log_msg(LOG_DEBUG, _("relocate_watches for \"%s\"\n"), pw->path); 00933 00934 /* 00935 * Because we may modify the descendant table as we iterate over all the 00936 * descendants we must use a temporary copy of the list of descendants. 00937 */ 00938 if ((error = hash_keys(pw->descendant_path_table, &count, &descendant_keys)) != HASH_SUCCESS) { 00939 log_msg(LOG_ERROR, _("could not get descendant_path_table key list in \"%s\" (%s)\n"), 00940 pw->path, error_string(error)); 00941 return error; 00942 } 00943 00944 /* Iterate over all descendant watches of this path watch object */ 00945 for (i = 0, descendant = descendant_keys; i < count; i++, descendant++) { 00946 if ((error = copy_path(descendant_path, descendant->str, sizeof(descendant_path))) != SUCCESS) { 00947 log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), descendant->str, error_string(error)); 00948 continue; 00949 } 00950 /* Find what is currently the closest directory avaliable for watching the descendant */ 00951 if ((error = find_existing_directory_ancestor(existing_directory_ancestor, 00952 sizeof(existing_directory_ancestor), 00953 descendant_path) != SUCCESS)) { 00954 log_msg(LOG_ERROR, _("failed find_existing_directory_ancestor \"%s\" (%s)\n"), 00955 descendant_path, error_string(error)); 00956 continue; 00957 } 00958 /* 00959 * If the closest existing directory is different than this directory 00960 * then move the watch from here to a closer directory by deleting it 00961 * from our table and re-creating the watch. 00962 */ 00963 if (strcmp(descendant_path, existing_directory_ancestor) != 0) { 00964 log_msg(LOG_VERBOSE_DEBUG, _("existing watch \"%s\" not equal to new watch \"%s\"\n"), 00965 descendant_path, existing_directory_ancestor); 00966 00967 if ((error = hash_delete(pw->descendant_path_table, descendant)) != HASH_SUCCESS) { 00968 log_msg(LOG_ERROR, _("cannot delete from descendant_path_table \"%s\" (%s)\n"), 00969 descendant_path, error_string(error)); 00970 continue; 00971 } 00972 00973 /* Set the watch on the optimal watch directory for this target */ 00974 if ((error = inotify_start_monitoring(descendant_path)) != SUCCESS) { 00975 log_msg(LOG_ERROR, _("cannot inotify_start_monitoring for \"%s\" (%s)\n"), 00976 descendant_path, error_string(error)); 00977 continue; 00978 } 00979 } 00980 } 00981 free(descendant_keys); /* clean up our temporary copy of the descendant list */ 00982 00983 /* 00984 * After processing all descendant watches it's possible they have all been 00985 * moved elsewhere leaving this directory without any targets to watch for, 00986 * if so delete ourself. 00987 */ 00988 if ((error = destroy_path_watch_if_empty(&pw)) != SUCCESS) { 00989 log_msg(LOG_ERROR, _("destroy_path_watch_if_empty failed (%s)\n"), 00990 error_string(error)); 00991 } 00992 00993 return SUCCESS; 00994 }
static int remove_path_from_target_list | ( | const char * | path | ) | [static] |
Removes a path from the list of watch targets.
[in] | path | The path to remove from the list of watch targets |
Definition at line 527 of file inotify_watch.c.
Referenced by inotify_stop_monitoring().
00528 { 00529 int error; 00530 hash_key_t key; 00531 00532 key.type = HASH_KEY_STRING; 00533 key.str = path; 00534 00535 if ((error = hash_delete(target_table, &key)) != HASH_SUCCESS) { 00536 log_msg(LOG_ERROR, _("cannot remove from target table \"%s\" (%s)\n"), 00537 path, error_string(error)); 00538 return error; 00539 } 00540 return SUCCESS; 00541 }
static int track_path_watch | ( | struct path_watch_t * | pw | ) | [static] |
Do the bookkeeping to track a path watch object.
[in] | pw | The path watch object to track in our internal data structures |
Definition at line 548 of file inotify_watch.c.
Referenced by inotify_start_monitoring().
00549 { 00550 int error = SUCCESS; 00551 hash_key_t key; 00552 hash_value_t value; 00553 00554 key.type = HASH_KEY_STRING; 00555 key.str = pw->path; 00556 value.type = HASH_VALUE_PTR; 00557 value.ptr = pw; 00558 00559 if ((error = hash_enter(watch_path_table, &key, &value)) != HASH_SUCCESS) { 00560 log_msg(LOG_ERROR, _("cannot add to pathname table \"%s\" (%s)\n"), 00561 pw->path, error_string(error)); 00562 return error; 00563 } 00564 00565 key.type = HASH_KEY_ULONG; 00566 key.ul = pw->watch_id; 00567 00568 if ((error = hash_enter(watch_id_table, &key, &value)) != HASH_SUCCESS) { 00569 log_msg(LOG_ERROR, _("cannot add to watch id table %d (%s)\n"), 00570 pw->watch_id, error_string(error)); 00571 return error; 00572 } 00573 00574 if (debug > 1) log_watch_path_table("after inserting \"%s\"", pw->path); 00575 return SUCCESS; 00576 }
static int untrack_path_watch | ( | struct path_watch_t * | pw | ) | [static] |
Remove a path watch object from our tracking data structures.
[in] | pw | The path watch object to remove from our internal data structures |
Definition at line 583 of file inotify_watch.c.
Referenced by destroy_path_watch(), and process_event().
00584 { 00585 int error = SUCCESS; 00586 hash_key_t key; 00587 00588 if (!pw) { 00589 error = INOTIFY_WATCH_ERROR_NULL_WATCH; 00590 log_msg(LOG_ERROR, _("NULL path_watch (%s)\n"), error_string(error)); 00591 return error; 00592 } 00593 00594 key.type = HASH_KEY_STRING; 00595 key.str = pw->path; 00596 00597 if ((error = hash_delete(watch_path_table, &key)) != HASH_SUCCESS) { 00598 log_msg(LOG_ERROR, _("cannot delete from pathname table \"%s\" (%s)\n"), 00599 pw->path, error_string(error)); 00600 return error; 00601 } 00602 00603 key.type = HASH_KEY_ULONG; 00604 key.ul = pw->watch_id; 00605 00606 if ((error = hash_delete(watch_id_table, &key) != HASH_SUCCESS)) { 00607 log_msg(LOG_ERROR, _("cannot delete from watch id table \"%s\" (%d)\n"), 00608 pw->watch_id, error_string(error)); 00609 return error; 00610 } 00611 00612 if (debug > 1) log_watch_path_table("after deleting \"%s\"", pw->path); 00613 return SUCCESS; 00614 }
static bool visit_descendants_and_test_for_ancestor | ( | hash_entry_t * | item, | |
void * | user_data | |||
) | [static] |
Hash iteration callback used to determine if subject path is an ancestor of any descendant.
[in] | item | A hash table entry, the key is one of the descendant paths |
[in] | user_data | Contains the subject path, and the return result of the test. |
Definition at line 324 of file inotify_watch.c.
Referenced by is_path_an_ancestor_of_any_watch_descendants().
00325 { 00326 struct path_and_result_t *data = (struct path_and_result_t *) user_data; 00327 const char *descendant_path = item->key.str; 00328 const char *subject_path = data->path; 00329 00330 data->result = is_ancestor_path(subject_path, descendant_path); 00331 return !data->result; /* returning false terminates the iteration */ 00332 }
struct inotify_cookie_cache_entry_t inotify_cookie_cache[INOTIFY_COOKIE_CACHE_SIZE] [static] |
Cache of incomplete inotify rename (MOVE) events.
See How inotify rename cookies are managed. for more explanation.
Definition at line 273 of file inotify_watch.c.
Referenced by flush_inotify_cookie_cache(), and inotify_cookie_cache_operator().
inotify_lost_cookie_callback_t inotify_lost_cookie_callback = NULL [static] |
User callback invoked when an inotify move has a "lost" old or new name as part of a rename (MOVE) event.
See How inotify rename cookies are managed. for more explanation.
Definition at line 268 of file inotify_watch.c.
Referenced by flush_inotify_cookie_cache(), inotify_cookie_cache_operator(), and inotify_watch_init().
lwatch_event_callback_t lwatch_event_callback = NULL [static] |
User callback for sending log watch events from the inotify back end to the log watcher.
See Independent backends for watching. for an explantion of how inotify serves as a backend for the log watching code.
Definition at line 305 of file inotify_watch.c.
Referenced by lost_rename_callback(), lwatch_init(), process_event(), and register_lwatch_event_callback().
int n_cookies = 0 [static] |
Number of entries in the cache of incomplete inotify rename (MOVE) events.
See How inotify rename cookies are managed. for more explanation.
Definition at line 278 of file inotify_watch.c.
Referenced by flush_inotify_cookie_cache(), and inotify_cookie_cache_operator().
hash_table_t* target_table = NULL [static] |
Hash table of watch targets.
See Vocabulary Terms
Definition at line 293 of file inotify_watch.c.
Referenced by add_path_to_target_list(), inotify_watch_fini(), inotify_watch_init(), is_watch_target(), log_target_table(), and remove_path_from_target_list().
hash_table_t* watch_id_table = NULL [static] |
Hash table which maps from an id provided by inotify to a path watch object.
See Vocabulary Terms
Definition at line 289 of file inotify_watch.c.
Referenced by find_watch_by_watch_id(), inotify_stop_monitoring(), inotify_watch_fini(), inotify_watch_init(), track_path_watch(), and untrack_path_watch().
hash_table_t* watch_path_table = NULL [static] |
Hash table which maps from a path name being watched by inotify to a path watch object.
See Vocabulary Terms
Definition at line 284 of file inotify_watch.c.
Referenced by find_watch_by_path(), inotify_watch_fini(), inotify_watch_init(), is_path_watched(), log_watch_path_table(), track_path_watch(), and untrack_path_watch().