diff --git a/src/cli.c b/src/cli.c index 26fabac8..d0e8245c 100644 --- a/src/cli.c +++ b/src/cli.c @@ -621,9 +621,18 @@ main(int argc, char** argv) if (configuration_path != NULL) { - if (action != ACTION_UNKNOWN && exit_code != 0) + if (action != ACTION_UNKNOWN) { - printf("Connection error on %s\n", config->unix_socket_dir); + switch (exit_code) + { + case EXIT_STATUS_CONNECTION_ERROR: + printf("Connection error on %s\n", config->unix_socket_dir); + break; + case EXIT_STATUS_DATA_ERROR: + case EXIT_STATUS_OK: + break; + } + } } @@ -637,7 +646,7 @@ main(int argc, char** argv) if (verbose) { - warnx("%s (%d)\n", exit_code == 0 ? "Success" : "Error", exit_code); + warnx("%s (%d)\n", exit_code == EXIT_STATUS_OK ? "Success" : "Error", exit_code); } return exit_code; @@ -648,10 +657,10 @@ flush(SSL* ssl, int socket, int32_t mode, char* database) { if (pgagroal_management_flush(ssl, socket, mode, database)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -659,10 +668,10 @@ enabledb(SSL* ssl, int socket, char* database) { if (pgagroal_management_enabledb(ssl, socket, database)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -670,10 +679,10 @@ disabledb(SSL* ssl, int socket, char* database) { if (pgagroal_management_disabledb(ssl, socket, database)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -681,10 +690,10 @@ gracefully(SSL* ssl, int socket) { if (pgagroal_management_gracefully(ssl, socket)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -692,10 +701,10 @@ stop(SSL* ssl, int socket) { if (pgagroal_management_stop(ssl, socket)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -703,10 +712,10 @@ cancel_shutdown(SSL* ssl, int socket) { if (pgagroal_management_cancel_shutdown(ssl, socket)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -718,7 +727,7 @@ status(SSL* ssl, int socket) } else { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } } @@ -734,7 +743,7 @@ details(SSL* ssl, int socket) } // if here, an error occurred - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } @@ -747,20 +756,20 @@ isalive(SSL* ssl, int socket) { if (pgagroal_management_read_isalive(ssl, socket, &status)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } if (status != 1 && status != 2) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } } else { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -768,10 +777,10 @@ reset(SSL* ssl, int socket) { if (pgagroal_management_reset(ssl, socket)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -779,10 +788,10 @@ reset_server(SSL* ssl, int socket, char* server) { if (pgagroal_management_reset_server(ssl, socket, server)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -790,10 +799,10 @@ switch_to(SSL* ssl, int socket, char* server) { if (pgagroal_management_switch_to(ssl, socket, server)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } static int @@ -801,10 +810,10 @@ reload(SSL* ssl, int socket) { if (pgagroal_management_reload(ssl, socket)) { - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } - return 0; + return EXIT_STATUS_OK; } /** @@ -818,7 +827,7 @@ reload(SSL* ssl, int socket) * @param config_key the key of the configuration parameter, that is the name * of the configuration parameter to read. * @param verbose if true the function will print on STDOUT also the config key - * @returns 0 on success, 1 on failure + * @returns 0 on success, 1 on network failure, 2 on data failure */ static int config_get(SSL* ssl, int socket, char* config_key, bool verbose) @@ -844,9 +853,9 @@ config_get(SSL* ssl, int socket, char* config_key, bool verbose) goto error; } - // assume an empty response is ok, - // do not throw an error to indicate no configuration - // setting with such name as been found + // an empty response means that the + // requested configuration parameter has not been + // found, so throw an error if (buffer && strlen(buffer)) { if (verbose) @@ -858,13 +867,17 @@ config_get(SSL* ssl, int socket, char* config_key, bool verbose) printf("%s\n", buffer); } } + else + { + free(buffer); + return EXIT_STATUS_DATA_ERROR; + } free(buffer); - return 0; } - return 0; + return EXIT_STATUS_OK; error: - return 1; + return EXIT_STATUS_CONNECTION_ERROR; } diff --git a/src/include/configuration.h b/src/include/configuration.h index 531ea522..ea737358 100644 --- a/src/include/configuration.h +++ b/src/include/configuration.h @@ -284,10 +284,11 @@ pgagroal_can_prefill(void); * @param buffer where to write the configuration value. The buffer must * be already allocated. In case of failure, the buffer is zero filled. * @param config_key the name of the configuration parameter + * @param buffer_size the max length of the buffer where the result will be stored * @return 0 on success, 1 when the key cannot be found */ int -pgagroal_write_config_value(char* buffer, char* config_key); +pgagroal_write_config_value(char* buffer, char* config_key, size_t buffer_size); #ifdef __cplusplus } diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index 0b9c89e1..bdcf4f67 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -141,6 +141,15 @@ extern "C" { #define UPDATE_PROCESS_TITLE_MINIMAL 2 #define UPDATE_PROCESS_TITLE_VERBOSE 3 +/** + * Constants used to manage the exit code + * of a command sent over the socket in the + * management stuff, e.g., `pgagroal-cli`. + */ +#define EXIT_STATUS_OK 0 +#define EXIT_STATUS_CONNECTION_ERROR 1 +#define EXIT_STATUS_DATA_ERROR 2 + #define likely(x) __builtin_expect (!!(x), 1) #define unlikely(x) __builtin_expect (!!(x), 0) diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index a3aef1dd..6bec5ee0 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -90,11 +90,11 @@ static bool key_in_section(char* wanted, char* section, char* key, bool global, static bool is_comment_line(char* line); static bool section_line(char* line, char* section); -static int pgagroal_write_server_config_value(char* buffer, char* server_name, char* config_key); -static int pgagroal_write_hba_config_value(char* buffer, char* username, char* config_key); -static int pgagroal_write_limit_config_value(char* buffer, char* database, char* config_key); +static int pgagroal_write_server_config_value(char* buffer, char* server_name, char* config_key, size_t buffer_size); +static int pgagroal_write_hba_config_value(char* buffer, char* username, char* config_key, size_t buffer_size); +static int pgagroal_write_limit_config_value(char* buffer, char* database, char* config_key, size_t buffer_size); -static int to_string(char* where, char* value); +static int to_string(char* where, char* value, size_t max_length); static int to_bool(char* where, bool value); static int to_int(char* where, int value); static int to_update_process_title(char* where, int value); @@ -3262,7 +3262,7 @@ as_update_process_title(char* str, unsigned int* policy, unsigned int default_po } int -pgagroal_write_config_value(char* buffer, char* config_key) +pgagroal_write_config_value(char* buffer, char* config_key, size_t buffer_size) { struct configuration* config; @@ -3330,15 +3330,15 @@ pgagroal_write_config_value(char* buffer, char* config_key) if (!strncmp(section, "server", MISC_LENGTH)) { - return pgagroal_write_server_config_value(buffer, context, key); + return pgagroal_write_server_config_value(buffer, context, key, buffer_size); } else if (!strncmp(section, "hba", MISC_LENGTH)) { - return pgagroal_write_hba_config_value(buffer, context, key); + return pgagroal_write_hba_config_value(buffer, context, key, buffer_size); } else if (!strncmp(section, "limit", MISC_LENGTH)) { - return pgagroal_write_limit_config_value(buffer, context, key); + return pgagroal_write_limit_config_value(buffer, context, key, buffer_size); } else if (main_section) { @@ -3347,7 +3347,7 @@ pgagroal_write_config_value(char* buffer, char* config_key) if (!strncmp(key, "host", MISC_LENGTH)) { - return to_string(buffer, config->host); + return to_string(buffer, config->host, buffer_size); } else if (!strncmp(key, "port", MISC_LENGTH)) { @@ -3363,7 +3363,7 @@ pgagroal_write_config_value(char* buffer, char* config_key) } else if (!strncmp(key, "log_line_prefix", MISC_LENGTH)) { - return to_string(buffer, config->log_line_prefix); + return to_string(buffer, config->log_line_prefix, buffer_size); } else if (!strncmp(key, "log_level", MISC_LENGTH)) @@ -3390,7 +3390,7 @@ pgagroal_write_config_value(char* buffer, char* config_key) } else if (!strncmp(key, "log_path", MISC_LENGTH)) { - return to_string(buffer, config->log_path); + return to_string(buffer, config->log_path, buffer_size); } else if (!strncmp(key, "metrics", MISC_LENGTH)) { @@ -3415,7 +3415,7 @@ pgagroal_write_config_value(char* buffer, char* config_key) } else if (!strncmp(key, "failover_script", MISC_LENGTH)) { - return to_string(buffer, config->failover_script); + return to_string(buffer, config->failover_script, buffer_size); } else if (!strncmp(key, "tls", MISC_LENGTH)) { @@ -3427,15 +3427,15 @@ pgagroal_write_config_value(char* buffer, char* config_key) } else if (!strncmp(key, "tls_ca_file", MISC_LENGTH)) { - return to_string(buffer, config->tls_ca_file); + return to_string(buffer, config->tls_ca_file, buffer_size); } else if (!strncmp(key, "tls_cert_file", MISC_LENGTH)) { - return to_string(buffer, config->tls_cert_file); + return to_string(buffer, config->tls_cert_file, buffer_size); } else if (!strncmp(key, "tls_key_file", MISC_LENGTH)) { - return to_string(buffer, config->tls_key_file); + return to_string(buffer, config->tls_key_file, buffer_size); } else if (!strncmp(key, "blocking_timeout", MISC_LENGTH)) { @@ -3471,7 +3471,7 @@ pgagroal_write_config_value(char* buffer, char* config_key) } else if (!strncmp(key, "pidfile", MISC_LENGTH)) { - return to_string(buffer, config->pidfile); + return to_string(buffer, config->pidfile, buffer_size); } else if (!strncmp(key, "allow_unknown_users", MISC_LENGTH)) { @@ -3483,7 +3483,7 @@ pgagroal_write_config_value(char* buffer, char* config_key) } else if (!strncmp(key, "unix_socket_dir", MISC_LENGTH)) { - return to_string(buffer, config->unix_socket_dir); + return to_string(buffer, config->unix_socket_dir, buffer_size); } else if (!strncmp(key, "buffer_size", MISC_LENGTH)) { @@ -3513,6 +3513,11 @@ pgagroal_write_config_value(char* buffer, char* config_key) { return to_bool(buffer, config->track_prepared_statements); } + else + { + goto error; + } + } // end of global configuration settings else { @@ -3531,10 +3536,11 @@ pgagroal_write_config_value(char* buffer, char* config_key) * @param server_name the name of the server * @param config_key one of the configuration keys allowed in the server section * @param buffer the buffer where to write the stringified version of the value + * @param buffer_size the max size of the buffer where the result will be stored * @return 0 on success */ static int -pgagroal_write_server_config_value(char* buffer, char* server_name, char* config_key) +pgagroal_write_server_config_value(char* buffer, char* server_name, char* config_key, size_t buffer_size) { int server_index = -1; struct configuration* config; @@ -3560,7 +3566,7 @@ pgagroal_write_server_config_value(char* buffer, char* server_name, char* config if (!strncmp(config_key, "host", MISC_LENGTH)) { - return to_string(buffer, config->servers[server_index].host); + return to_string(buffer, config->servers[server_index].host, buffer_size); } else if (!strncmp(config_key, "port", MISC_LENGTH)) { @@ -3602,10 +3608,11 @@ pgagroal_write_server_config_value(char* buffer, char* server_name, char* config * @param buffer where to write the stringified value * @param username the username that must match the entry on the HBA entry line * @param config_key the configuration parameter to search for + * @param buffer_size the max length of the destination buffer * @return 0 on success */ static int -pgagroal_write_hba_config_value(char* buffer, char* username, char* config_key) +pgagroal_write_hba_config_value(char* buffer, char* username, char* config_key, size_t buffer_size) { int hba_index = -1; struct configuration* config; @@ -3630,23 +3637,23 @@ pgagroal_write_hba_config_value(char* buffer, char* username, char* config_key) if (!strncmp(config_key, "type", MISC_LENGTH)) { - return to_string(buffer, config->hbas[hba_index].type); + return to_string(buffer, config->hbas[hba_index].type, buffer_size); } else if (!strncmp(config_key, "database", MISC_LENGTH)) { - return to_string(buffer, config->hbas[hba_index].database); + return to_string(buffer, config->hbas[hba_index].database, buffer_size); } else if (!strncmp(config_key, "username", MISC_LENGTH)) { - return to_string(buffer, config->hbas[hba_index].username); + return to_string(buffer, config->hbas[hba_index].username, buffer_size); } else if (!strncmp(config_key, "address", MISC_LENGTH)) { - return to_string(buffer, config->hbas[hba_index].address); + return to_string(buffer, config->hbas[hba_index].address, buffer_size); } else if (!strncmp(config_key, "method", MISC_LENGTH)) { - return to_string(buffer, config->hbas[hba_index].method); + return to_string(buffer, config->hbas[hba_index].method, buffer_size); } else { @@ -3665,10 +3672,11 @@ pgagroal_write_hba_config_value(char* buffer, char* username, char* config_key) * @param buffer where to write the information * @param database the username to search for * @param config_key the value to seek into the limits + * @param buffer_size the max size of the destination buffer where the result will be written * @return 0 on success */ static int -pgagroal_write_limit_config_value(char* buffer, char* database, char* config_key) +pgagroal_write_limit_config_value(char* buffer, char* database, char* config_key, size_t buffer_size) { int limit_index = -1; struct configuration* config; @@ -3693,11 +3701,11 @@ pgagroal_write_limit_config_value(char* buffer, char* database, char* config_key if (!strncmp(config_key, "username", MISC_LENGTH)) { - return to_string(buffer, config->limits[limit_index].username); + return to_string(buffer, config->limits[limit_index].username, buffer_size); } else if (!strncmp(config_key, "database", MISC_LENGTH)) { - return to_string(buffer, config->limits[limit_index].database); + return to_string(buffer, config->limits[limit_index].database, buffer_size); } else if (!strncmp(config_key, "max_size", MISC_LENGTH)) { @@ -3765,18 +3773,28 @@ to_bool(char* where, bool value) * tries to be as smart as possible identifying if there is the need for * single or double quotes. * + * The function accepts the size of the destination string, and before writing + * into such a string the result, it zero fills it. This means it is not mandatory + * to zero fill the destination string before calling this function. + * Also please note that if the string that is copied into the destination string + * has a length bigger than that specified, the function will not copy any data + * (and will not zero set the destination string, that will remain untouched!) + * * @param where the string where to print the value, must be already allocated * @param value the value to convert into a string + * @param max_length the max length of the 'where' destination string * @return 0 on success, 1 otherwise */ static int -to_string(char* where, char* value) +to_string(char* where, char* value, size_t max_length) { bool needs_quotes = false; bool has_double_quotes = false; bool has_single_quotes = false; + char quoting_char = '\0'; + int index = 0; - if (!where || !value || strlen(value) > MISC_LENGTH) + if (!where || !value || strlen(value) >= max_length) { return 1; } @@ -3796,26 +3814,51 @@ to_string(char* where, char* value) { has_single_quotes = true; } + } needs_quotes = needs_quotes || has_double_quotes || has_single_quotes; if (needs_quotes) { + // there must be space for quotes + if (strlen(value) > max_length - 2 - 1) + { + return 1; + } + if (!has_single_quotes) { - snprintf(where, MISC_LENGTH, "'%s'", value); + quoting_char = '\''; } else if (!has_double_quotes) { - snprintf(where, MISC_LENGTH, "\"%s\"", value); + quoting_char = '"'; } + } - else + + // if here, the size of the string is appropriate, + // so do the copy + memset(where, 0, max_length); + + if (needs_quotes) { - snprintf(where, MISC_LENGTH, "%s", value); + memcpy(&where[index], "ing_char, sizeof(quoting_char)); + index += sizeof(quoting_char); } + memcpy(&where[index], value, strlen(value)); + index += strlen(value); + + if (needs_quotes) + { + memcpy(&where[index], "ing_char, sizeof(quoting_char)); + index += sizeof(quoting_char); + } + + where[index] = '\0'; + return 0; } diff --git a/src/libpgagroal/management.c b/src/libpgagroal/management.c index fef8ef33..cc9a7c3b 100644 --- a/src/libpgagroal/management.c +++ b/src/libpgagroal/management.c @@ -1549,10 +1549,10 @@ pgagroal_management_write_config_get(int socket, char* config_key) memset(&data, 0, sizeof(data)); - if (pgagroal_write_config_value(&data[0], config_key)) + if (pgagroal_write_config_value(&data[0], config_key, sizeof(data))) { pgagroal_log_warn("pgagroal_management_write_config_get: unknwon configuration key <%s>", config_key); - goto error; + // leave the payload empty, so a zero filled payload will be sent } // send the size of the payload