diff --git a/server/core/externcmd.cc b/server/core/externcmd.cc index 2a4d6c605..c31648b1c 100644 --- a/server/core/externcmd.cc +++ b/server/core/externcmd.cc @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include +#include @@ -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; diff --git a/server/core/gateway.cc b/server/core/gateway.cc index 60e96478f..afa547856 100644 --- a/server/core/gateway.cc +++ b/server/core/gateway.cc @@ -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)) { diff --git a/server/core/maxscale/externcmd.h b/server/core/maxscale/externcmd.h index c23b3ac45..096e5a2b3 100644 --- a/server/core/maxscale/externcmd.h +++ b/server/core/maxscale/externcmd.h @@ -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);