diff --git a/server/modules/monitor/mon_exec.c b/server/modules/monitor/mon_exec.c index 122390da9..bae0d31fc 100644 --- a/server/modules/monitor/mon_exec.c +++ b/server/modules/monitor/mon_exec.c @@ -5,12 +5,123 @@ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; extern __thread log_info_t tls_log_info; +/** + * Tokenize a string into arguments suitable for a execvp call. + * @param args Argument string + * @param argv Array of char pointers to be filled with tokenized arguments + * @return 0 on success, -1 on error + */ +int tokenize_arguments(char* args, char** argv) +{ + int i = 0; + bool quoted = false; + bool read = false; + bool escaped = false; + char *ptr,*start; + char qc; + + start = args; + ptr = start; + + while(*ptr != '\0' && i < MAXSCALE_EXTCMD_ARG_MAX) + { + if(escaped) + { + escaped = false; + } + else + { + if(*ptr == '\\') + { + escaped = true; + } + else if(quoted && !escaped && *ptr == qc) /** End of quoted string */ + { + *ptr = '\0'; + argv[i++] = strdup(start); + read = false; + quoted = false; + } + else if (!quoted) + { + if(isspace(*ptr)) + { + *ptr = '\0'; + if(read) /** New token */ + { + argv[i++] = strdup(start); + read = false; + } + } + else if( *ptr == '\"' || *ptr == '\'') + { + /** New quoted token, strip quotes */ + quoted = true; + qc = *ptr; + start = ptr + 1; + } + else if(!read) + { + start = ptr; + read = true; + } + } + } + ptr++; + } + if(read) + argv[i++] = strdup(start); + argv[i] = NULL; + + return 0; +} + +/** + * Allocate a new external command. + * The name and parameters are copied into the external command structure so + * the original memory can be freed if needed. + * @param command Command to execute with the parameters + * @return Pointer to new external command struct or NULL if an error occurred + */ +EXTERNCMD* externcmd_allocate(char* argstr) +{ + EXTERNCMD* cmd; + + if(argstr == NULL) + return NULL; + + if((cmd = (EXTERNCMD*)malloc(sizeof(EXTERNCMD))) != NULL) + { + if(tokenize_arguments(argstr,cmd->parameters) == -1) + { + free(cmd); + return NULL; + } + } + return cmd; +} + +/** + * Free a previously allocated external command. + * @param cmd Command to free + */ +void externcmd_free(EXTERNCMD* cmd) +{ + int i; + + for(i = 0;cmd->parameters[i] != NULL;i++) + { + free(cmd->parameters[i]); + } + free(cmd); +} + /** *Execute a command in a separate process. *@param cmd Command to execute *@return 0 on success, -1 on error. */ -int monitor_exec_cmd(char* cmd) +int externcmd_execute(EXTERNCMD* cmd) { int rval = 0; pid_t pid; @@ -20,19 +131,20 @@ int monitor_exec_cmd(char* cmd) if(pid < 0) { skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s", - cmd,errno,strerror(errno)); + cmd->parameters[0],errno,strerror(errno)); rval = -1; } else if(pid == 0) { /** Child process, execute command */ - execl(cmd,cmd,NULL); + execvp(cmd->parameters[0],cmd->parameters); } else { - LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); + cmd->n_exec++; + LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); } return rval; - } + diff --git a/server/modules/monitor/mon_exec.h b/server/modules/monitor/mon_exec.h index ed277ab3e..e1ef7ad37 100644 --- a/server/modules/monitor/mon_exec.h +++ b/server/modules/monitor/mon_exec.h @@ -1,13 +1,19 @@ #ifndef MON_EXEC_HG #define MON_EXEC_HG -#include #include #include #include #include #include +#define MAXSCALE_EXTCMD_ARG_MAX 256 -int monitor_exec_cmd(char* cmd); +typedef struct extern_cmd_t{ + char* parameters[MAXSCALE_EXTCMD_ARG_MAX]; /*< Command arguments */ + int n_exec; /*< Number of times executed */ +}EXTERNCMD; +EXTERNCMD* externcmd_allocate(char* argstr); +void externcmd_free(EXTERNCMD* cmd); +int externcmd_execute(EXTERNCMD* cmd); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index d52a6902d..aced27a13 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -197,15 +197,9 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->replicationHeartbeat = config_truth_value(params->value); else if(!strcmp(params->name,"master_down_script")) { - if(access(params->value,F_OK) == 0) - { - handle->master_down_script = strdup(params->value); - handle->master_down_script_called = 0; - } - else - { - skygw_log_write(LOGFILE_ERROR,"Error: could not find master_down_script file: %s",params->value); - } + if(handle->master_down_script) + externcmd_free(handle->master_down_script); + handle->master_down_script = externcmd_allocate(params->value); } params = params->next; } @@ -700,8 +694,11 @@ int log_no_master = 1; ptr->server->port))); if(handle->master_down_script) { - if(monitor_exec_cmd(handle->master_down_script)) - skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s' on server state change.",handle->master_down_script); + if(externcmd_execute(handle->master_down_script)) + skygw_log_write(LOGFILE_ERROR, + "Error: Failed to execute command " + "'%s' on server state change.", + handle->master_down_script->parameters[0]); } } /** diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 966bebd15..d6bed5578 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -20,7 +20,7 @@ #include #include #include - +#include /** * @file mysqlmon.h - The MySQL monitor functionality within the gateway * @@ -81,8 +81,7 @@ typedef struct { int write_timeout; /**< Timeout in seconds for each attempt to write to the server. * There are retries and the total effective timeout value is two times the option value. */ - char *master_down_script; - int master_down_script_called; + EXTERNCMD* master_down_script; } MYSQL_MONITOR; #define MONITOR_RUNNING 1