diff --git a/lib/console.c b/lib/console.c index c72717dab7..da5a919d18 100644 --- a/lib/console.c +++ b/lib/console.c @@ -78,41 +78,31 @@ 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 */ -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..c65347357b 100644 --- a/lib/console.h +++ b/lib/console.h @@ -30,9 +30,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); diff --git a/lib/control/control-command-thread.c b/lib/control/control-command-thread.c index 9d5ed8b976..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 @@ -44,6 +45,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) { @@ -64,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)); @@ -82,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(); } 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); diff --git a/lib/control/control-connection.c b/lib/control/control-connection.c index 03819103cf..ed58460e81 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) { @@ -215,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-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) diff --git a/lib/control/control-server.c b/lib/control/control-server.c index 66a5c57867..058237467d 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,21 @@ _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); - msg_debug("Control server worker threads have been cancelled"); + g_list_foreach(self->worker_threads, _cancel_worker, cc); } } +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 +94,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-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/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(); 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(); } } 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/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 \ 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