diff options
Diffstat (limited to 'devel/glib20/files')
| -rw-r--r-- | devel/glib20/files/gkqueuefilemonitor.c | 224 | ||||
| -rw-r--r-- | devel/glib20/files/kqueue_fnm.c | 1461 | ||||
| -rw-r--r-- | devel/glib20/files/kqueue_fnm.h | 74 |
3 files changed, 0 insertions, 1759 deletions
diff --git a/devel/glib20/files/gkqueuefilemonitor.c b/devel/glib20/files/gkqueuefilemonitor.c deleted file mode 100644 index 8f89b139e89e..000000000000 --- a/devel/glib20/files/gkqueuefilemonitor.c +++ /dev/null @@ -1,224 +0,0 @@ -/*- - * Copyright (c) 2016 - 2019 Rozhuk Ivan <rozhuk.im@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Author: Rozhuk Ivan <rozhuk.im@gmail.com> - * - */ - -#include "config.h" - -#include <glib-object.h> -#include <string.h> -#include <gio/gfilemonitor.h> -#include <gio/glocalfilemonitor.h> -#include <gio/giomodule.h> -#include "glib-private.h" -#include <glib-unix.h> -#include "kqueue_fnm.h" - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -/* Defaults. */ -#ifndef KQUEUE_MON_RATE_LIMIT_TIME_INIT -# define KQUEUE_MON_RATE_LIMIT_TIME_INIT 1000 -#endif - -#ifndef KQUEUE_MON_RATE_LIMIT_TIME_MAX -# define KQUEUE_MON_RATE_LIMIT_TIME_MAX 4000 -#endif -#ifndef KQUEUE_MON_RATE_LIMIT_TIME_MUL -# define KQUEUE_MON_RATE_LIMIT_TIME_MUL 2 -#endif -#ifndef KQUEUE_MON_MAX_DIR_FILES -# define KQUEUE_MON_MAX_DIR_FILES 128 -#endif -#ifndef KQUEUE_MON_LOCAL_SUBFILES -# define KQUEUE_MON_LOCAL_SUBFILES 1 -#endif -#ifndef KQUEUE_MON_LOCAL_SUBDIRS -# define KQUEUE_MON_LOCAL_SUBDIRS 0 -#endif - - -static GMutex kqueue_lock; -static volatile kq_fnm_p kqueue_fnm = NULL; -/* Exclude from file changes monitoring, watch only for dirs. */ -static const char *non_local_fs[] = { - "fusefs.sshfs", - NULL -}; - -#define G_TYPE_KQUEUE_FILE_MONITOR (g_kqueue_file_monitor_get_type()) -#define G_KQUEUE_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST((inst), \ - G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor)) - -typedef GLocalFileMonitorClass GKqueueFileMonitorClass; - -typedef struct { - GLocalFileMonitor parent_instance; - kq_fnme_p fnme; -} GKqueueFileMonitor; - -GType g_kqueue_file_monitor_get_type(void); -G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR, - g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, - g_define_type_id, - "kqueue", - 10)) - - -static void -kqueue_event_handler(kq_fnm_p kfnm, - kq_fnme_p fnme, void *udata, uint32_t event, - const char *base, const char *filename, const char *new_filename) { - static const uint32_t kfnm_to_glib_map[] = { - 0, /* KF_EVENT_NOT_CHANGED */ - G_FILE_MONITOR_EVENT_CREATED, /* KF_EVENT_CREATED */ - G_FILE_MONITOR_EVENT_DELETED, /* KF_EVENT_DELETED */ - G_FILE_MONITOR_EVENT_RENAMED, /* KF_EVENT_RENAMED */ - G_FILE_MONITOR_EVENT_CHANGED /* KF_EVENT_CHANGED */ - }; - - if (NULL == kfnm || NULL == filename || 0 == filename[0] || - strchr(filename, '/') || - KF_EVENT_CREATED > event || - KF_EVENT_CHANGED < event) - return; - - g_file_monitor_source_handle_event(udata, - kfnm_to_glib_map[event], - filename, new_filename, NULL, - g_get_monotonic_time()); -} - -static gboolean -g_kqueue_file_monitor_is_supported(void) { - kq_file_mon_settings_t kfms; - - if (NULL != kqueue_fnm) - return (TRUE); - /* Init only once. */ - g_mutex_lock(&kqueue_lock); - if (NULL != kqueue_fnm) { - g_mutex_unlock(&kqueue_lock); - return (TRUE); /* Initialized while wait lock. */ - } - - memset(&kfms, 0x00, sizeof(kq_file_mon_settings_t)); - kfms.rate_limit_time_init = KQUEUE_MON_RATE_LIMIT_TIME_INIT; - kfms.rate_limit_time_max = KQUEUE_MON_RATE_LIMIT_TIME_MAX; - kfms.rate_limit_time_mul = KQUEUE_MON_RATE_LIMIT_TIME_MUL; - kfms.max_dir_files = KQUEUE_MON_MAX_DIR_FILES; - kfms.mon_local_subfiles = KQUEUE_MON_LOCAL_SUBFILES; - kfms.mon_local_subdirs = KQUEUE_MON_LOCAL_SUBDIRS; - kfms.local_fs = NULL; - kfms.non_local_fs = non_local_fs; - - kqueue_fnm = kq_fnm_create(&kfms, kqueue_event_handler); - if (NULL == kqueue_fnm) { - g_mutex_unlock(&kqueue_lock); - return (FALSE); /* Init fail. */ - } - g_mutex_unlock(&kqueue_lock); - - return (TRUE); -} - -static gboolean -g_kqueue_file_monitor_cancel(GFileMonitor *monitor) { - GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(monitor); - - kq_fnm_del(kqueue_fnm, gffm->fnme); - gffm->fnme = NULL; - - return (TRUE); -} - -static void -g_kqueue_file_monitor_finalize(GObject *object) { - //GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(object); - - //g_mutex_lock(&kqueue_lock); - //kq_fnm_free(kqueue_fnm); - //kqueue_fnm = NULL; - //g_mutex_unlock(&kqueue_lock); - //G_OBJECT_CLASS(g_kqueue_file_monitor_parent_class)->finalize(object); -} - -static void -g_kqueue_file_monitor_start(GLocalFileMonitor *local_monitor, - const gchar *dirname, const gchar *basename, - const gchar *filename, GFileMonitorSource *source) { - GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(local_monitor); - - g_assert(NULL != kqueue_fnm); - //g_source_ref((GSource*)source); - - if (NULL == filename) { - filename = dirname; - } - gffm->fnme = kq_fnm_add(kqueue_fnm, filename, source); -} - -static void -g_kqueue_file_monitor_init(GKqueueFileMonitor *monitor) { - -} - -static void -g_kqueue_file_monitor_class_init(GKqueueFileMonitorClass *class) { - GObjectClass *gobject_class = G_OBJECT_CLASS(class); - GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS(class); - - class->is_supported = g_kqueue_file_monitor_is_supported; - class->start = g_kqueue_file_monitor_start; - class->mount_notify = TRUE; /* TODO: ??? */ - file_monitor_class->cancel = g_kqueue_file_monitor_cancel; - gobject_class->finalize = g_kqueue_file_monitor_finalize; -} - -static void -g_kqueue_file_monitor_class_finalize(GKqueueFileMonitorClass *class) { - -} - -void -g_io_module_load(GIOModule *module) { - - g_type_module_use(G_TYPE_MODULE(module)); - - g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, - G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10); - g_io_extension_point_implement(G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME, - G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10); -} - -void -g_io_module_unload(GIOModule *module) { - - g_assert_not_reached(); -} diff --git a/devel/glib20/files/kqueue_fnm.c b/devel/glib20/files/kqueue_fnm.c deleted file mode 100644 index cb8568669611..000000000000 --- a/devel/glib20/files/kqueue_fnm.c +++ /dev/null @@ -1,1461 +0,0 @@ -/*- - * Copyright (c) 2016 - 2021 Rozhuk Ivan <rozhuk.im@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Author: Rozhuk Ivan <rozhuk.im@gmail.com> - * - */ - -#include <sys/param.h> -#include <sys/types.h> -#include <sys/event.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <sys/ucred.h> -#include <sys/mount.h> -#include <sys/fcntl.h> /* open, fcntl */ -#include <sys/queue.h> - -#include <inttypes.h> -#include <stdlib.h> /* malloc, exit */ -#include <unistd.h> /* close, write, sysconf */ -#include <string.h> /* bcopy, bzero, memcpy, memmove, memset, strerror... */ -#include <dirent.h> /* opendir, readdir */ -#include <errno.h> -#include <pthread.h> -#include <glib.h> - -#include "kqueue_fnm.h" - - -/* Preallocate items count. */ -#ifndef FILES_ALLOC_BLK_SIZE -# define FILES_ALLOC_BLK_SIZE 32 -#endif - - -typedef struct readdir_context_s { - int fd; - uint8_t *buf; - size_t buf_size; - size_t buf_used; - size_t buf_pos; -} readdir_ctx_t, *readdir_ctx_p; - - -typedef struct file_info_s { /* Directory file. */ - int fd; /* For notify kqueue(). */ - struct dirent de; /* d_reclen used for action. */ - struct stat sb; -} file_info_t, *file_info_p; - - -typedef struct kq_file_nonify_monitor_obj_s *kq_fnmo_p; - -typedef struct kq_file_nonify_monitor_entry_s { - TAILQ_ENTRY(kq_file_nonify_monitor_entry_s) next; - kq_fnmo_p fnmo; - void *udata; - volatile int enabled; -} kq_fnme_t; - -TAILQ_HEAD(kq_fnme_head, kq_file_nonify_monitor_entry_s); - -typedef struct kq_file_nonify_monitor_obj_s { - int fd; /* For notify kqueue(). */ - int is_dir; - int is_local; /* Is file system local. */ - int is_removed; /* File/dir deleted, reported and can be only free. */ - int is_cached; /* Added to fnmo_cache. */ - struct stat sb; - char path[(PATH_MAX + sizeof(void*))]; - size_t path_size; - size_t name_offset; /* Parent path size. */ - uint32_t rate_lim_cur_interval; /* From rate_limit_time_init to rate_limit_time_max. 0 disabled. */ - size_t rate_lim_ev_cnt; /* Events count then rate_lim_cur_interval != 0 since last report. */ - sbintime_t rate_lim_ev_last; /* Last event time. */ - void *udata; - kq_fnm_p kfnm; - struct kq_fnme_head entry_head; - /* For dir. */ - file_info_p files; - volatile size_t files_count; - size_t files_allocated; -} kq_fnmo_t; - - -typedef struct kq_file_nonify_monitor_s { - int fd; /* kqueue() fd. */ - int pfd[2]; /* pipe queue specific. */ - GHashTable *fnmo_cache; - struct statfs *mounts; - size_t mounts_count; - kfnm_event_handler_cb cb_func; /* Callback on dir/file change. */ - kq_file_mon_settings_t s; - sbintime_t rate_lim_time_init; /* rate_limit_time_init */ - pthread_t tid; -} kq_fnm_t; - - -typedef void (*kq_msg_cb)(void *arg); - -typedef struct kq_file_mon_msg_pkt_s { - size_t magic; - kq_msg_cb msg_cb; - void *arg; - size_t chk_sum; -} kq_fnm_msg_pkt_t, *kq_fnm_msg_pkt_p; - -#define KF_MSG_PKT_MAGIC 0xffddaa00 - - -#ifdef O_PATH -# define OPEN_MODE_FLAG O_PATH -#elif defined(O_EVTONLY) -# define OPEN_MODE_FLAG O_EVTONLY -#else -# define OPEN_MODE_FLAG O_RDONLY -#endif -#ifndef O_NOATIME -# define O_NOATIME 0 -#endif -#define OPEN_FILE_MON_FLAGS (O_NONBLOCK | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | OPEN_MODE_FLAG) -#define OPEN_FILE_READ_FLAGS (O_NONBLOCK | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | O_RDONLY) - -#ifndef NOTE_CLOSE_WRITE -# define NOTE_CLOSE_WRITE 0 -#endif -#define EVFILT_VNODE_SUB_FLAGS (NOTE_WRITE | \ - NOTE_EXTEND | \ - NOTE_ATTRIB | \ - NOTE_LINK | \ - NOTE_CLOSE_WRITE) - -#define EVFILT_VNODE_FLAGS_ALL (NOTE_DELETE | \ - EVFILT_VNODE_SUB_FLAGS | \ - NOTE_RENAME | \ - NOTE_REVOKE) - -#ifndef _GENERIC_DIRSIZ -# define _GENERIC_DIRSIZ(__de) MIN((__de)->d_reclen, sizeof(struct dirent)) -#endif - -#define IS_NAME_DOTS(__name) ('.' == (__name)[0] && \ - ('\0' == (__name)[1] || \ - ('.' == (__name)[1] && '\0' == (__name)[2]))) -#define IS_DE_NAME_EQ(__de1, __de2) (0 == mem_cmpn((__de1)->d_name, \ - (__de1)->d_namlen, \ - (__de2)->d_name, \ - (__de2)->d_namlen)) -#define zalloc(__size) calloc(1, (__size)) - -#if (!defined(HAVE_REALLOCARRAY) && (!defined(__FreeBSD_version) || __FreeBSD_version < 1100000)) -# define reallocarray(__mem, __size, __count) realloc((__mem), ((__size) * (__count))) -#endif - -/* To not depend from compiler version. */ -#define MSTOSBT(__ms) ((sbintime_t)((((uint64_t)1 << 32) * (uint64_t)(__ms)) / 1000)) - -#ifndef TAILQ_FOREACH_SAFE -#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = TAILQ_FIRST((head)); \ - (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) -#endif - -#ifndef CLOCK_MONOTONIC_FAST -# define CLOCK_MONOTONIC_FAST CLOCK_MONOTONIC -#endif - - -void *kq_fnm_proccess_events_proc(void *data); - -static inline int -mem_cmpn(const void *buf1, const size_t buf1_size, - const void *buf2, const size_t buf2_size) { - - if (buf1_size != buf2_size) - return (((buf1_size > buf2_size) ? 127 : -127)); - if (0 == buf1_size || buf1 == buf2) - return (0); - if (NULL == buf1) - return (-127); - if (NULL == buf2) - return (127); - return (memcmp(buf1, buf2, buf1_size)); -} - -static int -realloc_items(void **items, const size_t item_size, - size_t *allocated, const size_t alloc_blk_cnt, const size_t count) { - size_t allocated_prev, allocated_new; - uint8_t *items_new; - - if (NULL == items || 0 == item_size || NULL == allocated || - 0 == alloc_blk_cnt) - return (EINVAL); - allocated_prev = (*allocated); - if (NULL != (*items) && - allocated_prev > count && - allocated_prev <= (count + alloc_blk_cnt)) - return (0); - allocated_new = (((count / alloc_blk_cnt) + 1) * alloc_blk_cnt); - items_new = (uint8_t*)reallocarray((*items), item_size, allocated_new); - if (NULL == items_new) /* Realloc fail! */ - return (ENOMEM); - - if (allocated_new > allocated_prev) { /* Init new mem. */ - memset((items_new + (allocated_prev * item_size)), 0x00, - ((allocated_new - allocated_prev) * item_size)); - } - (*items) = items_new; - (*allocated) = allocated_new; - - return (0); -} - -/* Like getmntinfo() but allow free mem. */ -static int -getmntinfo_ex(struct statfs **mntbufp, int mode) { - int ret; - struct statfs *buf; - - if (NULL == mntbufp) - return (EINVAL); - /* Request count. */ - ret = getfsstat(NULL, 0, mode); - if (-1 == ret) - return (ret); - /* Alloc mem. */ - buf = calloc((ret + 1), sizeof(struct statfs)); - if (NULL == buf) - return (-1); - /* Request data. */ - ret = getfsstat(buf, ((ret + 1) * sizeof(struct statfs)), mode); - if (-1 == ret) { - free(buf); - buf = NULL; - } - (*mntbufp) = buf; - - return (ret); -} - -static int -mounts_find_name(struct statfs *mounts, size_t mounts_count, - const char *mntonname) { - size_t i; - - if (NULL == mounts || NULL == mntonname) - return (0); - for (i = 0; i < mounts_count; i ++) { - if (0 != strncmp(mntonname, mounts[i].f_mntonname, MNAMELEN)) - continue; - return (1); - } - return (0); -} - - -static void -readdir_free(readdir_ctx_p rdd) { - - if (NULL == rdd) - return; - - if (-1 != rdd->fd) { - close(rdd->fd); - rdd->fd = -1; - } - if (NULL != rdd->buf) { - free(rdd->buf); - rdd->buf = NULL; - } -} - -static int -readdir_start(const int fd, struct stat *sb, const size_t exp_count, - readdir_ctx_p rdd) { - struct stat _sb; - - if (-1 == fd || NULL == rdd) - return (EINVAL); - - if (NULL == sb) { - sb = &_sb; - if (0 != fstat(fd, sb)) - return (errno); - } - /* Init. */ - memset(rdd, 0x00, sizeof(readdir_ctx_t)); - /* Reopen for read. */ - rdd->fd = openat(fd, ".", (OPEN_FILE_READ_FLAGS | O_DIRECTORY)); - if (-1 == rdd->fd) - return (errno); - /* Calculate buf size for getdents(). */ - rdd->buf_size = MAX((size_t)sb->st_size, (exp_count * sizeof(struct dirent))); - if (0 == rdd->buf_size) { - rdd->buf_size = (16 * PAGE_SIZE); - } - /* Make buf size well aligned. */ - if (0 != sb->st_blksize) { - if (powerof2(sb->st_blksize)) { - rdd->buf_size = roundup2(rdd->buf_size, sb->st_blksize); - } else { - rdd->buf_size = roundup(rdd->buf_size, sb->st_blksize); - } - } else { - rdd->buf_size = round_page(rdd->buf_size); - } - /* Allocate buf. */ - rdd->buf = malloc(rdd->buf_size); - if (NULL == rdd->buf) { - readdir_free(rdd); - return (ENOMEM); - } - - return (0); -} - -static int -readdir_next(readdir_ctx_p rdd, struct dirent *de) { - int error = 0; - ssize_t ios; - uint8_t *ptr; - - if (NULL == rdd || NULL == rdd->buf || NULL == de) - return (EINVAL); - - for (;;) { - if (rdd->buf_used <= rdd->buf_pos) { - /* Called once if buf size calculated ok. */ - ios = getdents(rdd->fd, (char*)rdd->buf, rdd->buf_size); - if (-1 == ios) { - error = errno; - break; - } - if (0 == ios) { - error = ESPIPE; /* EOF. */ - break; - } - rdd->buf_used = (size_t)ios; - rdd->buf_pos = 0; - } - /* Keep data aligned. */ - ptr = (rdd->buf + rdd->buf_pos); - memcpy(de, ptr, (sizeof(struct dirent) - sizeof(de->d_name))); - if (0 == de->d_reclen) { - error = ESPIPE; /* EOF. */ - break; - } - rdd->buf_pos += de->d_reclen; -#ifdef DT_WHT - if (DT_WHT == de->d_type) - continue; -#endif - if (0 == de->d_namlen) - continue; /* Empty. */ - memcpy(de, ptr, _GENERIC_DIRSIZ(de)); - if (0 == de->d_name[0]) - continue; /* Empty. */ - if (IS_NAME_DOTS(de->d_name)) - continue; /* Dots. */ - return (0); /* OK. */ - } - - /* Err or no more files, auto cleanup. */ - readdir_free(rdd); - - return (error); -} - - -static int -file_info_find_ni(file_info_p files, size_t files_count, - file_info_p fi, size_t *idx) { - size_t i; - mode_t st_ftype; - - if (NULL == files || NULL == fi || NULL == idx) - return (0); - st_ftype = (S_IFMT & fi->sb.st_mode); - for (i = 0; i < files_count; i ++) { - if ((S_IFMT & files[i].sb.st_mode) != st_ftype) - continue; - if ((fi->sb.st_ino != files[i].sb.st_ino || - fi->de.d_fileno != files[i].de.d_fileno) && - 0 == IS_DE_NAME_EQ(&fi->de, &files[i].de)) - continue; - (*idx) = i; - return (1); - } - (*idx) = files_count; - return (0); -} - -static int -file_info_find_ino(file_info_p files, size_t files_count, - file_info_p fi, size_t *idx) { - size_t i; - mode_t st_ftype; - - if (NULL == files || NULL == fi || NULL == idx) - return (0); - st_ftype = (S_IFMT & fi->sb.st_mode); - for (i = 0; i < files_count; i ++) { - if ((S_IFMT & files[i].sb.st_mode) != st_ftype || - fi->sb.st_ino != files[i].sb.st_ino || - fi->de.d_fileno != files[i].de.d_fileno) - continue; - (*idx) = i; - return (1); - } - (*idx) = files_count; - return (0); -} - -static int -file_info_find_name(file_info_p files, size_t files_count, - file_info_p fi, size_t *idx) { - size_t i; - mode_t st_ftype; - - if (NULL == files || NULL == fi || NULL == idx) - return (0); - st_ftype = (S_IFMT & fi->sb.st_mode); - for (i = 0; i < files_count; i ++) { - if ((S_IFMT & files[i].sb.st_mode) != st_ftype || - 0 == IS_DE_NAME_EQ(&fi->de, &files[i].de)) - continue; - (*idx) = i; - return (1); - } - (*idx) = files_count; - return (0); -} - -static void -file_info_fd_close(file_info_p files, size_t files_count) { - size_t i; - - if (NULL == files || 0 == files_count) - return; - for (i = 0; i < files_count; i ++) { - if (-1 == files[i].fd) - continue; - close(files[i].fd); - files[i].fd = -1; - } -} - - -static int -is_fs_local(struct statfs *stfs, const char **local_fs, const char **non_local_fs) { - size_t i; - - if (NULL == stfs) - return (0); - /* White listed fs. */ - if (NULL != local_fs) { - for (i = 0; NULL != local_fs[i]; i ++) { - if (0 == strncmp(stfs->f_fstypename, local_fs[i], - sizeof(stfs->f_fstypename))) - return (1); - } - } - if (0 == (MNT_LOCAL & stfs->f_flags)) - return (0); - /* Filter out black listed fs. */ - if (NULL != non_local_fs) { - for (i = 0; NULL != non_local_fs[i]; i ++) { - if (0 == strncmp(stfs->f_fstypename, non_local_fs[i], - sizeof(stfs->f_fstypename))) - return (0); - } - } - return (1); -} - - -static void -kq_fnmo_rate_lim_stop(kq_fnmo_p fnmo) { - struct kevent kev; - - if (NULL == fnmo || 0 == fnmo->rate_lim_cur_interval) - return; - fnmo->rate_lim_cur_interval = 0; - fnmo->rate_lim_ev_cnt = 0; - EV_SET(&kev, (uintptr_t)fnmo, EVFILT_TIMER, - (EV_DELETE | EV_CLEAR), 0, 0, NULL); - kevent(fnmo->kfnm->fd, &kev, 1, NULL, 0, NULL); -} - -static int -kq_fnmo_rate_lim_shedule_next(kq_fnmo_p fnmo) { - u_short flags = (EV_ADD | EV_CLEAR | EV_ONESHOT); - struct kevent kev; - - if (NULL == fnmo || -1 == fnmo->fd || - 0 == fnmo->kfnm->s.rate_limit_time_init) - return (EINVAL); - if (0 == fnmo->rate_lim_cur_interval) { /* First call. */ - fnmo->rate_lim_cur_interval = fnmo->kfnm->s.rate_limit_time_init; - } else { - if (fnmo->rate_lim_cur_interval == fnmo->kfnm->s.rate_limit_time_max) - return (0); /* No need to modify timer. */ - /* Increase rate limit interval. */ - fnmo->rate_lim_cur_interval *= fnmo->kfnm->s.rate_limit_time_mul; - } - if (fnmo->rate_lim_cur_interval >= fnmo->kfnm->s.rate_limit_time_max) { - /* Check upper limit and shedule periodic timer with upper rate limit time. */ - flags &= ~EV_ONESHOT; - fnmo->rate_lim_cur_interval = fnmo->kfnm->s.rate_limit_time_max; - } - /* Setup timer. */ - EV_SET(&kev, (uintptr_t)fnmo, EVFILT_TIMER, flags, - NOTE_MSECONDS, fnmo->rate_lim_cur_interval, fnmo); - if (-1 == kevent(fnmo->kfnm->fd, &kev, 1, NULL, 0, NULL)) { - fnmo->rate_lim_cur_interval = 0; - return (errno); - } - if (0 != (EV_ERROR & kev.flags)) { - fnmo->rate_lim_cur_interval = 0; - return ((int)kev.data); - } - return (0); -} - -/* Return: - * 0 for events that not handled - * 1 for handled = rate limited - * -1 on error. - */ -static int -kq_fnmo_rate_lim_check(kq_fnmo_p fnmo) { - sbintime_t sbt, sbt_now; - struct timespec ts; - - if (NULL == fnmo) - return (-1); - if (-1 == fnmo->fd || - 0 == fnmo->kfnm->s.rate_limit_time_init) - return (0); - if (0 != fnmo->rate_lim_cur_interval) { - fnmo->rate_lim_ev_cnt ++; /* Count event, timer is active. */ - return (1); - } - - /* Do we need to enable rate limit? */ - if (0 != clock_gettime(CLOCK_MONOTONIC_FAST, &ts)) - return (-1); - sbt_now = tstosbt(ts); - sbt = (fnmo->rate_lim_ev_last + fnmo->kfnm->rate_lim_time_init); - fnmo->rate_lim_ev_last = sbt_now; - if (sbt < sbt_now) /* Events rate to low. */ - return (0); - /* Try to enable rate limit. */ - if (0 != kq_fnmo_rate_lim_shedule_next(fnmo)) - return (-1); - /* Ok. */ - fnmo->rate_lim_ev_cnt ++; - - return (1); -} - -static void -kq_fnmo_clean(kq_fnmo_p fnmo) { - - if (NULL == fnmo) - return; - - kq_fnmo_rate_lim_stop(fnmo); - if (-1 != fnmo->fd) { - close(fnmo->fd); - fnmo->fd = -1; - } - fnmo->is_removed ++; - - /* Stop monitoring files/dirs. */ - file_info_fd_close(fnmo->files, fnmo->files_count); - free(fnmo->files); - fnmo->files = NULL; - fnmo->files_count = 0; - fnmo->files_allocated = 0; -} - -static void -kq_fnmo_free(kq_fnmo_p fnmo) { - - if (NULL == fnmo) - return; - - kq_fnmo_clean(fnmo); - - if (0 != fnmo->is_cached && - NULL != fnmo->kfnm && - g_hash_table_lookup(fnmo->kfnm->fnmo_cache, fnmo->path) == fnmo) { - g_hash_table_remove(fnmo->kfnm->fnmo_cache, fnmo->path); - } - free(fnmo); -} - -static kq_fnmo_p -kq_fnmo_alloc(kq_fnm_p kfnm, const char *path, kq_fnme_p fnme) { - kq_fnmo_p fnmo; - - if (NULL == kfnm || NULL == path) - return (NULL); - fnmo = zalloc(sizeof(kq_fnmo_t)); - if (NULL == fnmo) - return (NULL); - fnmo->fd = -1; - /* Remember args. */ - fnmo->path_size = strlcpy(fnmo->path, path, PATH_MAX); - /* Make sure that no trailing '/'. */ - while (1 < fnmo->path_size && '/' == fnmo->path[(fnmo->path_size - 1)]) { - fnmo->path_size --; - fnmo->path[fnmo->path_size] = 0; - } - /* Get parent folder name. */ - fnmo->name_offset = fnmo->path_size; - while (0 < fnmo->name_offset && '/' != fnmo->path[(fnmo->name_offset - 1)]) { - fnmo->name_offset --; - } - fnmo->kfnm = kfnm; - TAILQ_INIT(&fnmo->entry_head); - if (NULL != fnme) { - TAILQ_INSERT_HEAD(&fnmo->entry_head, fnme, next); - } - - return (fnmo); -} - -static void -kq_fnmo_cb_func_call(kq_fnmo_p fnmo, uint32_t event, - const char *base, const char *filename, const char *new_filename) { - kq_fnm_p kfnm; - kq_fnme_p fnme; - - if (NULL == fnmo) - return; - kfnm = fnmo->kfnm; - TAILQ_FOREACH(fnme, &fnmo->entry_head, next) { - if (0 == fnme->enabled) /* XXX: try lock here? */ - continue; - kfnm->cb_func(kfnm, fnme, fnme->udata, event, - base, filename, new_filename); - } - -} - -static int -kq_fnmo_readdir(kq_fnmo_p fnmo, size_t exp_count) { - int error; - struct dirent *de; - file_info_p tmfi; - readdir_ctx_t rdd; - - if (NULL == fnmo || -1 == fnmo->fd || 0 == fnmo->is_dir) - return (EINVAL); - - free(fnmo->files); - fnmo->files = NULL; - fnmo->files_count = 0; - fnmo->files_allocated = 0; - /* Pre allocate. */ - if (0 != realloc_items((void**)&fnmo->files, - sizeof(file_info_t), &fnmo->files_allocated, - FILES_ALLOC_BLK_SIZE, (exp_count + 1))) - return (ENOMEM); - - error = readdir_start(fnmo->fd, &fnmo->sb, exp_count, &rdd); - if (0 != error) - return (error); - for (;;) { - if (0 != realloc_items((void**)&fnmo->files, - sizeof(file_info_t), &fnmo->files_allocated, - FILES_ALLOC_BLK_SIZE, fnmo->files_count)) { - free(fnmo->files); - fnmo->files = NULL; - fnmo->files_count = 0; - fnmo->files_allocated = 0; - readdir_free(&rdd); /* Force cleanup here. */ - return (ENOMEM); - } - de = &fnmo->files[fnmo->files_count].de; /* Use short name. */ - /* Get file name from folder. */ - if (0 != readdir_next(&rdd, de)) - break; - /* Get file attrs. */ - if (0 != fstatat(fnmo->fd, de->d_name, - &fnmo->files[fnmo->files_count].sb, - AT_SYMLINK_NOFOLLOW)) { - if (ENOENT == errno) - continue; /* File deleted. */ - memset(&fnmo->files[fnmo->files_count].sb, 0x00, - sizeof(struct stat)); - } - fnmo->files[fnmo->files_count].fd = -1; - fnmo->files_count ++; - } - - /* Mem compact. */ - tmfi = reallocarray(fnmo->files, sizeof(file_info_t), (fnmo->files_count + 1)); - if (NULL != tmfi) { /* realloc ok. */ - fnmo->files = tmfi; - fnmo->files_allocated = (fnmo->files_count + 1); - } - - return (0); /* OK. */ -} - - -static void -kq_fnmo_fi_start(kq_fnmo_p fnmo, file_info_p fi) { - struct kevent kev; - - if (NULL == fnmo || -1 == fnmo->fd || NULL == fi) - return; - /* Filter files that can not be monitored, redice openat() calls. */ - switch (fi->de.d_type) { - case DT_FIFO: - case DT_SOCK: -#ifdef DT_WHT - case DT_WHT: -#endif - return; - } - fi->fd = openat(fnmo->fd, fi->de.d_name, OPEN_FILE_MON_FLAGS); - if (-1 == fi->fd) - return; - EV_SET(&kev, fi->fd, EVFILT_VNODE, - (EV_ADD | EV_CLEAR), - EVFILT_VNODE_SUB_FLAGS, 0, fnmo); - if (-1 == kevent(fnmo->kfnm->fd, &kev, 1, NULL, 0, NULL)) { - close(fi->fd); - fi->fd = -1; - } -} - -static int -kq_fnmo_is_fi_monitored(kq_fnmo_p fnmo, file_info_p fi) { - - if (NULL == fnmo) - return (0); - if (0 == fnmo->is_local || - (0 != fnmo->kfnm->s.max_dir_files && - fnmo->kfnm->s.max_dir_files < fnmo->files_count)) - return (0); - if (NULL != fi && - 0 == fnmo->kfnm->s.mon_local_subdirs && - S_ISDIR(fi->sb.st_mode)) - return (0); - return (1); -} - -static int -kq_fnmo_reopen_fd(kq_fnmo_p fnmo) { - int error, fd; - struct statfs stfs; - struct kevent kev; - - if (NULL == fnmo) - return (EINVAL); - - /* Close fd and stop timer. */ - kq_fnmo_rate_lim_stop(fnmo); - if (-1 != fnmo->fd) { - close(fnmo->fd); - fnmo->fd = -1; - } - - fd = open(fnmo->path, (OPEN_FILE_MON_FLAGS | O_DIRECTORY)); - if (-1 == fd) - return (errno); - if (0 != fstat(fd, &fnmo->sb)) { - error = errno; - goto err_out; - } - if (0 == fnmo->sb.st_nlink) { - error = ENOENT; - goto err_out; - } - if (S_ISDIR(fnmo->sb.st_mode)) { - fnmo->is_dir = 1; - /* Is file system local? */ - if (0 != fnmo->kfnm->s.mon_local_subfiles && - 0 == fstatfs(fd, &stfs)) { - fnmo->is_local = is_fs_local(&stfs, fnmo->kfnm->s.local_fs, - fnmo->kfnm->s.non_local_fs); - } - } - - /* Add to kqueue. */ - EV_SET(&kev, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR), - EVFILT_VNODE_FLAGS_ALL, 0, fnmo); - if (-1 == kevent(fnmo->kfnm->fd, &kev, 1, NULL, 0, NULL) || - 0 != (EV_ERROR & kev.flags)) { - error = errno; - goto err_out; - } - - fnmo->is_removed = 0; - fnmo->fd = fd; - - return (0); /* OK. */ - -err_out: - close(fd); - return (error); -} - -static int -kq_fnmo_init(kq_fnmo_p fnmo) { - int error; - size_t i; - - if (NULL == fnmo) - return (EINVAL); - - error = kq_fnmo_reopen_fd(fnmo); - if (0 != error) - goto err_out; - - if (0 == fnmo->is_dir) - return (0); /* OK. */ - /* Dir special processing. */ - - /* Read and remember dir content. */ - error = kq_fnmo_readdir(fnmo, 0); - if (0 != error) - goto err_out; - /* Add monitor sub files/dirs, ignory errors. */ - /* Check twice for performance reason. */ - if (0 != kq_fnmo_is_fi_monitored(fnmo, NULL)) { - for (i = 0; i < fnmo->files_count; i ++) { - if (0 != kq_fnmo_is_fi_monitored(fnmo, &fnmo->files[i])) { - kq_fnmo_fi_start(fnmo, &fnmo->files[i]); - } - } - } - - return (0); /* OK. */ - -err_out: - kq_fnmo_clean(fnmo); - return (error); -} - -static void -kq_fnme_init_cb(void *arg) { - kq_fnme_p fnme = arg; - kq_fnmo_p fnmo; - kq_fnm_p kfnm; - - if (NULL == fnme || NULL == fnme->fnmo) - return; - kfnm = fnme->fnmo->kfnm; - /* Is in cache? */ - fnmo = g_hash_table_lookup(kfnm->fnmo_cache, fnme->fnmo->path); - if (NULL == fnmo) { - /* Init and add to cache. */ - g_hash_table_insert(kfnm->fnmo_cache, - fnme->fnmo->path, fnme->fnmo); - fnme->fnmo->is_cached ++; - kq_fnmo_init(fnme->fnmo); - return; - } - /* Found in cache, use it. */ - TAILQ_REMOVE(&fnme->fnmo->entry_head, fnme, next); - kq_fnmo_free(fnme->fnmo); - fnme->fnmo = fnmo; - TAILQ_INSERT_HEAD(&fnmo->entry_head, fnme, next); - if (-1 == fnmo->fd) { - /* Re init existing, no notify. */ - kq_fnmo_init(fnme->fnmo); - } -} - -static void -kq_fnme_free(kq_fnme_p fnme) { - - if (NULL == fnme) - return; - if (NULL != fnme->fnmo) { - TAILQ_REMOVE(&fnme->fnmo->entry_head, fnme, next); - if (TAILQ_EMPTY(&fnme->fnmo->entry_head)) { - kq_fnmo_free(fnme->fnmo); - } - } - free(fnme); -} - -static void -kq_fnme_free_cb(void *arg) { - - kq_fnme_free((kq_fnme_p)arg); -} - - -static void -kq_handle_notify_removed(kq_fnmo_p fnmo) { - size_t i; - - if (NULL == fnmo || 0 != fnmo->is_removed) - return; - - if (0 != fnmo->is_dir) { - /* Notify removed files first. */ - for (i = 0; i < fnmo->files_count; i ++) { - kq_fnmo_cb_func_call(fnmo, KF_EVENT_DELETED, - fnmo->path, fnmo->files[i].de.d_name, NULL); - } - } - fnmo->path[fnmo->name_offset - 1] = 0; - kq_fnmo_cb_func_call(fnmo, KF_EVENT_DELETED, fnmo->path, - (fnmo->path + fnmo->name_offset), NULL); - fnmo->path[fnmo->name_offset - 1] = '/'; - kq_fnmo_clean(fnmo); -} - -static void -kq_handle_changes(kq_fnmo_p fnmo) { - size_t i, k, files_count; - file_info_p files; - - if (NULL == fnmo || -1 == fnmo->fd) - return; - if (0 != fstat(fnmo->fd, &fnmo->sb) || - 0 == fnmo->sb.st_nlink) { - kq_handle_notify_removed(fnmo); - return; - } - if (0 == fnmo->is_dir) { - fnmo->path[fnmo->name_offset - 1] = 0; - kq_fnmo_cb_func_call(fnmo, KF_EVENT_CHANGED, fnmo->path, - (fnmo->path + fnmo->name_offset), NULL); - fnmo->path[fnmo->name_offset - 1] = '/'; - return; - } - - /* Dir processing. */ - - /* Save prev. */ - files = fnmo->files; - files_count = fnmo->files_count; - fnmo->files = NULL; - fnmo->files_count = 0; - /* Update dir. */ - if (0 != kq_fnmo_readdir(fnmo, files_count)) { - /* Restore prev state on fail. */ - fnmo->files = files; - fnmo->files_count = files_count; - return; - } - - /* Notify removed first. */ - for (i = 0; i < files_count; i ++) { - if (0 != file_info_find_ni(fnmo->files, fnmo->files_count, - &files[i], &k)) /* Deleted? */ - continue; - if (-1 != files[i].fd) { - close(files[i].fd); - files[i].fd = -1; - } - kq_fnmo_cb_func_call(fnmo, KF_EVENT_DELETED, fnmo->path, - files[i].de.d_name, NULL); - } - /* Notify. */ - for (i = 0; i < fnmo->files_count; i ++) { - /* Is new file/folder? */ - if (0 == file_info_find_ino(files, files_count, &fnmo->files[i], &k) && - 0 == file_info_find_name(files, files_count, &fnmo->files[i], &k)) { /* Add new. */ - /* Add monitor sub files/dirs, ignory errors. */ - if (0 != kq_fnmo_is_fi_monitored(fnmo, &fnmo->files[i])) { - kq_fnmo_fi_start(fnmo, &fnmo->files[i]); - } - kq_fnmo_cb_func_call(fnmo, KF_EVENT_CREATED, - fnmo->path, fnmo->files[i].de.d_name, NULL); - continue; - } - /* Keep file fd. */ - fnmo->files[i].fd = files[k].fd; - files[k].fd = -1; - /* Is renamed? */ - if (0 == IS_DE_NAME_EQ(&files[k].de, &fnmo->files[i].de)) { - kq_fnmo_cb_func_call(fnmo, KF_EVENT_RENAMED, - fnmo->path, files[k].de.d_name, - fnmo->files[i].de.d_name); - continue; - } - /* Is modified? */ - if (0 != memcmp(&fnmo->files[i].sb, &files[k].sb, sizeof(struct stat))) { - kq_fnmo_cb_func_call(fnmo, KF_EVENT_CHANGED, - fnmo->path, fnmo->files[i].de.d_name, NULL); - continue; - } - /* Not changed. */ - } - - /* Prevent FD leak die to race conditions. - * All fd must be -1, check this while debuging. - */ - file_info_fd_close(files, files_count); - free(files); -} - -static int -kq_handle_rename(kq_fnmo_p fnmo) { - int error, up_dir_fd, found = 0; - readdir_ctx_t rdd; - struct dirent de; - struct stat sb; - char old_filename[(MAXNAMLEN + sizeof(void*))]; - size_t old_filename_size; - - if (NULL == fnmo || -1 == fnmo->fd) - return (EINVAL); - if (0 != fstat(fnmo->fd, &fnmo->sb) || - 0 == fnmo->sb.st_nlink) { -notify_removed_errno: - error = errno; -notify_removed: - kq_handle_notify_removed(fnmo); - return (error); - } - /* Save old file name. */ - old_filename_size = (fnmo->path_size - fnmo->name_offset); - memcpy(old_filename, - (fnmo->path + fnmo->name_offset), - old_filename_size); - old_filename[old_filename_size] = 0; - - /* Get parent folder name. */ - fnmo->path[fnmo->name_offset] = 0; - /* Try to open. */ - up_dir_fd = open(fnmo->path, (OPEN_FILE_MON_FLAGS | O_DIRECTORY)); - /* Restore '/' after parent folder. */ - fnmo->path[fnmo->name_offset] = '/'; - if (-1 == up_dir_fd) - goto notify_removed_errno; - error = readdir_start(up_dir_fd, NULL, 0, &rdd); - if (0 != error) { - close(up_dir_fd); - goto notify_removed; - } - /* Find new name by inode. */ - while (0 == readdir_next(&rdd, &de)) { - if (0 == fstatat(up_dir_fd, de.d_name, &sb, AT_SYMLINK_NOFOLLOW) && - 0 == memcmp(&fnmo->sb, &sb, sizeof(struct stat))) { - readdir_free(&rdd); /* Force cleanup here. */ - found ++; - break; - } - } - close(up_dir_fd); - if (0 == found) { - error = ENOENT; - goto notify_removed; /* Not found. */ - } - /* Update name. */ - if (PATH_MAX < (fnmo->name_offset + de.d_namlen)) { - error = EINVAL; - goto notify_removed; - } - memcpy((fnmo->path + fnmo->name_offset), de.d_name, de.d_namlen); - fnmo->path_size = (fnmo->name_offset + de.d_namlen); - fnmo->path[fnmo->path_size] = 0; - /* Notify. */ - kq_fnmo_cb_func_call(fnmo, KF_EVENT_RENAMED, fnmo->path, - old_filename, de.d_name); - - return (0); -} - - -static void -kq_fnm_delay_call_process(kq_fnm_p kfnm, kq_msg_cb forced_msg_cb) { - ssize_t ios; - kq_fnm_msg_pkt_t msg; - - for (;;) { - ios = read(kfnm->pfd[0], &msg, sizeof(msg)); - if (0 >= ios) - return; - if (sizeof(msg) != ios || - KF_MSG_PKT_MAGIC != msg.magic || - (((size_t)msg.msg_cb) ^ ((size_t)msg.arg)) != msg.chk_sum) - continue; - if (NULL != forced_msg_cb) { - forced_msg_cb(msg.arg); - continue; - } - if (NULL == msg.msg_cb) - continue; - msg.msg_cb(msg.arg); - } -} - -static int -kq_fnm_delay_call(kq_fnm_p kfnm, kq_msg_cb msg_cb, - void *arg) { - kq_fnm_msg_pkt_t msg; - - if (NULL == kfnm || NULL == arg) - return (EINVAL); - msg.magic = KF_MSG_PKT_MAGIC; - msg.msg_cb = msg_cb; - msg.arg = arg; - msg.chk_sum = (((size_t)msg.msg_cb) ^ ((size_t)msg.arg)); - if (sizeof(msg) == write(kfnm->pfd[1], &msg, sizeof(msg))) - return (0); - return (errno); -} - - -static void -kq_fnm_free_cb(void *arg) { - kq_fnm_p kfnm = arg; - - if (NULL == kfnm) - return; - close(kfnm->fd); - kfnm->fd = -1; -} -void -kq_fnm_free(kq_fnm_p kfnm) { - GHashTableIter iter; - gpointer key; - kq_fnmo_p fnmo; - kq_fnme_p fnme, fnme_temp; - - if (NULL == kfnm) - return; - kq_fnm_delay_call(kfnm, kq_fnm_free_cb, kfnm); - pthread_join(kfnm->tid, NULL); - /* Free all in delay calls queue. */ - close(kfnm->pfd[1]); - kq_fnm_delay_call_process(kfnm, kq_fnme_free_cb); - close(kfnm->pfd[0]); - /* Remove all objects. */ - g_hash_table_iter_init(&iter, kfnm->fnmo_cache); - while (g_hash_table_iter_next(&iter, &key, (gpointer)&fnmo)) { - TAILQ_FOREACH_SAFE(fnme, &fnmo->entry_head, next, fnme_temp) { - TAILQ_REMOVE(&fnmo->entry_head, fnme, next); - fnme->fnmo = NULL; /* Do not free fnmo here. */ - kq_fnme_free(fnme); - } - g_hash_table_iter_remove(&iter); /* Remove from cache here. */ - /* Prevent remove from cache or g_hash_table_iter_next() will fail. */ - fnmo->is_cached = 0; - kq_fnmo_free(fnmo); - } - g_hash_table_destroy(kfnm->fnmo_cache); - - free(kfnm->mounts); - free(kfnm); -} - -kq_fnm_p -kq_fnm_create(kq_file_mon_settings_p s, kfnm_event_handler_cb cb_func) { - kq_fnm_p kfnm; - struct kevent kev; - - if (NULL == s || NULL == cb_func) - return (NULL); - kfnm = zalloc(sizeof(kq_fnm_t)); - if (NULL == kfnm) - return (NULL); - kfnm->fd = kqueue(); - if (-1 == kfnm->fd) - goto err_out; - if (-1 == pipe2(kfnm->pfd, O_NONBLOCK)) - goto err_out; - kfnm->fnmo_cache = g_hash_table_new(g_str_hash, g_str_equal); - kfnm->cb_func = cb_func; - memcpy(&kfnm->s, s, sizeof(kq_file_mon_settings_t)); - if (kfnm->s.rate_limit_time_init >= kfnm->s.rate_limit_time_max) { - kfnm->s.rate_limit_time_max = kfnm->s.rate_limit_time_init; - } - if (0 == kfnm->s.rate_limit_time_mul) { - kfnm->s.rate_limit_time_mul ++; - } - kfnm->rate_lim_time_init = MSTOSBT(kfnm->s.rate_limit_time_init); - - EV_SET(&kev, kfnm->pfd[0], EVFILT_READ, EV_ADD, 0, 0, NULL); - if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, NULL) || - 0 != (EV_ERROR & kev.flags)) - goto err_out; - EV_SET(&kev, 0, EVFILT_FS, EV_ADD, 0, 0, NULL); - if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, NULL) || - 0 != (EV_ERROR & kev.flags)) - goto err_out; - /* Get initial mounts points. */ - kfnm->mounts_count = (size_t)getmntinfo_ex(&kfnm->mounts, MNT_WAIT); - - if (0 != pthread_create(&kfnm->tid, NULL, - kq_fnm_proccess_events_proc, kfnm)) - goto err_out; - - return (kfnm); - -err_out: - kq_fnm_free(kfnm); - return (NULL); -} - -kq_fnme_p -kq_fnm_add(kq_fnm_p kfnm, const char *path, void *udata) { - int error; - kq_fnme_p fnme; - - if (NULL == kfnm || NULL == path) - return (NULL); - fnme = zalloc(sizeof(kq_fnme_t)); - if (NULL == fnme) - return (NULL); - fnme->fnmo = kq_fnmo_alloc(kfnm, path, fnme); - if (NULL == fnme->fnmo) - goto err_out; - fnme->udata = udata; - fnme->enabled = 1; - /* Shedule delay call to init. */ - error = kq_fnm_delay_call(kfnm, kq_fnme_init_cb, fnme); - if (0 != error) { /* Error, do no directly init to avoid freezes. */ - kq_fnmo_free(fnme->fnmo); -err_out: - fnme->fnmo = NULL; - kq_fnme_free(fnme); - return (NULL); - } - return (fnme); -} - -void -kq_fnm_del(kq_fnm_p kfnm, kq_fnme_p fnme) { - int error; - - if (NULL == kfnm || NULL == fnme) - return; - /* Cancel notifications. */ - /* XXX: lock here? */ - fnme->enabled = 0; - /* Shedule delay call to free. */ - error = kq_fnm_delay_call(kfnm, kq_fnme_free_cb, fnme); - if (0 == error) - return; - /* Error, free directly. */ - kq_fnme_free(fnme); -} - - -static void -kq_fnm_handle_mnt_point_change(kq_fnm_p kfnm, const char *mntonname) { - int error; - GHashTableIter iter; - gpointer key; - kq_fnmo_p fnmo; - size_t mntonname_size; - - if (NULL == kfnm || NULL == mntonname || 0 == mntonname[0]) - return; - mntonname_size = strnlen(mntonname, MNAMELEN); - /* Remove all objects. */ - g_hash_table_iter_init(&iter, kfnm->fnmo_cache); - while (g_hash_table_iter_next(&iter, &key, (gpointer)&fnmo)) { - if (NULL == fnmo || -1 == fnmo->fd) /* Do not reopen deleted. */ - continue; - if (mntonname_size > fnmo->path_size) - continue; - if (0 != memcmp(fnmo->path, mntonname, mntonname_size)) - continue; - /* Try to reopen mount point after unmount. */ - error = kq_fnmo_reopen_fd(fnmo); - if (0 != error) { - kq_handle_notify_removed(fnmo); - continue; - } - /* Reread dir and notify about deleted and new files. */ - kq_handle_changes(fnmo); - } -} -static void -kq_handle_mount_changes(kq_fnm_p kfnm) { - size_t i, mounts_count; - struct statfs *mounts; - - if (NULL == kfnm) - return; - - /* Save prev. */ - mounts = kfnm->mounts; - mounts_count = kfnm->mounts_count; - kfnm->mounts = NULL; - kfnm->mounts_count = (size_t)getmntinfo_ex(&kfnm->mounts, MNT_WAIT); - - /* Removed mounts. */ - for (i = 0; i < mounts_count; i ++) { - if (0 != mounts_find_name(kfnm->mounts, kfnm->mounts_count, - mounts[i].f_mntonname)) - continue; - kq_fnm_handle_mnt_point_change(kfnm, mounts[i].f_mntonname); - } - /* New mounts. */ - for (i = 0; i < kfnm->mounts_count; i ++) { - if (0 != mounts_find_name(mounts, mounts_count, - kfnm->mounts[i].f_mntonname)) - continue; - kq_fnm_handle_mnt_point_change(kfnm, kfnm->mounts[i].f_mntonname); - } - - /* Free old mounts. */ - free(mounts); -} - - -static void -kq_fnm_proccess_event(kq_fnm_p kfnm, struct kevent *kev) { - int error; - kq_fnmo_p fnmo; - file_info_p fi; - size_t i; - int is_rate_lim_checked = 0; - struct stat sb; - - if (NULL == kfnm || NULL == kev) - return; - - /* Handle delay calls. */ - if (kev->ident == (uintptr_t)kfnm->pfd[0]) { - if (EVFILT_READ == kev->filter) { - kq_fnm_delay_call_process(kfnm, NULL); - } - return; - } - - /* Handle mount/unmount events. */ - if (EVFILT_FS == kev->filter) { - kq_handle_mount_changes(kfnm); - return; - } - - if (0 == kev->udata) - return; /* No associated data, skip. */ - fnmo = (kq_fnmo_p)kev->udata; - - /* FS delayed notifications. */ - if (EVFILT_TIMER == kev->filter) { - if (0 == fnmo->rate_lim_ev_cnt) { - /* No delayed events, disable rate limit polling. */ - kq_fnmo_rate_lim_stop(fnmo); - return; - } - fnmo->rate_lim_ev_cnt = 0; /* Reset counter. */ - kq_fnmo_rate_lim_shedule_next(fnmo); - kq_handle_changes(fnmo); - return; - } - - /* FS notifications. */ - if (EVFILT_VNODE != kev->filter) - return; /* Unknown event, skip. */ - /* Subdir/file */ - if (kev->ident != (uintptr_t)fnmo->fd) { - /* Is files changes rate limited? */ - if (1 == kq_fnmo_rate_lim_check(fnmo)) - return; - is_rate_lim_checked ++; - /* Try to find file and check it, without call kq_handle_changes(). */ - fi = NULL; - for (i = 0; i < fnmo->files_count; i ++) { - if (kev->ident != (uintptr_t)fnmo->files[i].fd) - continue; - fi = &fnmo->files[i]; - break; - } - if (NULL != fi) { - /* Get file attrs. */ - if (0 != fstat(fi->fd, &sb)) { - memset(&sb, 0x00, sizeof(struct stat)); - } - /* Is modified? */ - if (0 != memcmp(&fi->sb, &sb, sizeof(struct stat))) { - memcpy(&fi->sb, &sb, sizeof(struct stat)); - kq_fnmo_cb_func_call(fnmo, KF_EVENT_CHANGED, - fnmo->path, fi->de.d_name, NULL); - return; - } - } - /* fd not found or changes not found, rescan dir. */ - kev->fflags = NOTE_WRITE; - } - /* Monitored object. */ - /* All flags from EVFILT_VNODE_FLAGS_ALL must be handled here. */ - if (EV_ERROR & kev->flags) { - kev->fflags |= NOTE_REVOKE; /* Treat error as unmount. */ - } - if ((NOTE_DELETE | NOTE_REVOKE) & kev->fflags) { - /* No ratelimit here. */ - if (0 != fnmo->is_dir) { - /* Try to reopen mount point after unmount. */ - error = kq_fnmo_reopen_fd(fnmo); - if (0 == error) { - /* This will reread dir and notify about - * deleted and new files. */ - kq_handle_changes(fnmo); - } - } else { - error = -1; - } - if (0 != error) { - kq_handle_notify_removed(fnmo); - } - return; /* All events processed. */ - } - if (NOTE_RENAME & kev->fflags) { - error = kq_handle_rename(fnmo); - if (0 != error) - return; - } - if ((NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_CLOSE_WRITE) & kev->fflags) { - if (0 == is_rate_lim_checked && - 1 != kq_fnmo_rate_lim_check(fnmo)) { - kq_handle_changes(fnmo); - } - } -} - -void * -kq_fnm_proccess_events_proc(void *data) { - struct kevent kev; - kq_fnm_p kfnm = data; - - if (NULL == kfnm) - return (NULL); - /* Get and proccess events, no wait. */ - while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, NULL)) { - kq_fnm_proccess_event(kfnm, &kev); - } - return (NULL); -} diff --git a/devel/glib20/files/kqueue_fnm.h b/devel/glib20/files/kqueue_fnm.h deleted file mode 100644 index b97aa37a9159..000000000000 --- a/devel/glib20/files/kqueue_fnm.h +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * Copyright (c) 2016 - 2019 Rozhuk Ivan <rozhuk.im@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Author: Rozhuk Ivan <rozhuk.im@gmail.com> - * - */ - -#ifndef __KQUEUE_FILE_NOTIFY_MONITOR_H__ -#define __KQUEUE_FILE_NOTIFY_MONITOR_H__ - -#include <sys/param.h> -#include <sys/types.h> -#include <inttypes.h> - - -typedef struct kq_file_nonify_monitor_s *kq_fnm_p; -typedef struct kq_file_nonify_monitor_entry_s *kq_fnme_p; - -typedef void (*kfnm_event_handler_cb)(kq_fnm_p kfnm, - kq_fnme_p fnme, void *udata, - uint32_t event, - const char *base, - const char *filename, - const char *new_filename); -#define KF_EVENT_NOT_CHANGED 0 /* Internal use. */ -#define KF_EVENT_CREATED 1 -#define KF_EVENT_DELETED 2 -#define KF_EVENT_RENAMED 3 -#define KF_EVENT_CHANGED 4 - - -typedef struct kq_file_nonify_mon_settings_s { - uint32_t rate_limit_time_init; /* Fire events for dir min interval, mseconds. */ - uint32_t rate_limit_time_max; /* Fire events for dir max interval, mseconds. */ - uint32_t rate_limit_time_mul; /* Fire events time increment, mseconds. */ - size_t max_dir_files; /* If dir contain more than n files - do not mon files changes. */ - int mon_local_subfiles; /* Enable monitoring files changes on local file systems. */ - int mon_local_subdirs; /* Also mon for subdirs changes . */ - const char **local_fs; /* NULL terminated fs names list that threat as local. Keep utill kq_fnm_free() return. */ - const char **non_local_fs; /* NULL terminated fs names list that threat as not local. Keep utill kq_fnm_free() return. */ -} kq_file_mon_settings_t, *kq_file_mon_settings_p; - -kq_fnm_p kq_fnm_create(kq_file_mon_settings_p s, - kfnm_event_handler_cb cb_func); -void kq_fnm_free(kq_fnm_p kfnm); - -kq_fnme_p kq_fnm_add(kq_fnm_p kfnm, - const char *path, void *udata); -void kq_fnm_del(kq_fnm_p kfnm, kq_fnme_p fnme); - - -#endif /* __KQUEUE_FILE_NOTIFY_MONITOR_H__ */ |
