[patch] new feature for file destination: persistent symlinks
| Newsgroups | gmane.comp.syslog-ng |
|---|---|
| Message-ID | <[email protected]> |
Hi all,
I'm new here. I love syslog-ng: it's well written and documented and
each time I need some new feature, I dig up the documentation and I
find it's already implemented!
Like many others, I use macros in file destinations (say,
"/var/log/cron.${YEAR}${MONTH}"), but I also want to maintain a
persistent symlink to the current logfile (that would be
something like "/var/log/cron") in order to have "easier" access if I'm
grepping for something manually.
I couldn't find this feature in syslog-ng, so I wrote the code myself;
in the configuration file, it looks like this:
destination d_file_cron {
file("/var/log/cron.${YEAR}${MONTH}" symlink_as("/var/log/cron"));
};
It works for me on my Slackware boxes. I'm sharing the patch I put
together, hoping that it may be useful to others and perhaps even
considered for inclusion in mainstream.
From a functional perspective, the "symlink_as" file inherits both
"create-dirs" and file ownership from its file destination (permissions
are not applicable to symlinks, at least on linux).
Patch applies cleanly to latest production release (3.33). I tried to
keep the original coding style for easier review (admittedly, the code
could have gone into the function that opens the file destination, but
I didn't want to change too much).
Thanks for the good work and happy logging!
Andrea.
______________________________________________________________________________
Member info: https://lists.balabit.hu/mailman/listinfo/syslog-ng
Documentation: http://www.balabit.com/support/documentation/?product=syslog-ng
FAQ: http://www.balabit.com/wiki/syslog-ng-faq
filedest-symlink-as.diff
(text/x-patch, 8.3 KB)
--- a/lib/file-perms.c 2021-07-06 17:55:58.000000000 +0100
+++ b/lib/file-perms.c 2021-12-04 12:22:42.145929557 +0000
@@ -195,6 +195,20 @@
}
gboolean
+file_perm_options_apply_symlink(const FilePermOptions *self, const gchar *path)
+{
+#ifndef _MSC_VER
+ gboolean result = TRUE;
+
+ if (self->file_uid >= 0 && lchown(path, (uid_t) self->file_uid, -1) < 0)
+ result = FALSE;
+ if (self->file_gid >= 0 && lchown(path, -1, (gid_t) self->file_gid) < 0)
+ result = FALSE;
+ return result;
+#endif
+}
+
+gboolean
file_perm_options_apply_dir(const FilePermOptions *self, const gchar *path)
{
#ifndef _MSC_VER
--- a/lib/file-perms.h 2021-07-06 17:55:58.000000000 +0100
+++ b/lib/file-perms.h 2021-12-04 12:18:38.983916571 +0000
@@ -56,6 +56,7 @@
void file_perm_options_inherit_dont_change(FilePermOptions *self);
gboolean file_perm_options_apply_file(const FilePermOptions *self, const gchar *name);
+gboolean file_perm_options_apply_symlink(const FilePermOptions *self, const gchar *name);
gboolean file_perm_options_apply_dir(const FilePermOptions *self, const gchar *name);
gboolean file_perm_options_apply_fd(const FilePermOptions *self, gint fd);
gboolean file_perm_options_create_containing_directory(const FilePermOptions *self, const gchar *name);
--- a/modules/affile/affile-dest.c 2021-07-06 17:55:58.000000000 +0100
+++ b/modules/affile/affile-dest.c 2021-12-04 13:07:38.017073532 +0000
@@ -140,7 +140,8 @@
msg_verbose("Initializing destination file writer",
evt_tag_str("template", self->owner->filename_template->template),
- evt_tag_str("filename", self->filename));
+ evt_tag_str("filename", self->filename),
+ evt_tag_str("symlink_as", self->owner->symlink_as));
self->last_open_stamp = self->last_msg_stamp;
if (self->owner->overwrite_if_older > 0 &&
@@ -156,6 +157,9 @@
FileOpenerResult open_result = file_opener_open_fd(self->owner->file_opener, self->filename, AFFILE_DIR_WRITE, &fd);
if (open_result == FILE_OPENER_RESULT_SUCCESS)
{
+ if (self->owner->symlink_as != NULL)
+ file_opener_symlink(self->owner->file_opener, self->owner->symlink_as, self->filename);
+
LogTransport *transport = file_opener_construct_transport(self->owner->file_opener, fd);
proto = file_opener_construct_dst_proto(self->owner->file_opener, transport,
@@ -386,6 +390,15 @@
}
void
+affile_dd_set_symlink_as(LogDriver *s, const gchar *symlink_as)
+{
+ AFFileDestDriver *self = (AFFileDestDriver *) s;
+
+ g_free(self->symlink_as);
+ self->symlink_as = g_strdup(symlink_as);
+}
+
+void
affile_dd_set_fsync(LogDriver *s, gboolean use_fsync)
{
AFFileDestDriver *self = (AFFileDestDriver *) s;
@@ -756,6 +769,7 @@
log_writer_options_destroy(&self->writer_options);
file_opener_options_deinit(&self->file_opener_options);
file_opener_free(self->file_opener);
+ g_free(self->symlink_as);
log_dest_driver_free(s);
}
@@ -782,6 +796,8 @@
}
file_opener_options_defaults(&self->file_opener_options);
+ self->symlink_as = NULL;
+
affile_dd_set_time_reap(&self->super.super, self->filename_is_a_template ? -1 : 0);
g_static_mutex_init(&self->lock);
--- a/modules/affile/affile-dest.h 2021-07-06 17:55:58.000000000 +0100
+++ b/modules/affile/affile-dest.h 2021-12-03 19:11:40.931625812 +0000
@@ -47,6 +47,7 @@
GHashTable *writer_hash;
gint overwrite_if_older;
+ gchar* symlink_as;
gboolean use_time_recvd;
} AFFileDestDriver;
@@ -56,6 +57,7 @@
void affile_dd_set_create_dirs(LogDriver *s, gboolean create_dirs);
void affile_dd_set_fsync(LogDriver *s, gboolean enable);
void affile_dd_set_overwrite_if_older(LogDriver *s, gint overwrite_if_older);
+void affile_dd_set_symlink_as(LogDriver *s, const gchar *symlink_as);
void affile_dd_set_local_time_zone(LogDriver *s, const gchar *local_time_zone);
void affile_dd_set_time_reap(LogDriver *s, gint time_reap);
void affile_dd_global_init(void);
--- a/modules/affile/affile-grammar.y 2021-07-06 17:57:35.000000000 +0100
+++ b/modules/affile/affile-grammar.y 2021-12-03 19:11:40.930625812 +0000
@@ -430,6 +430,7 @@
%token KW_FSYNC
%token KW_FOLLOW_FREQ
%token KW_OVERWRITE_IF_OLDER
+%token KW_SYMLINK_AS
%token KW_MULTI_LINE_MODE
%token KW_MULTI_LINE_PREFIX
%token KW_MULTI_LINE_GARBAGE
@@ -637,6 +638,7 @@
| file_perm_option
| KW_OPTIONAL '(' yesno ')' { last_driver->optional = $3; }
| KW_OVERWRITE_IF_OLDER '(' nonnegative_integer ')' { affile_dd_set_overwrite_if_older(last_driver, $3); }
+ | KW_SYMLINK_AS '(' string ')' { affile_dd_set_symlink_as(last_driver, $3); }
| KW_FSYNC '(' yesno ')' { affile_dd_set_fsync(last_driver, $3); }
| dest_affile_common_option
;
--- a/modules/affile/affile-parser.c 2021-07-06 17:55:58.000000000 +0100
+++ b/modules/affile/affile-parser.c 2021-12-03 19:11:40.931625812 +0000
@@ -47,6 +47,7 @@
{ "fsync", KW_FSYNC },
{ "remove_if_older", KW_OVERWRITE_IF_OLDER, KWS_OBSOLETE, "overwrite_if_older" },
{ "overwrite_if_older", KW_OVERWRITE_IF_OLDER },
+ { "symlink_as", KW_SYMLINK_AS },
{ "follow_freq", KW_FOLLOW_FREQ },
{ "multi_line_mode", KW_MULTI_LINE_MODE },
{ "multi_line_prefix", KW_MULTI_LINE_PREFIX },
--- a/modules/affile/file-opener.c 2021-07-06 17:55:58.000000000 +0100
+++ b/modules/affile/file-opener.c 2021-12-04 13:08:20.879075821 +0000
@@ -183,6 +183,86 @@
}
void
+file_opener_symlink(FileOpener *self, const gchar *name, const gchar *target)
+{
+ cap_t saved_caps;
+ gboolean do_symlink = FALSE;
+
+ msg_trace("file_opener_symlink",
+ evt_tag_str("filename", name),
+ evt_tag_str("target", target));
+
+ /* do not proceed if name already exists and is not a symlink; also, do not
+ * disturb the symlink unnecessarily (avoids annoying a running "tail -f",
+ * etc.) */
+
+ size_t n = strlen(target);
+ if (n > SSIZE_MAX - 1) return; /* avoid overflowing ssize_t */
+ gchar *s = malloc(n + 1);
+ if (s == NULL) return;
+ ssize_t i = readlink(name, s, n + 1);
+
+ if (i == -1)
+ {
+ if (errno == ENOENT)
+ /* symlink doesn't exist: create it */
+ do_symlink = TRUE;
+ else
+ /* not a symlink: leave it alone */
+ msg_error("Error reading symlink",
+ evt_tag_str("filename", name),
+ evt_tag_errno(EVT_TAG_OSERROR, errno));
+ }
+ else
+ {
+ /* readlink() does not append a null terminator, so if i == n + 1,
+ * truncation may have occurred; in any case i != n implies s != target,
+ * which is all we're interested in */
+ if ((i != n) || (strncmp(s, target, n) != 0))
+ {
+ /* symlink to something else: replace it */
+ if (unlink(name) == -1)
+ msg_error("Error removing symlink",
+ evt_tag_str("filename", name),
+ evt_tag_errno(EVT_TAG_OSERROR, errno));
+ else
+ do_symlink = TRUE;
+ }
+ }
+ free(s);
+
+ if (!do_symlink) return;
+
+ saved_caps = g_process_cap_save();
+
+ if (!_obtain_capabilities(self, name, &saved_caps))
+ {
+ g_process_cap_restore(saved_caps);
+ return;
+ }
+
+ g_process_enable_cap("cap_chown");
+
+ msg_info("Creating symlink",
+ evt_tag_str("filename", name),
+ evt_tag_str("target", target));
+
+ i = symlink(target, name);
+ if (i == -1)
+ msg_error("Error creating symlink",
+ evt_tag_str("filename", name),
+ evt_tag_str("target", target),
+ evt_tag_errno(EVT_TAG_OSERROR, errno));
+ else if (!file_perm_options_apply_symlink(&self->options->file_perm_options, name))
+ msg_error("Error setting symlink ownership",
+ evt_tag_str("filename", name),
+ evt_tag_errno(EVT_TAG_OSERROR, errno));
+
+ g_process_cap_restore(saved_caps);
+
+}
+
+void
file_opener_set_options(FileOpener *self, FileOpenerOptions *options)
{
self->options = options;
--- a/modules/affile/file-opener.h 2021-07-06 17:55:58.000000000 +0100
+++ b/modules/affile/file-opener.h 2021-12-04 12:10:56.942891895 +0000
@@ -87,6 +87,8 @@
FileOpenerResult file_opener_open_fd(FileOpener *self, const gchar *name, FileDirection dir, gint *fd);
+void file_opener_symlink(FileOpener *self, const gchar *name, const gchar *target);
+
void file_opener_set_options(FileOpener *self, FileOpenerOptions *options);
void file_opener_init_instance(FileOpener *self);
FileOpener *file_opener_new(void);