Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

console: io backup restore #525

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
110 changes: 74 additions & 36 deletions lib/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
#include <syslog.h>

GMutex console_lock;
gboolean console_present = TRUE;
gboolean using_initial_console = TRUE;
const gchar *console_prefix;
gint initial_console_fds[3];
gint stolen_fds;

/**
* console_printf:
Expand All @@ -54,7 +55,7 @@ console_printf(const gchar *fmt, ...)
va_start(ap, fmt);
g_vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (console_is_present())
if (console_is_initial())
fprintf(stderr, "%s: %s\n", console_prefix, buf);
else
{
Expand All @@ -65,39 +66,75 @@ console_printf(const gchar *fmt, ...)
}


/* NOTE: this is not synced with any changes and is just an indication whether we have a console */
/* NOTE: this is not synced with any changes and is just an indication whether we already acquired the console */
gboolean
console_is_present(void)
console_is_initial(void)
{
gboolean result;

/* the lock only serves a memory barrier but is not a real synchronization */
g_mutex_lock(&console_lock);
result = console_present;
result = using_initial_console;
g_mutex_unlock(&console_lock);
return result;
}

GString *
_get_fn_names(gint fns)
{
GString *result = g_string_new(NULL);

if (fns & (1 << STDIN_FILENO))
g_string_append(result, "stdin");
if (fns & (1 << STDOUT_FILENO))
{
if (result->len > 0)
g_string_append_c(result, ',');
g_string_append(result, "stdout");
}
if (fns & (1 << STDERR_FILENO))
{
if (result->len > 0)
g_string_append_c(result, ',');
g_string_append(result, "stderr");
}

return result;
}

/* re-acquire a console after startup using an array of fds */
gboolean
console_acquire_from_fds(gint fds[3])
console_acquire_from_fds(gint fds[3], gint fds_to_steal)
{
const gchar *takeover_message_on_old_console = "[Console taken over, no further output here]\n";
gboolean result = FALSE;

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));
}
if (!using_initial_console)
goto exit;

dup2(fds[0], STDIN_FILENO);
dup2(fds[1], STDOUT_FILENO);
dup2(fds[2], STDERR_FILENO);
GString *stolen_fn_names = _get_fn_names(fds_to_steal);
gchar *takeover_message_on_old_console = g_strdup_printf("[Console(%s) taken over, no further output here]\n",
stolen_fn_names->str);
(void) write(STDOUT_FILENO, takeover_message_on_old_console, strlen(takeover_message_on_old_console));
g_free(takeover_message_on_old_console);
g_string_free(stolen_fn_names, TRUE);

stolen_fds = fds_to_steal;

if (stolen_fds & (1 << STDIN_FILENO))
initial_console_fds[0] = dup(STDIN_FILENO);
if (stolen_fds & (1 << STDOUT_FILENO))
initial_console_fds[1] = dup(STDOUT_FILENO);
if (stolen_fds & (1 << STDERR_FILENO))
initial_console_fds[2] = dup(STDERR_FILENO);

if (stolen_fds & (1 << STDIN_FILENO))
dup2(fds[0], STDIN_FILENO);
if (stolen_fds & (1 << STDOUT_FILENO))
dup2(fds[1], STDOUT_FILENO);
if (stolen_fds & (1 << STDERR_FILENO))
dup2(fds[2], STDERR_FILENO);

console_present = TRUE;
using_initial_console = FALSE;
result = TRUE;
exit:
Expand All @@ -108,38 +145,36 @@ console_acquire_from_fds(gint fds[3])
/**
* console_release:
*
* Use /dev/null as input/output/error. This function is idempotent, can be
* Restore input/output/error. This function is idempotent, can be
* called any number of times without harm.
**/
void
console_release(void)
{
gint devnull_fd;

g_mutex_lock(&console_lock);

if (!console_present)
if (using_initial_console)
goto exit;

devnull_fd = open("/dev/null", O_RDONLY);
if (devnull_fd >= 0)
if (initial_console_fds[0] > 0)
{
dup2(devnull_fd, STDIN_FILENO);
close(devnull_fd);
dup2(initial_console_fds[0], STDIN_FILENO);
close(initial_console_fds[0]);
initial_console_fds[0] = -1;
}
devnull_fd = open("/dev/null", O_WRONLY);
if (devnull_fd >= 0)
if (initial_console_fds[1] > 0)
{
dup2(devnull_fd, STDOUT_FILENO);
dup2(devnull_fd, STDERR_FILENO);
close(devnull_fd);
dup2(initial_console_fds[1], STDOUT_FILENO);
close(initial_console_fds[1]);
initial_console_fds[1] = -1;
}
clearerr(stdin);
clearerr(stdout);
clearerr(stderr);
console_present = FALSE;
using_initial_console = FALSE;

if (initial_console_fds[2] > 0)
{
dup2(initial_console_fds[2], STDERR_FILENO);
close(initial_console_fds[2]);
initial_console_fds[2] = -1;
}
using_initial_console = TRUE;
exit:
g_mutex_unlock(&console_lock);
}
Expand All @@ -149,6 +184,9 @@ console_global_init(const gchar *console_prefix_)
{
g_mutex_init(&console_lock);
console_prefix = console_prefix_;
initial_console_fds[0] = -1;
initial_console_fds[1] = -1;
initial_console_fds[2] = -1;
}

