diff --git a/src/cli.c b/src/cli.c index 91db9c0d..3fc12b7d 100644 --- a/src/cli.c +++ b/src/cli.c @@ -68,9 +68,9 @@ #define COMMAND_STATUS "status" #define COMMAND_STATUS_DETAILS "status-details" #define COMMAND_SWITCH_TO "switch-to" -/* #define COMMAND_CONFIG_GET "conf-get" */ -/* #define COMMAND_CONFIG_LS "conf-ls" */ -/* #define COMMAND_CONFIG_SET "conf-set" */ +#define COMMAND_CONFIG_LS "conf-ls" +#define COMMAND_CONFIG_GET "conf-get" +#define COMMAND_CONFIG_SET "conf-set" #define OUTPUT_FORMAT_JSON "json" #define OUTPUT_FORMAT_TEXT "text" @@ -91,9 +91,9 @@ static void help_status_details(void); static void help_switch_to(void); static int cancel_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); -/* static int config_get(SSL* ssl, int socket, char* config_key, bool verbose, uint8_t compression, uint8_t encryption, int32_t output_format); */ -/* static int config_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); */ -/* static int config_set(SSL* ssl, int socket, char* config_key, char* config_value, bool verbose, uint8_t compression, uint8_t encryption, int32_t output_format); */ +static int conf_get(SSL* ssl, int socket, char* config_key, uint8_t compression, uint8_t encryption, int32_t output_format); +static int conf_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); +static int conf_set(SSL* ssl, int socket, char* config_key, char* config_value, uint8_t compression, uint8_t encryption, int32_t output_format); static int details(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); static int disabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format); static int enabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format); @@ -107,7 +107,13 @@ static int clear_server(SSL* ssl, int socket, char* server, uint8_t compression, static int status(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); static int switch_to(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format); -static int process_result(SSL* ssl, int socket, int32_t output_format); +static int process_result(SSL* ssl, int socket, int32_t output_format); +static int process_ls_result(SSL* ssl, int socket, int32_t output_format); +static int process_get_result(SSL* ssl, int socket, char* config_key, int32_t output_format); +static int process_set_result(SSL* ssl, int socket, char* config_key, int32_t output_format); + +static int get_conf_path_result(struct json* j, uintptr_t* r); +static int get_config_key_result(char* config_key, struct json* j, uintptr_t* r, int32_t output_format); static char* translate_command(int32_t cmd_code); static char* translate_output_format(int32_t out_code); @@ -216,30 +222,30 @@ const struct pgagroal_command command_table[] = { .deprecated = false, .log_message = "" }, - /* { */ - /* .command = "conf", */ - /* .subcommand = "get", */ - /* .accepted_argument_count = {1}, */ - /* .action = MANAGEMENT_CONFIG_GET, */ - /* .deprecated = false, */ - /* .log_message = " [%s]" */ - /* }, */ - /* { */ - /* .command = "conf", */ - /* .subcommand = "set", */ - /* .accepted_argument_count = {2}, */ - /* .action = MANAGEMENT_CONFIG_SET, */ - /* .deprecated = false, */ - /* .log_message = " [%s] = [%s]" */ - /* }, */ - /* { */ - /* .command = "conf", */ - /* .subcommand = "ls", */ - /* .accepted_argument_count = {0}, */ - /* .action = MANAGEMENT_CONFIG_LS, */ - /* .deprecated = false, */ - /* .log_message = "" */ - /* }, */ + { + .command = "conf", + .subcommand = "ls", + .accepted_argument_count = {0}, + .action = MANAGEMENT_CONFIG_LS, + .deprecated = false, + .log_message = "" + }, + { + .command = "conf", + .subcommand = "get", + .accepted_argument_count = {0, 1}, + .action = MANAGEMENT_CONFIG_GET, + .deprecated = false, + .log_message = " [%s]" + }, + { + .command = "conf", + .subcommand = "set", + .accepted_argument_count = {2}, + .action = MANAGEMENT_CONFIG_SET, + .deprecated = false, + .log_message = " [%s] = [%s]" + }, { .command = "clear", .subcommand = "server", @@ -348,11 +354,11 @@ usage(void) printf(" conf Manages the configuration (e.g., reloads the configuration\n"); printf(" The subcommand can be:\n"); printf(" - 'reload' to issue a configuration reload;\n"); + printf(" - 'ls' lists the configuration files used.\n"); printf(" - 'get' to obtain information about a runtime configuration value;\n"); printf(" conf get \n"); printf(" - 'set' to modify a configuration value;\n"); printf(" conf set ;\n"); - printf(" - 'ls' lists the configuration files used.\n"); printf(" clear Resets either the Prometheus statistics or the specified server.\n"); printf(" can be\n"); printf(" - 'server' (default) followed by a server name\n"); @@ -379,7 +385,6 @@ main(int argc, char** argv) char* logfile = NULL; int c; int option_index = 0; - bool matched = false; size_t size; char un[MAX_USERNAME_LENGTH]; struct main_configuration* config = NULL; @@ -646,9 +651,6 @@ main(int argc, char** argv) exit_code = 1; goto done; } - pgagroal_log_trace((char*)parsed.cmd->log_message, parsed.args[0], parsed.args[1]); - - matched = true; config = (struct main_configuration*)shmem; @@ -797,18 +799,25 @@ main(int argc, char** argv) exit_code = reload(s_ssl, socket, compression, encryption, output_format); } } - /* else if (parsed.cmd->action == MANAGEMENT_CONFIG_GET) */ - /* { */ - /* exit_code = config_get(s_ssl, socket, parsed.args[0], verbose, compression, encryption, output_format); */ - /* } */ - /* else if (parsed.cmd->action == MANAGEMENT_CONFIG_SET) */ - /* { */ - /* exit_code = config_set(s_ssl, socket, parsed.args[0], parsed.args[1], verbose, compression, encryption, output_format); */ - /* } */ - /* else if (parsed.cmd->action == MANAGEMENT_CONFIG_LS) */ - /* { */ - /* exit_code = config_ls(s_ssl, socket, compression, encryption, output_format); */ - /* } */ + else if (parsed.cmd->action == MANAGEMENT_CONFIG_LS) + { + exit_code = conf_ls(s_ssl, socket, compression, encryption, output_format); + } + else if (parsed.cmd->action == MANAGEMENT_CONFIG_GET) + { + if (parsed.args[0]) + { + exit_code = conf_get(s_ssl, socket, parsed.args[0], compression, encryption, output_format); + } + else + { + exit_code = conf_get(s_ssl, socket, NULL, compression, encryption, output_format); + } + } + else if (parsed.cmd->action == MANAGEMENT_CONFIG_SET) + { + exit_code = conf_set(s_ssl, socket, parsed.args[0], parsed.args[1], compression, encryption, output_format); + } done: @@ -826,15 +835,6 @@ main(int argc, char** argv) } pgagroal_disconnect(socket); - - if (configuration_path != NULL) - { - if (matched && exit_code != 0) - { - warnx("No connection to pgagroal on %s", config->unix_socket_dir); - } - } - pgagroal_stop_logging(); pgagroal_destroy_shared_memory(shmem, size); @@ -895,6 +895,9 @@ help_conf(void) { printf("Manage the configuration\n"); printf(" pgagroal-cli conf [reload]\n"); + printf(" pgagroal-cli conf [ls]\n"); + printf(" pgagroal-cli conf [get] \n"); + printf(" pgagroal-cli conf [set] \n"); } static void @@ -925,9 +928,9 @@ display_helper(char* command) { help_cancel_shutdown(); } - else if (//!strcmp(command, COMMAND_CONFIG_GET) || - //!strcmp(command, COMMAND_CONFIG_LS) || - //!strcmp(command, COMMAND_CONFIG_SET) || + else if (!strcmp(command, COMMAND_CONFIG_GET) || + !strcmp(command, COMMAND_CONFIG_LS) || + !strcmp(command, COMMAND_CONFIG_SET) || !strcmp(command, COMMAND_RELOAD)) { help_conf(); @@ -1231,71 +1234,65 @@ reload(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t ou return 1; } -/* static int */ -/* config_get(SSL* ssl, int socket, char* config_key, bool verbose, uint8_t compression, uint8_t encryption, int32_t output_format) */ -/* { */ - -/* if (!config_key || strlen(config_key) > MISC_LENGTH) */ -/* { */ -/* goto error; */ -/* } */ - -/* if (pgagroal_management_config_get(ssl, socket, config_key)) */ -/* { */ -/* goto error; */ -/* } */ - -/* if (pgagroal_management_read_config_get(socket, config_key, NULL, verbose, output_format)) */ -/* { */ -/* goto error; */ -/* } */ - -/* return 0; */ - -/* error: */ -/* return 1; */ -/* } */ - -/* static int */ -/* config_set(SSL* ssl, int socket, char* config_key, char* config_value, bool verbose, uint8_t compression, uint8_t encryption, int32_t output_format) */ -/* { */ -/* int status = EXIT_STATUS_OK; */ - -/* if (!config_key || strlen(config_key) > MISC_LENGTH */ -/* || !config_value || strlen(config_value) > MISC_LENGTH) */ -/* { */ -/* goto error; */ -/* } */ - -/* if (pgagroal_management_config_set(ssl, socket, config_key, config_value)) */ -/* { */ -/* goto error; */ -/* } */ - -/* status = pgagroal_management_read_config_get(socket, config_key, config_value, verbose, output_format); */ - -/* return status; */ -/* error: */ -/* return 1; */ -/* } */ - -/* static int */ -/* config_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) */ -/* { */ -/* if (pgagroal_management_conf_ls(ssl, socket)) */ -/* { */ -/* goto error; */ -/* } */ - -/* if (pgagroal_management_read_conf_ls(ssl, socket, output_format)) */ -/* { */ -/* goto error; */ -/* } */ - -/* return EXIT_STATUS_OK; */ -/* error: */ -/* return EXIT_STATUS_CONNECTION_ERROR; */ -/* } */ +static int +conf_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + if (pgagroal_management_request_conf_ls(ssl, socket, compression, encryption, output_format)) + { + goto error; + } + + if (process_ls_result(ssl, socket, output_format)) + { + goto error; + } + + return 0; + +error: + + return 1; +} + +static int +conf_get(SSL* ssl, int socket, char* config_key, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + if (pgagroal_management_request_conf_get(ssl, socket, compression, encryption, output_format)) + { + goto error; + } + + if (process_get_result(ssl, socket, config_key, output_format)) + { + goto error; + } + + return 0; + +error: + + return 1; +} + +static int +conf_set(SSL* ssl, int socket, char* config_key, char* config_value, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + if (pgagroal_management_request_conf_set(ssl, socket, config_key, config_value, compression, encryption, output_format)) + { + goto error; + } + + if (process_set_result(ssl, socket, config_key, output_format)) + { + goto error; + } + + return 0; + +error: + + return 1; +} static int process_result(SSL* ssl, int socket, int32_t output_format) @@ -1332,6 +1329,407 @@ process_result(SSL* ssl, int socket, int32_t output_format) return 1; } +static int +process_ls_result(SSL* ssl, int socket, int32_t output_format) +{ + struct json* read = NULL; + struct json* json_res = NULL; + uintptr_t res; + + if (pgagroal_management_read_json(ssl, socket, NULL, NULL, &read)) + { + goto error; + } + + if (get_conf_path_result(read, &res)) + { + goto error; + } + + json_res = (struct json*)res; + + if (MANAGEMENT_OUTPUT_FORMAT_JSON == output_format) + { + pgagroal_json_print(json_res, FORMAT_JSON_COMPACT); + } + else + { + struct json_iterator* iter = NULL; + pgagroal_json_iterator_create(json_res, &iter); + while (pgagroal_json_iterator_next(iter)) + { + char* value = pgagroal_value_to_string(iter->value, FORMAT_TEXT, NULL, 0); + printf("%s\n", value); + free(value); + } + pgagroal_json_iterator_destroy(iter); + } + + pgagroal_json_destroy(read); + pgagroal_json_destroy(json_res); + return 0; + +error: + + pgagroal_json_destroy(read); + pgagroal_json_destroy(json_res); + return 1; +} + +static int +process_get_result(SSL* ssl, int socket, char* config_key, int32_t output_format) +{ + struct json* read = NULL; + bool is_char = false; + char* char_res = NULL; + struct json* json_res = NULL; + uintptr_t res; + + if (pgagroal_management_read_json(ssl, socket, NULL, NULL, &read)) + { + goto error; + } + + if (get_config_key_result(config_key, read, &res, output_format)) + { + if (MANAGEMENT_OUTPUT_FORMAT_JSON == output_format) + { + json_res = (struct json*)res; + pgagroal_json_print(json_res, FORMAT_JSON_COMPACT); + } + else + { + is_char = true; + char_res = (char*)res; + printf("%s\n", char_res); + } + goto error; + } + + if (!config_key) // error response | complete configuration + { + json_res = (struct json*)res; + + if (MANAGEMENT_OUTPUT_FORMAT_TEXT == output_format) + { + pgagroal_json_print(json_res, FORMAT_TEXT); + } + else + { + pgagroal_json_print(json_res, FORMAT_JSON); + } + } + else + { + if (MANAGEMENT_OUTPUT_FORMAT_JSON == output_format) + { + json_res = (struct json*)res; + pgagroal_json_print(json_res, FORMAT_JSON_COMPACT); + } + else + { + is_char = true; + char_res = (char*)res; + printf("%s\n", char_res); + } + } + + pgagroal_json_destroy(read); + if (config_key) + { + if (is_char) + { + free(char_res); + } + else + { + pgagroal_json_destroy(json_res); + } + } + + return 0; + +error: + + pgagroal_json_destroy(read); + if (config_key) + { + if (is_char) + { + free(char_res); + } + else + { + pgagroal_json_destroy(json_res); + } + } + + return 1; +} + +static int +process_set_result(SSL* ssl, int socket, char* config_key, int32_t output_format) +{ + struct json* read = NULL; + bool is_char = false; + char* char_res = NULL; + int status = 0; + struct json* json_res = NULL; + uintptr_t res; + + if (pgagroal_management_read_json(ssl, socket, NULL, NULL, &read)) + { + goto error; + } + + status = get_config_key_result(config_key, read, &res, output_format); + if (MANAGEMENT_OUTPUT_FORMAT_JSON == output_format) + { + json_res = (struct json*)res; + pgagroal_json_print(json_res, FORMAT_JSON_COMPACT); + } + else + { + is_char = true; + char_res = (char*)res; + printf("%s\n", char_res); + } + + if (status == 1) + { + goto error; + } + + pgagroal_json_destroy(read); + if (config_key) + { + if (is_char) + { + free(char_res); + } + else + { + pgagroal_json_destroy(json_res); + } + } + + return 0; + +error: + + pgagroal_json_destroy(read); + if (config_key) + { + if (is_char) + { + free(char_res); + } + else + { + pgagroal_json_destroy(json_res); + } + } + + return 1; +} + +static int +get_config_key_result(char* config_key, struct json* j, uintptr_t* r, int32_t output_format) +{ + char server[MISC_LENGTH]; + char key[MISC_LENGTH]; + + struct json* configuration_js = NULL; + struct json* filtered_response = NULL; + struct json* response = NULL; + struct json* outcome = NULL; + struct json_iterator* iter; + char* config_value = NULL; + int begin = -1, end = -1; + + if (!config_key) + { + *r = (uintptr_t)j; + return 0; + } + + if (pgagroal_json_create(&filtered_response)) + { + goto error; + } + + memset(server, 0, MISC_LENGTH); + memset(key, 0, MISC_LENGTH); + + for (int i = 0; i < strlen(config_key); i++) + { + if (config_key[i] == '.') + { + if (!strlen(server)) + { + memcpy(server, &config_key[begin], end - begin + 1); + server[end - begin + 1] = '\0'; + begin = end = -1; + continue; + } + } + + if (begin < 0) + { + begin = i; + } + + end = i; + + } + + // if the key has not been found, since there is no ending dot, + // try to extract it from the string + if (!strlen(key)) + { + memcpy(key, &config_key[begin], end - begin + 1); + key[end - begin + 1] = '\0'; + } + + response = (struct json*)pgagroal_json_get(j, MANAGEMENT_CATEGORY_RESPONSE); + outcome = (struct json*)pgagroal_json_get(j, MANAGEMENT_CATEGORY_OUTCOME); + if (!response || !outcome) + { + goto error; + } + + // Check if error response + if (pgagroal_json_contains_key(outcome, MANAGEMENT_ARGUMENT_ERROR)) + { + goto error; + } + + if (strlen(server) > 0) + { + configuration_js = (struct json*)pgagroal_json_get(response, server); + if (!configuration_js) + { + goto error; + } + } + else + { + configuration_js = response; + } + + pgagroal_json_iterator_create(configuration_js, &iter); + while (pgagroal_json_iterator_next(iter)) + { + if (!strcmp(key, iter->key)) + { + config_value = pgagroal_value_to_string(iter->value, FORMAT_TEXT, NULL, 0); + if (iter->value->type == ValueJSON) + { + struct json* server_data = NULL; + pgagroal_json_clone((struct json*)iter->value->data, &server_data); + pgagroal_json_put(filtered_response, key, (uintptr_t)server_data, iter->value->type); + } + else + { + pgagroal_json_put(filtered_response, key, (uintptr_t)iter->value->data, iter->value->type); + } + } + } + pgagroal_json_iterator_destroy(iter); + + if (!config_value) // if key doesn't match with any field in configuration + { + goto error; + } + + if (output_format == MANAGEMENT_OUTPUT_FORMAT_JSON || !config_key) + { + *r = (uintptr_t)filtered_response; + free(config_value); + } + else + { + *r = (uintptr_t)config_value; + pgagroal_json_destroy(filtered_response); + } + + return 0; + +error: + + if (output_format == MANAGEMENT_OUTPUT_FORMAT_JSON) + { + pgagroal_json_put(filtered_response, "Outcome", (uintptr_t)false, ValueBool); + *r = (uintptr_t)filtered_response; + free(config_value); + } + else + { + config_value = (char*)malloc(6); + memcpy(config_value, "Error\0", 6); + *r = (uintptr_t)config_value; + pgagroal_json_destroy(filtered_response); + } + + return 1; +} + +static int +get_conf_path_result(struct json* j, uintptr_t* r) +{ + struct json* conf_path_response = NULL; + struct json* response = NULL; + + response = (struct json*)pgagroal_json_get(j, MANAGEMENT_CATEGORY_RESPONSE); + + if (!response) + { + goto error; + } + + if (pgagroal_json_create(&conf_path_response)) + { + goto error; + } + + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_ADMIN_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_ADMIN_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_ADMIN_CONF_PATH), ValueString); + } + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_MAIN_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_MAIN_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_MAIN_CONF_PATH), ValueString); + } + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_USER_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_USER_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_USER_CONF_PATH), ValueString); + } + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_HBA_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_HBA_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_HBA_CONF_PATH), ValueString); + } + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_FRONTEND_USERS_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_FRONTEND_USERS_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_FRONTEND_USERS_CONF_PATH), ValueString); + } + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_LIMIT_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_LIMIT_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_LIMIT_CONF_PATH), ValueString); + } + if (pgagroal_json_contains_key(response, CONFIGURATION_ARGUMENT_SUPERUSER_CONF_PATH)) + { + pgagroal_json_put(conf_path_response, CONFIGURATION_ARGUMENT_SUPERUSER_CONF_PATH, (uintptr_t)pgagroal_json_get(response, CONFIGURATION_ARGUMENT_SUPERUSER_CONF_PATH), ValueString); + } + + *r = (uintptr_t)conf_path_response; + + return 0; +error: + + return 1; + +} + static char* translate_command(int32_t cmd_code) { diff --git a/src/include/configuration.h b/src/include/configuration.h index 08a7c574..11b664b2 100644 --- a/src/include/configuration.h +++ b/src/include/configuration.h @@ -34,6 +34,7 @@ extern "C" { #endif #include +#include /* * The main section that must be present in the `pgagroal.conf` @@ -67,6 +68,61 @@ extern "C" { #define PGAGROAL_CONFIGURATION_STATUS_KO -3 #define PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT -4 +#define CONFIGURATION_ARGUMENT_MAIN_CONF_PATH "main_configuration_path" +#define CONFIGURATION_ARGUMENT_LIMIT_CONF_PATH "limit_configuration_path" +#define CONFIGURATION_ARGUMENT_HBA_CONF_PATH "hba_configuration_path" +#define CONFIGURATION_ARGUMENT_USER_CONF_PATH "users_configuration_path" +#define CONFIGURATION_ARGUMENT_FRONTEND_USERS_CONF_PATH "frontend_users_configuration_path" +#define CONFIGURATION_ARGUMENT_ADMIN_CONF_PATH "admin_configuration_path" +#define CONFIGURATION_ARGUMENT_SUPERUSER_CONF_PATH "superuser_configuration_path" + +#define CONFIGURATION_ARGUMENT_HOST "host" +#define CONFIGURATION_ARGUMENT_PORT "port" +#define CONFIGURATION_ARGUMENT_UNIX_SOCKET_DIR "unix_socket_dir" +#define CONFIGURATION_ARGUMENT_METRICS "metrics" +#define CONFIGURATION_ARGUMENT_METRICS_CACHE_MAX_AGE "metrics_cache_max_age" +#define CONFIGURATION_ARGUMENT_METRICS_CACHE_MAX_SIZE "metrics_cache_max_size" +#define CONFIGURATION_ARGUMENT_MANAGEMENT "management" +#define CONFIGURATION_ARGUMENT_LOG_TYPE "log_type" +#define CONFIGURATION_ARGUMENT_LOG_LEVEL "log_level" +#define CONFIGURATION_ARGUMENT_LOG_PATH "log_path" +#define CONFIGURATION_ARGUMENT_LOG_ROTATION_AGE "log_rotation_age" +#define CONFIGURATION_ARGUMENT_LOG_ROTATION_SIZE "log_rotation_size" +#define CONFIGURATION_ARGUMENT_LOG_LINE_PREFIX "log_line_prefix" +#define CONFIGURATION_ARGUMENT_LOG_MODE "log_mode" +#define CONFIGURATION_ARGUMENT_LOG_CONNECTIONS "log_connections" +#define CONFIGURATION_ARGUMENT_LOG_DISCONNECTIONS "log_disconnections" +#define CONFIGURATION_ARGUMENT_BLOCKING_TIMEOUT "blocking_timeout" +#define CONFIGURATION_ARGUMENT_IDLE_TIMEOUT "idle_timeout" +#define CONFIGURATION_ARGUMENT_ROTATE_FRONTEND_PASSWORD_TIMEOUT "rotate_frontend_password_timeout" +#define CONFIGURATION_ARGUMENT_ROTATE_FRONTEND_PASSWORD_LENGTH "rotate_frontend_password_length" +#define CONFIGURATION_ARGUMENT_MAX_CONNECTION_AGE "max_connection_age" +#define CONFIGURATION_ARGUMENT_VALIDATION "validation" +#define CONFIGURATION_ARGUMENT_BACKGROUND_INTERVAL "background_interval" +#define CONFIGURATION_ARGUMENT_MAX_RETRIES "max_retries" +#define CONFIGURATION_ARGUMENT_MAX_CONNECTIONS "max_connections" +#define CONFIGURATION_ARGUMENT_ALLOW_UNKNOWN_USERS "allow_unknown_users" +#define CONFIGURATION_ARGUMENT_AUTHENTICATION_TIMEOUT "authentication_timeout" +#define CONFIGURATION_ARGUMENT_PIPELINE "pipeline" +#define CONFIGURATION_ARGUMENT_AUTH_QUERY "auth_query" +#define CONFIGURATION_ARGUMENT_FAILOVER "failover" +#define CONFIGURATION_ARGUMENT_FAILOVER_SCRIPT "failover_script" +#define CONFIGURATION_ARGUMENT_TLS "tls" +#define CONFIGURATION_ARGUMENT_TLS_CERT_FILE "tls_cert_file" +#define CONFIGURATION_ARGUMENT_TLS_KEY_FILE "tls_key_file" +#define CONFIGURATION_ARGUMENT_TLS_CA_FILE "tls_ca_file" +#define CONFIGURATION_ARGUMENT_LIBEV "libev" +#define CONFIGURATION_ARGUMENT_KEEP_ALIVE "keep_alive" +#define CONFIGURATION_ARGUMENT_NODELAY "nodelay" +#define CONFIGURATION_ARGUMENT_NON_BLOCKING "non_blocking" +#define CONFIGURATION_ARGUMENT_BACKLOG "backlog" +#define CONFIGURATION_ARGUMENT_HUGEPAGE "hugepage" +#define CONFIGURATION_ARGUMENT_TRACKER "tracker" +#define CONFIGURATION_ARGUMENT_TRACK_PREPARED_STATEMENTS "track_prepared_statements" +#define CONFIGURATION_ARGUMENT_PIDFILE "pidfile" +#define CONFIGURATION_ARGUMENT_UPDATE_PROCESS_TITLE "update_process_title" +#define CONFIGURATION_ARGUMENT_PRIMARY "primary" + /** * Initialize the configuration structure * @param shmem The shared memory segment @@ -415,6 +471,28 @@ pgagroal_apply_vault_configuration(struct vault_configuration* config, int pgagroal_apply_configuration(char* config_key, char* config_value); +/** + * Get a configuration parameter value + * @param ssl The SSL connection + * @param client_fd The client + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param payload The payload + */ +void +pgagroal_conf_get(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload); + +/** + * Set a configuration parameter value + * @param ssl The SSL connection + * @param client_fd The client + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param payload The payload + */ +void +pgagroal_conf_set(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload); + #ifdef __cplusplus } #endif diff --git a/src/include/management.h b/src/include/management.h index 19006d5a..7fc3f59a 100644 --- a/src/include/management.h +++ b/src/include/management.h @@ -67,9 +67,9 @@ extern "C" { * Management commands */ #define MANAGEMENT_CANCEL_SHUTDOWN 1 -/* #define MANAGEMENT_CONFIG_GET 2 */ -/* #define MANAGEMENT_CONFIG_LS 3 */ -/* #define MANAGEMENT_CONFIG_SET 4 */ +#define MANAGEMENT_CONFIG_LS 2 +#define MANAGEMENT_CONFIG_GET 3 +#define MANAGEMENT_CONFIG_SET 4 #define MANAGEMENT_DETAILS 5 #define MANAGEMENT_DISABLEDB 6 #define MANAGEMENT_ENABLEDB 7 @@ -92,6 +92,8 @@ extern "C" { #define MANAGEMENT_ARGUMENT_CLIENT_VERSION "ClientVersion" #define MANAGEMENT_ARGUMENT_COMMAND "Command" #define MANAGEMENT_ARGUMENT_COMPRESSION "Compression" +#define MANAGEMENT_ARGUMENT_CONFIG_KEY "ConfigKey" +#define MANAGEMENT_ARGUMENT_CONFIG_VALUE "ConfigValue" #define MANAGEMENT_ARGUMENT_CONNECTIONS "Connections" #define MANAGEMENT_ARGUMENT_DATABASE "Database" #define MANAGEMENT_ARGUMENT_DATABASES "Databases" @@ -142,6 +144,18 @@ extern "C" { #define MANAGEMENT_ERROR_STATUS_DETAILS_NOFORK 800 #define MANAGEMENT_ERROR_STATUS_DETAILS_NETWORK 801 +#define MANAGEMENT_ERROR_CONF_GET_NOFORK 900 +#define MANAGEMENT_ERROR_CONF_GET_NETWORK 901 +#define MANAGEMENT_ERROR_CONF_GET_ERROR 902 + +#define MANAGEMENT_ERROR_CONF_SET_NOFORK 1000 +#define MANAGEMENT_ERROR_CONF_SET_NETWORK 1001 +#define MANAGEMENT_ERROR_CONF_SET_ERROR 1002 +#define MANAGEMENT_ERROR_CONF_SET_NOREQUEST 1003 +#define MANAGEMENT_ERROR_CONF_SET_NOCONFIG_KEY_OR_VALUE 1004 +#define MANAGEMENT_ERROR_CONF_SET_UNKNOWN_SERVER 1005 +#define MANAGEMENT_ERROR_CONF_SET_UNKNOWN_CONFIGURATION_KEY 1006 + /** * Output formats */ @@ -350,20 +364,43 @@ pgagroal_management_request_reload(SSL* ssl, int socket, uint8_t compression, ui /* int */ /* pgagroal_management_request_config_set(SSL* ssl, int socket, char* config_key, char* config_value, uint8_t compression, uint8_t encryption, int32_t output_format); */ -/* /\** */ -/* * Entry point for managing the `conf ls` command that */ -/* * will list all the configuration files used by the running */ -/* * daemon. */ -/* * */ -/* * @param ssl the SSL handler */ -/* * @param socket the socket file descriptor */ -/* * @param compression The compress method for wire protocol */ -/* * @param encryption The encrypt method for wire protocol */ -/* * @param output_format The output format */ -/* * @returns 0 on success */ -/* *\/ */ -/* int */ -/* pgagroal_management_request_conf_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); */ +/** + * Create a conf ls request + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param output_format The output format + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_management_request_conf_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); + +/** + * Create a conf ls request + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param output_format The output format + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_management_request_conf_get(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); + +/** + * Create a conf get request + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param config_key The configuration key + * @param config_value The configuration value + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param output_format The output format + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_management_request_conf_set(SSL* ssl, int socket, char* config_key, char* config_value, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Get the frontend password of a user diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 5792e7b3..0a7bb744 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -110,6 +112,9 @@ static int to_log_mode(char* where, int value); static int to_log_level(char* where, int value); static int to_log_type(char* where, int value); +static void add_configuration_response(struct json* res); +static void add_servers_configuration_response(struct json* res); + /** * */ @@ -5302,3 +5307,755 @@ pgagroal_apply_limit_configuration_int(struct limit* limit, return 1; } + +static void +add_configuration_response(struct json* res) +{ + struct main_configuration* config = NULL; + + config = (struct main_configuration*)shmem; + + // JSON of main configuration + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_HOST, (uintptr_t)config->common.host, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_PORT, (uintptr_t)config->common.port, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_UNIX_SOCKET_DIR, (uintptr_t)config->unix_socket_dir, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_METRICS, (uintptr_t)config->common.metrics, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_METRICS_CACHE_MAX_AGE, (uintptr_t)config->common.metrics_cache_max_age, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_METRICS_CACHE_MAX_SIZE, (uintptr_t)config->common.metrics_cache_max_size, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_MANAGEMENT, (uintptr_t)config->management, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_TYPE, (uintptr_t)config->common.log_type, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_LEVEL, (uintptr_t)config->common.log_level, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_PATH, (uintptr_t)config->common.log_path, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_ROTATION_AGE, (uintptr_t)config->common.log_rotation_age, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_ROTATION_SIZE, (uintptr_t)config->common.log_rotation_size, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_LINE_PREFIX, (uintptr_t)config->common.log_line_prefix, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_MODE, (uintptr_t)config->common.log_mode, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_CONNECTIONS, (uintptr_t)config->common.log_connections, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LOG_DISCONNECTIONS, (uintptr_t)config->common.log_disconnections, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_BLOCKING_TIMEOUT, (uintptr_t)config->blocking_timeout, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_IDLE_TIMEOUT, (uintptr_t)config->idle_timeout, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_ROTATE_FRONTEND_PASSWORD_TIMEOUT, (uintptr_t)config->rotate_frontend_password_timeout, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_ROTATE_FRONTEND_PASSWORD_LENGTH, (uintptr_t)config->rotate_frontend_password_length, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_MAX_CONNECTION_AGE, (uintptr_t)config->max_connection_age, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_VALIDATION, (uintptr_t)config->validation, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_BACKGROUND_INTERVAL, (uintptr_t)config->background_interval, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_MAX_RETRIES, (uintptr_t)config->max_retries, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_MAX_CONNECTIONS, (uintptr_t)config->max_connections, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_ALLOW_UNKNOWN_USERS, (uintptr_t)config->allow_unknown_users, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_AUTHENTICATION_TIMEOUT, (uintptr_t)config->common.authentication_timeout, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_PIPELINE, (uintptr_t)config->pipeline, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_AUTH_QUERY, (uintptr_t)config->authquery, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_FAILOVER, (uintptr_t)config->failover, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_FAILOVER_SCRIPT, (uintptr_t)config->failover_script, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_TLS, (uintptr_t)config->common.tls, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_TLS_CERT_FILE, (uintptr_t)config->common.tls_cert_file, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_TLS_KEY_FILE, (uintptr_t)config->common.tls_key_file, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_TLS_CA_FILE, (uintptr_t)config->common.tls_ca_file, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_LIBEV, (uintptr_t)config->libev, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_KEEP_ALIVE, (uintptr_t)config->keep_alive, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_NODELAY, (uintptr_t)config->nodelay, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_NON_BLOCKING, (uintptr_t)config->non_blocking, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_BACKLOG, (uintptr_t)config->backlog, ValueInt64); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_HUGEPAGE, (uintptr_t)config->common.hugepage, ValueChar); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_TRACKER, (uintptr_t)config->tracker, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_TRACK_PREPARED_STATEMENTS, (uintptr_t)config->track_prepared_statements, ValueBool); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_PIDFILE, (uintptr_t)config->pidfile, ValueString); + pgagroal_json_put(res, CONFIGURATION_ARGUMENT_UPDATE_PROCESS_TITLE, (uintptr_t)config->update_process_title, ValueInt64); +} + +static void +add_servers_configuration_response(struct json* res) +{ + struct main_configuration* config = NULL; + + config = (struct main_configuration*)shmem; + + // JSON of server configuration + for (int i = 0; i < config->number_of_servers; i++) + { + struct json* server_conf = NULL; + + if (pgagroal_json_create(&server_conf)) + { + return; + } + + pgagroal_json_put(server_conf, CONFIGURATION_ARGUMENT_HOST, (uintptr_t)config->servers[i].host, ValueString); + pgagroal_json_put(server_conf, CONFIGURATION_ARGUMENT_PORT, (uintptr_t)config->servers[i].port, ValueInt64); + pgagroal_json_put(server_conf, CONFIGURATION_ARGUMENT_TLS, (uintptr_t)config->servers[i].tls, ValueBool); + pgagroal_json_put(server_conf, CONFIGURATION_ARGUMENT_TLS_CERT_FILE, (uintptr_t)config->servers[i].tls_cert_file, ValueString); + pgagroal_json_put(server_conf, CONFIGURATION_ARGUMENT_TLS_KEY_FILE, (uintptr_t)config->servers[i].tls_key_file, ValueString); + pgagroal_json_put(server_conf, CONFIGURATION_ARGUMENT_TLS_CA_FILE, (uintptr_t)config->servers[i].tls_ca_file, ValueString); + + pgagroal_json_put(res, config->servers[i].name, (uintptr_t)server_conf, ValueJSON); + } +} + +void +pgagroal_conf_get(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload) +{ + struct json* response = NULL; + char* elapsed = NULL; + time_t start_time; + time_t end_time; + int total_seconds; + + pgagroal_start_logging(); + + start_time = time(NULL); + + if (pgagroal_management_create_response(payload, -1, &response)) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_GET_ERROR, compression, encryption, payload); + pgagroal_log_error("Conf Get: Error creating json object (%d)", MANAGEMENT_ERROR_CONF_GET_ERROR); + goto error; + } + + add_configuration_response(response); + add_servers_configuration_response(response); + + end_time = time(NULL); + + if (pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload)) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_GET_NETWORK, compression, encryption, payload); + pgagroal_log_error("Conf Get: Error sending response"); + + goto error; + } + + elapsed = pgagroal_get_timestamp_string(start_time, end_time, &total_seconds); + + pgagroal_log_info("Conf Get (Elapsed: %s)", elapsed); + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + + exit(0); +error: + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + + exit(1); +} + +void +pgagroal_conf_set(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload) +{ + struct json* response = NULL; + struct json* request = NULL; + char* config_key = NULL; + char* config_value = NULL; + char* elapsed = NULL; + time_t start_time; + time_t end_time; + char section[MISC_LENGTH]; + char key[MISC_LENGTH]; + int total_seconds; + struct main_configuration* config = NULL; + struct json* server_j = NULL; + int server_index = -1; + int begin = -1, end = -1; + size_t max; + + pgagroal_start_logging(); + + start_time = time(NULL); + + config = (struct main_configuration*)shmem; + // Extract config_key and config_value from request + request = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + if (!request) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_NOREQUEST, compression, encryption, payload); + pgagroal_log_error("Conf Set: No request category found in payload (%d)", MANAGEMENT_ERROR_CONF_SET_NOREQUEST); + goto error; + } + + config_key = (char*)pgagroal_json_get(request, MANAGEMENT_ARGUMENT_CONFIG_KEY); + config_value = (char*)pgagroal_json_get(request, MANAGEMENT_ARGUMENT_CONFIG_VALUE); + + if (!config_key || !config_value) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_NOCONFIG_KEY_OR_VALUE, compression, encryption, payload); + pgagroal_log_error("Conf Set: No config key or config value in request (%d)", MANAGEMENT_ERROR_CONF_SET_NOCONFIG_KEY_OR_VALUE); + goto error; + } + + // Modify + memset(section, 0, MISC_LENGTH); + memset(key, 0, MISC_LENGTH); + + for (int i = 0; i < strlen(config_key); i++) + { + if (config_key[i] == '.') + { + if (!strlen(section)) + { + memcpy(section, &config_key[begin], end - begin + 1); + section[end - begin + 1] = '\0'; + begin = end = -1; + continue; + } + } + + if (begin < 0) + { + begin = i; + } + + end = i; + } + // if the key has not been found, since there is no ending dot, + // try to extract it from the string + if (!strlen(key)) + { + memcpy(key, &config_key[begin], end - begin + 1); + key[end - begin + 1] = '\0'; + } + + if (strlen(section) > 0) + { + if (pgagroal_json_create(&server_j)) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_ERROR, compression, encryption, payload); + pgagroal_log_error("Conf Set: Error creating json object (%d)", MANAGEMENT_ERROR_CONF_SET_ERROR); + goto error; + } + + for (int i = 0; i < config->number_of_servers; i++) + { + if (!strcmp(config->servers[i].name, section)) + { + server_index = i; + break; + } + } + if (server_index == -1) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_UNKNOWN_SERVER, compression, encryption, payload); + pgagroal_log_error("Conf Set: Unknown server value parsed (%d)", MANAGEMENT_ERROR_CONF_SET_UNKNOWN_SERVER); + goto error; + } + } + + if (pgagroal_management_create_response(payload, -1, &response)) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_ERROR, compression, encryption, payload); + pgagroal_log_error("Conf Set: Error creating json object (%d)", MANAGEMENT_ERROR_CONF_SET_ERROR); + goto error; + } + + if (strlen(key) && config_value) + { + bool unknown = false; + if (!strcmp(key, "host")) + { + if (strlen(section) > 0) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(&config->servers[server_index].host, config_value, max); + config->servers[server_index].host[max] = '\0'; + pgagroal_json_put(server_j, key, (uintptr_t)config->servers[server_index].host, ValueString); + pgagroal_json_put(response, config->servers[server_index].name, (uintptr_t)server_j, ValueJSON); + } + else + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->common.host, config_value, max); + config->common.host[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->common.host, ValueString); + } + } + else if (!strcmp(key, "port")) + { + if (strlen(section) > 0) + { + if (as_int(config_value, &config->servers[server_index].port)) + { + unknown = true; + } + pgagroal_json_put(server_j, key, (uintptr_t)config->servers[server_index].port, ValueInt64); + pgagroal_json_put(response, config->servers[server_index].name, (uintptr_t)server_j, ValueJSON); + } + else + { + if (as_int(config_value, &config->common.port)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.port, ValueInt64); + } + } + else if (!strcmp(key, "tls")) + { + if (strlen(section) > 0) + { + if (as_bool(config_value, &config->servers[server_index].tls)) + { + unknown = true; + } + pgagroal_json_put(server_j, key, (uintptr_t)config->servers[server_index].tls, ValueBool); + pgagroal_json_put(response, config->servers[server_index].name, (uintptr_t)server_j, ValueJSON); + } + else + { + if (as_bool(config_value, &config->common.tls)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.tls, ValueBool); + } + } + else if (!strcmp(key, "tls_cert_file")) + { + if (strlen(section) > 0) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(&config->servers[server_index].tls_cert_file, config_value, max); + config->servers[server_index].tls_cert_file[max] = '\0'; + pgagroal_json_put(server_j, key, (uintptr_t)config->servers[server_index].tls_cert_file, ValueString); + pgagroal_json_put(response, config->servers[server_index].name, (uintptr_t)server_j, ValueJSON); + } + else + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->common.tls_cert_file, config_value, max); + config->common.tls_cert_file[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->common.tls_cert_file, ValueString); + } + } + else if (!strcmp(key, "tls_key_file")) + { + if (strlen(section) > 0) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(&config->servers[server_index].tls_key_file, config_value, max); + config->servers[server_index].tls_key_file[max] = '\0'; + pgagroal_json_put(server_j, key, (uintptr_t)config->servers[server_index].tls_key_file, ValueString); + pgagroal_json_put(response, config->servers[server_index].name, (uintptr_t)server_j, ValueJSON); + } + else + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->common.tls_key_file, config_value, max); + config->common.tls_key_file[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->common.tls_key_file, ValueString); + } + } + else if (!strcmp(key, "tls_ca_file")) + { + if (strlen(section) > 0) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(&config->servers[server_index].tls_ca_file, config_value, max); + config->servers[server_index].tls_ca_file[max] = '\0'; + pgagroal_json_put(server_j, key, (uintptr_t)config->servers[server_index].tls_ca_file, ValueString); + pgagroal_json_put(response, config->servers[server_index].name, (uintptr_t)server_j, ValueJSON); + } + else + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->common.tls_ca_file, config_value, max); + config->common.tls_ca_file[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->common.tls_ca_file, ValueString); + } + } + else if (!strcmp(key, "unix_socket_dir")) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->unix_socket_dir, config_value, max); + config->unix_socket_dir[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->unix_socket_dir, ValueString); + } + else if (!strcmp(key, "metrics")) + { + if (as_int(config_value, &config->common.metrics)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.metrics, ValueInt64); + } + else if (!strcmp(key, "metrics_cache_max_age")) + { + if (as_seconds(config_value, &config->common.metrics_cache_max_age, PGAGROAL_PROMETHEUS_CACHE_DISABLED)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.metrics_cache_max_age, ValueInt64); + } + else if (!strcmp(key, "metrics_cache_max_size")) + { + if (as_bytes(config_value, &config->common.metrics_cache_max_size, PROMETHEUS_DEFAULT_CACHE_SIZE)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.metrics_cache_max_size, ValueInt64); + } + else if (!strcmp(key, "management")) + { + if (as_int(config_value, &config->management)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->management, ValueInt64); + } + else if (!strcmp(key, "log_type")) + { + config->common.log_type = as_logging_type(config_value); + pgagroal_json_put(response, key, (uintptr_t)config->common.log_type, ValueInt64); + } + else if (!strcmp(key, "log_level")) + { + config->common.log_level = as_logging_level(config_value); + pgagroal_json_put(response, key, (uintptr_t)config->common.log_level, ValueInt64); + } + else if (!strcmp(key, "log_path")) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->common.log_path, config_value, max); + config->common.log_path[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->common.log_path, ValueString); + } + else if (!strcmp(key, "log_rotation_age")) + { + if (as_logging_rotation_age(config_value, &config->common.log_rotation_age)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.log_rotation_age, ValueInt64); + } + else if (!strcmp(key, "log_rotation_size")) + { + if (as_logging_rotation_size(config_value, &config->common.log_rotation_size)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.log_rotation_size, ValueInt64); + } + else if (!strcmp(key, "log_line_prefix")) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->common.log_path, config_value, max); + config->common.log_path[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->common.log_path, ValueString); + } + else if (!strcmp(key, "log_mode")) + { + config->common.log_mode = as_logging_mode(config_value); + pgagroal_json_put(response, key, (uintptr_t)config->common.log_mode, ValueInt64); + } + else if (!strcmp(key, "log_connetions")) + { + if (as_bool(config_value, &config->common.log_connections)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.log_connections, ValueBool); + } + else if (!strcmp(key, "log_disconnetions")) + { + if (as_bool(config_value, &config->common.log_disconnections)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.log_disconnections, ValueBool); + } + else if (!strcmp(key, "blocking_timeout")) + { + if (as_int(config_value, &config->blocking_timeout)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->blocking_timeout, ValueInt64); + } + else if (!strcmp(key, "idle_timeout")) + { + if (as_int(config_value, &config->idle_timeout)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->idle_timeout, ValueInt64); + } + else if (!strcmp(key, "rotate_frontend_password_length")) + { + if (as_int(config_value, &config->rotate_frontend_password_length)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->rotate_frontend_password_length, ValueInt64); + } + else if (!strcmp(key, "rotate_frontend_password_timeout")) + { + if (as_int(config_value, &config->rotate_frontend_password_timeout)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->rotate_frontend_password_timeout, ValueInt64); + } + else if (!strcmp(key, "max_connection_age")) + { + if (as_int(config_value, &config->max_connection_age)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->max_connection_age, ValueInt64); + } + else if (!strcmp(key, "validation")) + { + config->validation = as_validation(config_value); + pgagroal_json_put(response, key, (uintptr_t)config->validation, ValueInt64); + } + else if (!strcmp(key, "background_interval")) + { + if (as_int(config_value, &config->background_interval)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->background_interval, ValueInt64); + } + else if (!strcmp(key, "max_retries")) + { + if (as_int(config_value, &config->max_retries)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->max_retries, ValueInt64); + } + else if (!strcmp(key, "max_connections")) + { + if (as_int(config_value, &config->max_connections)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->max_connections, ValueInt64); + } + else if (!strcmp(key, "allow_unknown_users")) + { + if (as_bool(config_value, &config->allow_unknown_users)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->allow_unknown_users, ValueBool); + } + else if (!strcmp(key, "authentication_timeout")) + { + if (as_int(config_value, &config->common.authentication_timeout)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->common.authentication_timeout, ValueInt64); + } + else if (!strcmp(key, "pipeline")) + { + if (as_int(config_value, &config->pipeline)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->pipeline, ValueInt64); + } + else if (!strcmp(key, "auth_query")) + { + if (as_bool(config_value, &config->authquery)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->authquery, ValueBool); + } + else if (!strcmp(key, "failover")) + { + if (as_bool(config_value, &config->failover)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->failover, ValueBool); + } + else if (!strcmp(key, "keep_alive")) + { + if (as_bool(config_value, &config->keep_alive)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->keep_alive, ValueBool); + } + else if (!strcmp(key, "nodelay")) + { + if (as_bool(config_value, &config->nodelay)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->nodelay, ValueBool); + } + else if (!strcmp(key, "non_blocking")) + { + if (as_bool(config_value, &config->non_blocking)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->non_blocking, ValueBool); + } + else if (!strcmp(key, "backlog")) + { + if (as_int(config_value, &config->backlog)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->backlog, ValueInt64); + } + else if (!strcmp(key, "tracker")) + { + if (as_bool(config_value, &config->tracker)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->tracker, ValueBool); + } + else if (!strcmp(key, "track_prepared_statements")) + { + if (as_bool(config_value, &config->track_prepared_statements)) + { + unknown = true; + } + pgagroal_json_put(response, key, (uintptr_t)config->track_prepared_statements, ValueBool); + } + else if (!strcmp(key, "hugepage")) + { + config->common.hugepage = as_hugepage(config_value); + pgagroal_json_put(response, key, (uintptr_t)config->common.hugepage, ValueInt64); + } + else if (!strcmp(key, "pidfile")) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->pidfile, config_value, max); + config->pidfile[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->pidfile, ValueString); + } + else if (!strcmp(key, "failover_script")) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->failover_script, config_value, max); + config->failover_script[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->failover_script, ValueString); + } + else if (!strcmp(key, "libev")) + { + max = strlen(config_value); + if (max > MISC_LENGTH - 1) + { + max = MISC_LENGTH - 1; + } + memcpy(config->libev, config_value, max); + config->libev[max] = '\0'; + pgagroal_json_put(response, key, (uintptr_t)config->libev, ValueString); + } + else if (!strcmp(key, "update_process_title")) + { + if (as_update_process_title(config_value, &config->update_process_title, UPDATE_PROCESS_TITLE_VERBOSE)) + { + unknown = false; + } + pgagroal_json_put(response, key, (uintptr_t)config->update_process_title, ValueInt64); + } + else + { + unknown = true; + } + + if (unknown) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_UNKNOWN_CONFIGURATION_KEY, compression, encryption, payload); + pgagroal_log_error("Conf Set: Unknown configuration key found (%d)", MANAGEMENT_ERROR_CONF_SET_UNKNOWN_CONFIGURATION_KEY); + goto error; + } + } + + end_time = time(NULL); + + if (pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload)) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_NETWORK, compression, encryption, payload); + pgagroal_log_error("Conf Set: Error sending response"); + goto error; + } + + elapsed = pgagroal_get_timestamp_string(start_time, end_time, &total_seconds); + + pgagroal_log_info("Conf Set (Elapsed: %s)", elapsed); + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + + exit(0); +error: + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + + exit(1); + +} \ No newline at end of file diff --git a/src/libpgagroal/management.c b/src/libpgagroal/management.c index 2bdf6bf3..2d5e6ca1 100644 --- a/src/libpgagroal/management.c +++ b/src/libpgagroal/management.c @@ -533,6 +533,105 @@ pgagroal_management_request_reload(SSL* ssl, int socket, uint8_t compression, ui return 1; } +int +pgagroal_management_request_conf_ls(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + struct json* j = NULL; + struct json* request = NULL; + + if (create_header(MANAGEMENT_CONFIG_LS, compression, encryption, output_format, &j)) + { + goto error; + } + + if (create_request(j, &request)) + { + goto error; + } + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) + { + goto error; + } + + pgagroal_json_destroy(j); + + return 0; + +error: + + pgagroal_json_destroy(j); + + return 1; +} + +int +pgagroal_management_request_conf_get(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + struct json* j = NULL; + struct json* request = NULL; + + if (create_header(MANAGEMENT_CONFIG_GET, compression, encryption, output_format, &j)) + { + goto error; + } + + if (create_request(j, &request)) + { + goto error; + } + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) + { + goto error; + } + + pgagroal_json_destroy(j); + + return 0; + +error: + + pgagroal_json_destroy(j); + + return 1; +} + +int +pgagroal_management_request_conf_set(SSL* ssl, int socket, char* config_key, char* config_value, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + struct json* j = NULL; + struct json* request = NULL; + + if (create_header(MANAGEMENT_CONFIG_SET, compression, encryption, output_format, &j)) + { + goto error; + } + + if (create_request(j, &request)) + { + goto error; + } + + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_CONFIG_KEY, (uintptr_t)config_key, ValueString); + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_CONFIG_VALUE, (uintptr_t)config_value, ValueString); + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) + { + goto error; + } + + pgagroal_json_destroy(j); + + return 0; + +error: + + pgagroal_json_destroy(j); + + return 1; +} + static int create_header(int32_t command, uint8_t compression, uint8_t encryption, int32_t output_format, struct json** json) { diff --git a/src/main.c b/src/main.c index 20fac745..5e9a5f57 100644 --- a/src/main.c +++ b/src/main.c @@ -1786,23 +1786,68 @@ accept_mgt_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); } - /* else if (id == MANAGEMENT_CONFIG_GET) */ - /* { */ - /* pgagroal_log_debug("pgagroal: Management config-get for key <%s>", payload_s); */ - /* pgagroal_management_write_config_get(client_fd, payload_s); */ - /* } */ - /* else if (id == MANAGEMENT_CONFIG_SET) */ - /* { */ - /* // this command has a secondary payload to extract, that is the configuration value */ - /* pgagroal_management_read_payload(client_fd, id, &secondary_payload_i, &secondary_payload_s); */ - /* pgagroal_log_debug("pgagroal: Management config-set for key <%s> setting value to <%s>", payload_s, secondary_payload_s); */ - /* pgagroal_management_write_config_set(client_fd, payload_s, secondary_payload_s); */ - /* } */ - /* else if (id == MANAGEMENT_CONFIG_LS) */ - /* { */ - /* pgagroal_log_debug("pgagroal: Management conf ls"); */ - /* pgagroal_management_write_conf_ls(client_fd); */ - /* } */ + else if (id == MANAGEMENT_CONFIG_LS) + { + struct json* response = NULL; + + start_time = time(NULL); + + pgagroal_management_create_response(payload, -1, &response); + + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_MAIN_CONF_PATH, (uintptr_t)config->common.configuration_path, ValueString); + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_HBA_CONF_PATH, (uintptr_t)config->hba_path, ValueString); + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_LIMIT_CONF_PATH, (uintptr_t)config->limit_path, ValueString); + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_USER_CONF_PATH, (uintptr_t)config->users_path, ValueString); + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_FRONTEND_USERS_CONF_PATH, (uintptr_t)config->frontend_users_path, ValueString); + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_ADMIN_CONF_PATH, (uintptr_t)config->admins_path, ValueString); + pgagroal_json_put(response, CONFIGURATION_ARGUMENT_SUPERUSER_CONF_PATH, (uintptr_t)config->superuser_path, ValueString); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_CONFIG_GET) + { + pid = fork(); + if (pid == -1) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_GET_NOFORK, compression, encryption, payload); + pgagroal_log_error("Conf Get: No fork %s (%d)", NULL, MANAGEMENT_ERROR_CONF_GET_NOFORK); + goto error; + } + else if (pid == 0) + { + struct json* pyl = NULL; + + shutdown_ports(); + + pgagroal_json_clone(payload, &pyl); + + pgagroal_set_proc_title(1, ai->argv, "conf get", NULL); + pgagroal_conf_get(NULL, client_fd, compression, encryption, pyl); + } + } + else if (id == MANAGEMENT_CONFIG_SET) + { + pid = fork(); + if (pid == -1) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_CONF_SET_NOFORK, compression, encryption, payload); + pgagroal_log_error("Conf Set: No fork %s (%d)", NULL, MANAGEMENT_ERROR_CONF_SET_NOFORK); + goto error; + } + else if (pid == 0) + { + struct json* pyl = NULL; + + shutdown_ports(); + + pgagroal_json_clone(payload, &pyl); + + pgagroal_set_proc_title(1, ai->argv, "conf set", NULL); + pgagroal_conf_set(NULL, client_fd, compression, encryption, pyl); + } + } else if (id == MANAGEMENT_GET_PASSWORD) { int index = -1;