char buf[1024];
uint16_t buf_used;
bool use_syslog;
+ void (*logfn)(const char *, uint16_t, void *);
+ void *logfn_private;
};
/* we need this global to keep the DEBUG() syntax */
return 0;
}
-
+/* Note that do_debug always uses the global log state. */
+static void write_to_log(struct ctdb_log_state *log,
+ const char *buf, unsigned int len)
+{
+ if (script_log_level <= LogLevel) {
+ do_debug("%*.*s\n", len, len, buf);
+ /* log it in the eventsystem as well */
+ if (log->logfn)
+ log->logfn(log->buf, len, log->logfn_private);
+ }
+}
/*
called when log data comes in from a child process
if (n2 > 0 && log->buf[n2-1] == '\r') {
n2--;
}
- if (script_log_level <= LogLevel) {
- do_debug("%*.*s\n", n2, n2, log->buf);
- /* log it in the eventsystem as well */
- ctdb_log_event_script_output(log->ctdb, log->buf, n2);
- }
+ write_to_log(log, log->buf, n2);
memmove(log->buf, p+1, sizeof(log->buf) - n1);
log->buf_used -= n1;
}
/* the buffer could have completely filled - unfortunately we have
no choice but to dump it out straight away */
if (log->buf_used == sizeof(log->buf)) {
- if (script_log_level <= LogLevel) {
- do_debug("%*.*s\n",
- (int)log->buf_used, (int)log->buf_used, log->buf);
- /* log it in the eventsystem as well */
- ctdb_log_event_script_output(log->ctdb, log->buf, log->buf_used);
- }
+ write_to_log(log, log->buf, log->buf_used);
log->buf_used = 0;
}
}
+static int log_context_destructor(struct ctdb_log_state *log)
+{
+ /* Flush buffer in case it wasn't \n-terminated. */
+ if (log->buf_used > 0) {
+ this_log_level = script_log_level;
+ write_to_log(log, log->buf, log->buf_used);
+ }
+ return 0;
+}
+
+/*
+ fork(), redirecting child output to logging and specified callback.
+*/
+struct ctdb_log_state *ctdb_fork_with_logging(TALLOC_CTX *mem_ctx,
+ struct ctdb_context *ctdb,
+ void (*logfn)(const char *, uint16_t, void *),
+ void *logfn_private, pid_t *pid)
+{
+ int p[2];
+ int old_stdout, old_stderr;
+ int saved_errno;
+ struct ctdb_log_state *log;
+
+ log = talloc_zero(mem_ctx, struct ctdb_log_state);
+ CTDB_NO_MEMORY_NULL(ctdb, log);
+ log->ctdb = ctdb;
+ log->logfn = logfn;
+ log->logfn_private = (void *)logfn_private;
+
+ if (pipe(p) != 0) {
+ DEBUG(DEBUG_ERR,(__location__ " Failed to setup for child logging pipe\n"));
+ goto free_log;
+ }
+
+ /* We'll fail if stderr/stdout not already open; it's simpler. */
+ old_stdout = dup(STDOUT_FILENO);
+ old_stderr = dup(STDERR_FILENO);
+ if (dup2(p[1], STDOUT_FILENO) < 0 || dup2(p[1], STDERR_FILENO) < 0) {
+ DEBUG(DEBUG_ERR,(__location__ " Failed to setup output for child\n"));
+ goto close_pipe;
+ }
+ close(p[1]);
+
+ *pid = fork();
+
+ /* Child? */
+ if (*pid == 0) {
+ close(old_stdout);
+ close(old_stderr);
+ close(p[0]);
+ return log;
+ }
+ saved_errno = errno;
+ dup2(STDOUT_FILENO, old_stdout);
+ dup2(STDERR_FILENO, old_stderr);
+ close(old_stdout);
+ close(old_stderr);
+
+ /* We failed? */
+ if (*pid < 0) {
+ DEBUG(DEBUG_ERR, (__location__ " fork failed for child process\n"));
+ close(p[0]);
+ errno = saved_errno;
+ goto free_log;
+ }
+
+ log->pfd = p[0];
+ talloc_set_destructor(log, log_context_destructor);
+ event_add_fd(ctdb->ev, log, log->pfd, EVENT_FD_READ,
+ ctdb_log_handler, log);
+ return log;
+
+close_pipe:
+ close(p[0]);
+ close(p[1]);
+free_log:
+ talloc_free(log);
+ return NULL;
+}
/*
setup for logging of child process stdout
/* called from ctdb_logging when we have received output on STDERR from
* one of the eventscripts
*/
-int ctdb_log_event_script_output(struct ctdb_context *ctdb, char *str, uint16_t len)
+static void log_event_script_output(const char *str, uint16_t len, void *p)
{
- struct ctdb_monitor_script_status *script;
-
- if (ctdb->current_monitor == NULL) {
- return -1;
- }
+ struct ctdb_monitor_script_status *script =
+ talloc_get_type(p, struct ctdb_monitor_script_status);
- script = ctdb->current_monitor->scripts;
- if (script == NULL) {
- return -1;
- }
-
- if (script->output == NULL) {
- script->output = talloc_asprintf(script, "%*.*s", len, len, str);
- } else {
- script->output = talloc_asprintf_append(script->output, "%*.*s", len, len, str);
- }
-
- return 0;
+ script->output = talloc_asprintf_append(script->output, "%*.*s", len, len, str);
}
/* starting a new monitor event */
script->next = ctdb->current_monitor->scripts;
script->name = talloc_strdup(script, name);
CTDB_NO_MEMORY(ctdb, script->name);
+ script->output = talloc_strdup(script, "");
+ CTDB_NO_MEMORY(ctdb, script->output);
script->start = timeval_current();
ctdb->current_monitor->scripts = script;
struct ctdb_event_script_state *state)
{
int r;
+ void *mem_ctx = state;
+ void (*logfn)(const char *, uint16_t, void *) = NULL;
if (!state->from_user && state->call == CTDB_EVENT_MONITOR) {
ctdb_control_event_script_start(ctdb, state->script_list->name);
+ /* We need the logging destroyed after scripts, since it
+ * refers to them. */
+ mem_ctx = state->scripts->output;
+ logfn = log_event_script_output;
}
r = pipe(state->fd);
return -errno;
}
- state->child = fork();
-
- if (state->child == (pid_t)-1) {
+ if (!ctdb_fork_with_logging(mem_ctx, ctdb, logfn,
+ state->scripts, &state->child)) {
r = -errno;
- DEBUG(DEBUG_ERR, (__location__ " fork failed for child eventscript process\n"));
close(state->fd[0]);
close(state->fd[1]);
return r;
}
+ /* If we are the child, do the work. */
if (state->child == 0) {
int rt;