From b089d4399e276cca3412ab9460d881f2ec52d848 Mon Sep 17 00:00:00 2001 From: Luca Ferrari Date: Thu, 30 Jun 2022 10:08:17 -0400 Subject: [PATCH] [#285] Introduce `update_process_title` setting This commit adds the `update_process_title` configuration setting. Values for such setting are: - `never` or `off`, means the user does not want to update the process title at all; - `strict` (used on Linux and systems that do no provide a native call to update the process title) allows for changing the process title without overflowing the length of the initial command line; - `minimal` sets the process title to `user/database` even if this exceeds the initial command line length; - `verbose` sets the process title to `user@host:port/database` even if this overflows the initial command line length. By default the setting is configured as `verbose`, but this could break some aggressively secured environments, where no native way to set the process title is provided. The `pgagroal_set_proc_title` function has been refactored so that, if the update policy is set to `never` the process title will never be updated. This can be confusing because even the "main" process will not have a title update. Likely, if the policy is `strict`, the function will never try to overflow the initial command line length. When set to `verbose` or leaved as default, the process title is built with information about the hostname, port, username and database. This can produce a very long title, that is in any case cut at `MAX_PROCESS_TITLE_LENGTH` (256 bytes, including the terminator). In the unluckily case the primary server cannot be determined, the `verbose` mode is set back to `minimal`, but this indicates there is something wrong somewhere else. On those systems that do provide a native way to set the process title, there is no difference between `strict` and `minimal` and the `minmal` policy is always adopted. Documentation updated. Close #285 --- doc/CONFIGURATION.md | 1 + src/include/pgagroal.h | 8 +++++ src/include/utils.h | 21 ++++++++++- src/libpgagroal/configuration.c | 63 +++++++++++++++++++++++++++++++++ src/libpgagroal/utils.c | 38 ++++++++++++++++---- src/libpgagroal/worker.c | 14 +++++++- 6 files changed, 137 insertions(+), 8 deletions(-) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 9c4dff6b..3a10fac3 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -77,6 +77,7 @@ See a more complete [sample](./etc/pgagroal.conf) configuration for running `pga | tracker | off | Bool | No | Track connection lifecycle | | track_prepared_statements | off | Bool | No | Track prepared statements (transaction pooling) | | pidfile | | String | No | Path to the PID file. If omitted, automatically set to `unix_socket_dir`/pgagroal.`port`.pid | +| update_process_title | `verbose` | No | The behavior for updating the operating system process title, mainly related to connection processes. Allowed settings are: `never` (or `off`), does not update the process title; `strict` to set the process title without overriding the existing initial process title length; `minimal` to set the process title to `username/database`; `verbose` (or `full`) to set the process title to `user@host:port/database`. Please note that `strict` and `minimal` are honored only on those systems that do not provide a native way to set the process title (e.g., Linux). On other systems, there is no difference between `strict` and `minimal` and the assumed behaviour is `minimal` even if `strict` is used. `never` and `verbose` are always honored, on every system. On Linux systems the process title is always trimmed to 255 characters, while on system that provide a natve way to set the process title it can be longer. | __Danger zone__ diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index b04c3d6d..d91dc099 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -62,6 +62,7 @@ extern "C" { #define PGAGROAL_DEFAULT_ADMINS_FILE PGAGROAL_DEFAULT_CONFIGURATION_PATH "pgagroal_admins.conf" #define PGAGROAL_DEFAULT_SUPERUSER_FILE PGAGROAL_DEFAULT_CONFIGURATION_PATH "pgagroal_superuser.conf" +#define MAX_PROCESS_TITLE_LENGTH 256 #define MAX_BUFFER_SIZE 65535 #define DEFAULT_BUFFER_SIZE 65535 @@ -134,6 +135,11 @@ extern "C" { #define HUGEPAGE_TRY 1 #define HUGEPAGE_ON 2 +#define UPDATE_PROCESS_TITLE_NEVER 0 +#define UPDATE_PROCESS_TITLE_STRICT 1 +#define UPDATE_PROCESS_TITLE_MINIMAL 2 +#define UPDATE_PROCESS_TITLE_VERBOSE 3 + #define likely(x) __builtin_expect (!!(x), 1) #define unlikely(x) __builtin_expect (!!(x), 0) @@ -399,6 +405,8 @@ struct configuration char log_line_prefix[MISC_LENGTH]; /**< The logging prefix */ atomic_schar log_lock; /**< The logging lock */ + unsigned int update_process_title; /**< Behaviour for updating the process title */ + bool authquery; /**< Is authentication query enabled */ bool tls; /**< Is TLS enabled */ diff --git a/src/include/utils.h b/src/include/utils.h index ad0b0660..02a80ce8 100644 --- a/src/include/utils.h +++ b/src/include/utils.h @@ -245,7 +245,22 @@ int pgagroal_base64_decode(char* encoded, size_t encoded_length, char** raw, int* raw_length); /** - * Set process title + * Set process title. + * + * The function will autonomously check the update policy set + * via the configuration option `update_process_title` and + * will do nothing if the setting is `never`. + * In the case the policy is set to `strict`, the process title + * will not overflow the initial command line length (i.e., strlen(argv[*])) + * otherwise it will do its best to set the title to the desired string. + * + * The policies `strict` and `minimal` will be honored only on Linux platforms + * where a native call to set the process title is not available. + * + * + * The resulting process title will be set to either `s1` or `s1/s2` if there + * both strings and the length is allowed by the policy. + * * @param argc The number of arguments * @param argv The argv pointer * @param s1 The first string @@ -261,6 +276,10 @@ pgagroal_set_proc_title(int argc, char** argv, char* s1, char* s2); * with the form * user@host:port/database * + * This means that all the policies honored by the latter function and + * set via the `update_process_title` configuration paramter will be + * honored. + * * @param argc the number of arguments * @param argv command line arguments * @param connection the struct connection pointer for the established connection. diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index 1f960005..457ee357 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -66,6 +66,7 @@ static int as_logging_rotation_age(char* str, unsigned int* age); static int as_validation(char* str); static int as_pipeline(char* str); static int as_hugepage(char* str); +static unsigned int as_update_process_title(char* str, unsigned int* policy, unsigned int default_policy); static int extract_value(char* str, int offset, char** value); static void extract_hba(char* str, char** type, char** database, char** user, char** address, char** method); static void extract_limit(char* str, int server_max, char** database, char** user, int* max_size, int* initial_size, int* min_size); @@ -141,6 +142,8 @@ pgagroal_init_configuration(void* shm) atomic_init(&config->su_connection, STATE_FREE); + config->update_process_title = UPDATE_PROCESS_TITLE_VERBOSE; + return 0; } @@ -570,6 +573,13 @@ pgagroal_read_configuration(void* shm, char* filename, bool emitWarnings) unknown = true; } } + else if (key_in_section("update_process_title", section, key, true, &unknown)) + { + if (as_update_process_title(value, &config->update_process_title, UPDATE_PROCESS_TITLE_VERBOSE)) + { + unknown = false; + } + } else { unknown = true; @@ -2416,6 +2426,9 @@ transfer_configuration(struct configuration* config, struct configuration* reloa config->metrics_cache_max_age = reload->metrics_cache_max_age; restart_int("metrics_cache_max_size", config->metrics_cache_max_size, reload->metrics_cache_max_size); config->management = reload->management; + + config->update_process_title = reload->update_process_title; + /* gracefully */ /* disabled */ @@ -3133,3 +3146,53 @@ as_bytes(char* str, unsigned int* bytes, unsigned int default_bytes) return 1; } } + +/** + * Utility function to understand the setting for updating + * the process title. + * + * @param str the value obtained by the configuration parsing + * @param policy the pointer to the value where the setting will be stored + * @param default_policy a value to set when the configuration cannot be + * understood + * + * @return 0 on success, 1 on error. In any case the `policy` variable is set to + * `default_policy`. + */ +static unsigned int +as_update_process_title(char* str, unsigned int* policy, unsigned int default_policy) +{ + if (is_empty_string(str)) + { + *policy = default_policy; + return 1; + } + + if (!strncmp(str, "never", MISC_LENGTH) || !strncmp(str, "off", MISC_LENGTH)) + { + *policy = UPDATE_PROCESS_TITLE_NEVER; + return 0; + } + else if (!strncmp(str, "strict", MISC_LENGTH)) + { + *policy = UPDATE_PROCESS_TITLE_STRICT; + return 0; + } + else if (!strncmp(str, "minimal", MISC_LENGTH)) + { + *policy = UPDATE_PROCESS_TITLE_MINIMAL; + return 0; + } + else if (!strncmp(str, "verbose", MISC_LENGTH) || !strncmp(str, "full", MISC_LENGTH)) + { + *policy = UPDATE_PROCESS_TITLE_VERBOSE; + return 0; + } + else + { + // not a valid setting + *policy = default_policy; + return 1; + } + +} diff --git a/src/libpgagroal/utils.c b/src/libpgagroal/utils.c index d369c53d..978f15b5 100644 --- a/src/libpgagroal/utils.c +++ b/src/libpgagroal/utils.c @@ -720,10 +720,20 @@ void pgagroal_set_proc_title(int argc, char** argv, char* s1, char* s2) { #ifdef HAVE_LINUX - char title[256]; + char title[MAX_PROCESS_TITLE_LENGTH]; size_t size; char** env = environ; int es = 0; + struct configuration* config; + + config = (struct configuration*)shmem; + + // sanity check: if the user does not want to + // update the process title, do nothing + if (config->update_process_title == UPDATE_PROCESS_TITLE_NEVER) + { + return; + } if (!env_changed) { @@ -772,11 +782,21 @@ pgagroal_set_proc_title(int argc, char** argv, char* s1, char* s2) s1 != NULL && s2 != NULL ? "/" : "", s2 != NULL ? s2 : ""); - size = strlen(title) + 1; - // nuke the command line info memset(*argv, 0, max_process_title_size); + // copy the new title over argv checking + // the update_process_title policy + if (config->update_process_title == UPDATE_PROCESS_TITLE_STRICT) + { + size = max_process_title_size; + } + else + { + // here we can set the title to a full description + size = strlen(title) + 1; + } + memcpy(*argv, title, size); memset(*argv + size, 0, 1); @@ -797,13 +817,19 @@ pgagroal_set_connection_proc_title(int argc, char** argv, struct connection* con { struct configuration* config; int primary; - char info[MISC_LENGTH]; + char info[MAX_PROCESS_TITLE_LENGTH]; config = (struct configuration*)shmem; - pgagroal_get_primary(&primary); + if (pgagroal_get_primary(&primary)) + { + // cannot find the primary, this is a problem! + pgagroal_set_proc_title(argc, argv, connection->username, connection->database); + return; + } - snprintf(info, MISC_LENGTH, "%s@%s:%d", + memset(&info, 0, sizeof(info)); + snprintf(info, MAX_PROCESS_TITLE_LENGTH - 1, "%s@%s:%d", connection->username, config->servers[primary].host, config->servers[primary].port); diff --git a/src/libpgagroal/worker.c b/src/libpgagroal/worker.c index 614d9d66..2c2307d9 100644 --- a/src/libpgagroal/worker.c +++ b/src/libpgagroal/worker.c @@ -106,7 +106,19 @@ pgagroal_worker(int client_fd, char* address, char** argv) pgagroal_prometheus_client_active_add(); pgagroal_pool_status(); - pgagroal_set_connection_proc_title(1, argv, &config->connections[slot]); + + // do we have to update the process title? + switch (config->update_process_title) + { + case UPDATE_PROCESS_TITLE_MINIMAL: + case UPDATE_PROCESS_TITLE_STRICT: + // pgagroal_set_proc_title will check the policy + pgagroal_set_proc_title(1, argv, config->connections[slot].username, config->connections[slot].database); + break; + case UPDATE_PROCESS_TITLE_VERBOSE: + pgagroal_set_connection_proc_title(1, argv, &config->connections[slot]); + break; + } if (config->pipeline == PIPELINE_PERFORMANCE) {