static bool register_roots(array* new_roots, array* unwatchable) { for (int i=0; i<array_size(new_roots); i++) { char* new_root = array_get(new_roots, i); userlog(LOG_INFO, "registering root: %s", new_root); int id = watch(new_root, unwatchable); if (id == ERR_ABORT) { return false; } else if (id >= 0) { watch_root* root = malloc(sizeof(watch_root)); CHECK_NULL(root, false); root->id = id; root->name = new_root; CHECK_NULL(array_push(roots, root), false); } else { if (show_warning && watch_limit_reached()) { int limit = get_watch_count(); userlog(LOG_WARNING, "watch limit (%d) reached", limit); output("MESSAGE\n" INOTIFY_LIMIT_MSG, limit); show_warning = false; // warn only once } CHECK_NULL(array_push(unwatchable, new_root), false); } } return true; }
static int add_watch(int path_len, watch_node* parent) { int wd = inotify_add_watch(inotify_fd, path_buf, EVENT_MASK); if (wd < 0) { if (errno == EACCES || errno == ENOENT) { userlog(LOG_DEBUG, "inotify_add_watch(%s): %s", path_buf, strerror(errno)); return ERR_IGNORE; } else if (errno == ENOSPC) { userlog(LOG_WARNING, "inotify_add_watch(%s): %s", path_buf, strerror(errno)); watch_limit_reached(); return ERR_CONTINUE; } else { userlog(LOG_ERR, "inotify_add_watch(%s): %s", path_buf, strerror(errno)); return ERR_ABORT; } } else { userlog(LOG_DEBUG, "watching %s: %d", path_buf, wd); } watch_node* node = table_get(watches, wd); if (node != NULL) { if (node->wd != wd) { userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s)", wd, path_buf, node->wd, node->path); return ERR_ABORT; } else if (strcmp(node->path, path_buf) != 0) { char buf1[PATH_MAX], buf2[PATH_MAX]; const char* normalized1 = realpath(node->path, buf1); const char* normalized2 = realpath(path_buf, buf2); if (normalized1 == NULL || normalized2 == NULL || strcmp(normalized1, normalized2) != 0) { userlog(LOG_ERR, "table error: collision at %d (new %s, existing %s)", wd, path_buf, node->path); return ERR_ABORT; } else { userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path_buf, node->path, normalized1); return ERR_IGNORE; } } return wd; } node = malloc(sizeof(watch_node) + path_len + 1); CHECK_NULL(node, ERR_ABORT); memcpy(node->path, path_buf, path_len + 1); node->path_len = path_len; node->wd = wd; node->parent = parent; node->kids = NULL; if (parent != NULL) { if (parent->kids == NULL) { parent->kids = array_create(DEFAULT_SUBDIR_COUNT); CHECK_NULL(parent->kids, ERR_ABORT); } CHECK_NULL(array_push(parent->kids, node), ERR_ABORT); } if (table_put(watches, wd, node) == NULL) { userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path_buf); return ERR_ABORT; } return wd; }
static bool register_roots(array* new_roots, array* unwatchable, array* mounts) { for (int i=0; i<array_size(new_roots); i++) { char* new_root = array_get(new_roots, i); char* unflattened = new_root; if (unflattened[0] == '|') ++unflattened; userlog(LOG_INFO, "registering root: %s", new_root); if (unflattened[0] != '/') { userlog(LOG_WARNING, " ... not valid, skipped"); continue; } array* inner_mounts = array_create(5); CHECK_NULL(inner_mounts, false); bool skip = false; for (int j=0; j<array_size(mounts); j++) { char* mount = array_get(mounts, j); if (is_parent_path(mount, unflattened)) { userlog(LOG_DEBUG, "watch root '%s' is under mount point '%s' - skipping", unflattened, mount); CHECK_NULL(array_push(unwatchable, strdup(unflattened)), false); skip = true; break; } else if (is_parent_path(unflattened, mount)) { userlog(LOG_DEBUG, "watch root '%s' contains mount point '%s' - partial watch", unflattened, mount); char* copy = strdup(mount); CHECK_NULL(array_push(unwatchable, copy), false); CHECK_NULL(array_push(inner_mounts, copy), false); } } if (skip) { continue; } int id = watch(new_root, inner_mounts); array_delete(inner_mounts); if (id >= 0) { watch_root* root = malloc(sizeof(watch_root)); CHECK_NULL(root, false); root->id = id; root->name = strdup(new_root); CHECK_NULL(root->name, false); CHECK_NULL(array_push(roots, root), false); } else if (id == ERR_ABORT) { return false; } else if (id != ERR_IGNORE) { if (show_warning && watch_limit_reached()) { int limit = get_watch_count(); userlog(LOG_WARNING, "watch limit (%d) reached", limit); output("MESSAGE\n" INOTIFY_LIMIT_MSG, limit); show_warning = false; // warn only once } CHECK_NULL(array_push(unwatchable, strdup(unflattened)), false); } } return true; }