void
Expand Down
4 changes: 2 additions & 2 deletions lib/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

void console_printf(const gchar *fmt, ...) __attribute__ ((format (printf, 1, 2)));

gboolean console_is_present(void);
gboolean console_acquire_from_fds(gint fds[3]);
gboolean console_is_initial(void);
gboolean console_acquire_from_fds(gint fds[3], gint fds_to_steal);
void console_release(void);

void console_global_init(const gchar *console_prefix);
Expand Down
119 changes: 77 additions & 42 deletions lib/mainloop-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "debugger/debugger-main.h"

#include <string.h>
#include <unistd.h>

static gboolean
_control_process_log_level(const gchar *level, GString *result)
Expand Down Expand Up @@ -119,49 +120,47 @@ _wait_until_peer_disappears(ControlConnection *cc, gint max_seconds, gboolean *c
console_release();
}

static void
control_connection_attach(ControlConnection *cc, GString *command, gpointer user_data, gboolean *cancelled)
typedef struct _AttachCommandArgs
{
MainLoop *main_loop = (MainLoop *) user_data;
gchar **cmds = g_strsplit(command->str, " ", 4);
gboolean start_debugger;
gint fds_to_steal;
gint n_seconds;
gboolean log_stderr;
gint log_level;
} AttachCommandArgs;

GString *result = g_string_sized_new(128);
gint n_seconds = -1;
gboolean start_debugger = FALSE;
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;
static gboolean
_parse_attach_command_args(GString *command, AttachCommandArgs *args, GString *result)
{
gboolean success = FALSE;
gchar **cmds = g_strsplit(command->str, " ", 5);

if (!cmds[1])
if (cmds[1] == NULL)
{
g_string_assign(result, "FAIL Invalid arguments received");
goto exit;
}

if (g_str_equal(cmds[1], "STDIO"))
{
;
if (cmds[3])
args->fds_to_steal = atoi(cmds[3]);
}
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;
}
args->log_stderr = TRUE;
/* NOTE: as log_stderr uses stderr (what a surprise)
* - we need to steal only the stderr
* - caller of the `attach logs` will get the stderr output as well, so
* they should redirect stderr to stdout if they want to capture it for
* further processing e.g.
* syslog-ng-ctl attach logs |& grep -i error
*/
args->fds_to_steal = (1 << STDERR_FILENO);
}
else if (g_str_equal(cmds[1], "DEBUGGER"))
{
start_debugger = TRUE;
args->start_debugger = TRUE;
}
else
{
Expand All @@ -170,7 +169,47 @@ control_connection_attach(ControlConnection *cc, GString *command, gpointer user
}

if (cmds[2])
n_seconds = atoi(cmds[2]);
args->n_seconds = atoi(cmds[2]);

if (cmds[4])
args->log_level = msg_map_string_to_log_level(cmds[4]);
if (args->log_level < 0)
{
g_string_assign(result, "FAIL Invalid log level");
goto exit;
}
success = TRUE;

exit:
g_strfreev(cmds);
return success;
}

static void
control_connection_attach(ControlConnection *cc, GString *command, gpointer user_data, gboolean *cancelled)
{
MainLoop *main_loop = (MainLoop *) user_data;
GString *result = g_string_sized_new(128);
struct
{
gboolean log_stderr;
gint log_level;
} old_values =
{
log_stderr,
msg_get_log_level()
};
AttachCommandArgs cmd_args =
{
.start_debugger = FALSE,
.fds_to_steal = (1 << STDOUT_FILENO) | (1 << STDERR_FILENO),
.n_seconds = -1,
.log_stderr = old_values.log_stderr,
.log_level = old_values.log_level
};

if (!_parse_attach_command_args(command, &cmd_args, result))
goto exit;

gint fds[3];
gsize num_fds = G_N_ELEMENTS(fds);
Expand All @@ -181,38 +220,34 @@ control_connection_attach(ControlConnection *cc, GString *command, gpointer user
goto exit;
}

if (!console_acquire_from_fds(fds))
if (!console_acquire_from_fds(fds, cmd_args.fds_to_steal))
{
g_string_assign(result,
"FAIL Error acquiring console");
g_string_assign(result, "FAIL Error acquiring console");
goto exit;
}

log_stderr = new_values.log_stderr;
msg_set_log_level(new_values.log_level);
log_stderr = cmd_args.log_stderr;
msg_set_log_level(cmd_args.log_level);

if (start_debugger && !debugger_is_running())
if (cmd_args.start_debugger && !debugger_is_running())
{
//cfg_load_module(self->current_configuration, "mod-python");
debugger_start(main_loop, main_loop_get_current_config(main_loop));
}

_wait_until_peer_disappears(cc, n_seconds, cancelled);
_wait_until_peer_disappears(cc, cmd_args.n_seconds, cancelled);

if (start_debugger && debugger_is_running())
{
debugger_stop();
}
if (cmd_args.start_debugger && debugger_is_running())
debugger_stop();

log_stderr = old_values.log_stderr;
msg_set_log_level(old_values.log_level);

g_string_assign(result, "OK [console output ends here]");
exit:
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
Expand Down
Loading