MXS-1405: Execute EXTERNCMD synchronously

The execution of external commands in MaxScale by monitors needs to be
synchronous in order to prevent the unintended detection of state changes.
This commit is contained in:
Markus Mäkelä 2017-09-13 22:47:58 +03:00
parent efb1e541e2
commit 4f7606ebc0
3 changed files with 71 additions and 49 deletions

View File

@ -16,10 +16,12 @@
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <maxscale/alloc.h>
#include <maxscale/log_manager.h>
#include <maxscale/pcre2.h>
#include <maxscale/thread.h>
@ -175,6 +177,7 @@ int externcmd_execute(EXTERNCMD* cmd)
int rval = 0;
pid_t pid;
// The SIGCHLD handler must be disabled before child process is forked
pid = fork();
if (pid < 0)
@ -191,9 +194,71 @@ int externcmd_execute(EXTERNCMD* cmd)
}
else
{
MXS_INFO("Executing command '%s' in process %d", cmd->argv[0], pid);
cmd->child = pid;
cmd->n_exec++;
MXS_DEBUG("[monitor_exec_cmd] Forked child process %d : %s.", pid, cmd->argv[0]);
bool first_warning = true;
bool again = true;
uint64_t t = 0;
uint64_t t_max = cmd->timeout * 1000;
while (again)
{
int exit_status;
switch (waitpid(pid, &exit_status, WNOHANG))
{
case -1:
MXS_ERROR("Failed to wait for child process: %d, %s", errno, mxs_strerror(errno));
again = false;
break;
case 0:
if (t++ > t_max)
{
// Command timed out
t = 0;
if (first_warning)
{
MXS_WARNING("Soft timeout for command '%s', sending SIGTERM", cmd->argv[0]);
kill(pid, SIGTERM);
first_warning = false;
}
else
{
MXS_ERROR("Hard timeout for command '%s', sending SIGKILL", cmd->argv[0]);
kill(pid, SIGKILL);
}
}
else
{
// Sleep and try again
thread_millisleep(1);
}
break;
default:
again = false;
if (WIFEXITED(exit_status))
{
rval = WEXITSTATUS(exit_status);
}
else if (WIFSIGNALED(exit_status))
{
rval = WTERMSIG(exit_status);
}
else
{
rval = exit_status;
MXS_ERROR("Command '%s' did not exit normally. Exit status: %d",
cmd->argv[0], exit_status);
}
break;
}
}
}
return rval;

View File

@ -369,45 +369,6 @@ sigint_handler(int i)
}
}
static void
sigchld_handler (int i)
{
int exit_status = 0;
pid_t child = -1;
if ((child = wait(&exit_status)) == -1)
{
MXS_ERROR("Failed to wait child process: %d %s",
errno, mxs_strerror(errno));
}
else
{
if (WIFEXITED(exit_status))
{
if (WEXITSTATUS(exit_status) != 0)
{
MXS_ERROR("Child process %d exited with status %d",
child, WEXITSTATUS(exit_status));
}
else
{
MXS_INFO("Child process %d exited with status %d",
child, WEXITSTATUS(exit_status));
}
}
else if (WIFSIGNALED(exit_status))
{
MXS_ERROR("Child process %d was stopped by signal %d.",
child, WTERMSIG(exit_status));
}
else
{
MXS_ERROR("Child process %d did not exit normally. Exit status: %d",
child, exit_status);
}
}
}
volatile sig_atomic_t fatal_handling = 0;
static int signal_set(int sig, void (*handler)(int));
@ -1178,11 +1139,6 @@ bool configure_signals(void)
return false;
}
if (!configure_signal(SIGCHLD, "SIGCHLD", sigchld_handler))
{
return false;
}
#ifdef SIGBUS
if (!configure_signal(SIGBUS, "SIGBUS", sigfatal_handler))
{

View File

@ -22,10 +22,11 @@ MXS_BEGIN_DECLS
typedef struct extern_cmd_t
{
char** argv; /*< Argument vector for the command, first being the actual command
* being executed. */
int n_exec; /*< Number of times executed */
pid_t child; /*< PID of the child process */
char** argv; /**< Argument vector for the command, first being the
* actual command being executed */
int n_exec; /**< Number of times executed */
pid_t child; /**< PID of the child process */
uint32_t timeout; /**< Command timeout in seconds */
} EXTERNCMD;
char* externcmd_extract_command(const char* argstr);