Skip to content
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