Skip to content

Commit

Permalink
[#328] Introduce subcommand config-get.
Browse files Browse the repository at this point in the history
This commit introduces the `pgagroal-cli` subcommand `config-get` that
allows the user to query the running `pgagroal` instance and obtain
the value of a configuration parameter.

A new management action, named ACTION_CONFIG_GET, is added to manage
the communication over the socket between `pgagroal-cli` and the main
`pgagroal` process.
A few utility functions to handle the communication have been
introduced to send data over the socket and receive it on the socket.

Moreover, different utility functions to print out the value of a
configuration setting have been introduced, so that for instance all
the boolean values are always mapped to "on" and "off".

The `pgagroal-cli config-get` command accepts a key to search. Usually
the key is searched into the global configuration namespace, that is
the section `[pgagroal]`. However, it is possible to define other keys
that can be handled:
- `server.<server-name>.<what>` will search 'what' within the server
section named `[server-name]`, as an example `server.venkman.host`;
- `limit.<database>.<what>` will search 'what' within the
pgagroal_databases.conf file entries (runtime) for the specified
database name;
- `hba.<user>.<what>` will search 'what' among the runtime HBA
configuration for the user 'user'.

Note that in the case of 'hba.' and 'limit.', since the username or
database name can appear multiple times, only the first occurency is
matched. The match happens in a simple way, so for instance the
database name 'foo' does not match an entry with 'all'.

It is also possible to specify a key starting with `pgagroal.`, that
will instrument the search within the global namespace. Therefore
`max_entries` and `pgagroal.max_entries` are the same search terms.

If a key is not found, the main `pgagroal` process issue a warning in
the logs, while the application `pgagroal-cli` does receive an empty
answer and therefore does nothing.
Even in the case of a non-existing key, `pgagroal-cli` exists normally, to
indicate that the query has been performed well, simply with no
meaningful answer.

If `pgagroal-cli` is running with `--verbose` flag, the application
prints the configuration entry with the key and the value, like in

    pgagroal-cli get-config max_entries
    max_entries = 300

this is useful for humans, but not for automated systems, so the
default way of answering a query is by printing only the value

    pgagroal-cli get-config max_entries
    300

Documentation updated.
Shell completions updated.

Close #328
  • Loading branch information
fluca1978 authored and jesperpedersen committed Nov 29, 2022
1 parent 4278118 commit 07b79cc
Show file tree
Hide file tree
Showing 9 changed files with 1,124 additions and 3 deletions.
2 changes: 1 addition & 1 deletion contrib/shell_comp/pgagroal_comp.bash
Expand Up @@ -9,7 +9,7 @@ pgagroal_cli_completions()
if [ "${#COMP_WORDS[@]}" == "2" ]; then
# main completion: the user has specified nothing at all
# or a single word, that is a command
COMPREPLY=($(compgen -W "flush-idle flush-gracefully flush-all is-alive enable disable stop gracefully status details switch-to reload reset reset-server" "${COMP_WORDS[1]}"))
COMPREPLY=($(compgen -W "flush-idle flush-gracefully flush-all is-alive enable disable stop gracefully status details switch-to reload reset reset-server config-get" "${COMP_WORDS[1]}"))
fi
}

Expand Down
2 changes: 1 addition & 1 deletion contrib/shell_comp/pgagroal_comp.zsh
Expand Up @@ -6,7 +6,7 @@ function _pgagroal_cli()
{
local line
_arguments -C \
"1: :(flush-idle flush-all flush-gracefully is-alive enable disable stop gracefully status details switch-to reload reset reset-server)" \
"1: :(flush-idle flush-all flush-gracefully is-alive enable disable stop gracefully status details switch-to reload reset reset-server config-get)" \
"*::arg:->args"
}

