Skip to content

Commit

Permalink
Merge pull request #517 from bshifter/fx-cache-file-json
Browse files Browse the repository at this point in the history
filterx: cache_json_file() inotify based automatic reload
  • Loading branch information
MrAnno authored Feb 27, 2025
2 parents 6211e5d + 5d334d0 commit ae65a96
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 109 deletions.
4 changes: 2 additions & 2 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ set (LIB_HEADERS
cfg-tree.h
cfg-walker.h
cfg-persist.h
cfg-monitor.h
children.h
crypto.h
dnscache.h
driver.h
dynamic-window-pool.h
dynamic-window.h
fdhelpers.h
file-monitor.h
file-perms.h
find-crlf.h
generic-number.h
Expand Down Expand Up @@ -197,13 +197,13 @@ set(LIB_SOURCES
cfg-tree.c
cfg-walker.c
cfg-persist.c
cfg-monitor.c
children.c
dnscache.c
driver.c
dynamic-window.c
dynamic-window-pool.c
fdhelpers.c
file-monitor.c
file-perms.c
find-crlf.c
globals.c
Expand Down
4 changes: 2 additions & 2 deletions lib/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,14 @@ pkginclude_HEADERS += \
lib/cfg-tree.h \
lib/cfg-walker.h \
lib/cfg-persist.h \
lib/cfg-monitor.h \
lib/children.h \
lib/crypto.h \
lib/dnscache.h \
lib/driver.h \
lib/dynamic-window-pool.h \
lib/dynamic-window.h \
lib/fdhelpers.h \
lib/file-monitor.h \
lib/file-perms.h \
lib/find-crlf.h \
lib/generic-number.h \
Expand Down Expand Up @@ -212,13 +212,13 @@ lib_libsyslog_ng_la_SOURCES = \
lib/cfg-tree.c \
lib/cfg-walker.c \
lib/cfg-persist.c \
lib/cfg-monitor.c \
lib/children.c \
lib/dnscache.c \
lib/driver.c \
lib/dynamic-window.c \
lib/dynamic-window-pool.c \
lib/fdhelpers.c \
lib/file-monitor.c \
lib/file-perms.c \
lib/find-crlf.c \
lib/generic-number.c \
Expand Down
126 changes: 68 additions & 58 deletions lib/cfg-monitor.c → lib/file-monitor.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2024 Axoflow
* Copyright (c) 2023 László Várady
* Copyright (c) 2024 shifter
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand All @@ -21,9 +23,8 @@
*
*/

#include "cfg-monitor.h"
#include "file-monitor.h"
#include "messages.h"
#include "resolved-configurable-paths.h"
#include "timeutils/misc.h"

#include <iv.h>
Expand All @@ -32,112 +33,120 @@
#include <iv_inotify.h>
#endif

#define CFG_MONITOR_POLL_FREQ (15 * 60)
#define FILE_MONITOR_POLL_FREQ (15 * 60)

typedef struct _CfgMonitorCallbackListItem
typedef struct _FileMonitorCallbackListItem
{
CfgMonitorEventCB cb;
FileMonitorEventCB cb;
gpointer cb_data;
} CfgMonitorCallbackListItem;
} FileMonitorCallbackListItem;

struct _CfgMonitor
typedef GList FileMonitorCallbackList;

struct _FileMonitor
{
GList *callbacks;
FileMonitorCallbackList *callbacks;

const gchar *file_name;
struct iv_timer poll_timer;
time_t last_mtime;

#if SYSLOG_NG_HAVE_INOTIFY
gboolean in_started;
const gchar *cfgfile_basename;
struct iv_inotify inotify;
struct iv_inotify_watch in_watch;
#endif
};


void
cfg_monitor_add_watch(CfgMonitor *self, CfgMonitorEventCB cb, gpointer cb_data)
file_monitor_add_watch(FileMonitor *self, FileMonitorEventCB cb, gpointer cb_data)
{
if (!cb)
return;

CfgMonitorCallbackListItem *item = g_new(CfgMonitorCallbackListItem, 1);
FileMonitorCallbackListItem *item = g_new(FileMonitorCallbackListItem, 1);
item->cb = cb;
item->cb_data = cb_data;

self->callbacks = g_list_prepend(self->callbacks, item);

}

static gint
_compare_callback(gconstpointer a, gconstpointer b)
{
const CfgMonitorCallbackListItem *item_a = (const CfgMonitorCallbackListItem *) a;
const CfgMonitorCallbackListItem *item_b = (const CfgMonitorCallbackListItem *) b;
const FileMonitorCallbackListItem *item_a = (const FileMonitorCallbackListItem *) a;
const FileMonitorCallbackListItem *item_b = (const FileMonitorCallbackListItem *) b;

gboolean equal = item_a->cb == item_b->cb && item_a->cb_data == item_b->cb_data;
return equal ? 0 : -1;
}

void
cfg_monitor_remove_watch(CfgMonitor *self, CfgMonitorEventCB cb, gpointer cb_data)
file_monitor_remove_watch(FileMonitor *self, FileMonitorEventCB cb, gpointer cb_data)
{
CfgMonitorCallbackListItem item = { cb, cb_data };
FileMonitorCallbackListItem item = { cb, cb_data };
GList *i = g_list_find_custom(self->callbacks, &item, _compare_callback);

if (!i)
return;

CfgMonitorCallbackListItem *removed_item = i->data;
FileMonitorCallbackListItem *removed_item = i->data;
self->callbacks = g_list_delete_link(self->callbacks, i);
g_free(removed_item);
}

static void
_run_callbacks(CfgMonitor *self, const CfgMonitorEvent *event)
_run_callbacks(FileMonitor *self, const FileMonitorEvent *event)
{
for (GList *i = self->callbacks; i; i = i->next)
for (FileMonitorCallbackList *i = self->callbacks; i; i = i->next)
{
CfgMonitorCallbackListItem *item = i->data;
FileMonitorCallbackListItem *item = i->data;
item->cb(event, item->cb_data);
}
}

static void
_run_callbacks_if_main_config_was_modified(CfgMonitor *self)
_run_callbacks_if_file_was_modified(FileMonitor *self)
{
const gchar *main_cfg_file = resolved_configurable_paths.cfgfilename;
struct stat st = {0};
stat(main_cfg_file, &st);

if (self->last_mtime >= st.st_mtime)
return;

CfgMonitorEvent event =
FileMonitorEvent event =
{
.name = main_cfg_file,
.name = self->file_name,
.event = MODIFIED,
.st = st,
};

if (stat(self->file_name, &st) == -1)
{
event.event = DELETED;
goto callbacks;
}

event.st = st;

if (self->last_mtime >= st.st_mtime)
return;

callbacks:
_run_callbacks(self, &event);
self->last_mtime = st.st_mtime;
}

static void
_poll_start(CfgMonitor *self)
_poll_start(FileMonitor *self)
{
if (iv_timer_registered(&self->poll_timer))
return;

iv_validate_now();
self->poll_timer.expires = iv_now;
timespec_add_msec(&self->poll_timer.expires, CFG_MONITOR_POLL_FREQ * 1000);
timespec_add_msec(&self->poll_timer.expires, FILE_MONITOR_POLL_FREQ * 1000);
iv_timer_register(&self->poll_timer);
}

static void
_poll_stop(CfgMonitor *self)
_poll_stop(FileMonitor *self)
{
if (iv_timer_registered(&self->poll_timer))
iv_timer_unregister(&self->poll_timer);
Expand All @@ -146,9 +155,9 @@ _poll_stop(CfgMonitor *self)
static void
_poll_timer_tick(gpointer c)
{
CfgMonitor *self = (CfgMonitor *) c;
FileMonitor *self = (FileMonitor *) c;

_run_callbacks_if_main_config_was_modified(self);
_run_callbacks_if_file_was_modified(self);

_poll_start(self);
}
Expand All @@ -157,62 +166,57 @@ _poll_timer_tick(gpointer c)

static void _inotify_event_handler(void *c, struct inotify_event *event)
{
CfgMonitor *self = (CfgMonitor *) c;
FileMonitor *self = (FileMonitor *) c;

if (g_strcmp0(self->cfgfile_basename, event->name) == 0)
_run_callbacks_if_main_config_was_modified(self);
if (g_strcmp0(self->file_name, event->name) == 0)
_run_callbacks_if_file_was_modified(self);
}

/* moving/recreating the config directory itself is not implemented */
static gboolean
_inotify_start(CfgMonitor *self)
_inotify_start(FileMonitor *self)
{
if (self->in_started)
return FALSE;

IV_INOTIFY_INIT(&self->inotify);
if (iv_inotify_register(&self->inotify) != 0)
{
msg_warning("Error creating configuration monitor instance, can not register inotify", evt_tag_error("errno"));
msg_warning("Error creating file monitor instance, can not register inotify", evt_tag_error("errno"));
return FALSE;
}

gchar *config_dir = g_path_get_dirname(resolved_configurable_paths.cfgfilename);
gchar *file_dir = g_path_get_dirname(self->file_name);

IV_INOTIFY_WATCH_INIT(&self->in_watch);
self->in_watch.inotify = &self->inotify;
self->in_watch.pathname = config_dir;
self->in_watch.mask = IN_CREATE | IN_MOVED_TO | IN_CLOSE_WRITE;
self->in_watch.pathname = file_dir;
self->in_watch.mask = IN_CREATE | IN_MOVE | IN_CLOSE_WRITE | IN_DELETE;
self->in_watch.handler = _inotify_event_handler;
self->in_watch.cookie = self;

if (iv_inotify_watch_register(&self->in_watch) != 0)
{
iv_inotify_unregister(&self->inotify);
g_free(config_dir);
msg_warning("Error start configuration monitor, can not register inotify watch", evt_tag_error("errno"));
g_free(file_dir);
msg_warning("Error start file monitor, can not register inotify watch", evt_tag_error("errno"));
return FALSE;
}

g_free(config_dir);

self->cfgfile_basename = g_path_get_basename(resolved_configurable_paths.cfgfilename);
g_free(file_dir);

self->in_started = TRUE;
return TRUE;
}

static void
_inotify_stop(CfgMonitor *self)
_inotify_stop(FileMonitor *self)
{
if (!self->in_started)
return;

iv_inotify_watch_unregister(&self->in_watch);
iv_inotify_unregister(&self->inotify);

g_free((gchar *) self->cfgfile_basename);

self->in_started = FALSE;
}

Expand All @@ -221,35 +225,41 @@ _inotify_stop(CfgMonitor *self)
#define _inotify_stop(self)
#endif

void cfg_monitor_start(CfgMonitor *self)
void file_monitor_start(FileMonitor *self)
{
if (!_inotify_start(self))
_poll_start(self);
}

_run_callbacks_if_main_config_was_modified(self);
void file_monitor_start_and_check(FileMonitor *self)
{
file_monitor_start(self);
_run_callbacks_if_file_was_modified(self);
}

void cfg_monitor_stop(CfgMonitor *self)
void file_monitor_stop(FileMonitor *self)
{
_inotify_stop(self);
_poll_stop(self);
}

void
cfg_monitor_free(CfgMonitor *self)
file_monitor_free(FileMonitor *self)
{
g_list_free_full(self->callbacks, g_free);
g_free((gchar *)self->file_name);
g_free(self);
}

CfgMonitor *
cfg_monitor_new(void)
FileMonitor *
file_monitor_new(const gchar *file_name)
{
CfgMonitor *self = g_new0(CfgMonitor, 1);
FileMonitor *self = g_new0(FileMonitor, 1);

IV_TIMER_INIT(&self->poll_timer);
self->poll_timer.handler = _poll_timer_tick;
self->poll_timer.cookie = self;
self->file_name = g_strdup(file_name);

return self;
}
Loading

0 comments on commit ae65a96

Please sign in to comment.