diff --git a/include/maxscale/config.h b/include/maxscale/config.h index 813eaaf43..d4292b8c3 100644 --- a/include/maxscale/config.h +++ b/include/maxscale/config.h @@ -205,6 +205,17 @@ struct service* config_get_service(const MXS_CONFIG_PARAMETER *params, const cha */ struct server* config_get_server(const MXS_CONFIG_PARAMETER *params, const char *key); +/** + * @brief Get an array of servers. The caller should free the returned array, but not + * the array elements. + * + * @param params List of configuration parameters + * @param key Parameter name + * + * @return Pointer to a null-terminated array of servers, or null if none found. + */ +struct server** config_get_serverlist(const MXS_CONFIG_PARAMETER *params, const char *key); + /** * @brief Get copy of parameter value if it is defined * diff --git a/include/maxscale/modinfo.h b/include/maxscale/modinfo.h index 3a8e019f7..d3837ebfc 100644 --- a/include/maxscale/modinfo.h +++ b/include/maxscale/modinfo.h @@ -79,6 +79,7 @@ enum mxs_module_param_type MXS_MODULE_PARAM_PATH, /**< Path to a file or a directory */ MXS_MODULE_PARAM_SERVICE, /**< Service name */ MXS_MODULE_PARAM_SERVER, /**< Server name */ + MXS_MODULE_PARAM_SERVERLIST /**< List of server names, separated by ',' */ }; /** Maximum and minimum values for integer types */ diff --git a/include/maxscale/server.h b/include/maxscale/server.h index fc5c99b6e..dc927f25b 100644 --- a/include/maxscale/server.h +++ b/include/maxscale/server.h @@ -256,6 +256,7 @@ bool server_remove_parameter(SERVER *server, const char *name); extern int server_free(SERVER *server); extern SERVER *server_find_by_unique_name(const char *name); +extern SERVER** server_find_by_unique_names(char **server_names, int size); extern SERVER *server_find(const char *servname, unsigned short port); extern char *server_status(const SERVER *); extern void server_clear_set_status(SERVER *server, int specified_bits, int bits_to_set); diff --git a/server/core/config.c b/server/core/config.c index 9913f2958..764f54669 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1057,6 +1057,24 @@ SERVER* config_get_server(const MXS_CONFIG_PARAMETER *params, const char *key) return server_find_by_unique_name(value); } +SERVER** config_get_serverlist(const MXS_CONFIG_PARAMETER *params, const char *key) +{ + const char *value = config_get_value_string(params, key); + char **server_names = NULL; + SERVER **servers = NULL; + int n_names = config_parse_server_list(value, &server_names); + if (n_names > 0) + { + servers = server_find_by_unique_names(server_names, n_names); + for (int i = 0; i < n_names; i++) + { + MXS_FREE(server_names[i]); + } + MXS_FREE(server_names); + } + return servers; +} + char* config_copy_string(const MXS_CONFIG_PARAMETER *params, const char *key) { const char *value = config_get_value_string(params, key); @@ -3413,6 +3431,30 @@ bool config_param_is_valid(const MXS_MODULE_PARAM *params, const char *key, } break; + case MXS_MODULE_PARAM_SERVERLIST: + if (context) + { + valid = true; + char **server_names = NULL; + int n_serv = config_parse_server_list(value, &server_names); + if (n_serv > 0) + { + /* Check that every server name in the list is found in the config. */ + for (int i = 0; i < n_serv; i++) + { + if (valid && + !config_contains_type(context, server_names[i], "server")) + { + valid = false; + } + MXS_FREE(server_names[i]); + } + MXS_FREE(server_names); + } + break; + } + + case MXS_MODULE_PARAM_PATH: valid = check_path_parameter(¶ms[i], value); break; @@ -3427,3 +3469,70 @@ bool config_param_is_valid(const MXS_MODULE_PARAM *params, const char *key, return valid; } + +static int config_parse_server_list(const char *servers, char ***output_array) +{ + ss_dassert(servers); + + /* First, check the string for the maximum amount of servers it + * might contain by counting the commas. */ + int out_arr_size = 1; + const char *pos = servers; + while ((pos = strchr(pos, ',')) != NULL) + { + pos++; + out_arr_size++; + } + char **results = MXS_CALLOC(out_arr_size, sizeof(char*)); + if (!results) + { + return -1; + } + + /* Parse the server names from the list. They are separated by ',' and will + * be trimmed of whitespace. */ + char srv_list_tmp[strlen(servers) + 1]; + strcpy(srv_list_tmp, servers); + trim(srv_list_tmp); + + bool error = false; + int output_ind = 0; + char *lasts; + char *s = strtok_r(srv_list_tmp, ",", &lasts); + while (s) + { + char srv_name_tmp[strlen(s) + 1]; + strcpy(srv_name_tmp, s); + trim(srv_name_tmp); + if (strlen(srv_name_tmp) > 0) + { + results[output_ind] = MXS_STRDUP(srv_name_tmp); + if (!results[output_ind]) + { + error = true; + break; + } + output_ind++; + } + s = strtok_r(NULL, ",", &lasts); + } + + if (error) + { + int i = 0; + while (results[i]) + { + MXS_FREE(results[i]); + i++; + } + output_ind = 0; + } + + if (output_ind == 0) + { + MXS_FREE(results); + results = NULL; + } + *output_array = results; + return output_ind; +} diff --git a/server/core/maxscale/config.h b/server/core/maxscale/config.h index d3004d4cc..9bc55d7cc 100644 --- a/server/core/maxscale/config.h +++ b/server/core/maxscale/config.h @@ -111,4 +111,15 @@ SSL_LISTENER *make_ssl_structure(CONFIG_CONTEXT *obj, bool require_cert, int *er */ bool config_have_required_ssl_params(CONFIG_CONTEXT *obj); +/** + * Parses a list of server names and writes the results in an array of strings + * with one server in each. The output array and its elements should be deallocated + * by the caller. The server names are not checked to be valid servers. + * + * @param servers A list of server names. + * @param output_array Save location for the array of server names. Set to null if none found. + * @return How many servers were found and set into the array. + */ +static int config_parse_server_list(const char *servers, char ***output_array); + MXS_END_DECLS diff --git a/server/core/server.c b/server/core/server.c index 7fa5cb5a4..14fd7d844 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -296,6 +296,41 @@ SERVER * server_find_by_unique_name(const char *name) return server; } + + +/** + * Find several servers with the names specified in an array with a given size. + * The returned array (but not the elements) should be freed by the caller, and + * is null-terminated. + * + * @param servers An array of server names + * @param size number of elements in the server names array + * @return A null-terminated array of SERVERs. May contain less elements than + * requested if some server names are not found. + */ +SERVER** server_find_by_unique_names(char **server_names, int size) +{ + ss_dassert(server_names); + + SERVER **results = MXS_CALLOC(size + 1, sizeof(SERVER*)); // +1 for null + if (!results) + { + return NULL; + } + + int res_ind = 0; + for (int i = 0; server_names[i] != NULL; i++) + { + SERVER *serv = server_find_by_unique_name(server_names[i]); + if (serv) + { + results[res_ind] = serv; + res_ind++; + } + } + return results; +} + /** * Find an existing server *