Expand Down
48 changes: 48 additions & 0 deletions doc/CLI.md
Expand Up @@ -254,6 +254,54 @@ Example
pgagroal-cli reset-server primary
```

## config-get
Given a configuration setting name, provides the current value for such setting.

The configuration setting name must be the same as the one used in the configuration files.
It is possible to specify the setting name with words separated by dots, so that it can assume
the form `section.context.key` where:
- `section` can be either
- `pgagroal` (optional) the search will be performed into the main configuration settings, that is
those under the `[pgagroal]` settings in the `pgagroal.conf` file;
- `limit` the search will match against the dataabse/limit configuration, i.e., the file `pgagroal_databases.conf`;
- `hba` the search will match against the Host Based Access configuration, i.e., the `pgagroal_hbs.conf`;
- `server` the search will match against a single server defined in the main `pgagroal.conf` file.
- `context` is the match criteria to find the information into a specific context, depending on the value of the `section`:
- if the `section` is set to `limit`, than the `context` is the database name to match into the `pgagroal_databases.conf`.
Please note that the same user could be listed more than once, in such case *only the first match* is reported back;
- if the `section` is set to `hba`, than the `context` is the username to match into the `pgagroal_hba.conf`.
Please note that the same user could be listed more than once, in such case *only the first match* is reported back;
- if the `section` is set to `server`, than the `context` is the name of the server in the `pgagroal.conf` main file;
- if the `section` is set to `pgagroal`, the `context` must be empty;
- `key` is the configuration key to search for.


Examples
```
pgagroal-cli config-get pipeline
performance
pgagroal-cli config-get limit.pgbench.max_size
2
pgagroal-cli config-get server.venkman.primary
off
```

In the above examples, `pipeline` is equivalent to `pgagroal.pipeline` and looks for a global configuration setting named `pipeline`.
The `limit.pgbench.max_size` looks for the `max_size` set into the *limit* file (`pgagroal_databases.conf`) for the database `pgbench`.
The `server.venkman.primary` searches for the configuration parameter `primary` into the *server* section named `venkman` in the main configuration file `pgagraol.conf`.

If the `--verbose` option is specified, a descriptive string of the configuration parameter is printed as *name = value*:

```
pgagroal-cli config-get max_connections --verbose
max_connections = 4
Success (0)
```

If the parameter name specified is not found or invalid, the program `pgagroal-cli` exit normally without printing any value.

## Shell completions

Expand Down
77 changes: 76 additions & 1 deletion src/cli.c
Expand Up @@ -64,6 +64,7 @@
#define ACTION_RESET_SERVER 11
#define ACTION_SWITCH_TO 12
#define ACTION_RELOAD 13
#define ACTION_CONFIG_GET 14

static int flush(SSL* ssl, int socket, int32_t mode, char* database);
static int enabledb(SSL* ssl, int socket, char* database);
Expand All @@ -78,6 +79,7 @@ static int reset(SSL* ssl, int socket);
static int reset_server(SSL* ssl, int socket, char* server);
static int switch_to(SSL* ssl, int socket, char* server);
static int reload(SSL* ssl, int socket);
static int config_get(SSL* ssl, int socket, char* config_key, bool verbose);

static void
version(void)
Expand Down Expand Up @@ -124,6 +126,7 @@ usage(void)
printf(" reload Reload the configuration\n");
printf(" reset Reset the Prometheus statistics\n");
printf(" reset-server Reset the state of a server\n");
printf(" config-get Retrieves a configuration value\n");
printf("\n");
printf("pgagroal: %s\n", PGAGROAL_HOMEPAGE);
printf("Report bugs: %s\n", PGAGROAL_ISSUES);
Expand Down Expand Up @@ -155,6 +158,7 @@ main(int argc, char** argv)
struct configuration* config = NULL;
bool remote_connection = false;
long l_port;
char* config_key = NULL; /* key for a configuration setting */

while (1)
{
Expand Down Expand Up @@ -435,7 +439,12 @@ main(int argc, char** argv)
action = ACTION_RELOAD;
}
}

else if (argc > 2 && !strncmp("config-get", argv[argc - 2], MISC_LENGTH) && strlen(argv[argc - 1]) > 0)
{
/* get a configuration value */
action = ACTION_CONFIG_GET;
config_key = argv[argc - 1];
}
if (action != ACTION_UNKNOWN)
{
if (!remote_connection)
Expand Down Expand Up @@ -581,6 +590,10 @@ main(int argc, char** argv)
{
exit_code = reload(s_ssl, socket);
}
else if (action == ACTION_CONFIG_GET)
{
exit_code = config_get(s_ssl, socket, config_key, verbose);
}
}

done:
Expand Down Expand Up @@ -793,3 +806,65 @@ reload(SSL* ssl, int socket)

return 0;
}

/**
* Entry point for a config-get command line action.
*
* First it sends the message to pgagroal process to execute a config-get,
* then reads back the answer.
*
* @param ssl the SSL mode
* @param socket the socket file descriptor
* @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
*/
static int
config_get(SSL* ssl, int socket, char* config_key, bool verbose)
{
char* buffer = NULL;

if (!config_key || strlen(config_key) > MISC_LENGTH)
{
goto error;
}

if (pgagroal_management_config_get(ssl, socket, config_key))
{
goto error;
}
else
{
buffer = malloc(MISC_LENGTH);
memset(buffer, 0, MISC_LENGTH);
if (pgagroal_management_read_config_get(socket, &buffer))
{
free(buffer);
goto error;
}

// assume an empty response is ok,
// do not throw an error to indicate no configuration
// setting with such name as been found
if (buffer && strlen(buffer))
{
if (verbose)
{
printf("%s = %s\n", config_key, buffer);
}
else
{
printf("%s\n", buffer);
}
}

free(buffer);
return 0;
}

return 0;

error:
return 1;
}
48 changes: 48 additions & 0 deletions src/include/configuration.h
Expand Up @@ -241,6 +241,54 @@ pgagroal_init_pidfile_if_needed(void);
bool
pgagroal_can_prefill(void);

/**
* Gets a configuration parameter and places into the string pointer.
* This is used, for example, to get a writable string to send over the
* management socket.
*
* The key can contain words separated by a dot '.' to indicate different search criterias.
* A "dotted" key is made by a 'context', a 'section' and a 'search term', so that
* it can be written as 'section.context.search'.
* If both the section and the context are omitted, the 'search' is performed among the
* pgagroal global settings (i.e., those under the [pgagroal] main section). The same
* happens if the the section is specified as 'pgagroal', therefore the following two
* terms do the same search:
* - `update_process_title`
* - `pgagroal.update_process_title`
*
* Other possible sections are:
* - server to search for a specific server, the match is performed on the server name;
* - hba to search for a specific HBA entry, the match is performed on the username;
* - limit to search for a specific database in the limit (database) configuration file.
*
* When one the above sections is specified, the search is done identifying the entry to snoop
* by means of 'context', and within such the 'search' is performed.
*
* In the case of the `server` section, the `context` has to be the name of a server configured,
* while the `search` has to be the keyword to look for. AS an example: `server.venkman.port` provides
* the value of the 'port' setting under the server section '[venkman]'.
*
* In the case of the 'hba` section, the `context` has to be a username as it appears in a line
* of the pgagroal_hba.conf file, while the `search` has to be the column keyword to snoop.
* For example, `hba.luca.method` will seek for the `method` used to authenticate the user `luca`.
* Please note that, since the same user could be listed more than once, only the first matching
* entry is reported.
*
* In the case of the 'limit` section, the `context` has to be a database name as it appears in a line
* of the pgagroal_database.conf file, while the `search` has to be the column keyword to snoop.
* For example, `limit.pgbench.max_size` will seek for the `max_size` connection limit for the
* database 'pgbench'.
* Please note that, since the same database could be listed more than once, only the first matching
* entry is reported.
*
* @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
* @return 0 on success, 1 when the key cannot be found
*/
int
pgagroal_write_config_value(char* buffer, char* config_key);

