From 5c313bcef86464193e3a39e5fba42e02a319b8c6 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sun, 6 Oct 2024 10:30:55 +0200 Subject: [PATCH 01/10] scratch-buffers: do not use ivykis in non-ivykis threads scratch_buffers_lazy_update_stats() uses the ivykis time state to check if it is time to update the stats about scratch buffers. Do not do that if ivykis is not initialized. This may happen in control threads and the debugger threads that do use scratch buffers but don't use ivykis. Signed-off-by: Balazs Scheidler --- lib/scratch-buffers.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/scratch-buffers.c b/lib/scratch-buffers.c index 494d2a73ce..6c5b6fa520 100644 --- a/lib/scratch-buffers.c +++ b/lib/scratch-buffers.c @@ -245,10 +245,17 @@ _thread_maintenance_update_time(void) void scratch_buffers_lazy_update_stats(void) { - if (_thread_maintenance_period_elapsed()) + if (iv_inited()) + { + if (_thread_maintenance_period_elapsed()) + { + scratch_buffers_update_stats(); + _thread_maintenance_update_time(); + } + } + else { scratch_buffers_update_stats(); - _thread_maintenance_update_time(); } } From 3f9231722c156e72ef1aee453e9b67ce5c6a61dc Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sat, 5 Oct 2024 17:21:25 +0200 Subject: [PATCH 02/10] control: make it possible to query if a worker relates to a connection A better solution would be to have a connection specific worker list, and a list of connections. But for now this suffices for my purposes of being able to cancel connection specific workers. Signed-off-by: Balazs Scheidler --- lib/control/control-command-thread.c | 6 ++++++ lib/control/control-command-thread.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lib/control/control-command-thread.c b/lib/control/control-command-thread.c index 9d5ed8b976..f66ecf6220 100644 --- a/lib/control/control-command-thread.c +++ b/lib/control/control-command-thread.c @@ -44,6 +44,12 @@ struct _ControlCommandThread struct iv_event thread_finished; }; +gboolean +control_command_thread_relates_to_connection(ControlCommandThread *self, ControlConnection *cc) +{ + return self->connection == cc; +} + static void _on_thread_finished(gpointer user_data) { diff --git a/lib/control/control-command-thread.h b/lib/control/control-command-thread.h index 609247d320..cf8787694e 100644 --- a/lib/control/control-command-thread.h +++ b/lib/control/control-command-thread.h @@ -27,6 +27,8 @@ #include "control.h" +gboolean control_command_thread_relates_to_connection(ControlCommandThread *self, ControlConnection *cc); + void control_command_thread_run(ControlCommandThread *self); void control_command_thread_cancel(ControlCommandThread *self); const gchar *control_command_thread_get_command(ControlCommandThread *self); From 48faef3bddf11a128d17442c3023f22626096b6d Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sat, 5 Oct 2024 17:19:30 +0200 Subject: [PATCH 03/10] control: cancel connection related workers Up to now, control worker threads were only cancelled at exit. Truth be told we never really detected if the peer has disconnected either. This patch implements thread cancellation whenever a connection closes, to detect the closure of the connection comes in a separate patch. Signed-off-by: Balazs Scheidler --- lib/control/control-server.c | 23 ++++++++++++++++++++--- lib/control/control-server.h | 2 +- lib/mainloop.c | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/control/control-server.c b/lib/control/control-server.c index 66a5c57867..18d1c04953 100644 --- a/lib/control/control-server.c +++ b/lib/control/control-server.c @@ -31,6 +31,16 @@ void _cancel_worker(gpointer data, gpointer user_data) { ControlCommandThread *thread = (ControlCommandThread *) data; + ControlConnection *cc = (ControlConnection *) user_data; + + if (cc && !control_command_thread_relates_to_connection(thread, cc)) + { + /* check if we relate to a specific connection and cancel only those. + * This is only used when a connection closed while the thread is + * still running. + */ + return; + } msg_warning("Requesting the cancellation of control command thread", evt_tag_str("control_command", control_command_thread_get_command(thread))); @@ -52,17 +62,23 @@ _cancel_worker(gpointer data, gpointer user_data) */ } -void -control_server_cancel_workers(ControlServer *self) +static void +control_server_cancel_workers(ControlServer *self, ControlConnection *cc) { if (self->worker_threads) { msg_debug("Cancelling control server worker threads"); - g_list_foreach(self->worker_threads, _cancel_worker, NULL); + g_list_foreach(self->worker_threads, _cancel_worker, cc); msg_debug("Control server worker threads have been cancelled"); } } +void +control_server_cancel_all_workers(ControlServer *self) +{ + control_server_cancel_workers(self, NULL); +} + void control_server_worker_started(ControlServer *self, ControlCommandThread *worker) { @@ -80,6 +96,7 @@ control_server_worker_finished(ControlServer *self, ControlCommandThread *worker void control_server_connection_closed(ControlServer *self, ControlConnection *cc) { + control_server_cancel_workers(self, cc); control_connection_stop_watches(cc); control_connection_unref(cc); } diff --git a/lib/control/control-server.h b/lib/control/control-server.h index 5c85e8c7f1..5d77c74279 100644 --- a/lib/control/control-server.h +++ b/lib/control/control-server.h @@ -38,7 +38,7 @@ struct _ControlServer void (*free_fn)(ControlServer *self); }; -void control_server_cancel_workers(ControlServer *self); +void control_server_cancel_all_workers(ControlServer *self); void control_server_connection_closed(ControlServer *self, ControlConnection *cc); void control_server_worker_started(ControlServer *self, ControlCommandThread *worker); void control_server_worker_finished(ControlServer *self, ControlCommandThread *worker); diff --git a/lib/mainloop.c b/lib/mainloop.c index 405625ff39..e126bd3395 100644 --- a/lib/mainloop.c +++ b/lib/mainloop.c @@ -463,7 +463,7 @@ main_loop_exit_initiate(gpointer user_data) if (main_loop_is_terminating(self)) return; - control_server_cancel_workers(self->control_server); + control_server_cancel_all_workers(self->control_server); app_pre_shutdown(); From f3d934bdd32dda11a63fd8d4ed906c91a3738105 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sun, 6 Oct 2024 10:28:48 +0200 Subject: [PATCH 04/10] control: call app_thread_start/stop from command threads Signed-off-by: Balazs Scheidler --- lib/control/control-command-thread.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/control/control-command-thread.c b/lib/control/control-command-thread.c index f66ecf6220..f97605f5f9 100644 --- a/lib/control/control-command-thread.c +++ b/lib/control/control-command-thread.c @@ -27,6 +27,7 @@ #include "messages.h" #include "secret-storage/secret-storage.h" #include "scratch-buffers.h" +#include "apphook.h" #include struct _ControlCommandThread @@ -70,7 +71,7 @@ _thread(gpointer user_data) ControlCommandThread *self = (ControlCommandThread *) user_data; iv_init(); - scratch_buffers_allocator_init(); + app_thread_start(); msg_debug("Control command thread has started", evt_tag_str("control_command", self->command->str)); @@ -88,8 +89,8 @@ _thread(gpointer user_data) evt_tag_str("control_command", self->command->str)); scratch_buffers_explicit_gc(); - scratch_buffers_allocator_deinit(); control_command_thread_unref(self); + app_thread_stop(); iv_deinit(); } From 784b2d320bb870113a9dd8e17758f684b83d1056 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sat, 5 Oct 2024 14:23:04 +0200 Subject: [PATCH 05/10] control: add support for passing 3 fds through the control socket This will be used to pass over the stdio file descriptors from syslog-ng-ctl so we can attach to the syslog-ng process after it was started in the background. Signed-off-by: Balazs Scheidler --- lib/control/control-connection.c | 8 ++++ lib/control/control-connection.h | 2 + lib/control/control-server-unix.c | 74 ++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/lib/control/control-connection.c b/lib/control/control-connection.c index 03819103cf..2f19dbb570 100644 --- a/lib/control/control-connection.c +++ b/lib/control/control-connection.c @@ -36,6 +36,14 @@ _g_string_destroy(gpointer user_data) g_string_free(str, TRUE); } +gboolean +control_connection_get_attached_fds(ControlConnection *self, gint *fds, gsize *num_fds) +{ + if (self->get_attached_fds) + return self->get_attached_fds(self, fds, num_fds); + return FALSE; +} + static void _control_connection_free(ControlConnection *self) { diff --git a/lib/control/control-connection.h b/lib/control/control-connection.h index 7d61fa8400..5ad25dd75d 100644 --- a/lib/control/control-connection.h +++ b/lib/control/control-connection.h @@ -41,6 +41,7 @@ struct _ControlConnection GString *output_buffer; gsize pos; ControlServer *server; + gboolean (*get_attached_fds)(ControlConnection *self, gint *fds, gsize *num_fds); gboolean (*run_command)(ControlConnection *self, ControlCommand *command_desc, GString *command_string); int (*read)(ControlConnection *self, gpointer buffer, gsize size); int (*write)(ControlConnection *self, gpointer buffer, gsize size); @@ -56,6 +57,7 @@ struct _ControlConnection }; +gboolean control_connection_get_attached_fds(ControlConnection *self, gint *fds, gsize *num_fds); gboolean control_connection_run_command(ControlConnection *self, GString *command_string); void control_connection_send_reply(ControlConnection *self, GString *reply); void control_connection_send_batched_reply(ControlConnection *self, GString *reply); diff --git a/lib/control/control-server-unix.c b/lib/control/control-server-unix.c index 726453f720..4b9db5c1d5 100644 --- a/lib/control/control-server-unix.c +++ b/lib/control/control-server-unix.c @@ -42,8 +42,21 @@ typedef struct _ControlConnectionUnix ControlConnection super; struct iv_fd control_io; gint fd; + /* stdin, stdout, stderr as passed by syslog-ng-ctl */ + gint attached_fds[3]; } ControlConnectionUnix; +static gboolean +control_connection_unix_get_attached_fds(ControlConnection *s, gint *fds, gsize *num_fds) +{ + ControlConnectionUnix *self = (ControlConnectionUnix *)s; + + g_assert(*num_fds >= 3); + memcpy(fds, self->attached_fds, sizeof(self->attached_fds)); + *num_fds = 3; + return TRUE; +} + gint control_connection_unix_write(ControlConnection *s, gpointer buffer, gsize size) { @@ -51,11 +64,58 @@ control_connection_unix_write(ControlConnection *s, gpointer buffer, gsize size) return write(self->control_io.fd, buffer, size); } +static gint +_extract_ancillary_data(ControlConnectionUnix *self, gint rc, struct msghdr *msg) +{ + if (G_UNLIKELY(msg->msg_flags & MSG_CTRUNC)) + { + msg_warning_once("WARNING: recvmsg() on control socket returned truncated control data", + evt_tag_int("control_len", msg->msg_controllen)); + return -1; + } + + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + gint header_len = CMSG_DATA(cmsg) - (unsigned char *) cmsg; + gint fd_array_size = (cmsg->cmsg_len - header_len); + + if (fd_array_size != sizeof(self->attached_fds)) + { + msg_warning_once("WARNING: invalid number of fds received on control socket", + evt_tag_int("fd_array_size", fd_array_size)); + return -1; + } + memcpy(&self->attached_fds, CMSG_DATA(cmsg), sizeof(self->attached_fds)); + break; + } + } + + return rc; +} + gint control_connection_unix_read(ControlConnection *s, gpointer buffer, gsize size) { ControlConnectionUnix *self = (ControlConnectionUnix *)s; - return read(self->control_io.fd, buffer, size); + gchar cmsg_buf[256]; + struct iovec iov[1] = + { + { .iov_base = buffer, .iov_len = size }, + }; + struct msghdr msg = + { + .msg_iov = iov, + .msg_iovlen = G_N_ELEMENTS(iov), + .msg_control = cmsg_buf, + .msg_controllen = sizeof(cmsg_buf), + }; + gint rc = recvmsg(self->control_io.fd, &msg, 0); + if (rc < 0) + return rc; + + return _extract_ancillary_data(self, rc, &msg); } static void @@ -114,6 +174,11 @@ control_connection_unix_free(ControlConnection *s) { ControlConnectionUnix *self = (ControlConnectionUnix *)s; close(self->control_io.fd); + for (gint i = 0; i < G_N_ELEMENTS(self->attached_fds); i++) + { + if (self->attached_fds[i] >= 0) + close(self->attached_fds[i]); + } } ControlConnection * @@ -129,6 +194,12 @@ control_connection_unix_new(ControlServer *server, gint sock) self->super.events.start_watches = control_connection_unix_start_watches; self->super.events.update_watches = control_connection_unix_update_watches; self->super.events.stop_watches = control_connection_unix_stop_watches; + self->super.get_attached_fds = control_connection_unix_get_attached_fds; + + for (gint i = 0; i < G_N_ELEMENTS(self->attached_fds); i++) + { + self->attached_fds[i] = -1; + } return &self->super; } @@ -152,7 +223,6 @@ _control_socket_accept(void *cookie) goto error; } - cc = control_connection_unix_new(&self->super, conn_socket); /* NOTE: with the call below, the reference to the control connection (cc) From 880477997ce6ca5f4c9d3442d4252fbfdf17d198 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Thu, 7 Nov 2024 14:25:10 +0100 Subject: [PATCH 06/10] console: rework console_acquire_from_fds() function To avoid being racy. Signed-off-by: Balazs Scheidler --- lib/console.c | 14 +++++++++----- lib/console.h | 3 +-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/console.c b/lib/console.c index c72717dab7..00f99ed2df 100644 --- a/lib/console.c +++ b/lib/console.c @@ -93,26 +93,30 @@ console_is_attached(void) } /* re-acquire a console after startup using an array of fds */ -void +gboolean console_acquire_from_fds(gint fds[3]) { const gchar *takeover_message_on_old_console = "[Console taken over, no further output here]\n"; - g_assert(!console_is_attached()); + gboolean result = FALSE; - if (using_initial_console) + g_mutex_lock(&console_lock); + if (console_present) { + if (!using_initial_console) + goto exit; (void) write(1, takeover_message_on_old_console, strlen(takeover_message_on_old_console)); } - g_mutex_lock(&console_lock); - dup2(fds[0], STDIN_FILENO); dup2(fds[1], STDOUT_FILENO); dup2(fds[2], STDERR_FILENO); console_present = TRUE; using_initial_console = FALSE; + result = TRUE; +exit: g_mutex_unlock(&console_lock); + return result; } /** diff --git a/lib/console.h b/lib/console.h index 932f9c673c..1d2e2da984 100644 --- a/lib/console.h +++ b/lib/console.h @@ -31,8 +31,7 @@ void console_printf(const gchar *fmt, ...) __attribute__ ((format (printf, 1, 2) gboolean console_is_present(void); gboolean console_is_attached(void); -void console_acquire_from_fds(gint fds[3]); -void console_acquire_from_stdio(void); +gboolean console_acquire_from_fds(gint fds[3]); void console_release(void); void console_global_init(const gchar *console_prefix); From dc5b1f6a35779b9ed6321bd18218d7a2f5706cfa Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Thu, 7 Nov 2024 15:15:16 +0100 Subject: [PATCH 07/10] console: drop console_is_attached() function We don't need this anymore. Signed-off-by: Balazs Scheidler --- lib/console.c | 14 -------------- lib/console.h | 1 - 2 files changed, 15 deletions(-) diff --git a/lib/console.c b/lib/console.c index 00f99ed2df..da5a919d18 100644 --- a/lib/console.c +++ b/lib/console.c @@ -78,20 +78,6 @@ console_is_present(void) return result; } -gboolean -console_is_attached(void) -{ - gboolean result; - /* the lock only serves a memory barrier but is not a real synchronization */ - g_mutex_lock(&console_lock); - if (using_initial_console) - result = FALSE; - else - result = console_present; - g_mutex_unlock(&console_lock); - return result; -} - /* re-acquire a console after startup using an array of fds */ gboolean console_acquire_from_fds(gint fds[3]) diff --git a/lib/console.h b/lib/console.h index 1d2e2da984..c65347357b 100644 --- a/lib/console.h +++ b/lib/console.h @@ -30,7 +30,6 @@ void console_printf(const gchar *fmt, ...) __attribute__ ((format (printf, 1, 2))); gboolean console_is_present(void); -gboolean console_is_attached(void); gboolean console_acquire_from_fds(gint fds[3]); void console_release(void); From 1f7d52fd15de7ec64ef2704106c801651192c8f7 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sat, 5 Oct 2024 14:23:25 +0200 Subject: [PATCH 08/10] mainloop-control: add support for ATTACH STDIO This new command allows to reconnect the stdio streams even if syslog-ng runs in the background. syslog-ng-ctl will be able to pass 3 fds to the syslog-ng process, which will be duplicated into the standard fds and with that syslog-ng will happily start displaying its stdout to that terminal. The ATTACH command itself is threaded and the control socket is only used to indicate that the peers are still alive. syslog-ng will start sending "ALIVE" messages to this stream every second or so. If sending this packet is not successful, the connection is closed and the thread is cancelled. Upon cancellation, the stdio fds are restored to point to /dev/null. Signed-off-by: Balazs Scheidler --- lib/mainloop-control.c | 93 +++++++++++++++++++++++++++++++++++++++ syslog-ng-ctl/Makefile.am | 2 + 2 files changed, 95 insertions(+) diff --git a/lib/mainloop-control.c b/lib/mainloop-control.c index 8b94f9db4b..2781cc38c6 100644 --- a/lib/mainloop-control.c +++ b/lib/mainloop-control.c @@ -31,6 +31,7 @@ #include "secret-storage/secret-storage.h" #include "cfg-walker.h" #include "logpipe.h" +#include "console.h" #include @@ -104,6 +105,97 @@ control_connection_message_log(ControlConnection *cc, GString *command, gpointer control_connection_send_reply(cc, result); } +void +_wait_until_peer_disappears(ControlConnection *cc, gint max_seconds, gboolean *cancelled) +{ + while (max_seconds != 0 && !(*cancelled)) + { + sleep(1); + if (max_seconds > 0) + max_seconds--; + control_connection_send_batched_reply(cc, g_string_new("ALIVE\n")); + } + console_release(); +} + +static void +control_connection_attach(ControlConnection *cc, GString *command, gpointer user_data, gboolean *cancelled) +{ + gchar **cmds = g_strsplit(command->str, " ", 4); + GString *result = g_string_sized_new(128); + gint n_seconds = -1; + struct + { + gboolean log_stderr; + gint log_level; + } old_values, new_values; + + old_values.log_stderr = log_stderr; + old_values.log_level = msg_get_log_level(); + new_values = old_values; + + if (!cmds[1]) + { + g_string_assign(result, "FAIL Invalid arguments received"); + goto exit; + } + + if (g_str_equal(cmds[1], "STDIO")) + { + ; + } + else if (g_str_equal(cmds[1], "LOGS")) + { + new_values.log_stderr = TRUE; + if (cmds[3]) + new_values.log_level = msg_map_string_to_log_level(cmds[3]); + if (new_values.log_level < 0) + { + g_string_assign(result, "FAIL Invalid log level"); + goto exit; + } + } + else + { + g_string_assign(result, "FAIL This version of syslog-ng only supports attaching to STDIO or LOGS"); + goto exit; + } + + if (cmds[2]) + n_seconds = atoi(cmds[2]); + + gint fds[3]; + gsize num_fds = G_N_ELEMENTS(fds); + if (!control_connection_get_attached_fds(cc, fds, &num_fds) || num_fds != 3) + { + g_string_assign(result, + "FAIL The underlying transport for syslog-ng-ctl does not support fd passing or incorrect number of fds received"); + goto exit; + } + + if (!console_acquire_from_fds(fds)) + { + g_string_assign(result, + "FAIL Error acquiring console"); + goto exit; + } + + log_stderr = new_values.log_stderr; + msg_set_log_level(new_values.log_level); + + _wait_until_peer_disappears(cc, n_seconds, cancelled); + + log_stderr = old_values.log_stderr; + msg_set_log_level(old_values.log_level); + + g_string_assign(result, "OK [console output ends here]"); +exit: + + control_connection_send_batched_reply(cc, result); + control_connection_send_close_batch(cc); + g_strfreev(cmds); +} + static void control_connection_stop_process(ControlConnection *cc, GString *command, gpointer user_data, gboolean *cancelled) { @@ -452,6 +544,7 @@ export_config_graph(ControlConnection *cc, GString *command, gpointer user_data, ControlCommand default_commands[] = { + { "ATTACH", control_connection_attach, .threaded = TRUE }, { "LOG", control_connection_message_log }, { "STOP", control_connection_stop_process }, { "RELOAD", control_connection_reload }, diff --git a/syslog-ng-ctl/Makefile.am b/syslog-ng-ctl/Makefile.am index 601143fb4e..88c393e11c 100644 --- a/syslog-ng-ctl/Makefile.am +++ b/syslog-ng-ctl/Makefile.am @@ -6,6 +6,8 @@ syslog_ng_ctl_syslog_ng_ctl_SOURCES = \ syslog-ng-ctl/syslog-ng-ctl.c \ syslog-ng-ctl/commands/commands.h \ syslog-ng-ctl/commands/commands.c \ + syslog-ng-ctl/commands/attach.h \ + syslog-ng-ctl/commands/attach.c \ syslog-ng-ctl/commands/config.h \ syslog-ng-ctl/commands/config.c \ syslog-ng-ctl/commands/credentials.h \ From edf45b4b8484b07b396c300ba16603f87f0d4197 Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sat, 5 Oct 2024 14:24:28 +0200 Subject: [PATCH 09/10] syslog-ng-ctl: add support for "attach" command Signed-off-by: Balazs Scheidler --- syslog-ng-ctl/CMakeLists.txt | 2 + syslog-ng-ctl/commands/attach.c | 88 +++++++++++++++++++++++++++++ syslog-ng-ctl/commands/attach.h | 32 +++++++++++ syslog-ng-ctl/commands/commands.c | 43 ++++++++++++-- syslog-ng-ctl/commands/commands.h | 2 +- syslog-ng-ctl/control-client-unix.c | 33 ++++++++++- syslog-ng-ctl/control-client.h | 2 +- syslog-ng-ctl/syslog-ng-ctl.c | 2 + tests/copyright/policy | 1 + 9 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 syslog-ng-ctl/commands/attach.c create mode 100644 syslog-ng-ctl/commands/attach.h diff --git a/syslog-ng-ctl/CMakeLists.txt b/syslog-ng-ctl/CMakeLists.txt index 0deab8846f..162364f0cf 100644 --- a/syslog-ng-ctl/CMakeLists.txt +++ b/syslog-ng-ctl/CMakeLists.txt @@ -1,6 +1,8 @@ set(SYSLOG_NG_CTL_SOURCES syslog-ng-ctl.c control-client.h + commands/attach.h + commands/attach.c commands/commands.h commands/commands.c commands/credentials.h diff --git a/syslog-ng-ctl/commands/attach.c b/syslog-ng-ctl/commands/attach.c new file mode 100644 index 0000000000..f1e1f4b655 --- /dev/null +++ b/syslog-ng-ctl/commands/attach.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 Balazs Scheidler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#include "ctl-stats.h" +#include "syslog-ng.h" + +static gint attach_options_seconds; +static gchar *attach_options_log_level = NULL; +static gchar **attach_commands = NULL; + +static gboolean +_store_log_level(const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + if (!attach_options_log_level) + { + attach_options_log_level = g_strdup(value); + return TRUE; + } + g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "You can't specify multiple log-levels at a time."); + return FALSE; +} + +GOptionEntry attach_options[] = +{ + { "seconds", 0, 0, G_OPTION_ARG_INT, &attach_options_seconds, "amount of time to attach for", NULL }, + { "log-level", 0, 0, G_OPTION_ARG_CALLBACK, _store_log_level, "change syslog-ng log level", "" }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attach_commands, "attach mode: logs, stdio", NULL }, + { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } +}; + +gint +slng_attach(int argc, char *argv[], const gchar *mode, GOptionContext *ctx) +{ + GString *command = g_string_new("ATTACH"); + const gchar *attach_mode; + + if (attach_commands) + { + if (attach_commands[1]) + { + fprintf(stderr, "Too many arguments"); + return 1; + } + attach_mode = attach_commands[0]; + } + else + attach_mode = "stdio"; + + if (g_str_equal(attach_mode, "stdio")) + g_string_append(command, " STDIO"); + else if (g_str_equal(attach_mode, "logs")) + g_string_append(command, " LOGS"); + else + { + fprintf(stderr, "Unknown attach mode\n"); + return 1; + } + + g_string_append_printf(command, " %d", attach_options_seconds ? : -1); + if (attach_options_log_level) + g_string_append_printf(command, " %s", attach_options_log_level); + gint result = attach_command(command->str); + g_string_free(command, TRUE); + return result; +} diff --git a/syslog-ng-ctl/commands/attach.h b/syslog-ng-ctl/commands/attach.h new file mode 100644 index 0000000000..b544924ff4 --- /dev/null +++ b/syslog-ng-ctl/commands/attach.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Balazs Scheidler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#ifndef SYSLOG_NG_CTL_ATTACH_H_INCLUDED +#define SYSLOG_NG_CTL_ATTACH_H_INCLUDED 1 + +#include "commands.h" + +extern GOptionEntry attach_options[]; +gint slng_attach(int argc, char *argv[], const gchar *mode, GOptionContext *ctx); + +#endif diff --git a/syslog-ng-ctl/commands/commands.c b/syslog-ng-ctl/commands/commands.c index 2ff1add1e3..7a4275d5a6 100644 --- a/syslog-ng-ctl/commands/commands.c +++ b/syslog-ng-ctl/commands/commands.c @@ -57,14 +57,14 @@ process_response_status(GString *response) } static gboolean -slng_send_cmd(const gchar *cmd) +slng_send_cmd(const gchar *cmd, gboolean attach) { if (!control_client_connect(control_client)) { return FALSE; } - if (control_client_send_command(control_client, cmd) < 0) + if (control_client_send_command(control_client, cmd, attach) < 0) { return FALSE; } @@ -75,7 +75,16 @@ slng_send_cmd(const gchar *cmd) gint slng_run_command(const gchar *command, CommandResponseHandlerFunc cb, gpointer user_data) { - if (!slng_send_cmd(command)) + if (!slng_send_cmd(command, FALSE)) + return 1; + + return control_client_read_reply(control_client, cb, user_data); +} + +gint +slng_attach_command(const gchar *command, CommandResponseHandlerFunc cb, gpointer user_data) +{ + if (!slng_send_cmd(command, TRUE)) return 1; return control_client_read_reply(control_client, cb, user_data); @@ -87,18 +96,30 @@ _is_response_empty(GString *response) return (response == NULL || g_str_equal(response->str, "")); } +static gboolean +_is_response_alive(GString *response) +{ + return strncmp(response->str, "ALIVE", 5) == 0; +} + static gint _print_reply_to_stdout(GString *reply, gpointer user_data) { gboolean first_response = *((gboolean *)user_data); gint retval = 0; + + if (_is_response_alive(reply)) + return 0; + if (first_response) { if (_is_response_empty(reply)) retval = 1; - else retval = process_response_status(reply); + else + retval = process_response_status(reply); } + printf("%s", reply->str); return retval; } @@ -117,6 +138,20 @@ dispatch_command(const gchar *cmd) return retval; } +gint +attach_command(const gchar *cmd) +{ + gboolean first_response = TRUE; + gint retval = 0; + gchar *dispatchable_command = g_strdup_printf("%s\n", cmd); + retval = slng_attach_command(dispatchable_command, _print_reply_to_stdout, &first_response); + + secret_storage_wipe(dispatchable_command, strlen(dispatchable_command)); + g_free(dispatchable_command); + + return retval; +} + gint run(const gchar *control_name, gint argc, gchar **argv, CommandDescriptor *mode, GOptionContext *ctx) { diff --git a/syslog-ng-ctl/commands/commands.h b/syslog-ng-ctl/commands/commands.h index 0072f5568e..f24c4f586a 100644 --- a/syslog-ng-ctl/commands/commands.h +++ b/syslog-ng-ctl/commands/commands.h @@ -43,7 +43,7 @@ typedef struct _CommandDescriptor typedef gint (*CommandResponseHandlerFunc)(GString *response, gpointer user_data); gint dispatch_command(const gchar *cmd); -gint slng_run_command(const gchar *command, CommandResponseHandlerFunc cb, gpointer user_data); +gint attach_command(const gchar *cmd); gint process_response_status(GString *response); gboolean is_syslog_ng_running(void); diff --git a/syslog-ng-ctl/control-client-unix.c b/syslog-ng-ctl/control-client-unix.c index 0bcd8bc99e..f960dab67c 100644 --- a/syslog-ng-ctl/control-client-unix.c +++ b/syslog-ng-ctl/control-client-unix.c @@ -79,9 +79,38 @@ control_client_connect(ControlClient *self) } gint -control_client_send_command(ControlClient *self, const gchar *cmd) +control_client_send_command(ControlClient *self, const gchar *cmd, gboolean attach) { - return fwrite(cmd, strlen(cmd), 1, self->control_socket); + struct iovec iov[1] = + { + { .iov_base = (gchar *) cmd, .iov_len = strlen(cmd) }, + }; + gint fds[3] = { 0, 1, 2 }; + union + { + char buf[CMSG_SPACE(sizeof(fds))]; + struct cmsghdr align; + } u; + struct msghdr msg = + { + .msg_iov = iov, + .msg_iovlen = G_N_ELEMENTS(iov), + 0 + }; + if (attach) + { + msg.msg_control = u.buf; + msg.msg_controllen = sizeof(u.buf); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fds)); + memcpy(CMSG_DATA(cmsg), fds, sizeof(fds)); + } + + return sendmsg(self->control_fd, &msg, 0); +// return fwrite(cmd, strlen(cmd), 1, self->control_socket); } #define BUFF_LEN 8192 diff --git a/syslog-ng-ctl/control-client.h b/syslog-ng-ctl/control-client.h index c3efbf15a5..081e1ca988 100644 --- a/syslog-ng-ctl/control-client.h +++ b/syslog-ng-ctl/control-client.h @@ -32,7 +32,7 @@ typedef struct _ControlClient ControlClient; ControlClient *control_client_new(const gchar *path); gboolean control_client_connect(ControlClient *self); -gint control_client_send_command(ControlClient *self, const gchar *cmd); +gint control_client_send_command(ControlClient *self, const gchar *cmd, gboolean attach); gint control_client_read_reply(ControlClient *self, CommandResponseHandlerFunc cb, gpointer user_data); void control_client_free(ControlClient *self); diff --git a/syslog-ng-ctl/syslog-ng-ctl.c b/syslog-ng-ctl/syslog-ng-ctl.c index d029a1a591..0e2fd45aa6 100644 --- a/syslog-ng-ctl/syslog-ng-ctl.c +++ b/syslog-ng-ctl/syslog-ng-ctl.c @@ -37,6 +37,7 @@ #include "commands/query.h" #include "commands/license.h" #include "commands/healthcheck.h" +#include "commands/attach.h" #include #include @@ -111,6 +112,7 @@ slng_export_config_graph(int argc, char *argv[], const gchar *mode, GOptionConte static CommandDescriptor modes[] = { + { "attach", attach_options, "Attach to a running syslog-ng instance", slng_attach, NULL }, { "stats", stats_options, "Get syslog-ng statistics. Possible commands: csv, prometheus; default: csv", slng_stats, NULL }, { "verbose", verbose_options, "Enable/query verbose messages", slng_verbose, NULL }, { "debug", verbose_options, "Enable/query debug messages", slng_verbose, NULL }, diff --git a/tests/copyright/policy b/tests/copyright/policy index 83a9e1ba7b..1a810effb7 100644 --- a/tests/copyright/policy +++ b/tests/copyright/policy @@ -122,6 +122,7 @@ lib/tests/test_generic_number\.c lib/severity-aliases\.table lib/transport/transport-adapter\.[ch] syslog-ng-ctl/commands/log-level.[ch] +syslog-ng-ctl/commands/attach.[ch] modules/afsocket/afsocket-signals.h syslog-ng-ctl/commands/healthcheck.[ch] modules/python-modules/syslogng/confgen\.py From fa16ac676dff8b9ef4ab192bee72a8272751effe Mon Sep 17 00:00:00 2001 From: Balazs Scheidler Date: Sat, 5 Oct 2024 19:03:50 +0200 Subject: [PATCH 10/10] control: remove and bump verbosity for some control socket related messages Signed-off-by: Balazs Scheidler --- lib/control/control-connection.c | 2 +- lib/control/control-server.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/control/control-connection.c b/lib/control/control-connection.c index 2f19dbb570..ed58460e81 100644 --- a/lib/control/control-connection.c +++ b/lib/control/control-connection.c @@ -223,7 +223,7 @@ control_connection_io_input(void *s) } else if (rc == 0) { - msg_debug("EOF on control channel, closing connection"); + msg_trace("EOF on control channel, closing connection"); goto destroy_connection; } else diff --git a/lib/control/control-server.c b/lib/control/control-server.c index 18d1c04953..058237467d 100644 --- a/lib/control/control-server.c +++ b/lib/control/control-server.c @@ -67,9 +67,7 @@ control_server_cancel_workers(ControlServer *self, ControlConnection *cc) { if (self->worker_threads) { - msg_debug("Cancelling control server worker threads"); g_list_foreach(self->worker_threads, _cancel_worker, cc); - msg_debug("Control server worker threads have been cancelled"); } }