#ifdef __cplusplus
}
#endif
Expand Down
42 changes: 42 additions & 0 deletions src/include/management.h
Expand Up @@ -59,6 +59,7 @@ extern "C" {
#define MANAGEMENT_SWITCH_TO 17
#define MANAGEMENT_RELOAD 18
#define MANAGEMENT_REMOVE_FD 19
#define MANAGEMENT_CONFIG_GET 20

/**
* Read the management header
Expand Down Expand Up @@ -306,6 +307,47 @@ pgagroal_management_reload(SSL* ssl, int socket);
int
pgagroal_management_remove_fd(int32_t slot, int socket, pid_t pid);

/**
* Management operation: get a configuration setting.
* This function sends over the socket the message to get a configuration
* value.
* In particular, the message block for the action config_get is sent,
* then the size of the configuration parameter to get (e.g., `max_connections`)
* and last the parameter name itself.
*
* @param ssl the SSL connection
* @param socket the socket file descriptor
* @param config_key the name of the configuration parameter to get back
* @return 0 on success, 1 on error
*/
int
pgagroal_management_config_get(SSL* ssl, int socket, char* config_key);

/**
* Management operation result: receives the key to read in the configuration.
*
* Internally, exploits the function to read the payload from the socket.
* @see pgagroal_management_read_payload
*
* @param ssl the socket file descriptor
* @return 0 on success
*/
int
pgagroal_management_read_config_get(int socket, char** data);

/**
* Management operation: write the result of a config_get action on the socket.
*
* Writes on the socket the result of the request for a specific
* configuration parameter.
*
° @param socket the socket file descriptor
* @param config_key the name of the configuration parameter to get
* @return 0 on success
*/
int
pgagroal_management_write_config_get(int socket, char* config_key);

#ifdef __cplusplus
}
#endif
Expand Down

0 comments on commit 07b79cc

Please sign in to comment.