diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc100590..6cffbc0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,12 +23,14 @@ jobs: run: sudo apt update - name: Install libev run: sudo apt install -y libev4 libev-dev - - name: Install cJSON - run: sudo apt install -y libcjson1 libcjson-dev - name: Install systemd run: sudo apt install -y libsystemd-dev - name: Install rst2man run: sudo apt install -y python3-docutils + - name: Install zstd + run: sudo apt install -y libzstd-dev + - name: Install lz4 + run: sudo apt install -y liblz4-dev - name: Install graphviz run: sudo apt install graphviz - name: Install doxygen @@ -113,8 +115,10 @@ jobs: run: brew install openssl - name: Install libev run: brew install libev - - name: Install cJSON - run: brew install cjson + - name: Install zstd + run: brew install libzstd + - name: Install lz4 + run: brew install liblz4 - name: Install rst2man run: brew install docutils - name: Install graphviz diff --git a/CMakeLists.txt b/CMakeLists.txt index e38e1c72..7f7eb03e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,34 @@ if(NOT COMPILER_SUPPORTS_C17) message(FATAL_ERROR "The compiler ${CMAKE_C_COMPILER} has no C17 support. Please use a different C compiler.") endif() +find_package(ZLIB) +if (ZLIB_FOUND) + message(STATUS "zlib found") +else () + message(FATAL_ERROR "zlib needed") +endif() + +find_package(BZip2) +if (BZIP2_FOUND) + message(STATUS "bzip2 found") +else () + message(FATAL_ERROR "bzip2 needed") +endif() + +find_package(Zstd) +if (ZSTD_FOUND) + message(STATUS "zstd found") +else () + message(FATAL_ERROR "zstd needed") +endif() + +find_package(Lz4) +if (LZ4_FOUND) + message(STATUS "lz4 found") +else () + message(FATAL_ERROR "lz4 needed") +endif() + find_package(Libev 4.11) if (LIBEV_FOUND) message(STATUS "libev found") @@ -87,15 +115,6 @@ else () message(FATAL_ERROR "pthread needed") endif() -# search for cJSON library -# -find_package(cJSON) -if (cJSON_FOUND) - message(STATUS "cJSON found version ${CJSON_VERSION}") -else () - message(FATAL_ERROR "cJSON needed") -endif() - find_package(Pandoc) if (PANDOC_FOUND) message(STATUS "pandoc found") diff --git a/README.md b/README.md index 240bf82c..9dcee362 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,10 @@ See [Architecture](./doc/ARCHITECTURE.md) for the architecture of [**pgagroal**] * [systemd](https://www.freedesktop.org/wiki/Software/systemd/) * [rst2man](https://docutils.sourceforge.io/) * [libatomic](https://gcc.gnu.org/wiki/Atomic) -* [cJSON](https://github.com/DaveGamble/cJSON) +* [zlib](https://zlib.net) +* [zstd](http://www.zstd.net) +* [lz4](https://lz4.github.io/lz4/) +* [bzip2](http://sourceware.org/bzip2/) On Rocky Linux (and similar) operating systems, the dependencies can be installed via `dnf(8)` as follows: @@ -77,8 +80,11 @@ dnf install git gcc cmake make \ openssl openssl-devel \ systemd systemd-devel \ python3-docutils \ - libatomic \ - cjson cjson-devel + libatomic \ + zlib zlib-devel \ + libzstd libzstd-devel \ + lz4 lz4-devel \ + bzip2 bzip2-devel ``` Please note that, on Rocky Linux, in order to install the `python3-docutils` diff --git a/cmake/FindLz4.cmake b/cmake/FindLz4.cmake new file mode 100644 index 00000000..4b9e27f8 --- /dev/null +++ b/cmake/FindLz4.cmake @@ -0,0 +1,21 @@ +# +# LZ4 Support +# + +find_path(LZ4_INCLUDE_DIR + NAMES lz4.h +) +find_library(LZ4_LIBRARY + NAMES lz4 +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Lz4 DEFAULT_MSG + LZ4_LIBRARY LZ4_INCLUDE_DIR) + +if(LZ4_FOUND) + set(LZ4_LIBRARIES ${LZ4_LIBRARY}) + set(LZ4_INCLUDE_DIRS ${LZ4_INCLUDE_DIR}) +endif() + +mark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY) diff --git a/cmake/FindZstd.cmake b/cmake/FindZstd.cmake new file mode 100644 index 00000000..0277a160 --- /dev/null +++ b/cmake/FindZstd.cmake @@ -0,0 +1,21 @@ +# +# ZSTD support +# + +find_path(ZSTD_INCLUDE_DIR + NAMES zstd.h +) +find_library(ZSTD_LIBRARY + NAMES zstd +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Zstd REQUIRED_VARS + ZSTD_LIBRARY ZSTD_INCLUDE_DIR) + +if(ZSTD_FOUND) + set(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) + set(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) +endif() + +mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) diff --git a/cmake/FindcJSON.cmake b/cmake/FindcJSON.cmake deleted file mode 100644 index 6f30e309..00000000 --- a/cmake/FindcJSON.cmake +++ /dev/null @@ -1,51 +0,0 @@ -# FindcJSON.cmake -# Tries to find cJSON libraries on the system -# (e.g., on Rocky Linux: cjson and cjson-devel) -# -# Inspired by -# -# If cJSON is found, sets the following variables: -# - CJSON_INCLUDE_DIRS -# - CJSON_LIBRARIES -# - CJSON_VERSION -# -# In the header file cJSON.h the library version is specified as: -# #define CJSON_VERSION_MAJOR 1 -# #define CJSON_VERSION_MINOR 7 -# #define CJSON_VERSION_PATCH 14 - - -find_path( - CJSON_INCLUDE_DIR - NAMES cjson/cJSON.h - PATH_SUFFIXES include) -find_library( - CJSON_LIBRARY - NAMES cjson - PATH_SUFFIXES lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(cJSON REQUIRED_VARS CJSON_INCLUDE_DIR - CJSON_LIBRARY) -if(CJSON_FOUND) - # these variables are needed for the build - set( CJSON_INCLUDE_DIRS "${CJSON_INCLUDE_DIR}" ) - set( CJSON_LIBRARIES "${CJSON_LIBRARY}" ) - - # try to get out the library version from the headers - file(STRINGS "${CJSON_INCLUDE_DIR}/cjson/cJSON.h" - CJSON_VERSION_MAJOR REGEX "^#define[ \t]+CJSON_VERSION_MAJOR[ \t]+[0-9]+") - file(STRINGS "${CJSON_INCLUDE_DIR}/cjson/cJSON.h" - CJSON_VERSION_MINOR REGEX "^#define[ \t]+CJSON_VERSION_MINOR[ \t]+[0-9]+") - file(STRINGS "${CJSON_INCLUDE_DIR}/cjson/cJSON.h" - CJSON_VERSION_PATCH REGEX "^#define[ \t]+CJSON_VERSION_PATCH[ \t]+[0-9]+") - string(REGEX REPLACE "[^0-9]+" "" CJSON_VERSION_MAJOR "${CJSON_VERSION_MAJOR}") - string(REGEX REPLACE "[^0-9]+" "" CJSON_VERSION_MINOR "${CJSON_VERSION_MINOR}") - string(REGEX REPLACE "[^0-9]+" "" CJSON_VERSION_PATCH "${CJSON_VERSION_PATCH}") - set(CJSON_VERSION "${CJSON_VERSION_MAJOR}.${CJSON_VERSION_MINOR}.${CJSON_VERSION_PATCH}") - unset(CJSON_VERSION_MINOR) - unset(CJSON_VERSION_MAJOR) - unset(CJSON_VERSION_PATCH) -endif() - -mark_as_advanced( CJSON_INCLUDE_DIR CJSON_LIBRARY ) diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md index 0f670b49..e1a8aee7 100644 --- a/doc/ARCHITECTURE.md +++ b/doc/ARCHITECTURE.md @@ -69,26 +69,51 @@ The memory interface is defined in [memory.h](../src/include/memory.h) ([memory. ## Management -[**pgagroal**](https://github.com/agroal/pgagroal) has a management interface which serves two purposes. +`pgagroal` has a management interface which defines the administrator abilities that can be performed when it is running. +This include for example taking a backup. The `pgagroal-cli` program is used for these operations ([cli.c](../src/cli.c)). -First, it defines the administrator abilities that can be performed on the pool when it is running. This include -for example flushing the pool. The `pgagroal-cli` program is used for these operations ([cli.c](../src/cli.c)). +The management interface is defined in [management.h](../src/include/management.h). The management interface +uses its own protocol which uses JSON as its foundation. -Second, the interface is used internally to transfer the connection (socket descriptor) from the child process -to the main [**pgagroal**](https://github.com/agroal/pgagroal) process after a new connection has been created. This is necessary since the socket descriptor -needs to be available to subsequent client and hence processes. +### Write -The management interface use Unix Domain Socket for communication. +The client sends a single JSON string to the server, -The management interface is defined in [management.h](../src/include/management.h). The management interface -uses its own protocol which always consist of a header +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | + +The server sends a single JSON string to the client, + +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | + +### Read + +The server sends a single JSON string to the client, + +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | -| Field | Type | Description | -|------------|------|-------------| -| `id` | Byte | The identifier of the message type | -| `slot` | Int | The slot that the message is for | +The client sends to the server a single JSON documents, -The rest of the message is depending on the message type. +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | ### Remote management @@ -245,7 +270,7 @@ However, some configuration settings requires a full restart of [**pgagroal**](h * Limit rules defined by `pgagroal_databases.conf` * TLS rules defined by server section -The configuration can also be reloaded using `pgagroal-cli -c pgagroal.conf reload`. The command is only supported +The configuration can also be reloaded using `pgagroal-cli -c pgagroal.conf conf reload`. The command is only supported over the local interface, and hence doesn't work remotely. ## Prometheus diff --git a/doc/DEVELOPERS.md b/doc/DEVELOPERS.md index 14c548c5..2a4e18a1 100644 --- a/doc/DEVELOPERS.md +++ b/doc/DEVELOPERS.md @@ -17,7 +17,7 @@ dnf install postgresql-server #### Basic dependencies ``` sh -dnf install git gcc cmake make libev libev-devel openssl openssl-devel systemd systemd-devel python3-docutils libatomic cjson cjson-devel +dnf install git gcc cmake make libev libev-devel openssl openssl-devel systemd systemd-devel python3-docutils libatomic zlib zlib-devel libzstd libzstd-devel lz4 lz4-devel bzip2 bzip2-devel ``` #### Generate user and developer guide @@ -257,10 +257,10 @@ pgagroal -c pgagroal.conf -a pgagroal_hba.conf psql -h localhost -p 2345 -U myuser mydb ``` -#### Stop pgagroal +#### Shutdown pgagroal ``` sh -pgagroal-cli -c pgagroal.conf stop +pgagroal-cli -c pgagroal.conf shutdown ``` ## Basic git guide diff --git a/doc/man/pgagroal-admin.1.rst b/doc/man/pgagroal-admin.1.rst index c4d96a00..28968e4a 100644 --- a/doc/man/pgagroal-admin.1.rst +++ b/doc/man/pgagroal-admin.1.rst @@ -48,16 +48,16 @@ COMMANDS master-key Create or update the master key. The master key will be created in the pgagroal user home directory under ~/.pgagroal -add-user +user add Add a user -update-user +user edit Update a user -remove-user - Remove a user +user del + Delete a user -list-users +user ls List all users REPORTING BUGS diff --git a/doc/manual/02-installation.md b/doc/manual/02-installation.md index f373a2d6..62c42fe1 100644 --- a/doc/manual/02-installation.md +++ b/doc/manual/02-installation.md @@ -46,14 +46,20 @@ We recommend using Fedora to test and run [**pgagroal**][pgagroal], but other Li * [systemd](https://www.freedesktop.org/wiki/Software/systemd/) * [rst2man](https://docutils.sourceforge.io/) * [libatomic](https://gcc.gnu.org/wiki/Atomic) -* [cJSON](https://github.com/DaveGamble/cJSON) +* [zlib](https://zlib.net) +* [zstd](http://www.zstd.net) +* [lz4](https://lz4.github.io/lz4/) +* [bzip2](http://sourceware.org/bzip2/) ```sh dnf install git gcc cmake make libev libev-devel \ openssl openssl-devel \ systemd systemd-devel \ python3-docutils libatomic \ - cjson cjson-devel + zlib zlib-devel \ + libzstd libzstd-devel \ + lz4 lz4-devel \ + bzip2 bzip2-devel ``` Alternative [clang 8+](https://clang.llvm.org/) can be used. @@ -101,7 +107,7 @@ On FreeBSD, `pkg` is used instead of `dnf` or `yum`. Use `pkg install ` to install the following packages ``` sh -git gcc cmake libev openssl libssh py39-docutils libcjson +git gcc cmake libev openssl libssh py39-docutils ``` ### Build diff --git a/doc/manual/99-references.md b/doc/manual/99-references.md index 5fe684e3..69154256 100644 --- a/doc/manual/99-references.md +++ b/doc/manual/99-references.md @@ -14,7 +14,6 @@ [openssl]: http://www.openssl.org/ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/ [rst2man]: https://docutils.sourceforge.io/ - [cjson]: https://github.com/DaveGamble/cJSON [pandoc]: https://pandoc.org/ [pandoc_latex_template]: https://github.com/Wandmalfarbe/pandoc-latex-template [texlive]: https://www.tug.org/texlive/ diff --git a/doc/manual/dev-02-architecture.md b/doc/manual/dev-02-architecture.md index 94858db2..5c672111 100644 --- a/doc/manual/dev-02-architecture.md +++ b/doc/manual/dev-02-architecture.md @@ -71,26 +71,51 @@ The memory interface is defined in [memory.h](../src/include/memory.h) ([memory. ## Management -[**pgagroal**](https://github.com/agroal/pgagroal) has a management interface which serves two purposes. +[**pgagroal**][pgagroal] has a management interface which defines the administrator abilities that can be performed when it is running. +This include for example taking a backup. The `pgagroal-cli` program is used for these operations ([cli.c][cli_c]). -First, it defines the administrator abilities that can be performed on the pool when it is running. This include -for example flushing the pool. The `pgagroal-cli` program is used for these operations ([cli.c](../src/cli.c)). +The management interface is defined in [management.h][management_h]. The management interface +uses its own protocol which uses JSON as its foundation. -Second, the interface is used internally to transfer the connection (socket descriptor) from the child process -to the main [**pgagroal**](https://github.com/agroal/pgagroal) process after a new connection has been created. This is necessary since the socket descriptor -needs to be available to subsequent client and hence processes. +### Write -The management interface use Unix Domain Socket for communication. +The client sends a single JSON string to the server, -The management interface is defined in [management.h](../src/include/management.h). The management interface -uses its own protocol which always consist of a header +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | -| Field | Type | Description | -|------------|------|-------------| -| `id` | Byte | The identifier of the message type | -| `slot` | Int | The slot that the message is for | +The server sends a single JSON string to the client, -The rest of the message is depending on the message type. +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | + +### Read + +The server sends a single JSON string to the client, + +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | + +The client sends to the server a single JSON documents, + +| Field | Type | Description | +| :------------ | :----- | :------------------------------ | +| `compression` | uint8 | The compression type | +| `encryption` | uint8 | The encryption type | +| `length` | uint32 | The length of the JSON document | +| `json` | String | The JSON document | ### Remote management @@ -247,7 +272,7 @@ However, some configuration settings requires a full restart of [**pgagroal**](h * Limit rules defined by `pgagroal_databases.conf` * TLS rules defined by server section -The configuration can also be reloaded using `pgagroal-cli -c pgagroal.conf reload`. The command is only supported +The configuration can also be reloaded using `pgagroal-cli -c pgagroal.conf conf reload`. The command is only supported over the local interface, and hence doesn't work remotely. ## Prometheus diff --git a/doc/manual/user-01-quickstart.md b/doc/manual/user-01-quickstart.md index 6c8abc32..93d3db89 100644 --- a/doc/manual/user-01-quickstart.md +++ b/doc/manual/user-01-quickstart.md @@ -151,10 +151,10 @@ To flush all idle connections you would use pgagroal-cli -c pgagroal.conf flush idle ``` -To stop pgagroal you would use +To shutdown pgagroal you would use ``` -pgagroal-cli -c pgagroal.conf stop +pgagroal-cli -c pgagroal.conf shutdown ``` Check the outcome of the operations by verifying the exit code, like diff --git a/pgagroal.spec b/pgagroal.spec index cc8d8e7e..3edad542 100644 --- a/pgagroal.spec +++ b/pgagroal.spec @@ -7,8 +7,8 @@ URL: https://github.com/agroal/pgagroal Source0: https://github.com/agroal/pgagroal/archive/%{version}.tar.gz BuildRequires: gcc cmake make python3-docutils -BuildRequires: libev libev-devel openssl openssl-devel systemd systemd-devel libatomic cjson cjson-devel -Requires: libev openssl systemd libatomic cjson +BuildRequires: libev libev-devel openssl openssl-devel systemd systemd-devel libatomic zlib zlib-devel libzstd libzstd-devel lz4 lz4-devel bzip2 bzip2-devel +Requires: libev openssl systemd libatomic zlib libzstd lz4 bzip2 %description pgagroal is a high-performance connection pool for PostgreSQL. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 655633e4..476a91b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,8 +22,11 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") ${LIBEV_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${SYSTEMD_INCLUDE_DIRS} - ${CJSON_INCLUDE_DIRS} ${THREAD_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ${BZIP2_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} + ${LZ4_INCLUDE_DIRS} ) # @@ -35,8 +38,11 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") ${OPENSSL_SSL_LIBRARY} ${SYSTEMD_LIBRARIES} ${LIBATOMIC_LIBRARY} - ${CJSON_LIBRARIES} ${THREAD_LIBRARY} + ${ZLIB_LIBRARIES} + ${BZIP2_LIBRARIES} + ${ZSTD_LIBRARIES} + ${LZ4_LIBRARIES} ) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") @@ -73,8 +79,11 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") ${CMAKE_CURRENT_SOURCE_DIR}/include ${LIBEV_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} - ${CJSON_INCLUDE_DIRS} ${THREAD_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ${BZIP2_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} + ${LZ4_INCLUDE_DIRS} ) # @@ -83,8 +92,11 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") link_libraries( ${LIBEV_LIBRARIES} ${OPENSSL_LIBRARIES} - ${CJSON_LIBRARIES} ${THREAD_LIBRARY} + ${ZLIB_LIBRARIES} + ${BZIP2_LIBRARIES} + ${ZSTD_LIBRARIES} + ${LZ4_LIBRARIES} ) else() @@ -106,8 +118,11 @@ else() ${CMAKE_CURRENT_SOURCE_DIR}/include ${LIBEV_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} - ${CJSON_INCLUDE_DIRS} ${THREAD_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ${BZIP2_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} + ${LZ4_INCLUDE_DIRS} ) # @@ -116,8 +131,11 @@ else() link_libraries( ${LIBEV_LIBRARIES} ${OPENSSL_LIBRARIES} - ${CJSON_LIBRARIES} ${THREAD_LIBRARY} + ${ZLIB_LIBRARIES} + ${BZIP2_LIBRARIES} + ${ZSTD_LIBRARIES} + ${LZ4_LIBRARIES} ) endif() diff --git a/src/admin.c b/src/admin.c index 00a905ea..d10736e4 100644 --- a/src/admin.c +++ b/src/admin.c @@ -28,6 +28,7 @@ /* pgagroal */ #include +#include #include #include #include @@ -100,50 +101,6 @@ const struct pgagroal_command command_table[] = .action = ACTION_LIST_USERS, .log_message = "", }, - { - .command = "add-user", - .subcommand = "", - .accepted_argument_count = {0}, - .deprecated = true, - .action = ACTION_ADD_USER, - .log_message = " [%s]", - .deprecated_since_major = 1, - .deprecated_since_minor = 6, - .deprecated_by = "user add", - }, - { - .command = "update-user", - .subcommand = "", - .accepted_argument_count = {0}, - .deprecated = true, - .action = ACTION_UPDATE_USER, - .log_message = " [%s]", - .deprecated_since_major = 1, - .deprecated_since_minor = 6, - .deprecated_by = "user edit", - }, - { - .command = "remove-user", - .subcommand = "", - .accepted_argument_count = {0}, - .deprecated = true, - .action = ACTION_REMOVE_USER, - .log_message = "", - .deprecated_since_major = 1, - .deprecated_since_minor = 6, - .deprecated_by = "user del", - }, - { - .command = "list-users", - .subcommand = "", - .accepted_argument_count = {0}, - .deprecated = true, - .action = ACTION_LIST_USERS, - .log_message = "", - .deprecated_since_major = 1, - .deprecated_since_minor = 6, - .deprecated_by = "user ls", - }, }; static void @@ -597,7 +554,7 @@ add_user(char* users_path, char* username, char* password, bool generate_pwd, in } } - pgagroal_encrypt(password, master_key, &encrypted, &encrypted_length); + pgagroal_encrypt(password, master_key, &encrypted, &encrypted_length, ENCRYPTION_AES_256_CBC); pgagroal_base64_encode(encrypted, encrypted_length, &encoded, &encoded_length); entry = pgagroal_append(entry, username); @@ -776,7 +733,7 @@ update_user(char* users_path, char* username, char* password, bool generate_pwd, } } - pgagroal_encrypt(password, master_key, &encrypted, &encrypted_length); + pgagroal_encrypt(password, master_key, &encrypted, &encrypted_length, ENCRYPTION_AES_256_CBC); pgagroal_base64_encode(encrypted, encrypted_length, &encoded, &encoded_length); entry = NULL; diff --git a/src/cli.c b/src/cli.c index 0c909e33..5cc1a141 100644 --- a/src/cli.c +++ b/src/cli.c @@ -29,12 +29,15 @@ /* pgagroal */ #include #include +#include #include #include +#include #include #include #include #include +#include /* system */ #include @@ -50,47 +53,74 @@ #include -#define ACTION_UNKNOWN 0 -#define ACTION_FLUSH 1 -#define ACTION_GRACEFULLY 2 -#define ACTION_STOP 3 -#define ACTION_STATUS 4 -#define ACTION_STATUS_DETAILS 5 -#define ACTION_ISALIVE 6 -#define ACTION_CANCELSHUTDOWN 7 -#define ACTION_ENABLEDB 8 -#define ACTION_DISABLEDB 9 -#define ACTION_RESET 10 -#define ACTION_RESET_SERVER 11 -#define ACTION_SWITCH_TO 12 -#define ACTION_RELOAD 13 -#define ACTION_CONFIG_GET 14 -#define ACTION_CONFIG_SET 15 -#define ACTION_CONFIG_LS 16 - -static int flush(SSL* ssl, int socket, int32_t mode, char* database); -static int enabledb(SSL* ssl, int socket, char* database); -static int disabledb(SSL* ssl, int socket, char* database); -static int gracefully(SSL* ssl, int socket); -static int stop(SSL* ssl, int socket); -static int cancel_shutdown(SSL* ssl, int socket); -static int status(SSL* ssl, int socket, char output_format); -static int details(SSL* ssl, int socket, char output_format); -static int isalive(SSL* ssl, int socket, char output_format); -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_ls(SSL* ssl, int socket, char output_format); -static int config_get(SSL* ssl, int socket, char* config_key, bool verbose, char output_format); -static int config_set(SSL* ssl, int socket, char* config_key, char* config_value, bool verbose, char output_format); +#define HELP 99 + +#define COMMAND_CANCELSHUTDOWN "cancel-shutdown" +#define COMMAND_CLEAR "clear" +#define COMMAND_CLEAR_SERVER "clear-server" +#define COMMAND_DISABLEDB "disable-db" +#define COMMAND_ENABLEDB "enable-db" +#define COMMAND_FLUSH "flush" +#define COMMAND_GRACEFULLY "shutdown-gracefully" +#define COMMAND_PING "ping" +#define COMMAND_RELOAD "reload" +#define COMMAND_SHUTDOWN "shutdown" +#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 OUTPUT_FORMAT_JSON "json" +#define OUTPUT_FORMAT_TEXT "text" + +#define UNSPECIFIED "Unspecified" + +static void display_helper(char* command); +static void help_cancel_shutdown(void); +/* static void help_config(void); */ +static void help_clear(void); +static void help_conf(void); +static void help_disabledb(void); +static void help_enabledb(void); +static void help_flush(void); +static void help_ping(void); +static void help_shutdown(void); +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 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); +static int flush(SSL* ssl, int socket, int32_t mode, char* database, uint8_t compression, uint8_t encryption, int32_t output_format); +static int gracefully(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); +static int pgagroal_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); +static int ping(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); +static int reload(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); +static int clear(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); +static int clear_server(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format); +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 char* translate_command(int32_t cmd_code); +static char* translate_output_format(int32_t out_code); +static char* translate_compression(int32_t compression_code); +static char* translate_encryption(int32_t encryption_code); +static void translate_json_object(struct json* j); const struct pgagroal_command command_table[] = { { .command = "flush", .subcommand = "", .accepted_argument_count = {0, 1}, - .action = ACTION_FLUSH, + .action = MANAGEMENT_FLUSH, .mode = FLUSH_GRACEFULLY, .default_argument = "*", .deprecated = false, @@ -100,15 +130,15 @@ const struct pgagroal_command command_table[] = { .command = "ping", .subcommand = "", .accepted_argument_count = {0}, - .action = ACTION_ISALIVE, + .action = MANAGEMENT_PING, .deprecated = false, - .log_message = "" + .log_message = "" }, { .command = "enable", .subcommand = "", .accepted_argument_count = {0, 1}, - .action = ACTION_ENABLEDB, + .action = MANAGEMENT_ENABLEDB, .default_argument = "*", .deprecated = false, .log_message = " [%s]", @@ -117,7 +147,7 @@ const struct pgagroal_command command_table[] = { .command = "disable", .subcommand = "", .accepted_argument_count = {0, 1}, - .action = ACTION_DISABLEDB, + .action = MANAGEMENT_DISABLEDB, .default_argument = "*", .deprecated = false, .log_message = " [%s]", @@ -126,7 +156,7 @@ const struct pgagroal_command command_table[] = { .command = "shutdown", .subcommand = "", .accepted_argument_count = {0}, - .action = ACTION_GRACEFULLY, + .action = MANAGEMENT_GRACEFULLY, .deprecated = false, .log_message = "" }, @@ -134,7 +164,7 @@ const struct pgagroal_command command_table[] = { .command = "status", .subcommand = "", .accepted_argument_count = {0}, - .action = ACTION_STATUS, + .action = MANAGEMENT_STATUS, .deprecated = false, .log_message = "" }, @@ -142,7 +172,7 @@ const struct pgagroal_command command_table[] = { .command = "switch-to", .subcommand = "", .accepted_argument_count = {1}, - .action = ACTION_SWITCH_TO, + .action = MANAGEMENT_SWITCH_TO, .deprecated = false, .log_message = " [%s]" }, @@ -150,7 +180,7 @@ const struct pgagroal_command command_table[] = { .command = "clear", .subcommand = "", .accepted_argument_count = {1}, - .action = ACTION_RESET_SERVER, + .action = MANAGEMENT_CLEAR_SERVER, .deprecated = false, .log_message = "", }, @@ -158,7 +188,7 @@ const struct pgagroal_command command_table[] = { .command = "shutdown", .subcommand = "gracefully", .accepted_argument_count = {0}, - .action = ACTION_GRACEFULLY, + .action = MANAGEMENT_GRACEFULLY, .deprecated = false, .log_message = "" }, @@ -166,7 +196,7 @@ const struct pgagroal_command command_table[] = { .command = "shutdown", .subcommand = "immediate", .accepted_argument_count = {0}, - .action = ACTION_STOP, + .action = MANAGEMENT_SHUTDOWN, .deprecated = false, .log_message = "" }, @@ -174,7 +204,7 @@ const struct pgagroal_command command_table[] = { .command = "shutdown", .subcommand = "cancel", .accepted_argument_count = {0}, - .action = ACTION_CANCELSHUTDOWN, + .action = MANAGEMENT_CANCEL_SHUTDOWN, .deprecated = false, .log_message = "" }, @@ -182,39 +212,39 @@ const struct pgagroal_command command_table[] = { .command = "conf", .subcommand = "reload", .accepted_argument_count = {0}, - .action = ACTION_RELOAD, + .action = MANAGEMENT_RELOAD, .deprecated = false, .log_message = "" }, - { - .command = "conf", - .subcommand = "get", - .accepted_argument_count = {1}, - .action = ACTION_CONFIG_GET, - .deprecated = false, - .log_message = " [%s]" - }, - { - .command = "conf", - .subcommand = "set", - .accepted_argument_count = {2}, - .action = ACTION_CONFIG_SET, - .deprecated = false, - .log_message = " [%s] = [%s]" - }, - { - .command = "conf", - .subcommand = "ls", - .accepted_argument_count = {0}, - .action = ACTION_CONFIG_LS, - .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 = "clear", .subcommand = "server", .accepted_argument_count = {0, 1}, - .action = ACTION_RESET_SERVER, + .action = MANAGEMENT_CLEAR_SERVER, .default_argument = "server", .deprecated = false, .log_message = " [%s]", @@ -223,7 +253,7 @@ const struct pgagroal_command command_table[] = { .command = "flush", .subcommand = "idle", .accepted_argument_count = {0, 1}, - .action = ACTION_FLUSH, + .action = MANAGEMENT_FLUSH, .mode = FLUSH_IDLE, .default_argument = "*", .deprecated = false, @@ -233,7 +263,7 @@ const struct pgagroal_command command_table[] = { .command = "flush", .subcommand = "gracefully", .accepted_argument_count = {0, 1}, - .action = ACTION_FLUSH, + .action = MANAGEMENT_FLUSH, .mode = FLUSH_GRACEFULLY, .default_argument = "*", .deprecated = false, @@ -243,7 +273,7 @@ const struct pgagroal_command command_table[] = { .command = "flush", .subcommand = "all", .accepted_argument_count = {0, 1}, - .action = ACTION_FLUSH, + .action = MANAGEMENT_FLUSH, .mode = FLUSH_ALL, .default_argument = "*", .deprecated = false, @@ -253,7 +283,7 @@ const struct pgagroal_command command_table[] = { .command = "clear", .subcommand = "prometheus", .accepted_argument_count = {0}, - .action = ACTION_RESET, + .action = MANAGEMENT_CLEAR, .deprecated = false, .log_message = "" }, @@ -261,7 +291,7 @@ const struct pgagroal_command command_table[] = { .command = "status", .subcommand = "details", .accepted_argument_count = {0}, - .action = ACTION_STATUS_DETAILS, + .action = MANAGEMENT_DETAILS, .deprecated = false, .log_message = "" }, @@ -285,17 +315,19 @@ usage(void) printf(" pgagroal-cli [ OPTIONS ] [ COMMAND ] \n"); printf("\n"); printf("Options:\n"); - printf(" -c, --config CONFIG_FILE Set the path to the pgagroal.conf file\n"); - printf(" Default: %s\n", PGAGROAL_DEFAULT_CONF_FILE); - printf(" -h, --host HOST Set the host name\n"); - printf(" -p, --port PORT Set the port number\n"); - printf(" -U, --user USERNAME Set the user name\n"); - printf(" -P, --password PASSWORD Set the password\n"); - printf(" -L, --logfile FILE Set the log file\n"); - printf(" -F, --format text|json Set the output format\n"); - printf(" -v, --verbose Output text string of result\n"); - printf(" -V, --version Display version information\n"); - printf(" -?, --help Display help\n"); + printf(" -c, --config CONFIG_FILE Set the path to the pgagroal.conf file\n"); + printf(" Default: %s\n", PGAGROAL_DEFAULT_CONF_FILE); + printf(" -h, --host HOST Set the host name\n"); + printf(" -p, --port PORT Set the port number\n"); + printf(" -U, --user USERNAME Set the user name\n"); + printf(" -P, --password PASSWORD Set the password\n"); + printf(" -L, --logfile FILE Set the log file\n"); + printf(" -F, --format text|json|raw Set the output format\n"); + printf(" -C, --compress none|gz|zstd|lz4|bz2 Compress the wire protocol\n"); + printf(" -E, --encrypt none|aes|aes256|aes192|aes128 Encrypt the wire protocol\n"); + printf(" -v, --verbose Output text string of result\n"); + printf(" -V, --version Display version information\n"); + printf(" -?, --help Display help\n"); printf("\n"); printf("Commands:\n"); printf(" flush [mode] [database] Flush connections according to [mode].\n"); @@ -347,12 +379,15 @@ 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; bool remote_connection = false; long l_port; - char output_format = COMMAND_OUTPUT_FORMAT_TEXT; + int32_t output_format = MANAGEMENT_OUTPUT_FORMAT_TEXT; + int32_t compression = MANAGEMENT_COMPRESSION_NONE; + int32_t encryption = MANAGEMENT_ENCRYPTION_NONE; size_t command_count = sizeof(command_table) / sizeof(struct pgagroal_command); struct pgagroal_parsed_command parsed = {.cmd = NULL, .args = {0}}; @@ -367,12 +402,14 @@ main(int argc, char** argv) {"password", required_argument, 0, 'P'}, {"logfile", required_argument, 0, 'L'}, {"format", required_argument, 0, 'F' }, + {"compress", required_argument, 0, 'C'}, + {"encrypt", required_argument, 0, 'E'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, '?'} }; - c = getopt_long(argc, argv, "vV?c:h:p:U:P:L:F:", + c = getopt_long(argc, argv, "vV?c:h:p:U:P:L:F:C:E:", long_options, &option_index); if (c == -1) @@ -407,11 +444,74 @@ main(int argc, char** argv) case 'F': if (!strncmp(optarg, "json", MISC_LENGTH)) { - output_format = COMMAND_OUTPUT_FORMAT_JSON; + output_format = MANAGEMENT_OUTPUT_FORMAT_JSON; + } + else if (!strncmp(optarg, "raw", MISC_LENGTH)) + { + output_format = MANAGEMENT_OUTPUT_FORMAT_RAW; + } + else if (!strncmp(optarg, "text", MISC_LENGTH)) + { + output_format = MANAGEMENT_OUTPUT_FORMAT_TEXT; + } + else + { + warnx("pgagroal-cli: Format type is not correct"); + exit(1); + } + break; + case 'C': + if (!strncmp(optarg, "gz", MISC_LENGTH)) + { + compression = MANAGEMENT_COMPRESSION_GZIP; + } + else if (!strncmp(optarg, "zstd", MISC_LENGTH)) + { + compression = MANAGEMENT_COMPRESSION_ZSTD; + } + else if (!strncmp(optarg, "lz4", MISC_LENGTH)) + { + compression = MANAGEMENT_COMPRESSION_LZ4; + } + else if (!strncmp(optarg, "bz2", MISC_LENGTH)) + { + compression = MANAGEMENT_COMPRESSION_BZIP2; + } + else if (!strncmp(optarg, "none", MISC_LENGTH)) + { + break; + } + else + { + warnx("pgagroal-cli: Compress method is not correct"); + exit(1); + } + break; + case 'E': + if (!strncmp(optarg, "aes", MISC_LENGTH)) + { + encryption = MANAGEMENT_ENCRYPTION_AES256; + } + else if (!strncmp(optarg, "aes256", MISC_LENGTH)) + { + encryption = MANAGEMENT_ENCRYPTION_AES256; + } + else if (!strncmp(optarg, "aes192", MISC_LENGTH)) + { + encryption = MANAGEMENT_ENCRYPTION_AES192; + } + else if (!strncmp(optarg, "aes128", MISC_LENGTH)) + { + encryption = MANAGEMENT_ENCRYPTION_AES128; + } + else if (!strncmp(optarg, "none", MISC_LENGTH)) + { + break; } else { - output_format = COMMAND_OUTPUT_FORMAT_TEXT; + warnx("pgagroal-cli: Encrypt method is not correct"); + exit(1); } break; case 'v': @@ -534,12 +634,22 @@ main(int argc, char** argv) if (!parse_command(argc, argv, optind, &parsed, command_table, command_count)) { - usage(); + if (argc > optind) + { + char* command = argv[optind]; + display_helper(command); + } + else + { + usage(); + } 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; if (!remote_connection) @@ -613,8 +723,7 @@ main(int argc, char** argv) { if ((unsigned char)(*(password + i)) & 0x80) { - - warnx("Bad credentials for %s\n", username); + warnx("pgagroal-cli: Bad credentials for %s\n", username); goto done; } } @@ -627,55 +736,55 @@ main(int argc, char** argv) } } - if (parsed.cmd->action == ACTION_FLUSH) + if (parsed.cmd->action == MANAGEMENT_FLUSH) { - exit_code = flush(s_ssl, socket, parsed.cmd->mode, parsed.args[0]); + exit_code = flush(s_ssl, socket, parsed.cmd->mode, parsed.args[0], compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_ENABLEDB) + else if (parsed.cmd->action == MANAGEMENT_ENABLEDB) { - exit_code = enabledb(s_ssl, socket, parsed.args[0]); + exit_code = enabledb(s_ssl, socket, parsed.args[0], compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_DISABLEDB) + else if (parsed.cmd->action == MANAGEMENT_DISABLEDB) { - exit_code = disabledb(s_ssl, socket, parsed.args[0]); + exit_code = disabledb(s_ssl, socket, parsed.args[0], compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_GRACEFULLY) + else if (parsed.cmd->action == MANAGEMENT_GRACEFULLY) { - exit_code = gracefully(s_ssl, socket); + exit_code = gracefully(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_STOP) + else if (parsed.cmd->action == MANAGEMENT_SHUTDOWN) { - exit_code = stop(s_ssl, socket); + exit_code = pgagroal_shutdown(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_CANCELSHUTDOWN) + else if (parsed.cmd->action == MANAGEMENT_CANCEL_SHUTDOWN) { - exit_code = cancel_shutdown(s_ssl, socket); + exit_code = cancel_shutdown(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_STATUS) + else if (parsed.cmd->action == MANAGEMENT_STATUS) { - exit_code = status(s_ssl, socket, output_format); + exit_code = status(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_STATUS_DETAILS) + else if (parsed.cmd->action == MANAGEMENT_DETAILS) { - exit_code = details(s_ssl, socket, output_format); + exit_code = details(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_ISALIVE) + else if (parsed.cmd->action == MANAGEMENT_PING) { - exit_code = isalive(s_ssl, socket, output_format); + exit_code = ping(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_RESET) + else if (parsed.cmd->action == MANAGEMENT_CLEAR) { - exit_code = reset(s_ssl, socket); + exit_code = clear(s_ssl, socket, compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_RESET_SERVER) + else if (parsed.cmd->action == MANAGEMENT_CLEAR_SERVER) { - exit_code = reset_server(s_ssl, socket, parsed.args[0]); + exit_code = clear_server(s_ssl, socket, parsed.args[0], compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_SWITCH_TO) + else if (parsed.cmd->action == MANAGEMENT_SWITCH_TO) { - exit_code = switch_to(s_ssl, socket, parsed.args[0]); + exit_code = switch_to(s_ssl, socket, parsed.args[0], compression, encryption, output_format); } - else if (parsed.cmd->action == ACTION_RELOAD) + else if (parsed.cmd->action == MANAGEMENT_RELOAD) { if (configuration_path == NULL) { @@ -685,21 +794,21 @@ main(int argc, char** argv) } else { - exit_code = reload(s_ssl, socket); + exit_code = reload(s_ssl, socket, compression, encryption, output_format); } } - else if (parsed.cmd->action == ACTION_CONFIG_GET) - { - exit_code = config_get(s_ssl, socket, parsed.args[0], verbose, output_format); - } - else if (parsed.cmd->action == ACTION_CONFIG_SET) - { - exit_code = config_set(s_ssl, socket, parsed.args[0], parsed.args[1], verbose, output_format); - } - else if (parsed.cmd->action == ACTION_CONFIG_LS) - { - exit_code = config_ls(s_ssl, socket, 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); */ + /* } */ done: @@ -720,17 +829,9 @@ main(int argc, char** argv) if (configuration_path != NULL) { - if (parsed.cmd != NULL) + if (matched && exit_code != 0) { - 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; - } + warnx("No connection to pgagroal on %s", config->unix_socket_dir); } } @@ -741,270 +842,673 @@ main(int argc, char** argv) if (verbose) { - warnx("%s (%d)", exit_code == EXIT_STATUS_OK ? "Success" : "Error", exit_code); + warnx("%s (%d)", exit_code == 0 ? "Success" : "Error", exit_code); } return exit_code; } -static int -flush(SSL* ssl, int socket, int32_t mode, char* database) +static void +help_cancel_shutdown(void) { - if (pgagroal_management_flush(ssl, socket, mode, database)) - { - return EXIT_STATUS_CONNECTION_ERROR; - } + printf("Cancel shutdown of pgagroal\n"); + printf(" pgagroal-cli cancel-shutdown\n"); +} - return EXIT_STATUS_OK; +static void +help_shutdown(void) +{ + printf("Shutdown pgagroal\n"); + printf(" pgagroal-cli shutdown\n"); } -static int -enabledb(SSL* ssl, int socket, char* database) +static void +help_ping(void) { - if (pgagroal_management_enabledb(ssl, socket, database)) - { - return EXIT_STATUS_CONNECTION_ERROR; - } + printf("Check if pgagroal is alive\n"); + printf(" pgagroal-cli ping\n"); +} - return EXIT_STATUS_OK; +static void +help_status_details(void) +{ + printf("Status of pgagroal\n"); + printf(" pgagroal-cli status [details]\n"); } -static int -disabledb(SSL* ssl, int socket, char* database) +static void +help_disabledb(void) +{ + printf("Disable a database\n"); + printf(" pgagroal-cli disabledb |*\n"); +} + +static void +help_enabledb(void) +{ + printf("Enable a database\n"); + printf(" pgagroal-cli enabledb |*\n"); +} + +static void +help_conf(void) +{ + printf("Manage the configuration\n"); + printf(" pgagroal-cli conf [reload]\n"); +} + +static void +help_clear(void) +{ + printf("Reset data\n"); + printf(" pgagroal-cli clear [prometheus]\n"); +} + +static void +help_flush(void) { - if (pgagroal_management_disabledb(ssl, socket, database)) + printf("Flush connections\n"); + printf(" pgagroal-cli flush [gracefully|idle|all] [*|]\n"); +} + +static void +help_switch_to(void) +{ + printf("Switch to another primary server\n"); + printf(" pgagroal-cli switch-to \n"); +} + +static void +display_helper(char* command) +{ + if (!strcmp(command, COMMAND_CANCELSHUTDOWN)) { - return EXIT_STATUS_CONNECTION_ERROR; + help_cancel_shutdown(); + } + else if (//!strcmp(command, COMMAND_CONFIG_GET) || + //!strcmp(command, COMMAND_CONFIG_LS) || + //!strcmp(command, COMMAND_CONFIG_SET) || + !strcmp(command, COMMAND_RELOAD)) + { + help_conf(); + } + else if (!strcmp(command, COMMAND_DISABLEDB)) + { + help_disabledb(); + } + else if (!strcmp(command, COMMAND_ENABLEDB)) + { + help_enabledb(); + } + else if (!strcmp(command, COMMAND_FLUSH)) + { + help_flush(); + } + else if (!strcmp(command, COMMAND_PING)) + { + help_ping(); + } + else if (!strcmp(command, COMMAND_CLEAR) || + !strcmp(command, COMMAND_CLEAR_SERVER)) + { + help_clear(); + } + else if (!strcmp(command, COMMAND_SHUTDOWN)) + { + help_shutdown(); + } + else if (!strcmp(command, COMMAND_STATUS)) + { + help_status_details(); + } + else if (!strcmp(command, COMMAND_SWITCH_TO)) + { + help_switch_to(); + } + else + { + usage(); } - - return EXIT_STATUS_OK; } static int -gracefully(SSL* ssl, int socket) +flush(SSL* ssl, int socket, int32_t mode, char* database, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_gracefully(ssl, socket)) + if (pgagroal_management_request_flush(ssl, socket, mode, database, compression, encryption, output_format)) + { + goto error; + } + + if (process_result(ssl, socket, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + return 0; + +error: + + return 1; } static int -stop(SSL* ssl, int socket) +enabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_stop(ssl, socket)) + if (pgagroal_management_request_enabledb(ssl, socket, database, compression, encryption, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; + } + + if (process_result(ssl, socket, output_format)) + { + goto error; } - return EXIT_STATUS_OK; + return 0; + +error: + + return 1; } static int -cancel_shutdown(SSL* ssl, int socket) +disabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_cancel_shutdown(ssl, socket)) + if (pgagroal_management_request_disabledb(ssl, socket, database, compression, encryption, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + if (process_result(ssl, socket, output_format)) + { + goto error; + } + + return 0; + +error: + + return 1; } static int -status(SSL* ssl, int socket, char output_format) +gracefully(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_status(ssl, socket) == 0) + if (pgagroal_management_request_gracefully(ssl, socket, compression, encryption, output_format)) { - return pgagroal_management_read_status(ssl, socket, output_format); + goto error; } - else + + if (process_result(ssl, socket, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } + + return 0; + +error: + + return 1; } static int -details(SSL* ssl, int socket, char output_format) +pgagroal_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_details(ssl, socket) == 0) + if (pgagroal_management_request_shutdown(ssl, socket, compression, encryption, output_format)) { - return pgagroal_management_read_details(ssl, socket, output_format); + goto error; + } + if (process_result(ssl, socket, output_format)) + { + goto error; } - // if here, an error occurred - return EXIT_STATUS_CONNECTION_ERROR; + return 0; + +error: + return 1; } static int -isalive(SSL* ssl, int socket, char output_format) +cancel_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - int status = -1; - - if (pgagroal_management_isalive(ssl, socket) == 0) + if (pgagroal_management_request_cancel_shutdown(ssl, socket, compression, encryption, output_format)) { - if (pgagroal_management_read_isalive(ssl, socket, &status, output_format)) - { - return EXIT_STATUS_CONNECTION_ERROR; - } - - if (status != PING_STATUS_RUNNING && status != PING_STATUS_SHUTDOWN_GRACEFULLY) - { - return EXIT_STATUS_CONNECTION_ERROR; - } + goto error; } - else + + if (process_result(ssl, socket, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + return 0; + +error: + + return 1; } static int -reset(SSL* ssl, int socket) +status(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_reset(ssl, socket)) + if (pgagroal_management_request_status(ssl, socket, compression, encryption, output_format)) + { + goto error; + } + + if (process_result(ssl, socket, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + return 0; + +error: + + return 1; } static int -reset_server(SSL* ssl, int socket, char* server) +details(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_reset_server(ssl, socket, server)) + if (pgagroal_management_request_details(ssl, socket, compression, encryption, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + if (process_result(ssl, socket, output_format)) + { + goto error; + } + + return 0; + +error: + + return 1; } static int -switch_to(SSL* ssl, int socket, char* server) +ping(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_switch_to(ssl, socket, server)) + if (pgagroal_management_request_ping(ssl, socket, compression, encryption, output_format)) + { + goto error; + } + + if (process_result(ssl, socket, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + return 0; + +error: + + return 1; } static int -reload(SSL* ssl, int socket) +clear(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (pgagroal_management_reload(ssl, socket)) + if (pgagroal_management_request_clear(ssl, socket, compression, encryption, output_format)) + { + goto error; + } + + if (process_result(ssl, socket, output_format)) { - return EXIT_STATUS_CONNECTION_ERROR; + goto error; } - return EXIT_STATUS_OK; + return 0; + +error: + + return 1; } -/** - * 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 - * @param output_format the format for the output (e.g., json) - * @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, char output_format) +clear_server(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format) { + if (pgagroal_management_request_clear_server(ssl, socket, server, compression, encryption, output_format)) + { + goto error; + } - if (!config_key || strlen(config_key) > MISC_LENGTH) + if (process_result(ssl, socket, output_format)) { goto error; } - if (pgagroal_management_config_get(ssl, socket, config_key)) + return 0; + +error: + + return 1; +} + +static int +switch_to(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format) +{ + if (pgagroal_management_request_switch_to(ssl, socket, server, compression, encryption, output_format)) { goto error; } - if (pgagroal_management_read_config_get(socket, config_key, NULL, verbose, output_format)) + if (process_result(ssl, socket, output_format)) { goto error; } - return EXIT_STATUS_OK; + return 0; error: - return EXIT_STATUS_CONNECTION_ERROR; + + return 1; } -/** - * Entry point for a config-set command. - * - * The function requires the configuration parameter to set and its value. - * It then sends the command over the socket and reads the answer back. - * - * @param ssl the SSL connection - * @param socket the socket to use - * @param config_key the parameter name to set - * @param config_value the value to set the parameter to - * @param verbose if true the system will print back the new value of the configuration parameter - * @return 0 on success - */ static int -config_set(SSL* ssl, int socket, char* config_key, char* config_value, bool verbose, char output_format) +reload(SSL* ssl, int socket, 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) + if (pgagroal_management_request_reload(ssl, socket, compression, encryption, output_format)) { goto error; } - if (pgagroal_management_config_set(ssl, socket, config_key, config_value)) + if (process_result(ssl, socket, output_format)) { goto error; } - status = pgagroal_management_read_config_get(socket, config_key, config_value, verbose, output_format); + return 0; - return status; error: - return EXIT_STATUS_CONNECTION_ERROR; + + return 1; } -/** - * Asks the daemon about the configuration file location. - * - * @returns 0 on success - */ +/* 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 -config_ls(SSL* ssl, int socket, char output_format) +process_result(SSL* ssl, int socket, int32_t output_format) { + struct json* read = NULL; - if (pgagroal_management_conf_ls(ssl, socket)) + if (pgagroal_management_read_json(ssl, socket, NULL, NULL, &read)) { goto error; } - if (pgagroal_management_read_conf_ls(ssl, socket, output_format)) + if (MANAGEMENT_OUTPUT_FORMAT_RAW != output_format) { - goto error; + translate_json_object(read); + } + + if (MANAGEMENT_OUTPUT_FORMAT_TEXT == output_format) + { + pgagroal_json_print(read, FORMAT_TEXT); } + else + { + pgagroal_json_print(read, FORMAT_JSON); + } + + pgagroal_json_destroy(read); + + return 0; - return EXIT_STATUS_OK; error: - return EXIT_STATUS_CONNECTION_ERROR; + + pgagroal_json_destroy(read); + + return 1; +} + +static char* +translate_command(int32_t cmd_code) +{ + char* command_output = NULL; + switch (cmd_code) + { + case MANAGEMENT_CANCEL_SHUTDOWN: + command_output = pgagroal_append(command_output, COMMAND_CANCELSHUTDOWN); + break; + case MANAGEMENT_DETAILS: + command_output = pgagroal_append(command_output, COMMAND_STATUS_DETAILS); + break; + case MANAGEMENT_DISABLEDB: + command_output = pgagroal_append(command_output, COMMAND_DISABLEDB); + break; + case MANAGEMENT_ENABLEDB: + command_output = pgagroal_append(command_output, COMMAND_ENABLEDB); + break; + case MANAGEMENT_FLUSH: + command_output = pgagroal_append(command_output, COMMAND_FLUSH); + break; + case MANAGEMENT_GRACEFULLY: + command_output = pgagroal_append(command_output, COMMAND_GRACEFULLY); + break; + case MANAGEMENT_PING: + command_output = pgagroal_append(command_output, COMMAND_PING); + break; + case MANAGEMENT_RELOAD: + command_output = pgagroal_append(command_output, COMMAND_RELOAD); + break; + case MANAGEMENT_CLEAR: + command_output = pgagroal_append(command_output, COMMAND_CLEAR); + break; + case MANAGEMENT_CLEAR_SERVER: + command_output = pgagroal_append(command_output, COMMAND_CLEAR_SERVER); + break; + case MANAGEMENT_SHUTDOWN: + command_output = pgagroal_append(command_output, COMMAND_SHUTDOWN); + break; + case MANAGEMENT_STATUS: + command_output = pgagroal_append(command_output, COMMAND_STATUS); + break; + case MANAGEMENT_SWITCH_TO: + command_output = pgagroal_append(command_output, COMMAND_SWITCH_TO); + break; + default: + break; + } + return command_output; +} + +static char* +translate_output_format(int32_t out_code) +{ + char* output_format_output = NULL; + switch (out_code) + { + case MANAGEMENT_OUTPUT_FORMAT_JSON: + output_format_output = pgagroal_append(output_format_output, OUTPUT_FORMAT_JSON); + break; + case MANAGEMENT_OUTPUT_FORMAT_TEXT: + output_format_output = pgagroal_append(output_format_output, OUTPUT_FORMAT_TEXT); + break; + default: + break; + } + return output_format_output; +} + +static char* +translate_compression(int32_t compression_code) +{ + char* compression_output = NULL; + switch (compression_code) + { + case COMPRESSION_CLIENT_GZIP: + case COMPRESSION_SERVER_GZIP: + compression_output = pgagroal_append(compression_output, "gzip"); + break; + case COMPRESSION_CLIENT_ZSTD: + case COMPRESSION_SERVER_ZSTD: + compression_output = pgagroal_append(compression_output, "zstd"); + break; + case COMPRESSION_CLIENT_LZ4: + case COMPRESSION_SERVER_LZ4: + compression_output = pgagroal_append(compression_output, "lz4"); + break; + case COMPRESSION_CLIENT_BZIP2: + compression_output = pgagroal_append(compression_output, "bzip2"); + break; + default: + compression_output = pgagroal_append(compression_output, "none"); + break; + } + return compression_output; +} + +static char* +translate_encryption(int32_t encryption_code) +{ + char* encryption_output = NULL; + switch (encryption_code) + { + case ENCRYPTION_AES_256_CBC: + encryption_output = pgagroal_append(encryption_output, "aes-256-cbc"); + break; + case ENCRYPTION_AES_192_CBC: + encryption_output = pgagroal_append(encryption_output, "aes-192-cbc"); + break; + case ENCRYPTION_AES_128_CBC: + encryption_output = pgagroal_append(encryption_output, "aes-128-cbc"); + break; + case ENCRYPTION_AES_256_CTR: + encryption_output = pgagroal_append(encryption_output, "aes-256-ctr"); + break; + case ENCRYPTION_AES_192_CTR: + encryption_output = pgagroal_append(encryption_output, "aes-192-ctr"); + break; + case ENCRYPTION_AES_128_CTR: + encryption_output = pgagroal_append(encryption_output, "aes-128-ctr"); + break; + default: + encryption_output = pgagroal_append(encryption_output, "none"); + break; + } + return encryption_output; +} + +static void +translate_json_object(struct json* j) +{ + struct json* header = NULL; + int32_t command = 0; + char* translated_command = NULL; + int32_t out_format = -1; + char* translated_out_format = NULL; + int32_t out_compression = -1; + char* translated_compression = NULL; + int32_t out_encryption = -1; + char* translated_encryption = NULL; + + // Translate arguments of header + header = (struct json*)pgagroal_json_get(j, MANAGEMENT_CATEGORY_HEADER); + + if (header) + { + command = (int32_t)pgagroal_json_get(header, MANAGEMENT_ARGUMENT_COMMAND); + translated_command = translate_command(command); + if (translated_command) + { + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_COMMAND, (uintptr_t)translated_command, ValueString); + } + + out_format = (int32_t)pgagroal_json_get(header, MANAGEMENT_ARGUMENT_OUTPUT); + translated_out_format = translate_output_format(out_format); + if (translated_out_format) + { + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_OUTPUT, (uintptr_t)translated_out_format, ValueString); + } + + out_compression = (int32_t)pgagroal_json_get(header, MANAGEMENT_ARGUMENT_COMPRESSION); + translated_compression = translate_compression(out_compression); + if (translated_compression) + { + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_COMPRESSION, (uintptr_t)translated_compression, ValueString); + } + + out_encryption = (int32_t)pgagroal_json_get(header, MANAGEMENT_ARGUMENT_ENCRYPTION); + translated_encryption = translate_encryption(out_encryption); + if (translated_encryption) + { + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_ENCRYPTION, (uintptr_t)translated_encryption, ValueString); + } + + free(translated_command); + free(translated_out_format); + free(translated_compression); + free(translated_encryption); + } } diff --git a/src/include/aes.h b/src/include/aes.h new file mode 100644 index 00000000..ff00c81d --- /dev/null +++ b/src/include/aes.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_AES_H +#define PGAGROAL_AES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/** + * Encrypt a string + * @param plaintext The string + * @param password The master password + * @param ciphertext The ciphertext output + * @param ciphertext_length The length of the ciphertext + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_encrypt(char* plaintext, char* password, char** ciphertext, int* ciphertext_length, int mode); + +/** + * Decrypt a string + * @param ciphertext The string + * @param ciphertext_length The length of the ciphertext + * @param password The master password + * @param plaintext The plaintext output + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_decrypt(char* ciphertext, int ciphertext_length, char* password, char** plaintext, int mode); + +/** + * + * Encrypt a buffer + * @param origin_buffer The original buffer + * @param origin_size The size of the buffer + * @param enc_buffer The result buffer + * @param enc_size The result buffer size + * @param mode The aes mode + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_encrypt_buffer(unsigned char* origin_buffer, size_t origin_size, unsigned char** enc_buffer, size_t* enc_size, int mode); + +/** + * + * Decrypt a buffer + * @param origin_buffer The original buffer + * @param origin_size The size of the buffer + * @param dec_buffer The result buffer + * @param dec_size The result buffer size + * @param mode The aes mode + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_decrypt_buffer(unsigned char* origin_buffer, size_t origin_size, unsigned char** dec_buffer, size_t* dec_size, int mode); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/bzip2_compression.h b/src/include/bzip2_compression.h new file mode 100644 index 00000000..49f57b04 --- /dev/null +++ b/src/include/bzip2_compression.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_BZIP_H +#define PGAGROAL_BZIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * BZip compress a string + * @param s The original string + * @param buffer The point to the compressed data buffer + * @param buffer_size The size of the compressed buffer will be stored. + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_bzip2_string(char* s, unsigned char** buffer, size_t* buffer_size); + +/** + * BUNZip decompress a buffer to string + * @param compressed_buffer The buffer containing the GZIP compressed data + * @param compressed_size The size of the compressed buffer + * @param output_string The pointer to a string where the decompressed data will be stored + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_bunzip2_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/configuration.h b/src/include/configuration.h index 1d15d24e..08a7c574 100644 --- a/src/include/configuration.h +++ b/src/include/configuration.h @@ -274,10 +274,11 @@ pgagroal_validate_superuser_configuration(void* shmem); /** * Reload the configuration + * @param reload Should the server be reloaded * @return 0 upon success, otherwise 1 */ int -pgagroal_reload_configuration(void); +pgagroal_reload_configuration(bool* reload); /** * Automatically initialize the 'pidfile' diff --git a/src/include/connection.h b/src/include/connection.h new file mode 100644 index 00000000..378dbb7c --- /dev/null +++ b/src/include/connection.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_CONNECTION_H +#define PGAGROAL_CONNECTION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +#include + +#define CONNECTION_TRANSFER 0 +#define CONNECTION_RETURN 1 +#define CONNECTION_KILL 2 +#define CONNECTION_CLIENT_FD 3 +#define CONNECTION_REMOVE_FD 4 +#define CONNECTION_CLIENT_DONE 5 + +/** + * Connection: Get a connection + * @param client_fd The client descriptor + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_get(int* client_fd); + +/** + * Connection: Get a connection based on a PID + * @param client_fd The client descriptor + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_get_pid(pid_t pid, int* client_fd); + +/** + * Connection: Opetation id write + * @param client_fd The client descriptor + * @param id The identifier + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_id_write(int client_fd, int id); + +/** + * Connection: Opetation id read + * @param client_fd The client descriptor + * @param id The identifier + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_id_read(int client_fd, int* id); + +/** + * Connection: Transfer write + * @param client_fd The client descriptor + * @param slot The slot + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_transfer_write(int client_fd, int32_t slot); + +/** + * Connection: Transfer read + * @param client_fd The client descriptor + * @param slot The slot + * @param fd The file descriptor + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_transfer_read(int client_fd, int32_t* slot, int* fd); + +/** + * Connection: Slot write + * @param slot The slot + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_slot_write(int client_fd, int32_t slot); + +/** + * Connection: Slot read + * @param slot The slot + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_slot_read(int client_fd, int32_t* slot); + +/** + * Connection: Socket write + * @param socket The socket + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_socket_write(int client_fd, int socket); + +/** + * Connection: Socket read + * @param socket The socket + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_socket_read(int client_fd, int* socket); + +/** + * Connection: PID write + * @param pid The PID + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_pid_write(int client_fd, pid_t pid); + +/** + * Connection: PID read + * @param pid The PID + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_connection_pid_read(int client_fd, pid_t* pid); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/gzip_compression.h b/src/include/gzip_compression.h new file mode 100644 index 00000000..f22dabc4 --- /dev/null +++ b/src/include/gzip_compression.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_GZIP_H +#define PGAGROAL_GZIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * GZip a string + * @param s The original string + * @param buffer The point to the compressed data buffer + * @param buffer_size The size of the compressed buffer will be stored. + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_gzip_string(char* s, unsigned char** buffer, size_t* buffer_size); + +/** + * GUNZip a buffer to string + * @param compressed_buffer The buffer containing the GZIP compressed data + * @param compressed_size The size of the compressed buffer + * @param output_string The pointer to a string where the decompressed data will be stored + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_gunzip_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/lz4_compression.h b/src/include/lz4_compression.h new file mode 100644 index 00000000..0d4bbb26 --- /dev/null +++ b/src/include/lz4_compression.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_LZ4_H +#define PGAGROAL_LZ4_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define BLOCK_BYTES 1024 * 4 + +/** + * LZ4 compress a string + * @param s The original string + * @param buffer The point to the compressed data buffer + * @param buffer_size The size of the compressed buffer will be stored. + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_lz4c_string(char* s, unsigned char** buffer, size_t* buffer_size); + +/** + * LZ4 decompress a buffer to string + * @param compressed_buffer The buffer containing the GZIP compressed data + * @param compressed_size The size of the compressed buffer + * @param output_string The pointer to a string where the decompressed data will be stored + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_lz4d_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/management.h b/src/include/management.h index d2219fc9..19006d5a 100644 --- a/src/include/management.h +++ b/src/include/management.h @@ -34,132 +34,120 @@ extern "C" { #endif #include +#include #include #include #include -#define MANAGEMENT_TRANSFER_CONNECTION 1 -#define MANAGEMENT_RETURN_CONNECTION 2 -#define MANAGEMENT_KILL_CONNECTION 3 -#define MANAGEMENT_FLUSH 4 -#define MANAGEMENT_GRACEFULLY 5 -#define MANAGEMENT_STOP 6 -#define MANAGEMENT_STATUS 7 -#define MANAGEMENT_DETAILS 8 -#define MANAGEMENT_ISALIVE 9 -#define MANAGEMENT_CANCEL_SHUTDOWN 10 -#define MANAGEMENT_ENABLEDB 11 -#define MANAGEMENT_DISABLEDB 12 -#define MANAGEMENT_RESET 13 -#define MANAGEMENT_RESET_SERVER 14 -#define MANAGEMENT_CLIENT_DONE 15 -#define MANAGEMENT_CLIENT_FD 16 -#define MANAGEMENT_SWITCH_TO 17 -#define MANAGEMENT_RELOAD 18 -#define MANAGEMENT_REMOVE_FD 19 -#define MANAGEMENT_CONFIG_GET 20 -#define MANAGEMENT_CONFIG_SET 21 -#define MANAGEMENT_CONFIG_LS 22 -#define MANAGEMENT_GET_PASSWORD 23 - -/** - * Status for the 'ping' (i.e., is-alive) command - */ -#define PING_STATUS_RUNNING 1 -#define PING_STATUS_SHUTDOWN_GRACEFULLY 2 - /** - * Available command output formats + * Management header */ -#define COMMAND_OUTPUT_FORMAT_TEXT 'T' -#define COMMAND_OUTPUT_FORMAT_JSON 'J' +#define MANAGEMENT_COMPRESSION_NONE 0 +#define MANAGEMENT_COMPRESSION_GZIP 1 +#define MANAGEMENT_COMPRESSION_ZSTD 2 +#define MANAGEMENT_COMPRESSION_LZ4 3 +#define MANAGEMENT_COMPRESSION_BZIP2 4 -/** - * Available applications - */ -#define PGAGROAL_EXECUTABLE 1 -#define PGAGROAL_EXECUTABLE_CLI 2 -#define PGAGROAL_EXECUTABLE_VAULT 3 - -/** @struct pgagroal_version_info - * stores the application name and its version - * which are sent through the socket - */ -struct pgagroal_version_info -{ - char s[2]; /**< The command encoded */ - int command; /**< The command */ - char v[3]; /**< The version encoded */ - int version; /**< The version */ -}; +#define MANAGEMENT_ENCRYPTION_NONE 0 +#define MANAGEMENT_ENCRYPTION_AES256 1 +#define MANAGEMENT_ENCRYPTION_AES192 2 +#define MANAGEMENT_ENCRYPTION_AES128 3 /** - * Get the frontend password of a user - * @param ssl The SSL connection - * @param socket The socket descriptor - * @param user The frontend user - * @param password The desired password - * @return 0 upon success, otherwise 1 + * Management categories */ -int -pgagroal_management_get_password(SSL* ssl, int socket, char* username, char* password); +#define MANAGEMENT_CATEGORY_HEADER "Header" +#define MANAGEMENT_CATEGORY_REQUEST "Request" +#define MANAGEMENT_CATEGORY_RESPONSE "Response" +#define MANAGEMENT_CATEGORY_OUTCOME "Outcome" /** - * Write the frontend password of a user to the socket - * @param socket The socket descriptor - * @param password The frontend user - * @return 0 upon success, otherwise 1 + * Management commands */ -int -pgagroal_management_write_get_password(SSL* ssl, int socket, char* password); - -/** - * Read the management header - * @param socket The socket descriptor - * @param id The resulting management identifier - * @param slot The resulting slot - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_read_header(int socket, signed char* id, int32_t* slot); - -/** - * Read the management payload - * @param socket The socket descriptor - * @param id The management identifier - * @param payload_i The resulting integer payload - * @param payload_s The resulting string payload - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_read_payload(int socket, signed char id, int* payload_i, char** payload_s); - -/** - * Management operation: Transfer a connection - * @param slot The slot - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_transfer_connection(int32_t slot); +#define MANAGEMENT_CANCEL_SHUTDOWN 1 +/* #define MANAGEMENT_CONFIG_GET 2 */ +/* #define MANAGEMENT_CONFIG_LS 3 */ +/* #define MANAGEMENT_CONFIG_SET 4 */ +#define MANAGEMENT_DETAILS 5 +#define MANAGEMENT_DISABLEDB 6 +#define MANAGEMENT_ENABLEDB 7 +#define MANAGEMENT_FLUSH 8 +#define MANAGEMENT_GET_PASSWORD 9 +#define MANAGEMENT_GRACEFULLY 10 +#define MANAGEMENT_PING 11 +#define MANAGEMENT_RELOAD 12 +#define MANAGEMENT_CLEAR 13 +#define MANAGEMENT_CLEAR_SERVER 14 +#define MANAGEMENT_SHUTDOWN 15 +#define MANAGEMENT_STATUS 16 +#define MANAGEMENT_SWITCH_TO 17 /** - * Management operation: Return a connection - * @param slot The slot - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_return_connection(int32_t slot); - -/** - * Management operation: Kill a connection - * @param slot The slot - * @param socket The socket - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_kill_connection(int32_t slot, int socket); + * Management arguments + */ +#define MANAGEMENT_ARGUMENT_ACTIVE_CONNECTIONS "ActiveConnections" +#define MANAGEMENT_ARGUMENT_APPNAME "AppName" +#define MANAGEMENT_ARGUMENT_CLIENT_VERSION "ClientVersion" +#define MANAGEMENT_ARGUMENT_COMMAND "Command" +#define MANAGEMENT_ARGUMENT_COMPRESSION "Compression" +#define MANAGEMENT_ARGUMENT_CONNECTIONS "Connections" +#define MANAGEMENT_ARGUMENT_DATABASE "Database" +#define MANAGEMENT_ARGUMENT_DATABASES "Databases" +#define MANAGEMENT_ARGUMENT_ENABLED "Enabled" +#define MANAGEMENT_ARGUMENT_ENCRYPTION "Encryption" +#define MANAGEMENT_ARGUMENT_ERROR "Error" +#define MANAGEMENT_ARGUMENT_FD "FD" +#define MANAGEMENT_ARGUMENT_HOST "Host" +#define MANAGEMENT_ARGUMENT_INITIAL_CONNECTIONS "InitialConnections" +#define MANAGEMENT_ARGUMENT_LIMITS "Limits" +#define MANAGEMENT_ARGUMENT_MAX_CONNECTIONS "MaxConnections" +#define MANAGEMENT_ARGUMENT_MIN_CONNECTIONS "MinConnections" +#define MANAGEMENT_ARGUMENT_MODE "Mode" +#define MANAGEMENT_ARGUMENT_NUMBER_OF_SERVERS "NumberOfServers" +#define MANAGEMENT_ARGUMENT_OUTPUT "Output" +#define MANAGEMENT_ARGUMENT_PASSWORD "Password" +#define MANAGEMENT_ARGUMENT_PID "PID" +#define MANAGEMENT_ARGUMENT_PORT "Port" +#define MANAGEMENT_ARGUMENT_RESTART "Restart" +#define MANAGEMENT_ARGUMENT_SERVER "Server" +#define MANAGEMENT_ARGUMENT_SERVERS "Servers" +#define MANAGEMENT_ARGUMENT_SERVER_VERSION "ServerVersion" +#define MANAGEMENT_ARGUMENT_START_TIME "StartTime" +#define MANAGEMENT_ARGUMENT_STATE "State" +#define MANAGEMENT_ARGUMENT_STATUS "Status" +#define MANAGEMENT_ARGUMENT_TIME "Time" +#define MANAGEMENT_ARGUMENT_TIMESTAMP "Timestamp" +#define MANAGEMENT_ARGUMENT_TIMESTAMP "Timestamp" +#define MANAGEMENT_ARGUMENT_TOTAL_CONNECTIONS "TotalConnections" +#define MANAGEMENT_ARGUMENT_USERNAME "Username" + +/** + * Management error + */ +#define MANAGEMENT_ERROR_BAD_PAYLOAD 1 +#define MANAGEMENT_ERROR_UNKNOWN_COMMAND 2 +#define MANAGEMENT_ERROR_ALLOCATION 3 + +#define MANAGEMENT_ERROR_METRICS_NOFORK 100 +#define MANAGEMENT_ERROR_METRICS_NETWORK 101 + +#define MANAGEMENT_ERROR_FLUSH_NOFORK 200 +#define MANAGEMENT_ERROR_FLUSH_NETWORK 201 + +#define MANAGEMENT_ERROR_STATUS_NOFORK 700 +#define MANAGEMENT_ERROR_STATUS_NETWORK 701 + +#define MANAGEMENT_ERROR_STATUS_DETAILS_NOFORK 800 +#define MANAGEMENT_ERROR_STATUS_DETAILS_NETWORK 801 + +/** + * Output formats + */ +#define MANAGEMENT_OUTPUT_FORMAT_TEXT 0 +#define MANAGEMENT_OUTPUT_FORMAT_JSON 1 +#define MANAGEMENT_OUTPUT_FORMAT_RAW 2 /** * Management operation: Flush the pool @@ -167,338 +155,289 @@ pgagroal_management_kill_connection(int32_t slot, int socket); * @param socket The socket descriptor * @param mode The flush mode * @param database The database + * @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_flush(SSL* ssl, int socket, int32_t mode, char* database); +pgagroal_management_request_flush(SSL* ssl, int socket, int32_t mode, char* database, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Enable database * @param ssl The SSL connection * @param socket The socket descriptor * @param database The database name + * @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_enabledb(SSL* ssl, int socket, char* database); +pgagroal_management_request_enabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Disable database * @param ssl The SSL connection * @param socket The socket descriptor * @param database The database name + * @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_disabledb(SSL* ssl, int socket, char* database); +pgagroal_management_request_disabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Gracefully * @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_gracefully(SSL* ssl, int socket); +pgagroal_management_request_gracefully(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Stop * @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_stop(SSL* ssl, int socket); +pgagroal_management_request_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Cancel shutdown * @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_cancel_shutdown(SSL* ssl, int socket); +pgagroal_management_request_cancel_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Status * @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_status(SSL* ssl, int socket); - -/** - * Management: Read status - * @param socket The socket - * @param output_format a char describing the type of output (text or json) - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_read_status(SSL* ssl, int socket, char output_format); - -/** - * Management: Write status - * @param socket The socket - * @param graceful Is pgagroal in graceful shutdown - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_write_status(int socket, bool graceful); +pgagroal_management_request_status(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Details * @param ssl The SSL connection * @param socket The socket + * @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_details(SSL* ssl, int socket); - -/** - * Management: Read details - * @param socket The socket - * @param output_format the output format for this command (text, json) - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_read_details(SSL* ssl, int socket, char output_format); - -/** - * Management: Write details - * @param socket The socket - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_write_details(int socket); +pgagroal_management_request_details(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: isalive * @param socket The socket + * @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_isalive(SSL* ssl, int socket); +pgagroal_management_request_ping(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** - * Management: Read isalive - * @param socket The socket - * @param status The resulting status - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_read_isalive(SSL* ssl, int socket, int* status, char output_format); - -/** - * Management: Write isalive - * @param socket The socket - * @param gracefully Is the server shutting down gracefully - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_write_isalive(int socket, bool gracefully); - -/** - * Management operation: Reset + * Management operation: Clear * @param ssl The SSL connection * @param socket The socket + * @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_reset(SSL* ssl, int socket); +pgagroal_management_request_clear(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); /** - * Management operation: Reset server + * Management operation: Clear server * @param ssl The SSL connection * @param socket The socket * @param server The server + * @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_reset_server(SSL* ssl, int socket, char* server); - -/** - * Management operation: Client done - * @param pid The pid - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_client_done(pid_t pid); - -/** - * Management operation: Client file descriptor - * @param slot The slot - * @param pid The pid - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_management_client_fd(int32_t slot, pid_t pid); +pgagroal_management_request_clear_server(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Switch to * @param ssl The SSL connection * @param socket The socket * @param server The server + * @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_switch_to(SSL* ssl, int socket, char* server); +pgagroal_management_request_switch_to(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format); /** * Management operation: Reload * @param ssl The SSL connection * @param socket The socket + * @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_reload(SSL* ssl, int socket); +pgagroal_management_request_reload(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format); + +/* /\** */ +/* * 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 */ +/* * @param compression The compress method for wire protocol */ +/* * @param encryption The encrypt method for wire protocol */ +/* * @param output_format The output format */ +/* * @return 0 on success, 1 on error */ +/* *\/ */ +/* int */ +/* pgagroal_management_request_config_get(SSL* ssl, int socket, char* config_key, uint8_t compression, uint8_t encryption, int32_t output_format); */ + +/* /\** */ +/* * Management operation: set a configuration setting. */ +/* * This function sends over the socket the message to set a configuration */ +/* * value. */ +/* * In particular, the message block for the action config_set is sent, */ +/* * then the size of the configuration parameter to set (e.g., `max_connections`), */ +/* * then the parameter name. At this point another couple of "size" and "value" is */ +/* * sent with the size of the value to set and its value. */ +/* * */ +/* * @param ssl the SSL connection */ +/* * @param socket the socket file descriptor */ +/* * @param config_key the name of the configuration parameter to set */ +/* * @param config_value the value to set for the new parameter */ +/* * @param compression The compress method for wire protocol */ +/* * @param encryption The encrypt method for wire protocol */ +/* * @param output_format The output format */ +/* * @return 0 on success, 1 on error */ +/* *\/ */ +/* 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); */ /** - * Management operation: Remove socket descriptor - * @param slot The slot - * @param socket The socket - * @param pid The pid + * Get the frontend password of a user + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param user The frontend user + * @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_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 - * @param config_key the key to read (is used only to print in the output) - * @param verbose verbosity flag - * @param output_format the output format - * @param expected_value if set, a value that the configuration should match - * @return 0 on success - */ -int -pgagroal_management_read_config_get(int socket, char* config_key, char* expected_value, bool verbose, char output_format); +pgagroal_management_request_get_password(SSL* ssl, int socket, char* username, uint8_t compression, uint8_t encryption, int32_t output_format); /** - * 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); - -/** - * Management operation: set a configuration setting. - * This function sends over the socket the message to set a configuration - * value. - * In particular, the message block for the action config_set is sent, - * then the size of the configuration parameter to set (e.g., `max_connections`), - * then the parameter name. At this point another couple of "size" and "value" is - * sent with the size of the value to set and its value. - * - * @param ssl the SSL connection - * @param socket the socket file descriptor - * @param config_key the name of the configuration parameter to set - * @param config_value the value to set for the new parameter - * @return 0 on success, 1 on error + * Create an ok response + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param start_time The start time + * @param end_time The end time + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param payload The full payload + * @return 0 upon success, otherwise 1 */ int -pgagroal_management_config_set(SSL* ssl, int socket, char* config_key, char* config_value); +pgagroal_management_response_ok(SSL* ssl, int socket, time_t start_time, time_t end_time, uint8_t compression, uint8_t encryption, struct json* payload); /** - * Function to execute the config-set and write over the socket - * the result. - * - * If the parameter is set, the function calls the - * pgagroal_management_write_config_get to send back over the - * socket the current value of the parameter. Therefore, - * this function answers always back the current value - * so that it is possible to reason about the new value and - * see if it has changed. - * - * @param socket the socket to use for communication - * @param config_key the key to set - * @param config_value the value to use - * @return 0 on success + * Create an error response + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param server The server + * @param error The error code + * @param compression The compress method for wire protocol + * @param encryption The encrypt method for wire protocol + * @param payload The full payload + * @return 0 upon success, otherwise 1 */ int -pgagroal_management_write_config_set(int socket, char* config_key, char* config_value); +pgagroal_management_response_error(SSL* ssl, int socket, char* server, int32_t error, uint8_t compression, uint8_t encryption, struct json* payload); /** - * 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 fd the socket file descriptor - * @returns 0 on success + * Create a response + * @param json The JSON structure + * @param server The server + * @param response The response + * @return 0 upon success, otherwise 1 */ int -pgagroal_management_conf_ls(SSL* ssl, int fd); +pgagroal_management_create_response(struct json* json, int server, struct json** response); /** - * Reads out of the socket the list of configuration - * files and prints them out to the standard output. - * - * The order of the read paths is: - * - configuration path - * - HBA path - * - limit path - * - frontend users path - * - admins path - * - Superusers path - * - users path - * - * @param socket the file descriptor of the open socket - * @param ssl the SSL handler - * @param output_format the format to output the command result - * @returns 0 on success + * Read the management JSON + * @param ssl The SSL connection + * @param socket The socket descriptor + * @param compression The pointer to an integer that will store the compress method + * @param json The JSON structure + * @return 0 upon success, otherwise 1 */ int -pgagroal_management_read_conf_ls(SSL* ssl, int socket, char output_format); +pgagroal_management_read_json(SSL* ssl, int socket, uint8_t* compression, uint8_t* encryption, struct json** json); /** - * The management function responsible for sending - * the configuration paths into the socket. - * - * The function sends every path following the path length, - * that must be limited to MAX_PATH size. - * - * The order of the sent paths is: - * - configuration path - * - HBA path - * - limit path - * - frontend users path - * - admins path - * - Superusers path - * - users path - * - * @params socket the file descriptor of the open socket - * @returns 0 on success + * Write the management JSON + * @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 json The JSON structure + * @return 0 upon success, otherwise 1 */ int -pgagroal_management_write_conf_ls(int socket); +pgagroal_management_write_json(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, struct json* json); #ifdef __cplusplus } diff --git a/src/include/memory.h b/src/include/memory.h index ef501d6e..f711ecab 100644 --- a/src/include/memory.h +++ b/src/include/memory.h @@ -34,6 +34,7 @@ extern "C" { #endif #include +#include #include diff --git a/src/include/message.h b/src/include/message.h index 6ba1ccab..c3b1bef0 100644 --- a/src/include/message.h +++ b/src/include/message.h @@ -51,7 +51,6 @@ struct message { signed char kind; /**< The kind of the message */ ssize_t length; /**< The length of the message */ - size_t max_length; /**< The maximum size of the message */ void* data; /**< The message data */ } __attribute__ ((aligned (64))); diff --git a/src/include/pgagroal.h b/src/include/pgagroal.h index e1d4fe95..da99a15b 100644 --- a/src/include/pgagroal.h +++ b/src/include/pgagroal.h @@ -47,7 +47,8 @@ extern "C" { #define PGAGROAL_HOMEPAGE "https://agroal.github.io/pgagroal/" #define PGAGROAL_ISSUES "https://github.com/agroal/pgagroal/issues" -#define MAIN_UDS ".s.pgagroal" +#define MAIN_UDS ".s.pgagroal" +#define TRANSFER_UDS ".s.pgagroal.tu" #ifdef HAVE_FREEBSD #define PGAGROAL_DEFAULT_CONFIGURATION_PATH "/usr/local/etc/pgagroal/" @@ -141,6 +142,23 @@ extern "C" { #define HUGEPAGE_TRY 1 #define HUGEPAGE_ON 2 +#define ENCRYPTION_NONE 0 +#define ENCRYPTION_AES_256_CBC 1 +#define ENCRYPTION_AES_192_CBC 2 +#define ENCRYPTION_AES_128_CBC 3 +#define ENCRYPTION_AES_256_CTR 4 +#define ENCRYPTION_AES_192_CTR 5 +#define ENCRYPTION_AES_128_CTR 6 + +#define COMPRESSION_NONE 0 +#define COMPRESSION_CLIENT_GZIP 1 +#define COMPRESSION_CLIENT_ZSTD 2 +#define COMPRESSION_CLIENT_LZ4 3 +#define COMPRESSION_CLIENT_BZIP2 4 +#define COMPRESSION_SERVER_GZIP 5 +#define COMPRESSION_SERVER_ZSTD 6 +#define COMPRESSION_SERVER_LZ4 7 + #define UPDATE_PROCESS_TITLE_NEVER 0 #define UPDATE_PROCESS_TITLE_STRICT 1 #define UPDATE_PROCESS_TITLE_MINIMAL 2 @@ -183,6 +201,8 @@ extern "C" { #define likely(x) __builtin_expect (!!(x), 1) #define unlikely(x) __builtin_expect (!!(x), 0) +#define EMPTY_STR(_s) (_s[0] == 0) + #define MAX(a, b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ @@ -496,7 +516,7 @@ struct vault_configuration */ struct main_configuration { - struct configuration common; /**< Common configurations */ + struct configuration common; /**< Common configurations */ char hba_path[MAX_PATH]; /**< The HBA path */ char limit_path[MAX_PATH]; /**< The limit path */ char users_path[MAX_PATH]; /**< The users path */ diff --git a/src/include/pool.h b/src/include/pool.h index 2ebd5233..159ebd1a 100644 --- a/src/include/pool.h +++ b/src/include/pool.h @@ -34,6 +34,7 @@ extern "C" { #endif #include +#include #include #include @@ -90,8 +91,8 @@ void pgagroal_validation(void); /** - * Flush the pool - * @param mode The flush mode + * Flush the pool (JSON) + * @param mode The mode * @param database The database */ void @@ -104,6 +105,17 @@ pgagroal_flush(int mode, char* database); void pgagroal_flush_server(signed char server); +/** + * Flush the pool (JSON) + * @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_request_flush(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload); + /** * Prefill the pool * @param initial Use initial size diff --git a/src/include/prometheus.h b/src/include/prometheus.h index 6dbff92f..54bfb257 100644 --- a/src/include/prometheus.h +++ b/src/include/prometheus.h @@ -304,7 +304,7 @@ pgagroal_prometheus_self_sockets_sub(void); * Reset the counters and histograms */ void -pgagroal_prometheus_reset(void); +pgagroal_prometheus_clear(void); /** * Increase SERVER_ERROR for a server diff --git a/src/include/security.h b/src/include/security.h index 1b897a9f..a1ffa130 100644 --- a/src/include/security.h +++ b/src/include/security.h @@ -92,28 +92,6 @@ pgagroal_remote_management_scram_sha256(char* username, char* password, int serv int pgagroal_get_master_key(char** masterkey); -/** - * Encrypt a string - * @param plaintext The string - * @param password The master password - * @param ciphertext The ciphertext output - * @param ciphertext_length The length of the ciphertext - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_encrypt(char* plaintext, char* password, char** ciphertext, int* ciphertext_length); - -/** - * Decrypt a string - * @param ciphertext The string - * @param ciphertext_length The length of the ciphertext - * @param password The master password - * @param plaintext The plaintext output - * @return 0 upon success, otherwise 1 - */ -int -pgagroal_decrypt(char* ciphertext, int ciphertext_length, char* password, char** plaintext); - /** * MD5 a string * @param str The string diff --git a/src/include/server.h b/src/include/server.h index bff9d32b..c559b04b 100644 --- a/src/include/server.h +++ b/src/include/server.h @@ -80,12 +80,12 @@ int pgagroal_server_force_failover(int server); /** - * Reset server + * Clear server * @param server The server * @return 0 upon success, otherwise 1 */ int -pgagroal_server_reset(char* server); +pgagroal_server_clear(char* server); /** * Switch server diff --git a/src/include/status.h b/src/include/status.h new file mode 100644 index 00000000..ff030d3c --- /dev/null +++ b/src/include/status.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_STATUS_H +#define PGAGROAL_STATUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** + * Create an status + * @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_status(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload); + +/** + * Create an status details + * @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_status_details(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/utils.h b/src/include/utils.h index 7d0d207d..431d812b 100644 --- a/src/include/utils.h +++ b/src/include/utils.h @@ -164,6 +164,14 @@ pgagroal_extract_error_message(struct message* msg, char** error); signed char pgagroal_read_byte(void* data); +/** + * Read an uint8 + * @param data Pointer to the data + * @return The uint8 + */ +uint8_t +pgagroal_read_uint8(void* data); + /** * Read an int16 * @param data Pointer to the data @@ -180,6 +188,14 @@ pgagroal_read_int16(void* data); int32_t pgagroal_read_int32(void* data); +/** + * Read an uint32 + * @param data Pointer to the data + * @return The uint32 + */ +uint32_t +pgagroal_read_uint32(void* data); + /** * Read a long * @param data Pointer to the data @@ -204,6 +220,14 @@ pgagroal_read_string(void* data); void pgagroal_write_byte(void* data, signed char b); +/** + * Write a uint8 + * @param data Pointer to the data + * @param b The uint8 + */ +void +pgagroal_write_uint8(void* data, uint8_t b); + /** * Write an int32 * @param data Pointer to the data @@ -212,6 +236,14 @@ pgagroal_write_byte(void* data, signed char b); void pgagroal_write_int32(void* data, int32_t i); +/** + * Write an uint32 + * @param data Pointer to the data + * @param i The uint32 + */ +void +pgagroal_write_uint32(void* data, uint32_t i); + /** * Write a long * @param data Pointer to the data @@ -303,7 +335,7 @@ pgagroal_exists(char* f); * @return 0 if success, otherwise 1 */ int -pgagroal_base64_encode(char* raw, size_t raw_length, char** encoded, size_t* encoded_length); +pgagroal_base64_encode(void* raw, size_t raw_length, char** encoded, size_t* encoded_length); /** * BASE64 decode a string @@ -314,7 +346,7 @@ pgagroal_base64_encode(char* raw, size_t raw_length, char** encoded, size_t* enc * @return 0 if success, otherwise 1 */ int -pgagroal_base64_decode(char* encoded, size_t encoded_length, char** raw, size_t* raw_length); +pgagroal_base64_decode(char* encoded, size_t encoded_length, void** raw, size_t* raw_length); /** * Set process title. @@ -359,6 +391,16 @@ pgagroal_set_proc_title(int argc, char** argv, char* s1, char* s2); void pgagroal_set_connection_proc_title(int argc, char** argv, struct connection* connection); +/** + * Get the timestramp difference as a string + * @param start_time The start time + * @param end_time The end time + * @param seconds The number of seconds + * @return The timestamp string + */ +char* +pgagroal_get_timestamp_string(time_t start_time, time_t end_time, int32_t* seconds); + /** * Provide the application version number as a unique value composed of the three * specified parts. For example, when invoked with (1,5,0) it returns 10500. @@ -396,6 +438,15 @@ pgagroal_version_number(void); bool pgagroal_version_ge(unsigned int major, unsigned int minor, unsigned int patch); +/** + * Does a string end with another string + * @param str The string + * @param suffix The suffix + * @return The result + */ +bool +pgagroal_ends_with(char* str, char* suffix); + /** * Append a string * diff --git a/src/include/zstandard_compression.h b/src/include/zstandard_compression.h new file mode 100644 index 00000000..49c37327 --- /dev/null +++ b/src/include/zstandard_compression.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PGAGROAL_ZSTANDARD_H +#define PGAGROAL_ZSTANDARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * ZSTD compress a string + * @param s The original string + * @param buffer The point to the compressed data buffer + * @param buffer_size The size of the compressed buffer will be stored. + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_zstdc_string(char* s, unsigned char** buffer, size_t* buffer_size); + +/** + * ZSTD decompress a buffer to string + * @param compressed_buffer The buffer containing the GZIP compressed data + * @param compressed_size The size of the compressed buffer + * @param output_string The pointer to a string where the decompressed data will be stored + * @return 0 upon success, otherwise 1 + */ +int +pgagroal_zstdd_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libpgagroal/aes.c b/src/libpgagroal/aes.c new file mode 100644 index 00000000..f48d3387 --- /dev/null +++ b/src/libpgagroal/aes.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +static int derive_key_iv(char* password, unsigned char* key, unsigned char* iv, int mode); +static int aes_encrypt(char* plaintext, unsigned char* key, unsigned char* iv, char** ciphertext, int* ciphertext_length, int mode); +static int aes_decrypt(char* ciphertext, int ciphertext_length, unsigned char* key, unsigned char* iv, char** plaintext, int mode); +static const EVP_CIPHER* (*get_cipher(int mode))(void); + +static int encrypt_decrypt_buffer(unsigned char* origin_buffer, size_t origin_size, unsigned char** res_buffer, size_t* res_size, int enc, int mode); + + +int +pgagroal_encrypt(char* plaintext, char* password, char** ciphertext, int* ciphertext_length, int mode) +{ + unsigned char key[EVP_MAX_KEY_LENGTH]; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + memset(&key, 0, sizeof(key)); + memset(&iv, 0, sizeof(iv)); + + if (derive_key_iv(password, key, iv, mode) != 0) + { + return 1; + } + + return aes_encrypt(plaintext, key, iv, ciphertext, ciphertext_length, mode); +} + +int +pgagroal_decrypt(char* ciphertext, int ciphertext_length, char* password, char** plaintext, int mode) +{ + unsigned char key[EVP_MAX_KEY_LENGTH]; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + memset(&key, 0, sizeof(key)); + memset(&iv, 0, sizeof(iv)); + + if (derive_key_iv(password, key, iv, mode) != 0) + { + return 1; + } + + return aes_decrypt(ciphertext, ciphertext_length, key, iv, plaintext, mode); +} + +// [private] +static int +derive_key_iv(char* password, unsigned char* key, unsigned char* iv, int mode) +{ + if (!EVP_BytesToKey(get_cipher(mode)(), EVP_sha1(), NULL, + (unsigned char*) password, strlen(password), 1, + key, iv)) + { + return 1; + } + + return 0; +} + +// [private] +static int +aes_encrypt(char* plaintext, unsigned char* key, unsigned char* iv, char** ciphertext, int* ciphertext_length, int mode) +{ + EVP_CIPHER_CTX* ctx = NULL; + int length; + size_t size; + unsigned char* ct = NULL; + int ct_length; + const EVP_CIPHER* (* cipher_fp)(void) = get_cipher(mode); + if (!(ctx = EVP_CIPHER_CTX_new())) + { + goto error; + } + + if (EVP_EncryptInit_ex(ctx, cipher_fp(), NULL, key, iv) != 1) + { + goto error; + } + + size = strlen(plaintext) + EVP_CIPHER_block_size(cipher_fp()); + ct = malloc(size); + + if (ct == NULL) + { + goto error; + } + + memset(ct, 0, size); + + if (EVP_EncryptUpdate(ctx, + ct, &length, + (unsigned char*)plaintext, strlen((char*)plaintext)) != 1) + { + goto error; + } + + ct_length = length; + + if (EVP_EncryptFinal_ex(ctx, ct + length, &length) != 1) + { + goto error; + } + + ct_length += length; + + EVP_CIPHER_CTX_free(ctx); + + *ciphertext = (char*)ct; + *ciphertext_length = ct_length; + + return 0; + +error: + if (ctx) + { + EVP_CIPHER_CTX_free(ctx); + } + + free(ct); + + return 1; +} + +// [private] +static int +aes_decrypt(char* ciphertext, int ciphertext_length, unsigned char* key, unsigned char* iv, char** plaintext, int mode) +{ + EVP_CIPHER_CTX* ctx = NULL; + int plaintext_length; + int length; + size_t size; + char* pt = NULL; + const EVP_CIPHER* (* cipher_fp)(void) = get_cipher(mode); + + if (!(ctx = EVP_CIPHER_CTX_new())) + { + goto error; + } + + if (EVP_DecryptInit_ex(ctx, cipher_fp(), NULL, key, iv) != 1) + { + goto error; + } + + size = ciphertext_length + EVP_CIPHER_block_size(cipher_fp()); + pt = malloc(size); + + if (pt == NULL) + { + goto error; + } + + memset(pt, 0, size); + + if (EVP_DecryptUpdate(ctx, + (unsigned char*)pt, &length, + (unsigned char*)ciphertext, ciphertext_length) != 1) + { + goto error; + } + + plaintext_length = length; + + if (EVP_DecryptFinal_ex(ctx, (unsigned char*)pt + length, &length) != 1) + { + goto error; + } + + plaintext_length += length; + + EVP_CIPHER_CTX_free(ctx); + + pt[plaintext_length] = 0; + *plaintext = pt; + + return 0; + +error: + if (ctx) + { + EVP_CIPHER_CTX_free(ctx); + } + + free(pt); + + return 1; +} + +static const EVP_CIPHER* (*get_cipher(int mode))(void) +{ + if (mode == ENCRYPTION_AES_256_CBC) + { + return &EVP_aes_256_cbc; + } + if (mode == ENCRYPTION_AES_192_CBC) + { + return &EVP_aes_192_cbc; + } + if (mode == ENCRYPTION_AES_128_CBC) + { + return &EVP_aes_128_cbc; + } + if (mode == ENCRYPTION_AES_256_CTR) + { + return &EVP_aes_256_ctr; + } + if (mode == ENCRYPTION_AES_192_CTR) + { + return &EVP_aes_192_ctr; + } + if (mode == ENCRYPTION_AES_128_CTR) + { + return &EVP_aes_128_ctr; + } + return &EVP_aes_256_cbc; +} + +int +pgagroal_encrypt_buffer(unsigned char* origin_buffer, size_t origin_size, unsigned char** enc_buffer, size_t* enc_size, int mode) +{ + return encrypt_decrypt_buffer(origin_buffer, origin_size, enc_buffer, enc_size, 1, mode); +} + +int +pgagroal_decrypt_buffer(unsigned char* origin_buffer, size_t origin_size, unsigned char** dec_buffer, size_t* dec_size, int mode) +{ + return encrypt_decrypt_buffer(origin_buffer, origin_size, dec_buffer, dec_size, 0, mode); +} + +static int +encrypt_decrypt_buffer(unsigned char* origin_buffer, size_t origin_size, unsigned char** res_buffer, size_t* res_size, int enc, int mode) +{ + unsigned char key[EVP_MAX_KEY_LENGTH]; + unsigned char iv[EVP_MAX_IV_LENGTH]; + char* master_key = NULL; + EVP_CIPHER_CTX* ctx = NULL; + const EVP_CIPHER* (*cipher_fp)(void) = NULL; + size_t cipher_block_size = 0; + size_t outbuf_size = 0; + size_t outl = 0; + size_t f_len = 0; + + cipher_fp = get_cipher(mode); + if (cipher_fp == NULL) + { + pgagroal_log_error("Invalid encryption method specified"); + goto error; + } + + cipher_block_size = EVP_CIPHER_block_size(cipher_fp()); + + if (enc == 1) + { + outbuf_size = origin_size + cipher_block_size; + } + else + { + outbuf_size = origin_size; + } + + *res_buffer = (unsigned char*)malloc(outbuf_size + 1); + if (*res_buffer == NULL) + { + pgagroal_log_error("pgagroal_encrypt_decrypt_buffer: Allocation failure"); + goto error; + } + + if (pgagroal_get_master_key(&master_key)) + { + pgagroal_log_error("pgagroal_get_master_key: Invalid master key"); + goto error; + } + + memset(&key, 0, sizeof(key)); + memset(&iv, 0, sizeof(iv)); + + if (derive_key_iv(master_key, key, iv, mode) != 0) + { + pgagroal_log_error("derive_key_iv: Failed to derive key and iv"); + goto error; + } + + if (!(ctx = EVP_CIPHER_CTX_new())) + { + pgagroal_log_error("EVP_CIPHER_CTX_new: Failed to create context"); + goto error; + } + + if (EVP_CipherInit_ex(ctx, cipher_fp(), NULL, key, iv, enc) == 0) + { + pgagroal_log_error("EVP_CipherInit_ex: Failed to initialize cipher context"); + goto error; + } + + if (EVP_CipherUpdate(ctx, *res_buffer, (int*)&outl, origin_buffer, origin_size) == 0) + { + pgagroal_log_error("EVP_CipherUpdate: Failed to process data"); + goto error; + } + + *res_size = outl; + + if (EVP_CipherFinal_ex(ctx, *res_buffer + outl, (int*)&f_len) == 0) + { + pgagroal_log_error("EVP_CipherFinal_ex: Failed to finalize operation"); + goto error; + } + + *res_size += f_len; + + if (enc == 0) + { + (*res_buffer)[*res_size] = '\0'; + } + + EVP_CIPHER_CTX_free(ctx); + free(master_key); + + return 0; + +error: + if (ctx) + { + EVP_CIPHER_CTX_free(ctx); + } + + if (master_key) + { + free(master_key); + } + + return 1; +} diff --git a/src/libpgagroal/bzip2_compression.c b/src/libpgagroal/bzip2_compression.c new file mode 100644 index 00000000..0eb9fe3f --- /dev/null +++ b/src/libpgagroal/bzip2_compression.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* pgagroal */ +#include +#include +#include +#include + +/* system */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_LENGTH 8192 + +int +pgagroal_bzip2_string(char* s, unsigned char** buffer, size_t* buffer_size) +{ + size_t source_len; + unsigned int dest_len; + int bzip2_err; + + source_len = strlen(s); + dest_len = source_len + (source_len * 0.01) + 600; + + *buffer = (unsigned char*)malloc(dest_len); + if (!*buffer) + { + pgagroal_log_error("Bzip2: Allocation failed"); + return 1; + } + + bzip2_err = BZ2_bzBuffToBuffCompress((char*)(*buffer), &dest_len, s, source_len, 9, 0, 0); + if (bzip2_err != BZ_OK) + { + pgagroal_log_error("Bzip2: Compress failed"); + free(*buffer); + return 1; + } + + *buffer_size = dest_len; + return 0; +} + +int +pgagroal_bunzip2_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string) +{ + int bzip2_err; + unsigned int estimated_size = compressed_size * 10; + unsigned int new_size; + + *output_string = (char*)malloc(estimated_size); + if (!*output_string) + { + pgagroal_log_error("Bzip2: Allocation failed"); + return 1; + } + + bzip2_err = BZ2_bzBuffToBuffDecompress(*output_string, &estimated_size, (char*)compressed_buffer, compressed_size, 0, 0); + + if (bzip2_err == BZ_OUTBUFF_FULL) + { + new_size = estimated_size * 2; + char* temp = realloc(*output_string, new_size); + + if (!temp) + { + pgagroal_log_error("Bzip2: Reallocation failed"); + free(*output_string); + return 1; + } + + *output_string = temp; + + bzip2_err = BZ2_bzBuffToBuffDecompress(*output_string, &new_size, (char*)compressed_buffer, compressed_size, 0, 0); + if (bzip2_err != BZ_OK) + { + pgagroal_log_error("Bzip2: Decompress failed"); + free(*output_string); + return 1; + } + estimated_size = new_size; + } + else if (bzip2_err != BZ_OK) + { + pgagroal_log_error("Bzip2: Decompress failed"); + free(*output_string); + return 1; + } + + return 0; +} diff --git a/src/libpgagroal/configuration.c b/src/libpgagroal/configuration.c index cbeed42e..6095a9ac 100644 --- a/src/libpgagroal/configuration.c +++ b/src/libpgagroal/configuration.c @@ -28,6 +28,7 @@ /* pgagroal */ #include +#include #include #include #include @@ -74,7 +75,7 @@ static void extract_limit(char* str, int server_max, char** database, char** use static unsigned int as_seconds(char* str, unsigned int* age, unsigned int default_age); static unsigned int as_bytes(char* str, unsigned int* bytes, unsigned int default_bytes); -static int transfer_configuration(struct main_configuration* config, struct main_configuration* reload); +static bool transfer_configuration(struct main_configuration* config, struct main_configuration* reload); static void copy_server(struct server* dst, struct server* src); static void copy_hba(struct hba* dst, struct hba* src); static void copy_user(struct user* dst, struct user* src); @@ -1312,13 +1313,13 @@ pgagroal_read_users_configuration(void* shm, char* filename) goto error; } - if (pgagroal_base64_decode(ptr, strlen(ptr), &decoded, &decoded_length)) + if (pgagroal_base64_decode(ptr, strlen(ptr), (void**)&decoded, &decoded_length)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; } - if (pgagroal_decrypt(decoded, decoded_length, master_key, &password)) + if (pgagroal_decrypt(decoded, decoded_length, master_key, &password, ENCRYPTION_AES_256_CBC)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; @@ -1434,13 +1435,13 @@ pgagroal_read_frontend_users_configuration(void* shm, char* filename) goto error; } - if (pgagroal_base64_decode(ptr, strlen(ptr), &decoded, &decoded_length)) + if (pgagroal_base64_decode(ptr, strlen(ptr), (void**)&decoded, &decoded_length)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; } - if (pgagroal_decrypt(decoded, decoded_length, master_key, &password)) + if (pgagroal_decrypt(decoded, decoded_length, master_key, &password, ENCRYPTION_AES_256_CBC)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; @@ -1581,13 +1582,13 @@ pgagroal_read_admins_configuration(void* shm, char* filename) goto error; } - if (pgagroal_base64_decode(ptr, strlen(ptr), &decoded, &decoded_length)) + if (pgagroal_base64_decode(ptr, strlen(ptr), (void**)&decoded, &decoded_length)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; } - if (pgagroal_decrypt(decoded, decoded_length, master_key, &password)) + if (pgagroal_decrypt(decoded, decoded_length, master_key, &password, ENCRYPTION_AES_256_CBC)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; @@ -1694,13 +1695,13 @@ pgagroal_vault_read_users_configuration(void* shm, char* filename) goto error; } - if (pgagroal_base64_decode(ptr, strlen(ptr), &decoded, &decoded_length)) + if (pgagroal_base64_decode(ptr, strlen(ptr), (void**)&decoded, &decoded_length)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; } - if (pgagroal_decrypt(decoded, decoded_length, master_key, &password)) + if (pgagroal_decrypt(decoded, decoded_length, master_key, &password, ENCRYPTION_AES_256_CBC)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; @@ -1828,14 +1829,14 @@ pgagroal_read_superuser_configuration(void* shm, char* filename) goto error; } - if (pgagroal_base64_decode(ptr, strlen(ptr), &decoded, &decoded_length)) + if (pgagroal_base64_decode(ptr, strlen(ptr), (void**)&decoded, &decoded_length)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; } - if (pgagroal_decrypt(decoded, decoded_length, master_key, &password)) + if (pgagroal_decrypt(decoded, decoded_length, master_key, &password, ENCRYPTION_AES_256_CBC)) { status = PGAGROAL_CONFIGURATION_STATUS_CANNOT_DECRYPT; goto error; @@ -1894,7 +1895,7 @@ pgagroal_validate_superuser_configuration(void* shm) } int -pgagroal_reload_configuration(void) +pgagroal_reload_configuration(bool* r) { size_t reload_size; struct main_configuration* reload = NULL; @@ -1902,6 +1903,8 @@ pgagroal_reload_configuration(void) config = (struct main_configuration*)shmem; + *r = false; + pgagroal_log_trace("Configuration: %s", config->common.configuration_path); pgagroal_log_trace("HBA: %s", config->hba_path); pgagroal_log_trace("Limit: %s", config->limit_path); @@ -2004,10 +2007,7 @@ pgagroal_reload_configuration(void) goto error; } - if (transfer_configuration(config, reload) > 0) - { - goto error; - } + *r = transfer_configuration(config, reload); pgagroal_destroy_shared_memory((void*)reload, reload_size); @@ -2567,24 +2567,25 @@ extract_value(char* str, int offset, char** value) * * @param config the new (clean) configuration * @param reload the one loaded from the configuration (i.e., the one to apply) - * @return 0 on success, a negative number in the case some parameters cannot be changed - * because require a restart (in such case, the value indicates the number of untouched - * parameters), a positive value in the case of a dramatic error. + * @return True, if restart or false if not */ -static int +static bool transfer_configuration(struct main_configuration* config, struct main_configuration* reload) { + bool changed = false; + #ifdef HAVE_LINUX sd_notify(0, "RELOADING=1"); #endif - int unchanged = 0; - memcpy(config->common.host, reload->common.host, MISC_LENGTH); config->common.port = reload->common.port; config->common.metrics = reload->common.metrics; config->common.metrics_cache_max_age = reload->common.metrics_cache_max_age; - unchanged -= restart_int("metrics_cache_max_size", config->common.metrics_cache_max_size, reload->common.metrics_cache_max_size); + if (restart_int("metrics_cache_max_size", config->common.metrics_cache_max_size, reload->common.metrics_cache_max_size)) + { + changed= true; + } config->management = reload->management; config->update_process_title = reload->update_process_title; @@ -2594,13 +2595,19 @@ transfer_configuration(struct main_configuration* config, struct main_configurat /* disabled */ /* pipeline */ - unchanged -= restart_int("pipeline", config->pipeline, reload->pipeline); + if (restart_int("pipeline", config->pipeline, reload->pipeline)) + { + changed = true; + } config->failover = reload->failover; memcpy(config->failover_script, reload->failover_script, MISC_LENGTH); /* log_type */ - restart_int("log_type", config->common.log_type, reload->common.log_type); + if (restart_int("log_type", config->common.log_type, reload->common.log_type)) + { + changed = true; + } config->common.log_level = reload->common.log_level; /* log_path */ @@ -2644,7 +2651,10 @@ transfer_configuration(struct main_configuration* config, struct main_configurat /* active_connections */ /* max_connections */ - unchanged -= restart_int("max_connections", config->max_connections, reload->max_connections); + if (restart_int("max_connections", config->max_connections, reload->max_connections)) + { + changed = true; + } config->allow_unknown_users = reload->allow_unknown_users; config->blocking_timeout = reload->blocking_timeout; @@ -2659,16 +2669,25 @@ transfer_configuration(struct main_configuration* config, struct main_configurat config->disconnect_client = reload->disconnect_client; config->disconnect_client_force = reload->disconnect_client_force; /* pidfile */ - restart_string("pidfile", config->pidfile, reload->pidfile, true); + if (restart_string("pidfile", config->pidfile, reload->pidfile, true)) + { + changed = true; + } /* libev */ - restart_string("libev", config->libev, reload->libev, true); + if (restart_string("libev", config->libev, reload->libev, true)) + { + changed = true; + } config->keep_alive = reload->keep_alive; config->nodelay = reload->nodelay; config->non_blocking = reload->non_blocking; config->backlog = reload->backlog; /* hugepage */ - unchanged -= restart_int("hugepage", config->common.hugepage, reload->common.hugepage); + if (restart_int("hugepage", config->common.hugepage, reload->common.hugepage)) + { + changed = true; + } config->tracker = reload->tracker; config->track_prepared_statements = reload->track_prepared_statements; @@ -2676,7 +2695,10 @@ transfer_configuration(struct main_configuration* config, struct main_configurat // does make sense to check for remote connections? Because in the case the Unix socket dir // changes the pgagroal-cli probably will not be able to connect in any case! - restart_string("unix_socket_dir", config->unix_socket_dir, reload->unix_socket_dir, false); + if (restart_string("unix_socket_dir", config->unix_socket_dir, reload->unix_socket_dir, false)) + { + changed = true; + } /* su_connection */ @@ -2685,7 +2707,10 @@ transfer_configuration(struct main_configuration* config, struct main_configurat // decreasing the number of servers is probably a bad idea if (config->number_of_servers > reload->number_of_servers) { - restart_int("decreasing number of servers", config->number_of_servers, reload->number_of_servers); + if (restart_int("decreasing number of servers", config->number_of_servers, reload->number_of_servers)) + { + changed = true; + } } for (int i = 0; i < reload->number_of_servers; i++) @@ -2693,7 +2718,10 @@ transfer_configuration(struct main_configuration* config, struct main_configurat // check and emit restart warning only for not-added servers if (i < config->number_of_servers) { - restart_server(&reload->servers[i], &config->servers[i]); + if (restart_server(&reload->servers[i], &config->servers[i])) + { + changed = true; + } } copy_server(&config->servers[i], &reload->servers[i]); @@ -2713,7 +2741,10 @@ transfer_configuration(struct main_configuration* config, struct main_configurat /* number_of_limits */ /* limits */ - unchanged -= restart_limit("limits", config, reload); + if (restart_limit("limits", config, reload)) + { + changed = true; + } memset(&config->users[0], 0, sizeof(struct user) * NUMBER_OF_USERS); for (int i = 0; i < reload->number_of_users; i++) @@ -2746,12 +2777,12 @@ transfer_configuration(struct main_configuration* config, struct main_configurat sd_notify(0, "READY=1"); #endif - if (unchanged < 0) + if (changed) { - pgagroal_log_warn("%d settings cannot be applied", unchanged * -1); + pgagroal_log_warn("Settings cannot be applied"); } - return unchanged; + return changed; } /** diff --git a/src/libpgagroal/connection.c b/src/libpgagroal/connection.c new file mode 100644 index 00000000..6a17818b --- /dev/null +++ b/src/libpgagroal/connection.c @@ -0,0 +1,921 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* pgagroal */ +#include +#include +#include +#include +#include +#include + +/* system */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int read_complete(SSL* ssl, int socket, void* buf, size_t size); +static int write_complete(SSL* ssl, int socket, void* buf, size_t size); +static int write_socket(int socket, void* buf, size_t size); +static int write_ssl(SSL* ssl, void* buf, size_t size); + + +int +pgagroal_connection_get(int* client_fd) +{ + int fd; + struct main_configuration* config; + + config = (struct main_configuration*)shmem; + + *client_fd = -1; + + if (pgagroal_connect_unix_socket(config->unix_socket_dir, TRANSFER_UDS, &fd)) + { + pgagroal_log_warn("pgagroal_management_transfer_connection: connect: %d", fd); + errno = 0; + goto error; + } + + *client_fd = fd; + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_get_pid(pid_t pid, int* client_fd) +{ + char* f = NULL; + int fd; + struct main_configuration* config; + + config = (struct main_configuration*)shmem; + + *client_fd = -1; + + f = pgagroal_append(f, ".s.pgagroal."); + f = pgagroal_append_int(f, (int)pid); + + if (pgagroal_connect_unix_socket(config->unix_socket_dir, f, &fd)) + { + pgagroal_log_warn("pgagroal_management_transfer_connection: connect: %d", fd); + errno = 0; + goto error; + } + + *client_fd = fd; + + free(f); + + return 0; + +error: + + free(f); + + return 1; +} + + +int +pgagroal_connection_id_write(int client_fd, int id) +{ + char buf4[4]; + + memset(&buf4[0], 0, sizeof(buf4)); + pgagroal_write_int32(&buf4, id); + + if (write_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_id_write: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_id_read(int client_fd, int* id) +{ + char buf4[4]; + + *id = -1; + + memset(&buf4[0], 0, sizeof(buf4)); + if (read_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_id_read: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + *id = pgagroal_read_int32(&buf4); + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_transfer_write(int client_fd, int32_t slot) +{ + struct cmsghdr* cmptr = NULL; + struct iovec iov[1]; + struct msghdr msg; + char buf2[2]; + char buf4[4]; + struct main_configuration* config; + + config = (struct main_configuration*)shmem; + + memset(&buf4[0], 0, sizeof(buf4)); + pgagroal_write_int32(&buf4, slot); + + if (write_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_management_transfer_connection: write: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + /* Write file descriptor */ + memset(&buf2[0], 0, sizeof(buf2)); + + iov[0].iov_base = &buf2[0]; + iov[0].iov_len = sizeof(buf2); + + cmptr = malloc(CMSG_SPACE(sizeof(int))); + memset(cmptr, 0, CMSG_SPACE(sizeof(int))); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = cmptr; + msg.msg_controllen = CMSG_SPACE(sizeof(int)); + msg.msg_flags = 0; + *(int*)CMSG_DATA(cmptr) = config->connections[slot].fd; + + if (sendmsg(client_fd, &msg, 0) != 2) + { + goto error; + } + + free(cmptr); + + return 0; + +error: + if (cmptr) + { + free(cmptr); + } + + return 1; +} + +int +pgagroal_connection_transfer_read(int client_fd, int* slot, int* fd) +{ + int nr; + char buf2[2]; + char buf4[4]; + struct cmsghdr* cmptr = NULL; + struct iovec iov[1]; + struct msghdr msg; + + *slot = -1; + *fd = -1; + + memset(&buf4[0], 0, sizeof(buf4)); + if (read_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_transfer_read: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + *slot = pgagroal_read_int32(&buf4); + + memset(&buf2[0], 0, sizeof(buf2)); + + iov[0].iov_base = &buf2[0]; + iov[0].iov_len = sizeof(buf2); + + cmptr = (struct cmsghdr*)calloc(1, CMSG_SPACE(sizeof(int))); + if (cmptr == NULL) + { + goto error; + } + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = cmptr; + msg.msg_controllen = CMSG_SPACE(sizeof(int)); + msg.msg_flags = 0; + + if ((nr = recvmsg(client_fd, &msg, 0)) < 0) + { + goto error; + } + else if (nr == 0) + { + goto error; + } + + *fd = *(int*)CMSG_DATA(cmptr); + + free(cmptr); + + return 0; + +error: + + if (cmptr != NULL) + { + free(cmptr); + } + + return 1; +} + +int +pgagroal_connection_slot_write(int client_fd, int32_t slot) +{ + char buf4[4]; + + memset(&buf4[0], 0, sizeof(buf4)); + pgagroal_write_int32(&buf4, slot); + + if (write_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_slot_write: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_slot_read(int client_fd, int32_t* slot) +{ + char buf4[4]; + + *slot = -1; + + memset(&buf4[0], 0, sizeof(buf4)); + if (read_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_id_read: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + *slot = (int32_t)pgagroal_read_int32(&buf4); + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_socket_write(int client_fd, int socket) +{ + char buf4[4]; + + memset(&buf4[0], 0, sizeof(buf4)); + pgagroal_write_int32(&buf4, socket); + + if (write_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_socket_write: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_socket_read(int client_fd, int* socket) +{ + char buf4[4]; + + *socket = -1; + + memset(&buf4[0], 0, sizeof(buf4)); + if (read_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_id_read: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + *socket = pgagroal_read_int32(&buf4); + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_pid_write(int client_fd, pid_t pid) +{ + char buf4[4]; + + memset(&buf4[0], 0, sizeof(buf4)); + pgagroal_write_int32(&buf4, (int)pid); + + if (write_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_pid_write: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + return 0; + +error: + + return 1; +} + +int +pgagroal_connection_pid_read(int client_fd, pid_t* pid) +{ + char buf4[4]; + + *pid = -1; + + memset(&buf4[0], 0, sizeof(buf4)); + if (read_complete(NULL, client_fd, &buf4, sizeof(buf4))) + { + pgagroal_log_warn("pgagroal_connection_id_read: %d %s", client_fd, strerror(errno)); + errno = 0; + goto error; + } + + *pid = (pid_t)pgagroal_read_int32(&buf4); + + return 0; + +error: + + return 1; +} + +static int +read_complete(SSL* ssl, int socket, void* buf, size_t size) +{ + ssize_t r; + size_t offset; + size_t needs; + int retries; + + offset = 0; + needs = size; + retries = 0; + +read: + + if (ssl == NULL) + { + r = read(socket, buf + offset, needs); + } + else + { + r = SSL_read(ssl, buf + offset, needs); + } + + if (r == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + errno = 0; + goto read; + } + + goto error; + } + else if (r < (ssize_t)needs) + { + /* Sleep for 10ms */ + SLEEP(10000000L); + + if (retries < 100) + { + offset += r; + needs -= r; + retries++; + goto read; + } + else + { + errno = EINVAL; + goto error; + } + } + + return 0; + +error: + + return 1; +} + +static int +write_complete(SSL* ssl, int socket, void* buf, size_t size) +{ + if (ssl == NULL) + { + return write_socket(socket, buf, size); + } + + return write_ssl(ssl, buf, size); +} + +static int +write_socket(int socket, void* buf, size_t size) +{ + bool keep_write = false; + ssize_t numbytes; + int offset; + ssize_t totalbytes; + ssize_t remaining; + + numbytes = 0; + offset = 0; + totalbytes = 0; + remaining = size; + + do + { + numbytes = write(socket, buf + offset, remaining); + + if (likely(numbytes == size)) + { + return 0; + } + else if (numbytes != -1) + { + offset += numbytes; + totalbytes += numbytes; + remaining -= numbytes; + + if (totalbytes == size) + { + return 0; + } + + pgagroal_log_trace("Write %d - %zd/%zd vs %zd", socket, numbytes, totalbytes, size); + keep_write = true; + errno = 0; + } + else + { + switch (errno) + { + case EAGAIN: + keep_write = true; + errno = 0; + break; + default: + keep_write = false; + break; + } + } + } + while (keep_write); + + return 1; +} + +static int +write_ssl(SSL* ssl, void* buf, size_t size) +{ + bool keep_write = false; + ssize_t numbytes; + int offset; + ssize_t totalbytes; + ssize_t remaining; + + numbytes = 0; + offset = 0; + totalbytes = 0; + remaining = size; + + do + { + numbytes = SSL_write(ssl, buf + offset, remaining); + + if (likely(numbytes == size)) + { + return 0; + } + else if (numbytes > 0) + { + offset += numbytes; + totalbytes += numbytes; + remaining -= numbytes; + + if (totalbytes == size) + { + return 0; + } + + pgagroal_log_trace("SSL/Write %d - %zd/%zd vs %zd", SSL_get_fd(ssl), numbytes, totalbytes, size); + keep_write = true; + errno = 0; + } + else + { + int err = SSL_get_error(ssl, numbytes); + + switch (err) + { + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + case SSL_ERROR_WANT_X509_LOOKUP: +#ifndef HAVE_OPENBSD + case SSL_ERROR_WANT_ASYNC: + case SSL_ERROR_WANT_ASYNC_JOB: + case SSL_ERROR_WANT_CLIENT_HELLO_CB: +#endif + errno = 0; + keep_write = true; + break; + case SSL_ERROR_SYSCALL: + pgagroal_log_error("SSL_ERROR_SYSCALL: %s (%d)", strerror(errno), SSL_get_fd(ssl)); + errno = 0; + keep_write = false; + break; + case SSL_ERROR_SSL: + pgagroal_log_error("SSL_ERROR_SSL: %s (%d)", strerror(errno), SSL_get_fd(ssl)); + errno = 0; + keep_write = false; + break; + } + ERR_clear_error(); + + if (!keep_write) + { + return 1; + } + } + } + while (keep_write); + + return 1; +} + + +/* int */ +/* pgagroal_management_client_done(pid_t pid) */ +/* { */ +/* char buf[4]; */ +/* int fd; */ +/* struct main_configuration* config; */ + +/* config = (struct main_configuration*)shmem; */ + +/* if (pgagroal_connect_unix_socket(config->unix_socket_dir, TRANSFER_UDS, &fd)) */ +/* { */ +/* pgagroal_log_warn("pgagroal_management_client_done: connect: %d", fd); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* if (write_header(NULL, fd, MANAGEMENT_CLIENT_DONE, -1)) */ +/* { */ +/* pgagroal_log_warn("pgagroal_management_client_done: write: %d", fd); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* memset(&buf, 0, sizeof(buf)); */ +/* pgagroal_write_int32(buf, pid); */ + +/* if (write_complete(NULL, fd, &buf, sizeof(buf))) */ +/* { */ +/* pgagroal_log_warn("pgagroal_management_client_done: write: %d %s", fd, strerror(errno)); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* pgagroal_disconnect(fd); */ + +/* return 0; */ + +/* error: */ +/* pgagroal_disconnect(fd); */ + +/* return 1; */ +/* } */ + + + + + +/* int */ +/* pgagroal_management_client_fd(int32_t slot, pid_t pid) */ +/* { */ +/* char p[MISC_LENGTH]; */ +/* int fd; */ +/* struct main_configuration* config; */ +/* struct cmsghdr* cmptr = NULL; */ +/* struct iovec iov[1]; */ +/* struct msghdr msg; */ +/* char buf[2]; /\* send_fd()/recv_fd() 2-byte protocol *\/ */ + +/* config = (struct main_configuration*)shmem; */ + +/* memset(&p, 0, sizeof(p)); */ +/* snprintf(&p[0], sizeof(p), ".s.%d", pid); */ + +/* if (pgagroal_connect_unix_socket(config->unix_socket_dir, &p[0], &fd)) */ +/* { */ +/* pgagroal_log_debug("pgagroal_management_client_fd: connect: %d", fd); */ +/* errno = 0; */ +/* goto unavailable; */ +/* } */ + +/* if (write_header(NULL, fd, MANAGEMENT_CLIENT_FD, slot)) */ +/* { */ +/* pgagroal_log_warn("pgagroal_management_client_fd: write: %d", fd); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* /\* Write file descriptor *\/ */ +/* iov[0].iov_base = buf; */ +/* iov[0].iov_len = 2; */ +/* msg.msg_iov = iov; */ +/* msg.msg_iovlen = 1; */ +/* msg.msg_name = NULL; */ +/* msg.msg_namelen = 0; */ + +/* cmptr = malloc(CMSG_LEN(sizeof(int))); */ +/* cmptr->cmsg_level = SOL_SOCKET; */ +/* cmptr->cmsg_type = SCM_RIGHTS; */ +/* cmptr->cmsg_len = CMSG_LEN(sizeof(int)); */ +/* msg.msg_control = cmptr; */ +/* msg.msg_controllen = CMSG_LEN(sizeof(int)); */ +/* *(int*)CMSG_DATA(cmptr) = config->connections[slot].fd; */ +/* buf[1] = 0; /\* zero status means OK *\/ */ +/* buf[0] = 0; /\* null byte flag to recv_fd() *\/ */ + +/* if (sendmsg(fd, &msg, 0) != 2) */ +/* { */ +/* goto error; */ +/* } */ + +/* free(cmptr); */ +/* pgagroal_disconnect(fd); */ + +/* return 0; */ + +/* unavailable: */ +/* free(cmptr); */ +/* pgagroal_disconnect(fd); */ + +/* return 1; */ + +/* error: */ +/* free(cmptr); */ +/* pgagroal_disconnect(fd); */ +/* pgagroal_kill_connection(slot, NULL); */ + +/* return 1; */ +/* } */ + + +/* int */ +/* pgagroal_management_remove_fd(int32_t slot, int socket, pid_t pid) */ +/* { */ +/* char p[MISC_LENGTH]; */ +/* int fd; */ +/* char buf[4]; */ +/* struct main_configuration* config; */ + +/* config = (struct main_configuration*)shmem; */ + +/* if (atomic_load(&config->states[slot]) == STATE_NOTINIT) */ +/* { */ +/* return 0; */ +/* } */ + +/* memset(&p, 0, sizeof(p)); */ +/* snprintf(&p[0], sizeof(p), ".s.%d", pid); */ + +/* if (pgagroal_connect_unix_socket(config->unix_socket_dir, &p[0], &fd)) */ +/* { */ +/* pgagroal_log_debug("pgagroal_management_remove_fd: slot %d state %d database %s user %s socket %d pid %d connect: %d", */ +/* slot, atomic_load(&config->states[slot]), */ +/* config->connections[slot].database, config->connections[slot].username, socket, pid, fd); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* if (write_header(NULL, fd, MANAGEMENT_REMOVE_FD, slot)) */ +/* { */ +/* pgagroal_log_warn("pgagroal_management_remove_fd: write: %d", fd); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* pgagroal_write_int32(&buf, socket); */ +/* if (write_complete(NULL, fd, &buf, sizeof(buf))) */ +/* { */ +/* pgagroal_log_warn("pgagroal_management_remove_fd: write: %d %s", fd, strerror(errno)); */ +/* errno = 0; */ +/* goto error; */ +/* } */ + +/* pgagroal_disconnect(fd); */ + +/* return 0; */ + +/* error: */ +/* pgagroal_disconnect(fd); */ + +/* return 1; */ +/* } */ + + +/* /\* */ +/* * Utility function to convert PGAGROAL_VERSION into a number. */ +/* * The major version is represented by a single digit. */ +/* * For minor and patch, a leading 0 is added if they are single digits. */ +/* *\/ */ +/* static int */ +/* pgagroal_executable_version_number(char* version, size_t version_size) */ +/* { */ +/* int major; */ +/* int minor; */ +/* int patch; */ + +/* long val; */ + +/* char* del = "."; */ +/* char* endptr; */ + +/* if (version == NULL) */ +/* { */ +/* version = PGAGROAL_VERSION; */ +/* version_size = sizeof(version); */ +/* } */ + +/* char buf[version_size]; */ + +/* memcpy(buf, version, sizeof(buf)); */ + +/* char* token = strtok(buf, del); */ +/* val = strtol(token, &endptr, 10); */ +/* if (errno == ERANGE || val <= LONG_MIN || val >= LONG_MAX) */ +/* { */ +/* goto error; */ +/* } */ +/* major = (int)val; */ + +/* token = strtok(NULL, del); */ +/* val = strtol(token, &endptr, 10); */ +/* if (errno == ERANGE || val <= LONG_MIN || val >= LONG_MAX) */ +/* { */ +/* goto error; */ +/* } */ +/* minor = (int)val; */ + +/* token = strtok(NULL, del); */ +/* val = strtol(token, &endptr, 10); */ +/* if (errno == ERANGE || val <= LONG_MIN || val >= LONG_MAX) */ +/* { */ +/* goto error; */ +/* } */ +/* patch = (int)val; */ + +/* int version_number = (major % 10) * 10000 + (minor / 10) * 1000 + (minor % 10) * 100 + patch; */ + +/* if (version_number < INT_MIN || version_number > INT_MAX || version_number < 10700) */ +/* { */ +/* goto error; */ +/* } */ + +/* return version_number; */ + +/* error: */ +/* pgagroal_log_debug("pgagroal_get_executable_number got overflowed or suspicious value: %s %s", version, strerror(errno)); */ +/* errno = 0; */ +/* return 1; */ +/* } */ + +/* /\* */ +/* * Utility function to convert back version_number into a string. */ +/* *\/ */ +/* static int */ +/* pgagroal_executable_version_string(char** version_string, int version_number) */ +/* { */ +/* char* v = NULL; */ +/* int major = version_number / 10000; */ +/* int minor = (version_number / 100) % 100; */ +/* int patch = version_number % 100; */ + +/* v = pgagroal_append_int(v, major); */ +/* v = pgagroal_append(v, "."); */ +/* v = pgagroal_append_int(v, minor); */ +/* v = pgagroal_append(v, "."); */ +/* v = pgagroal_append_int(v, patch); */ + +/* *version_string = v; */ + +/* return 0; */ +/* } */ + + +/* /\* */ +/* * Utility function to convert command into a string. */ +/* *\/ */ +/* static char* */ +/* pgagroal_executable_name(int command) */ +/* { */ +/* switch (command) */ +/* { */ +/* case PGAGROAL_EXECUTABLE: */ +/* return "pgagroal"; */ +/* case PGAGROAL_EXECUTABLE_CLI: */ +/* return "pgagroal-cli"; */ +/* case PGAGROAL_EXECUTABLE_VAULT: */ +/* return "pgagroal-vault"; */ +/* default: */ +/* pgagroal_log_debug("pgagroal_get_command_name got unexpected value: %d", command); */ +/* return NULL; */ +/* } */ +/* } */ diff --git a/src/libpgagroal/gzip_compression.c b/src/libpgagroal/gzip_compression.c new file mode 100644 index 00000000..9d9fda97 --- /dev/null +++ b/src/libpgagroal/gzip_compression.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* pgagroal */ +#include +#include +#include +#include + +/* system */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_LENGTH 8192 + +int +pgagroal_gzip_string(char* s, unsigned char** buffer, size_t* buffer_size) +{ + int ret; + z_stream stream; + size_t source_len; + size_t chunk_size; + unsigned char* temp_buffer; + unsigned char* final_buffer; + + source_len = strlen(s); + chunk_size = BUFFER_LENGTH; + + temp_buffer = (unsigned char*)malloc(chunk_size); + if (temp_buffer == NULL) + { + pgagroal_log_error("Gzip: Allocation error"); + return 1; + } + + memset(&stream, 0, sizeof(stream)); + stream.next_in = (unsigned char*)s; + stream.avail_in = source_len; + + ret = deflateInit2(&stream, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) + { + free(temp_buffer); + pgagroal_log_error("Gzip: Initialization failed"); + return 1; + } + + size_t total_out = 0; + do + { + if (stream.total_out >= chunk_size) + { + chunk_size *= 2; + unsigned char* new_buffer = (unsigned char*)realloc(temp_buffer, chunk_size); + if (new_buffer == NULL) + { + free(temp_buffer); + deflateEnd(&stream); + pgagroal_log_error("Gzip: Allocation error"); + return 1; + } + temp_buffer = new_buffer; + } + + stream.next_out = temp_buffer + stream.total_out; + stream.avail_out = chunk_size - stream.total_out; + + ret = deflate(&stream, Z_FINISH); + } + while (ret == Z_OK || ret == Z_BUF_ERROR); + + if (ret != Z_STREAM_END) + { + free(temp_buffer); + deflateEnd(&stream); + pgagroal_log_error("Gzip: Compression failed"); + return 1; + } + + total_out = stream.total_out; + + final_buffer = (unsigned char*)realloc(temp_buffer, total_out); + if (final_buffer == NULL) + { + *buffer = temp_buffer; + } + else + { + *buffer = final_buffer; + } + *buffer_size = total_out; + + deflateEnd(&stream); + + return 0; +} + +int +pgagroal_gunzip_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string) +{ + int ret; + z_stream stream; + size_t chunk_size; + size_t total_out = 0; + + chunk_size = BUFFER_LENGTH; + + char* temp_buffer = (char*)malloc(chunk_size); + if (temp_buffer == NULL) + { + pgagroal_log_error("GUNzip: Allocation failed"); + return 1; + } + + memset(&stream, 0, sizeof(stream)); + stream.next_in = (unsigned char*)compressed_buffer; + stream.avail_in = compressed_size; + + ret = inflateInit2(&stream, MAX_WBITS + 16); + if (ret != Z_OK) + { + free(temp_buffer); + pgagroal_log_error("GUNzip: Initialization failed"); + return 1; + } + + do + { + if (stream.total_out >= chunk_size) + { + chunk_size *= 2; + char* new_buffer = (char*)realloc(temp_buffer, chunk_size); + if (new_buffer == NULL) + { + free(temp_buffer); + inflateEnd(&stream); + pgagroal_log_error("GUNzip: Allocation error"); + return 1; + } + temp_buffer = new_buffer; + } + + stream.next_out = (unsigned char*)(temp_buffer + stream.total_out); + stream.avail_out = chunk_size - stream.total_out; + + ret = inflate(&stream, Z_NO_FLUSH); + } + while (ret == Z_OK || ret == Z_BUF_ERROR); + + if (ret != Z_STREAM_END) + { + free(temp_buffer); + inflateEnd(&stream); + pgagroal_log_error("GUNzip: Decompression failed"); + return 1; + } + + total_out = stream.total_out; + + char* final_buffer = (char*)realloc(temp_buffer, total_out + 1); + if (final_buffer == NULL) + { + free(temp_buffer); + inflateEnd(&stream); + pgagroal_log_error("GUNzip: Allocation failed"); + return 1; + } + temp_buffer = final_buffer; + + temp_buffer[total_out] = '\0'; + + *output_string = temp_buffer; + + inflateEnd(&stream); + + return 0; +} diff --git a/src/libpgagroal/lz4_compression.c b/src/libpgagroal/lz4_compression.c new file mode 100644 index 00000000..8a189f1a --- /dev/null +++ b/src/libpgagroal/lz4_compression.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 Red Hat + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* pgagroal */ +#include +#include +#include +#include + +/* system */ +#include +#include "lz4.h" +#include +#include +#include +#include +#include +#include + +int +pgagroal_lz4c_string(char* s, unsigned char** buffer, size_t* buffer_size) +{ + size_t input_size; + size_t max_compressed_size; + int compressed_size; + + input_size = strlen(s); + max_compressed_size = LZ4_compressBound(input_size); + + *buffer = (unsigned char*)malloc(max_compressed_size); + if (*buffer == NULL) + { + pgagroal_log_error("LZ4: Allocation failed"); + return 1; + } + + compressed_size = LZ4_compress_default(s, (char*)*buffer, input_size, max_compressed_size); + if (compressed_size <= 0) + { + pgagroal_log_error("LZ4: Compress failed"); + free(*buffer); + return 1; + } + + *buffer_size = (size_t)compressed_size; + + return 0; +} + +int +pgagroal_lz4d_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string) +{ + size_t max_decompressed_size; + int decompressed_size; + + max_decompressed_size = compressed_size * 4; + + *output_string = (char*)malloc(max_decompressed_size); + if (*output_string == NULL) + { + pgagroal_log_error("LZ4: Allocation failed"); + return 1; + } + + decompressed_size = LZ4_decompress_safe((const char*)compressed_buffer, *output_string, compressed_size, max_decompressed_size); + if (decompressed_size < 0) + { + pgagroal_log_error("LZ4: Decompress failed"); + free(*output_string); + return 1; + } + + (*output_string)[decompressed_size] = '\0'; + + return 0; +} diff --git a/src/libpgagroal/management.c b/src/libpgagroal/management.c index 1b2c7420..eff931ac 100644 --- a/src/libpgagroal/management.c +++ b/src/libpgagroal/management.c @@ -28,17 +28,22 @@ /* pgagroal */ #include +#include +#include +#include +#include +#include #include -#include +#include #include #include +#include #include #include -#include -#include +#include +#include /* system */ -#include #include #include #include @@ -53,1015 +58,666 @@ #include #include -#define MANAGEMENT_HEADER_SIZE 5 -#define MANAGEMENT_INFO_SIZE 13 - -/** - * JSON related command tags, used to build and retrieve - * a JSON piece of information related to a single command - */ -#define JSON_TAG_COMMAND "command" -#define JSON_TAG_COMMAND_NAME "name" -#define JSON_TAG_COMMAND_STATUS "status" -#define JSON_TAG_COMMAND_ERROR "error" -#define JSON_TAG_COMMAND_OUTPUT "output" -#define JSON_TAG_COMMAND_EXIT_STATUS "exit-status" - -#define JSON_TAG_APPLICATION_NAME "name" -#define JSON_TAG_APPLICATION_VERSION_MAJOR "major" -#define JSON_TAG_APPLICATION_VERSION_MINOR "minor" -#define JSON_TAG_APPLICATION_VERSION_PATCH "patch" -#define JSON_TAG_APPLICATION_VERSION "version" - -#define JSON_TAG_ARRAY_NAME "list" - -/** - * JSON pre-defined values - */ -#define JSON_STRING_SUCCESS "OK" -#define JSON_STRING_ERROR "KO" -#define JSON_BOOL_SUCCESS 0 -#define JSON_BOOL_ERROR 1 - -#define S "S:" -#define V ",V:" +static int create_header(int32_t command, uint8_t compression, uint8_t encryption, int32_t output_format, struct json** json); +static int create_request(struct json* json, struct json** request); +static int create_outcome_success(struct json* json, time_t start_time, time_t end_time, struct json** outcome); +static int create_outcome_failure(struct json* json, int32_t error, struct json** outcome); +static int read_uint8(char* prefix, SSL* ssl, int socket, uint8_t* i); +static int read_string(char* prefix, SSL* ssl, int socket, char** str); static int read_complete(SSL* ssl, int socket, void* buf, size_t size); +static int write_uint8(char* prefix, SSL* ssl, int socket, uint8_t i); +static int write_string(char* prefix, SSL* ssl, int socket, char* str); static int write_complete(SSL* ssl, int socket, void* buf, size_t size); static int write_socket(int socket, void* buf, size_t size); static int write_ssl(SSL* ssl, void* buf, size_t size); -static int write_header(SSL* ssl, int fd, signed char type, int slot); -static int write_info(char* buffer, int command, int offset); - -static int pgagroal_management_write_conf_ls_detail(int socket, char* what); -static int pgagroal_management_read_conf_ls_detail(SSL* ssl, int socket, char* buffer); - -static int pgagroal_management_json_print_status_details(cJSON* json); - -static cJSON* pgagroal_management_json_read_status_details(SSL* ssl, int socket, bool include_details); -static cJSON* pgagroal_managment_json_read_config_get(int socket, char* config_key, char* expected_value); - -static cJSON* pgagroal_management_json_read_conf_ls(SSL* ssl, int socket); -static int pgagroal_management_json_print_conf_ls(cJSON* json); - -static int pgagroal_executable_version_number(char* version, size_t version_size); -static int pgagroal_executable_version_string(char** version_string, int version_number); -static char* pgagroal_executable_name(int command); - -static cJSON* pgagroal_json_create_new_command_object(char* command_name, bool success, char* executable_name, char* executable_version); -static cJSON* pgagroal_json_extract_command_output_object(cJSON* json); -static int pgagroal_json_set_command_object_faulty(cJSON* json, char* message, int exit_status); -static const char* pgagroal_json_get_command_object_status(cJSON* json); -static bool pgagroal_json_is_command_name_equals_to(cJSON* json, char* command_name); -static int pgagroal_json_print_and_free_json_object(cJSON* json); -static int pgagroal_json_command_object_exit_status(cJSON* json); int -pgagroal_management_read_header(int socket, signed char* id, int32_t* slot) +pgagroal_management_request_flush(SSL* ssl, int socket, int32_t mode, char* database, uint8_t compression, uint8_t encryption, int32_t output_format) { - char header[MANAGEMENT_HEADER_SIZE]; - char buf_info[MANAGEMENT_INFO_SIZE]; + struct json* j = NULL; + struct json* request = NULL; - if (read_complete(NULL, socket, &header[0], sizeof(header))) + if (create_header(MANAGEMENT_FLUSH, compression, encryption, output_format, &j)) + { + goto error; + } + + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_read_header: %d %s", socket, strerror(errno)); - errno = 0; goto error; } - *id = pgagroal_read_byte(&(header)); - *slot = pgagroal_read_int32(&(header[1])); + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_MODE, (uintptr_t)mode, ValueInt32); + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)database, ValueString); - if (read_complete(NULL, socket, &buf_info[0], sizeof(buf_info))) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_read_header: %d %s", socket, strerror(errno)); - errno = 0; goto error; } + pgagroal_json_destroy(j); + return 0; error: - *id = -1; - *slot = -1; + pgagroal_json_destroy(j); return 1; } int -pgagroal_management_read_payload(int socket, signed char id, int* payload_i, char** payload_s) +pgagroal_management_request_enabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format) { - int nr; - char* s = NULL; - char buf2[2]; - char buf4[4]; - int size; - struct cmsghdr* cmptr = NULL; - struct iovec iov[1]; - struct msghdr msg; + struct json* j = NULL; + struct json* request = NULL; - *payload_i = -1; - *payload_s = NULL; - - switch (id) + if (create_header(MANAGEMENT_ENABLEDB, compression, encryption, output_format, &j)) { - case MANAGEMENT_TRANSFER_CONNECTION: - case MANAGEMENT_CLIENT_FD: - memset(&buf2[0], 0, sizeof(buf2)); - - iov[0].iov_base = &buf2[0]; - iov[0].iov_len = sizeof(buf2); - - cmptr = calloc(1, CMSG_SPACE(sizeof(int))); - if (cmptr == NULL) - { - goto error; - } - cmptr->cmsg_len = CMSG_LEN(sizeof(int)); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = cmptr; - msg.msg_controllen = CMSG_SPACE(sizeof(int)); - msg.msg_flags = 0; - - if ((nr = recvmsg(socket, &msg, 0)) < 0) - { - goto error; - } - else if (nr == 0) - { - goto error; - } - - *payload_i = *(int*)CMSG_DATA(cmptr); - - free(cmptr); - break; - case MANAGEMENT_FLUSH: - if (read_complete(NULL, socket, &buf4[0], sizeof(buf4))) - { - goto error; - } - *payload_i = pgagroal_read_int32(&buf4); + goto error; + } - if (read_complete(NULL, socket, &buf4[0], sizeof(buf4))) - { - goto error; - } - size = pgagroal_read_int32(&buf4); + if (create_request(j, &request)) + { + goto error; + } - s = calloc(1, size + 1); - if (s == NULL) - { - goto error; - } - if (read_complete(NULL, socket, s, size)) - { - goto error; - } - *payload_s = s; - break; - case MANAGEMENT_KILL_CONNECTION: - case MANAGEMENT_CLIENT_DONE: - case MANAGEMENT_REMOVE_FD: - if (read_complete(NULL, socket, &buf4[0], sizeof(buf4))) - { - goto error; - } - *payload_i = pgagroal_read_int32(&buf4); - break; - case MANAGEMENT_ENABLEDB: - case MANAGEMENT_DISABLEDB: - case MANAGEMENT_CONFIG_GET: - case MANAGEMENT_GET_PASSWORD: - case MANAGEMENT_CONFIG_SET: - if (read_complete(NULL, socket, &buf4[0], sizeof(buf4))) - { - goto error; - } - *payload_i = pgagroal_read_int32(&buf4); + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)database, ValueString); - s = calloc(1, *payload_i + 1); - if (s == NULL) - { - goto error; - } - if (read_complete(NULL, socket, s, *payload_i)) - { - goto error; - } - *payload_s = s; - break; - case MANAGEMENT_RESET_SERVER: - case MANAGEMENT_SWITCH_TO: - s = calloc(1, MISC_LENGTH); - if (s == NULL) - { - goto error; - } - if (read_complete(NULL, socket, s, MISC_LENGTH)) - { - goto error; - } - *payload_s = s; - break; - case MANAGEMENT_RETURN_CONNECTION: - case MANAGEMENT_GRACEFULLY: - case MANAGEMENT_STOP: - case MANAGEMENT_CANCEL_SHUTDOWN: - case MANAGEMENT_STATUS: - case MANAGEMENT_DETAILS: - case MANAGEMENT_RESET: - case MANAGEMENT_RELOAD: - case MANAGEMENT_CONFIG_LS: - break; - default: - goto error; - break; + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) + { + goto error; } + pgagroal_json_destroy(j); + return 0; error: - if (cmptr) - { - free(cmptr); - } + pgagroal_json_destroy(j); return 1; } int -pgagroal_management_transfer_connection(int32_t slot) +pgagroal_management_request_get_password(SSL* ssl, int socket, char* username, uint8_t compression, uint8_t encryption, int32_t output_format) { - int fd; - struct main_configuration* config; - struct cmsghdr* cmptr = NULL; - struct iovec iov[1]; - struct msghdr msg; - char buf2[2]; + struct json* j = NULL; + struct json* request = NULL; - config = (struct main_configuration*)shmem; - - if (pgagroal_connect_unix_socket(config->unix_socket_dir, MAIN_UDS, &fd)) - { - pgagroal_log_warn("pgagroal_management_transfer_connection: connect: %d", fd); - errno = 0; - goto error; - } - - if (write_header(NULL, fd, MANAGEMENT_TRANSFER_CONNECTION, slot)) + if (create_header(MANAGEMENT_GET_PASSWORD, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_transfer_connection: write: %d", fd); - errno = 0; goto error; } - /* Write file descriptor */ - memset(&buf2[0], 0, sizeof(buf2)); - - iov[0].iov_base = &buf2[0]; - iov[0].iov_len = sizeof(buf2); - - cmptr = calloc(1, CMSG_SPACE(sizeof(int))); - if (cmptr == NULL) + if (create_request(j, &request)) { goto error; } - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_USERNAME, (uintptr_t)username, ValueString); - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = cmptr; - msg.msg_controllen = CMSG_SPACE(sizeof(int)); - msg.msg_flags = 0; - *(int*)CMSG_DATA(cmptr) = config->connections[slot].fd; - - if (sendmsg(fd, &msg, 0) != 2) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { goto error; } - free(cmptr); - pgagroal_disconnect(fd); + pgagroal_json_destroy(j); return 0; error: - if (cmptr) - { - free(cmptr); - } - pgagroal_disconnect(fd); - pgagroal_kill_connection(slot, NULL); + + pgagroal_json_destroy(j); return 1; } int -pgagroal_management_return_connection(int32_t slot) +pgagroal_management_request_disabledb(SSL* ssl, int socket, char* database, uint8_t compression, uint8_t encryption, int32_t output_format) { - int fd; - struct main_configuration* config; + struct json* j = NULL; + struct json* request = NULL; - config = (struct main_configuration*)shmem; + if (create_header(MANAGEMENT_DISABLEDB, compression, encryption, output_format, &j)) + { + goto error; + } - if (pgagroal_connect_unix_socket(config->unix_socket_dir, MAIN_UDS, &fd)) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_return_connection: connect: %d", fd); - errno = 0; goto error; } - if (write_header(NULL, fd, MANAGEMENT_RETURN_CONNECTION, slot)) + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)database, ValueString); + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_return_connection: write: %d", fd); - errno = 0; goto error; } - pgagroal_disconnect(fd); + pgagroal_json_destroy(j); return 0; error: - pgagroal_disconnect(fd); + + pgagroal_json_destroy(j); return 1; } int -pgagroal_management_kill_connection(int32_t slot, int socket) +pgagroal_management_request_gracefully(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - int fd; - char buf[4]; - struct main_configuration* config; + struct json* j = NULL; + struct json* request = NULL; - config = (struct main_configuration*)shmem; - - if (pgagroal_connect_unix_socket(config->unix_socket_dir, MAIN_UDS, &fd)) + if (create_header(MANAGEMENT_GRACEFULLY, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_kill_connection: connect: %d", fd); - errno = 0; goto error; } - if (write_header(NULL, fd, MANAGEMENT_KILL_CONNECTION, slot)) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_kill_connection: write: %d", fd); - errno = 0; goto error; } - pgagroal_write_int32(&buf, socket); - if (write_complete(NULL, fd, &buf, sizeof(buf))) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_kill_connection: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - pgagroal_disconnect(fd); + pgagroal_json_destroy(j); return 0; error: - pgagroal_disconnect(fd); + + pgagroal_json_destroy(j); return 1; } int -pgagroal_management_flush(SSL* ssl, int fd, int32_t mode, char* database) +pgagroal_management_request_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - char buf[4]; + struct json* j = NULL; + struct json* request = NULL; - if (write_header(ssl, fd, MANAGEMENT_FLUSH, -1)) + if (create_header(MANAGEMENT_SHUTDOWN, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_flush: write: %d", fd); - errno = 0; goto error; } - pgagroal_write_int32(&buf, mode); - if (write_complete(ssl, fd, &buf, sizeof(buf))) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_flush: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - pgagroal_write_int32(&buf, strlen(database)); - if (write_complete(ssl, fd, &buf, sizeof(buf))) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_flush: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - if (write_complete(ssl, fd, database, strlen(database))) - { - pgagroal_log_warn("pgagroal_management_flush: write: %d %s", fd, strerror(errno)); - errno = 0; - goto error; - } + pgagroal_json_destroy(j); return 0; error: + pgagroal_json_destroy(j); + return 1; } int -pgagroal_management_enabledb(SSL* ssl, int fd, char* database) +pgagroal_management_request_cancel_shutdown(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - char buf[4]; + struct json* j = NULL; + struct json* request = NULL; - if (write_header(ssl, fd, MANAGEMENT_ENABLEDB, -1)) + if (create_header(MANAGEMENT_CANCEL_SHUTDOWN, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_enabledb: write: %d", fd); - errno = 0; goto error; } - pgagroal_write_int32(&buf, strlen(database)); - if (write_complete(ssl, fd, &buf, sizeof(buf))) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_enabledb: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - if (write_complete(ssl, fd, database, strlen(database))) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_enabledb: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } + pgagroal_json_destroy(j); + return 0; error: + pgagroal_json_destroy(j); + return 1; } int -pgagroal_management_get_password(SSL* ssl, int fd, char* username, char* pass) +pgagroal_management_request_status(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - char buf[4]; - int* password_length = NULL; - char password[MAX_PASSWORD_LENGTH]; - char buffer[strlen(username) + 4]; + struct json* j = NULL; + struct json* request = NULL; - password_length = (int*)malloc(sizeof(int)); - if (!password_length) + if (create_header(MANAGEMENT_STATUS, compression, encryption, output_format, &j)) { goto error; } - if (write_header(ssl, fd, MANAGEMENT_GET_PASSWORD, -1)) + if (create_request(j, &request)) + { + goto error; + } + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_get_password: write-header: %d", fd); - errno = 0; goto error; } - pgagroal_write_int32(&buf, (int32_t)strlen(username)); - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, buf, 4); - memcpy(buffer + 4, username, strlen(username)); + pgagroal_json_destroy(j); - // write username to the management port - if (write_complete(ssl, fd, buffer, strlen(username) + 4)) + return 0; + +error: + + pgagroal_json_destroy(j); + + return 1; +} + +int +pgagroal_management_request_details(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_DETAILS, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_get_password: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - // Read the Password length - if (read_complete(ssl, fd, &buf, sizeof(buf))) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_get_password: read: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - *password_length = pgagroal_read_int32(&buf); - // Read the Password - memset(password, 0, sizeof(password)); - if (read_complete(ssl, fd, password, *password_length)) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_get_password: read: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - memcpy(pass, password, *password_length); + pgagroal_json_destroy(j); - free(password_length); return 0; error: - free(password_length); + pgagroal_json_destroy(j); + return 1; } + int -pgagroal_management_disabledb(SSL* ssl, int fd, char* database) +pgagroal_management_request_ping(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - char buf[4]; + struct json* j = NULL; + struct json* request = NULL; - if (write_header(ssl, fd, MANAGEMENT_DISABLEDB, -1)) + if (create_header(MANAGEMENT_PING, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_disabledb: write: %d", fd); - errno = 0; goto error; } - pgagroal_write_int32(&buf, strlen(database)); - if (write_complete(ssl, fd, &buf, sizeof(buf))) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_disabledb: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } - if (write_complete(ssl, fd, database, strlen(database))) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_disabledb: write: %d %s", fd, strerror(errno)); - errno = 0; goto error; } + pgagroal_json_destroy(j); + return 0; error: + pgagroal_json_destroy(j); + return 1; } int -pgagroal_management_gracefully(SSL* ssl, int fd) +pgagroal_management_request_clear(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (write_header(ssl, fd, MANAGEMENT_GRACEFULLY, -1)) + struct json* j = NULL; + struct json* request = NULL; + + if (create_header(MANAGEMENT_CLEAR, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_gracefully: write: %d", fd); - errno = 0; goto error; } - return 0; - -error: - - return 1; -} + if (create_request(j, &request)) + { + goto error; + } -int -pgagroal_management_stop(SSL* ssl, int fd) -{ - if (write_header(ssl, fd, MANAGEMENT_STOP, -1)) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_stop: write: %d", fd); - errno = 0; goto error; } + pgagroal_json_destroy(j); + return 0; error: + pgagroal_json_destroy(j); + return 1; } int -pgagroal_management_cancel_shutdown(SSL* ssl, int fd) +pgagroal_management_request_clear_server(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format) { - if (write_header(ssl, fd, MANAGEMENT_CANCEL_SHUTDOWN, -1)) + struct json* j = NULL; + struct json* request = NULL; + + if (create_header(MANAGEMENT_CLEAR_SERVER, compression, encryption, output_format, &j)) { - pgagroal_log_warn("pgagroal_management_cancel_shutdown: write: %d", fd); - errno = 0; goto error; } - return 0; - -error: + if (create_request(j, &request)) + { + goto error; + } - return 1; -} + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_SERVER, (uintptr_t)server, ValueString); -int -pgagroal_management_status(SSL* ssl, int fd) -{ - if (write_header(ssl, fd, MANAGEMENT_STATUS, -1)) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_status: write: %d", fd); - errno = 0; goto error; } + pgagroal_json_destroy(j); + return 0; error: + pgagroal_json_destroy(j); + return 1; } int -pgagroal_management_read_status(SSL* ssl, int socket, char output_format) +pgagroal_management_request_switch_to(SSL* ssl, int socket, char* server, uint8_t compression, uint8_t encryption, int32_t output_format) { - cJSON* json = pgagroal_management_json_read_status_details(ssl, socket, false); + struct json* j = NULL; + struct json* request = NULL; - // check we have an answer, note that a faulty answer is still valid to be printed! - if (!json) + if (create_header(MANAGEMENT_SWITCH_TO, compression, encryption, output_format, &j)) { goto error; } - // print out the command answer - if (output_format == COMMAND_OUTPUT_FORMAT_JSON) + if (create_request(j, &request)) { - return pgagroal_json_print_and_free_json_object(json); + goto error; } - else + + pgagroal_json_put(request, MANAGEMENT_ARGUMENT_SERVER, (uintptr_t)server, ValueString); + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - return pgagroal_management_json_print_status_details(json); + goto error; } + pgagroal_json_destroy(j); + + return 0; + error: - pgagroal_log_warn("pgagroal_management_read_status: command error [%s]", - (json == NULL ? "" : pgagroal_json_get_command_object_status(json))); + + pgagroal_json_destroy(j); + return 1; } -/** - * Utility method that reads the answer from pgagroal about - * either the 'status' or the 'status details' command. - * The answer is then wrapped into a JSON object - * that contains all the information needed to be printed out in either - * JSON format or text format. - * - * @param ssl the SSL file descriptor for the socket - * @param socket the socket file descriptor - * @param include_details true if the method has to handle the 'status details' command - * or false if the answer is related only to the 'status' command - * - * @returns the json object, faulty if something goes wrong - */ -static cJSON* -pgagroal_management_json_read_status_details(SSL* ssl, int socket, bool include_details) +int +pgagroal_management_request_reload(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, int32_t output_format) { - char buf[16]; - char disabled[NUMBER_OF_DISABLED][MAX_DATABASE_LENGTH]; - int status; - int active; - int total; - int max; - int max_connections = 0; - int limits = 0; - int servers = 0; - char header[12 + MAX_NUMBER_OF_CONNECTIONS]; - char buf_info[MANAGEMENT_INFO_SIZE]; - int application; - int version; - - memset(&buf_info, 0, sizeof(buf_info)); - memset(&buf, 0, sizeof(buf)); - memset(&disabled, 0, sizeof(disabled)); - memset(&header, 0, sizeof(header)); - - if (read_complete(ssl, socket, &buf_info[0], sizeof(buf_info))) - { - pgagroal_log_warn("pgagroal_management_json_read_status_details: read: %d %s", socket, strerror(errno)); - errno = 0; - return NULL; - } + struct json* j = NULL; + struct json* request = NULL; - application = pgagroal_read_int32(&(buf_info[2])); - version = pgagroal_read_int32(&(buf_info[9])); - - char* version_buf = NULL; - - if (pgagroal_executable_version_string(&version_buf, version)) + if (create_header(MANAGEMENT_RELOAD, compression, encryption, output_format, &j)) { - return NULL; + goto error; } - cJSON* json = pgagroal_json_create_new_command_object(include_details ? "status details" : "status", true, pgagroal_executable_name(application), version_buf); - cJSON* output = pgagroal_json_extract_command_output_object(json); - - if (read_complete(ssl, socket, &buf[0], sizeof(buf))) + if (create_request(j, &request)) { - pgagroal_log_warn("pgagroal_management_json_read_status_details: read: %d %s", socket, strerror(errno)); goto error; } - if (read_complete(ssl, socket, &disabled[0], sizeof(disabled))) + if (pgagroal_management_write_json(ssl, socket, compression, encryption, j)) { - pgagroal_log_warn("pgagroal_management_json_read_status_details: read: %d %s", socket, strerror(errno)); goto error; } - status = pgagroal_read_int32(&buf); - active = pgagroal_read_int32(&(buf[4])); - total = pgagroal_read_int32(&(buf[8])); - max = pgagroal_read_int32(&(buf[12])); + pgagroal_json_destroy(j); - // status information - cJSON* status_json = cJSON_CreateObject(); - cJSON_AddStringToObject(status_json, "message", (status == 1 ? "Running" : "Graceful shutdown")); - cJSON_AddNumberToObject(status_json, "status", status); - cJSON_AddItemToObject(output, "status", status_json); + return 0; - // define all the information about connections - cJSON* connections = cJSON_CreateObject(); - cJSON_AddNumberToObject(connections, "active", active); - cJSON_AddNumberToObject(connections, "total", total); - cJSON_AddNumberToObject(connections, "max", max); - cJSON_AddItemToObject(output, "connections", connections); +error: - // define all the information about disabled databases - cJSON* databases = cJSON_CreateObject(); - cJSON* databases_array = cJSON_CreateArray(); + pgagroal_json_destroy(j); - int counter = 0; + return 1; +} - for (int i = 0; i < NUMBER_OF_DISABLED; i++) - { - if (strcmp(disabled[i], "")) - { - if (!strcmp(disabled[i], "*")) - { - cJSON_AddItemToArray(databases_array, cJSON_CreateString("ALL")); - counter = -1; - } - else - { - cJSON_AddItemToArray(databases_array, cJSON_CreateString(disabled[i])); - counter++; - } - } - } +static int +create_header(int32_t command, uint8_t compression, uint8_t encryption, int32_t output_format, struct json** json) +{ + time_t t; + char timestamp[128]; + struct tm* time_info; + struct json* j = NULL; + struct json* header = NULL; - cJSON* disabled_databases = cJSON_CreateObject(); - cJSON_AddNumberToObject(disabled_databases, "count", counter); - cJSON_AddStringToObject(disabled_databases, "state", "disabled"); - cJSON_AddItemToObject(disabled_databases, JSON_TAG_ARRAY_NAME, databases_array); - cJSON_AddItemToObject(databases, "disabled", disabled_databases); - cJSON_AddItemToObject(output, "databases", databases); + *json = NULL; - // the 'status' command ends here - if (!include_details) + if (pgagroal_json_create(&j)) { - goto end; + goto error; } - /*********** 'status details ************/ - - memset(&header, 0, sizeof(header)); - - if (read_complete(ssl, socket, &header[0], sizeof(header))) + if (pgagroal_json_create(&header)) { goto error; } - // quantity informations - max_connections = pgagroal_read_int32(&header); - limits = pgagroal_read_int32(&(header[4])); - servers = pgagroal_read_int32(&(header[8])); + time(&t); + time_info = localtime(&t); + strftime(×tamp[0], sizeof(timestamp), "%Y%m%d%H%M%S", time_info); - cJSON* json_servers = cJSON_CreateObject(); - cJSON* json_servers_array = cJSON_CreateArray(); - cJSON_AddItemToObject(output, "servers", json_servers); - cJSON_AddNumberToObject(json_servers, "count", servers); + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_COMMAND, (uintptr_t)command, ValueInt32); + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_CLIENT_VERSION, (uintptr_t)PGAGROAL_VERSION, ValueString); + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_OUTPUT, (uintptr_t)output_format, ValueUInt8); + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_TIMESTAMP, (uintptr_t)timestamp, ValueString); + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_COMPRESSION, (uintptr_t)compression, ValueUInt8); + pgagroal_json_put(header, MANAGEMENT_ARGUMENT_ENCRYPTION, (uintptr_t)encryption, ValueUInt8); - // details about the servers - for (int i = 0; i < servers; i++) - { - char server[5 + MISC_LENGTH + MISC_LENGTH]; + pgagroal_json_put(j, MANAGEMENT_CATEGORY_HEADER, (uintptr_t)header, ValueJSON); - memset(&server, 0, sizeof(server)); + *json = j; - if (read_complete(ssl, socket, &server[0], sizeof(server))) - { - goto error; - } + return 0; - cJSON* current_server_json = cJSON_CreateObject(); - cJSON_AddStringToObject(current_server_json, "server", pgagroal_read_string(&(server[0]))); - cJSON_AddStringToObject(current_server_json, "host", pgagroal_read_string(&(server[MISC_LENGTH]))); - cJSON_AddNumberToObject(current_server_json, "port", pgagroal_read_int32(&(server[MISC_LENGTH + MISC_LENGTH]))); - cJSON_AddStringToObject(current_server_json, "state", pgagroal_server_state_as_string(pgagroal_read_byte(&(server[MISC_LENGTH + MISC_LENGTH + 4])))); +error: - cJSON_AddItemToArray(json_servers_array, current_server_json); - } + pgagroal_json_destroy(header); + pgagroal_json_destroy(j); - cJSON_AddItemToObject(json_servers, JSON_TAG_ARRAY_NAME, json_servers_array); + *json = NULL; - // details about the limits - cJSON* json_limits = cJSON_CreateObject(); - cJSON* json_limits_array = cJSON_CreateArray(); - cJSON_AddItemToObject(json_limits, JSON_TAG_ARRAY_NAME, json_limits_array); - cJSON_AddItemToObject(output, "limits", json_limits); - cJSON_AddNumberToObject(json_limits, "count", limits); + return 1; +} - for (int i = 0; i < limits; i++) - { - char limit[16 + MAX_DATABASE_LENGTH + MAX_USERNAME_LENGTH]; - memset(&limit, 0, sizeof(limit)); +static int +create_request(struct json* json, struct json** request) +{ + struct json* r = NULL; - if (read_complete(ssl, socket, &limit[0], sizeof(limit))) - { - goto error; - } + *request = NULL; - cJSON* current_limit_json = cJSON_CreateObject(); + if (pgagroal_json_create(&r)) + { + goto error; + } - cJSON_AddStringToObject(current_limit_json, "database", pgagroal_read_string(&(limit[16]))); - cJSON_AddStringToObject(current_limit_json, "username", pgagroal_read_string(&(limit[16 + MAX_DATABASE_LENGTH]))); + pgagroal_json_put(json, MANAGEMENT_CATEGORY_REQUEST, (uintptr_t)r, ValueJSON); - cJSON* current_connections = cJSON_CreateObject(); + *request = r; - cJSON_AddNumberToObject(current_connections, "active", pgagroal_read_int32(&(limit))); - cJSON_AddNumberToObject(current_connections, "max", pgagroal_read_int32(&(limit[4]))); - cJSON_AddNumberToObject(current_connections, "initial", pgagroal_read_int32(&(limit[8]))); - cJSON_AddNumberToObject(current_connections, "min", pgagroal_read_int32(&(limit[12]))); + return 0; - cJSON_AddItemToObject(current_limit_json, "connections", current_connections); - cJSON_AddItemToArray(json_limits_array, current_limit_json); +error: - } + pgagroal_json_destroy(r); - // max connections details (note that the connections json object has been created - // as part of the status output) - cJSON* connections_array = cJSON_CreateArray(); - cJSON_AddItemToObject(connections, JSON_TAG_ARRAY_NAME, connections_array); + return 1; +} - for (int i = 0; i < max_connections; i++) - { - char details[16 + MAX_DATABASE_LENGTH + MAX_USERNAME_LENGTH + MAX_APPLICATION_NAME]; - signed char state; - long time; - time_t t; - char ts[20] = {0}; - int pid; - char p[10] = {0}; - int fd; - char f[10] = {0}; +static int +create_outcome_success(struct json* json, time_t start_time, time_t end_time, struct json** outcome) +{ + int32_t total_seconds = 0; + char* elapsed = NULL; + struct json* r = NULL; - memset(&details, 0, sizeof(details)); + *outcome = NULL; - if (read_complete(ssl, socket, &details[0], sizeof(details))) - { + if (pgagroal_json_create(&r)) + { + goto error; + } - goto error; - } + elapsed = pgagroal_get_timestamp_string(start_time, end_time, &total_seconds); - state = (signed char)header[12 + i]; - time = pgagroal_read_long(&(details[0])); - pid = pgagroal_read_int32(&(details[8])); - fd = pgagroal_read_int32(&(details[12])); + pgagroal_json_put(r, MANAGEMENT_ARGUMENT_STATUS, (uintptr_t)true, ValueBool); + pgagroal_json_put(r, MANAGEMENT_ARGUMENT_TIME, (uintptr_t)elapsed, ValueString); - t = time; - strftime(ts, 20, "%Y-%m-%d %H:%M:%S", localtime(&t)); + pgagroal_json_put(json, MANAGEMENT_CATEGORY_OUTCOME, (uintptr_t)r, ValueJSON); - sprintf(p, "%d", pid); - sprintf(f, "%d", fd); + *outcome = r; - cJSON* current_connection_json = cJSON_CreateObject(); + free(elapsed); - cJSON_AddNumberToObject(current_connection_json, "number", i + 1); - cJSON_AddStringToObject(current_connection_json, "state", pgagroal_connection_state_as_string(state)); - cJSON_AddStringToObject(current_connection_json, "time", time > 0 ? ts : ""); - cJSON_AddStringToObject(current_connection_json, "pid", pid > 0 ? p : ""); - cJSON_AddStringToObject(current_connection_json, "fd", fd > 0 ? f : ""); - cJSON_AddStringToObject(current_connection_json, "database", pgagroal_read_string(&(details[16]))); - cJSON_AddStringToObject(current_connection_json, "user", pgagroal_read_string(&(details[16 + MAX_DATABASE_LENGTH]))); - cJSON_AddStringToObject(current_connection_json, "detail", pgagroal_read_string(&(details[16 + MAX_DATABASE_LENGTH + MAX_USERNAME_LENGTH]))); + return 0; - cJSON_AddItemToArray(connections_array, current_connection_json); +error: - } + free(elapsed); -end: - free(version_buf); - return json; + pgagroal_json_destroy(r); -error: - // set the json object as faulty and erase the errno - pgagroal_json_set_command_object_faulty(json, strerror(errno), errno); - errno = 0; - return json; + return 1; } -int -pgagroal_management_write_status(int socket, bool graceful) +static int +create_outcome_failure(struct json* json, int32_t error, struct json** outcome) { - char buf[16]; - char buf_info[MANAGEMENT_INFO_SIZE]; + struct json* r = NULL; - int active; - int total; - struct main_configuration* config; + *outcome = NULL; - if (write_info(buf_info, PGAGROAL_EXECUTABLE, 0)) + if (pgagroal_json_create(&r)) { goto error; } - if (write_complete(NULL, socket, &buf_info, sizeof(buf_info))) - { - pgagroal_log_warn("pgagroal_management_write_status: write: %d %s", socket, strerror(errno)); - errno = 0; - goto error; - } + pgagroal_json_put(r, MANAGEMENT_ARGUMENT_STATUS, (uintptr_t)false, ValueBool); + pgagroal_json_put(r, MANAGEMENT_ARGUMENT_ERROR, (uintptr_t)error, ValueInt32); - memset(&buf, 0, sizeof(buf)); - active = 0; - total = 0; + pgagroal_json_put(json, MANAGEMENT_CATEGORY_OUTCOME, (uintptr_t)r, ValueJSON); - config = (struct main_configuration*)shmem; + *outcome = r; - if (!graceful) - { - pgagroal_write_int32(&buf, 1); - } - else - { - pgagroal_write_int32(&buf, 2); - } + return 0; - for (int i = 0; i < config->max_connections; i++) - { - int state = atomic_load(&config->states[i]); - switch (state) - { - case STATE_IN_USE: - case STATE_GRACEFULLY: - active++; - case STATE_INIT: - case STATE_FREE: - case STATE_FLUSH: - case STATE_IDLE_CHECK: - case STATE_MAX_CONNECTION_AGE: - case STATE_VALIDATION: - case STATE_REMOVE: - total++; - break; - default: - break; - } - } +error: + + pgagroal_json_destroy(r); + + return 1; +} - pgagroal_write_int32(&(buf[4]), active); - pgagroal_write_int32(&(buf[8]), total); - pgagroal_write_int32(&(buf[12]), config->max_connections); +int +pgagroal_management_create_response(struct json* json, int server, struct json** response) +{ + struct json* r = NULL; + struct main_configuration* config; + + config = (struct main_configuration*)shmem; + + *response = NULL; - if (write_complete(NULL, socket, &buf, sizeof(buf))) + if (pgagroal_json_create(&r)) { - pgagroal_log_warn("pgagroal_management_write_status: write: %d %s", socket, strerror(errno)); - errno = 0; goto error; } - if (write_complete(NULL, socket, &config->disabled, sizeof(config->disabled))) + pgagroal_json_put(json, MANAGEMENT_CATEGORY_RESPONSE, (uintptr_t)r, ValueJSON); + + if (server >= 0) { - pgagroal_log_warn("pgagroal_management_write_status: write: %d %s", socket, strerror(errno)); - errno = 0; - goto error; + pgagroal_json_put(r, MANAGEMENT_ARGUMENT_SERVER, (uintptr_t)config->servers[server].name, ValueString); } + pgagroal_json_put(r, MANAGEMENT_ARGUMENT_SERVER_VERSION, (uintptr_t)PGAGROAL_VERSION, ValueString); + + *response = r; + return 0; error: + pgagroal_json_destroy(r); + return 1; } int -pgagroal_management_details(SSL* ssl, int fd) +pgagroal_management_response_ok(SSL* ssl, int socket, time_t start_time, time_t end_time, uint8_t compression, uint8_t encryption, struct json* payload) { - if (write_header(ssl, fd, MANAGEMENT_DETAILS, -1)) + struct json* outcome = NULL; + + if (create_outcome_success(payload, start_time, end_time, &outcome)) + { + goto error; + } + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, payload)) { - pgagroal_log_warn("pgagroal_management_details: write: %d", fd); - errno = 0; goto error; } @@ -1073,267 +729,443 @@ pgagroal_management_details(SSL* ssl, int fd) } int -pgagroal_management_read_details(SSL* ssl, int socket, char output_format) +pgagroal_management_response_error(SSL* ssl, int socket, char* server, int32_t error, uint8_t compression, uint8_t encryption, struct json* payload) { - cJSON* json = pgagroal_management_json_read_status_details(ssl, socket, true); + int srv = -1; + struct json* response = NULL; + struct json* outcome = NULL; + struct main_configuration* config; - // check we have an answer, note that a faulty answer is still worth to be printed - if (!json) + config = (struct main_configuration*)shmem; + + if (create_outcome_failure(payload, error, &outcome)) { goto error; } - // print out the command answer - if (output_format == COMMAND_OUTPUT_FORMAT_JSON) + if (server != NULL && strlen(server) > 0) { - return pgagroal_json_print_and_free_json_object(json); + if (pgagroal_json_get(payload, MANAGEMENT_CATEGORY_RESPONSE) != 0) + { + response = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_RESPONSE); + } + else + { + for (int i = 0; i < config->number_of_servers; i++) + { + if (!strcmp(server, config->servers[i].name)) + { + srv = i; + } + } + + if (pgagroal_management_create_response(payload, srv, &response)) + { + goto error; + } + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_SERVER, (uintptr_t)server, ValueString); + } } - else + + if (pgagroal_management_write_json(ssl, socket, compression, encryption, payload)) { - return pgagroal_management_json_print_status_details(json); + goto error; } + return 0; + error: - pgagroal_log_warn("pgagroal_management_read_details: command error [%s]", - (json == NULL ? "" : pgagroal_json_get_command_object_status(json))); return 1; } int -pgagroal_management_write_details(int socket) +pgagroal_management_read_json(SSL* ssl, int socket, uint8_t* compression, uint8_t* encryption, struct json** json) { - struct main_configuration* config; - int offset = 12; - char header[offset + MAX_NUMBER_OF_CONNECTIONS]; - - config = (struct main_configuration*)shmem; + uint8_t compress_method = MANAGEMENT_COMPRESSION_NONE; + uint8_t encrypt_method = MANAGEMENT_ENCRYPTION_NONE; + char* s = NULL; + struct json* r = NULL; - memset(&header, 0, sizeof(header)); + unsigned char* transfer_buffer = NULL; + unsigned char* decoded_buffer = NULL; + unsigned char* decrypted_buffer = NULL; + char* decompressed = NULL; + size_t transfer_size = 0; + size_t decoded_size = 0; + size_t decrypted_size = 0; - pgagroal_write_int32(header, config->max_connections); - pgagroal_write_int32(header + 4, config->number_of_limits); - pgagroal_write_int32(header + 8, config->number_of_servers); + if (read_uint8("pgagroal-cli", ssl, socket, &compress_method)) + { + goto error; + } - for (int i = 0; i < config->max_connections; i++) + if (compression != NULL) { - signed char state = atomic_load(&config->states[i]); - header[offset + i] = (char)state; + *compression = compress_method; } - if (write_complete(NULL, socket, header, sizeof(header))) + if (read_uint8("pgagroal-cli", ssl, socket, &encrypt_method)) { - pgagroal_log_warn("pgagroal_management_write_details: write: %d %s", socket, strerror(errno)); - errno = 0; goto error; } - for (int i = 0; i < config->number_of_servers; i++) + if (encryption != NULL) { - char server[5 + MISC_LENGTH + MISC_LENGTH]; - - memset(&server, 0, sizeof(server)); + *encryption = encrypt_method; + } - pgagroal_write_string(server, config->servers[i].name); - pgagroal_write_string(server + MISC_LENGTH, config->servers[i].host); - pgagroal_write_int32(server + MISC_LENGTH + MISC_LENGTH, config->servers[i].port); - pgagroal_write_byte(server + MISC_LENGTH + MISC_LENGTH + 4, atomic_load(&config->servers[i].state)); + if (read_string("pgagroal-cli", ssl, socket, &s)) + { + goto error; + } - if (write_complete(NULL, socket, server, sizeof(server))) + if (compress_method || encrypt_method) + { + // First, perform decode + if (pgagroal_base64_decode(s, strlen(s), (void**)&decoded_buffer, &decoded_size) != 0) { - pgagroal_log_warn("pgagroal_management_write_details: write: %d %s", socket, strerror(errno)); - errno = 0; + pgagroal_log_error("pgagroal_management_read_json: Decoding failedg"); goto error; } - } + free(s); + s = NULL; + transfer_buffer = decoded_buffer; + transfer_size = decoded_size; + decoded_buffer = NULL; + + // Second, perform dencrypt + switch (encrypt_method) + { + case MANAGEMENT_ENCRYPTION_AES256: + if (pgagroal_decrypt_buffer(transfer_buffer, transfer_size, &decrypted_buffer, &decrypted_size, MANAGEMENT_ENCRYPTION_AES256)) + { + pgagroal_log_error("pgagroal_management_read_json: Failed to aes256 dencrypt the string"); + goto error; + } + free(transfer_buffer); + transfer_buffer = decrypted_buffer; + transfer_size = decrypted_size; + decrypted_buffer = NULL; - for (int i = 0; i < config->number_of_limits; i++) - { - char limit[16 + MAX_DATABASE_LENGTH + MAX_USERNAME_LENGTH]; + break; + case MANAGEMENT_ENCRYPTION_AES192: + if (pgagroal_decrypt_buffer(transfer_buffer, transfer_size, &decrypted_buffer, &decrypted_size, MANAGEMENT_ENCRYPTION_AES192)) + { + pgagroal_log_error("pgagroal_management_read_json: Failed to aes192 dencrypt the string"); + goto error; + } + free(transfer_buffer); + transfer_buffer = decrypted_buffer; + transfer_size = decrypted_size; + decrypted_buffer = NULL; - memset(&limit, 0, sizeof(limit)); + break; + case MANAGEMENT_ENCRYPTION_AES128: + if (pgagroal_decrypt_buffer(transfer_buffer, transfer_size, &decrypted_buffer, &decrypted_size, MANAGEMENT_ENCRYPTION_AES128)) + { + pgagroal_log_error("pgagroal_management_read_json: Failed to aes128 dencrypt the string"); + goto error; + } + free(transfer_buffer); + transfer_buffer = decrypted_buffer; + transfer_size = decrypted_size; + decrypted_buffer = NULL; - pgagroal_write_int32(limit, atomic_load(&config->limits[i].active_connections)); - pgagroal_write_int32(limit + 4, config->limits[i].max_size); - pgagroal_write_int32(limit + 8, config->limits[i].initial_size); - pgagroal_write_int32(limit + 12, config->limits[i].min_size); - pgagroal_write_string(limit + 16, config->limits[i].database); - pgagroal_write_string(limit + 16 + MAX_DATABASE_LENGTH, config->limits[i].username); + break; + default: + break; + } - if (write_complete(NULL, socket, &limit, sizeof(limit))) + // Third, perform decompress + switch (compress_method) { - pgagroal_log_warn("pgagroal_management_write_details: write: %d %s", socket, strerror(errno)); - errno = 0; - goto error; + case MANAGEMENT_COMPRESSION_GZIP: + if (pgagroal_gunzip_string(transfer_buffer, transfer_size, &decompressed)) + { + pgagroal_log_error("pgagroal_management_read_json: GZIP decompress failed"); + goto error; + } + free(transfer_buffer); + transfer_buffer = NULL; + s = decompressed; + decompressed = NULL; + break; + case MANAGEMENT_COMPRESSION_ZSTD: + if (pgagroal_zstdd_string(transfer_buffer, transfer_size, &decompressed)) + { + pgagroal_log_error("pgagroal_management_read_json: ZSTD decompress failed"); + goto error; + } + free(transfer_buffer); + transfer_buffer = NULL; + s = decompressed; + decompressed = NULL; + break; + case MANAGEMENT_COMPRESSION_LZ4: + if (pgagroal_lz4d_string(transfer_buffer, transfer_size, &decompressed)) + { + pgagroal_log_error("pgagroal_management_read_json: LZ4 decompress failed"); + goto error; + } + free(transfer_buffer); + transfer_buffer = NULL; + s = decompressed; + decompressed = NULL; + break; + case MANAGEMENT_COMPRESSION_BZIP2: + if (pgagroal_bunzip2_string(transfer_buffer, transfer_size, &decompressed)) + { + pgagroal_log_error("pgagroal_management_read_json: bzip2 decompress failed"); + goto error; + } + free(transfer_buffer); + transfer_buffer = NULL; + s = decompressed; + decompressed = NULL; + break; + default: + s = (char*) transfer_buffer; + transfer_buffer = NULL; + break; } } - for (int i = 0; i < config->max_connections; i++) + if (pgagroal_json_parse_string(s, &r)) { - char details[16 + MAX_DATABASE_LENGTH + MAX_USERNAME_LENGTH + MAX_APPLICATION_NAME]; - - memset(&details, 0, sizeof(details)); - - pgagroal_write_long(details, (long)config->connections[i].start_time); - pgagroal_write_long(details, (long)config->connections[i].timestamp); - pgagroal_write_int32(details + 8, (int)config->connections[i].pid); - pgagroal_write_int32(details + 12, (int)config->connections[i].fd); + goto error; + } - pgagroal_write_string(details + 16, config->connections[i].database); - pgagroal_write_string(details + 16 + MAX_DATABASE_LENGTH, config->connections[i].username); - pgagroal_write_string(details + 16 + MAX_DATABASE_LENGTH + MAX_USERNAME_LENGTH, config->connections[i].appname); + *json = r; - if (write_complete(NULL, socket, &details, sizeof(details))) - { - pgagroal_log_warn("pgagroal_management_write_details: write: %d %s", socket, strerror(errno)); - errno = 0; - goto error; - } - } + free(s); return 0; error: - return 1; -} + pgagroal_json_destroy(r); -int -pgagroal_management_isalive(SSL* ssl, int fd) -{ - if (write_header(ssl, fd, MANAGEMENT_ISALIVE, -1)) + if (s != NULL) { - pgagroal_log_warn("pgagroal_management_isalive: write: %d", fd); - errno = 0; - goto error; + free(s); + } + if (transfer_buffer != NULL) + { + free(transfer_buffer); + } + if (decoded_buffer != NULL) + { + free(decoded_buffer); + } + if (decrypted_buffer != NULL) + { + free(decrypted_buffer); + } + if (decompressed != NULL) + { + free(decompressed); } - - return 0; - -error: return 1; } int -pgagroal_management_read_isalive(SSL* ssl, int socket, int* status, char output_format) +pgagroal_management_write_json(SSL* ssl, int socket, uint8_t compression, uint8_t encryption, struct json* json) { - char buf[MANAGEMENT_INFO_SIZE + 4]; - int application; - int version; + char* s = NULL; + + unsigned char* transfer_buffer = NULL; + unsigned char* compressed_buffer = NULL; + unsigned char* encrypted_buffer = NULL; + char* encoded = NULL; + size_t transfer_size = 0; + size_t compressed_size = 0; + size_t encrypted_size = 0; + size_t encoded_size = 0; - memset(&buf, 0, sizeof(buf)); + s = pgagroal_json_to_string(json, FORMAT_JSON_COMPACT, NULL, 0); - if (read_complete(ssl, socket, &buf[0], sizeof(buf))) + if (write_uint8("pgagroal-cli", ssl, socket, compression)) { - pgagroal_log_warn("pgagroal_management_read_isalive: read: %d %s", socket, strerror(errno)); - errno = 0; goto error; } - application = pgagroal_read_int32(&(buf[2])); - version = pgagroal_read_int32(&(buf[9])); - - *status = pgagroal_read_int32(&buf[MANAGEMENT_INFO_SIZE]); - - // do I need to provide JSON output? - if (output_format == COMMAND_OUTPUT_FORMAT_JSON) + if (write_uint8("pgagroal-cli", ssl, socket, encryption)) { - char* version_buf = NULL; + goto error; + } - if (pgagroal_executable_version_string(&version_buf, version)) + if (compression || encryption) + { + // First, perform compress + switch (compression) { - goto error; + case MANAGEMENT_COMPRESSION_GZIP: + if (pgagroal_gzip_string(s, &compressed_buffer, &compressed_size)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to gzip the string"); + goto error; + } + transfer_buffer = compressed_buffer; + transfer_size = compressed_size; + + free(s); + compressed_buffer = NULL; + s = NULL; + break; + case MANAGEMENT_COMPRESSION_ZSTD: + if (pgagroal_zstdc_string(s, &compressed_buffer, &compressed_size)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to zstd the string"); + goto error; + } + transfer_buffer = compressed_buffer; + transfer_size = compressed_size; + + free(s); + compressed_buffer = NULL; + s = NULL; + break; + case MANAGEMENT_COMPRESSION_LZ4: + if (pgagroal_lz4c_string(s, &compressed_buffer, &compressed_size)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to lz4 the string"); + goto error; + } + transfer_buffer = compressed_buffer; + transfer_size = compressed_size; + + free(s); + compressed_buffer = NULL; + s = NULL; + break; + case MANAGEMENT_COMPRESSION_BZIP2: + if (pgagroal_bzip2_string(s, &compressed_buffer, &compressed_size)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to bzip2 the string"); + goto error; + } + transfer_buffer = compressed_buffer; + transfer_size = compressed_size; + + free(s); + compressed_buffer = NULL; + s = NULL; + break; + default: + transfer_buffer = (unsigned char*)s; + transfer_size = strlen(s); + s = NULL; + break; } - cJSON* json = pgagroal_json_create_new_command_object("ping", true, pgagroal_executable_name(application), version_buf); - cJSON* output = pgagroal_json_extract_command_output_object(json); + // Second, perform encrypt + switch (encryption) + { + case MANAGEMENT_ENCRYPTION_AES256: + if (pgagroal_encrypt_buffer(transfer_buffer, transfer_size, &encrypted_buffer, &encrypted_size, MANAGEMENT_ENCRYPTION_AES256)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to aes256 encrypt the string"); + goto error; + } + free(transfer_buffer); + transfer_buffer = encrypted_buffer; + transfer_size = encrypted_size; + encrypted_buffer = NULL; - cJSON_AddNumberToObject(output, "status", *status); + break; + case MANAGEMENT_ENCRYPTION_AES192: + if (pgagroal_encrypt_buffer(transfer_buffer, transfer_size, &encrypted_buffer, &encrypted_size, MANAGEMENT_ENCRYPTION_AES192)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to aes192 encrypt the string"); + goto error; + } + free(transfer_buffer); + transfer_buffer = encrypted_buffer; + transfer_size = encrypted_size; + encrypted_buffer = NULL; - if (*status == PING_STATUS_RUNNING) - { - cJSON_AddStringToObject(output, "message", "running"); - } - else if (*status == PING_STATUS_SHUTDOWN_GRACEFULLY) - { - cJSON_AddStringToObject(output, "message", "shutdown gracefully"); + break; + case MANAGEMENT_ENCRYPTION_AES128: + if (pgagroal_encrypt_buffer(transfer_buffer, transfer_size, &encrypted_buffer, &encrypted_size, MANAGEMENT_ENCRYPTION_AES128)) + { + pgagroal_log_error("pgagroal_management_write_json: Failed to aes128 encrypt the string"); + goto error; + } + free(transfer_buffer); + transfer_buffer = encrypted_buffer; + transfer_size = encrypted_size; + encrypted_buffer = NULL; + + break; + default: + break; } - else + + // Third, perform base64 encode + if (pgagroal_base64_encode(transfer_buffer, transfer_size, &encoded, &encoded_size) != 0) { - cJSON_AddStringToObject(output, "message", "unknown"); + pgagroal_log_error("pgagroal_management_write_json: Encoding failed"); + goto error; } - return pgagroal_json_print_and_free_json_object(json); + free(transfer_buffer); + s = encoded; + encoded = NULL; + } + if (write_string("pgagroal-cli", ssl, socket, s)) + { + goto error; } + free(s); + return 0; error: - - return 1; -} - -int -pgagroal_management_write_isalive(int socket, bool gracefully) -{ - char buf[4]; - char buf_info[MANAGEMENT_INFO_SIZE]; - - if (write_info(buf_info, PGAGROAL_EXECUTABLE, 0)) + if (s != NULL) { - goto error; + free(s); } - - if (write_complete(NULL, socket, &buf_info, sizeof(buf_info))) + if (transfer_buffer != NULL) { - pgagroal_log_warn("pgagroal_management_write_isalive: write: %d %s", socket, strerror(errno)); - errno = 0; - goto error; + free(transfer_buffer); } - - memset(&buf, 0, sizeof(buf)); - - if (!gracefully) + if (compressed_buffer != NULL) { - pgagroal_write_int32(buf, PING_STATUS_RUNNING); + free(compressed_buffer); } - else + if (encrypted_buffer != NULL) { - pgagroal_write_int32(buf, PING_STATUS_SHUTDOWN_GRACEFULLY); + free(encrypted_buffer); } - - if (write_complete(NULL, socket, &buf, sizeof(buf))) + if (encoded != NULL) { - pgagroal_log_warn("pgagroal_management_write_isalive: write: %d %s", socket, strerror(errno)); - errno = 0; - goto error; + free(encoded); } - return 0; - -error: - return 1; } -int -pgagroal_management_write_get_password(SSL* ssl, int socket, char* password) +static int +read_uint8(char* prefix, SSL* ssl, int socket, uint8_t* i) { - char buffer[MAX_PASSWORD_LENGTH + 4]; // first 4 bytes contains the length of the password - memset(buffer, 0, sizeof(buffer)); + char buf1[1] = {0}; - pgagroal_write_int32(&buffer, strlen(password)); - memcpy(buffer + 4, password, strlen(password)); + *i = 0; - if (write_complete(ssl, socket, buffer, strlen(password) + 4)) + if (read_complete(ssl, socket, &buf1[0], sizeof(buf1))) { - pgagroal_log_warn("pgagroal_management_write_get_password: write: %d %s\n", socket, strerror(errno)); + pgagroal_log_warn("%s: read_byte: %p %d %s", prefix, ssl, socket, strerror(errno)); errno = 0; goto error; } + *i = pgagroal_read_uint8(&buf1); + return 0; error: @@ -1341,41 +1173,62 @@ pgagroal_management_write_get_password(SSL* ssl, int socket, char* password) return 1; } -int -pgagroal_management_reset(SSL* ssl, int fd) +static int +read_string(char* prefix, SSL* ssl, int socket, char** str) { - if (write_header(ssl, fd, MANAGEMENT_RESET, -1)) + char* s = NULL; + char buf4[4] = {0}; + uint32_t size; + + *str = NULL; + + if (read_complete(ssl, socket, &buf4[0], sizeof(buf4))) { - pgagroal_log_warn("pgagroal_management_reset: write: %d", fd); + pgagroal_log_warn("%s: read_string: %p %d %s", prefix, ssl, socket, strerror(errno)); errno = 0; goto error; } + size = pgagroal_read_uint32(&buf4); + if (size > 0) + { + s = malloc(size + 1); + + if (s == NULL) + { + goto error; + } + + memset(s, 0, size + 1); + + if (read_complete(ssl, socket, s, size)) + { + pgagroal_log_warn("%s: read_string: %p %d %s", prefix, ssl, socket, strerror(errno)); + errno = 0; + goto error; + } + + *str = s; + } + return 0; error: + free(s); + return 1; } -int -pgagroal_management_reset_server(SSL* ssl, int fd, char* server) +static int +write_uint8(char* prefix, SSL* ssl, int socket, uint8_t i) { - char name[MISC_LENGTH]; + char buf1[1] = {0}; - if (write_header(ssl, fd, MANAGEMENT_RESET_SERVER, -1)) + pgagroal_write_uint8(&buf1, i); + if (write_complete(ssl, socket, &buf1, sizeof(buf1))) { - pgagroal_log_warn("pgagroal_management_reset_server: write: %d", fd); - errno = 0; - goto error; - } - - memset(&name[0], 0, MISC_LENGTH); - memcpy(&name[0], server, strlen(server)); - - if (write_complete(ssl, fd, &name[0], sizeof(name))) - { - pgagroal_log_warn("pgagroal_management_reset_server_: write: %d %s", fd, strerror(errno)); + pgagroal_log_warn("%s: write_string: %p %d %s", prefix, ssl, socket, strerror(errno)); errno = 0; goto error; } @@ -1387,249 +1240,66 @@ pgagroal_management_reset_server(SSL* ssl, int fd, char* server) return 1; } -int -pgagroal_management_client_done(pid_t pid) +static int +write_string(char* prefix, SSL* ssl, int socket, char* str) { - char buf[4]; - int fd; - struct main_configuration* config; - - config = (struct main_configuration*)shmem; - - if (pgagroal_connect_unix_socket(config->unix_socket_dir, MAIN_UDS, &fd)) - { - pgagroal_log_warn("pgagroal_management_client_done: connect: %d", fd); - errno = 0; - goto error; - } + char buf4[4] = {0}; - if (write_header(NULL, fd, MANAGEMENT_CLIENT_DONE, -1)) + pgagroal_write_uint32(&buf4, str != NULL ? strlen(str) : 0); + if (write_complete(ssl, socket, &buf4, sizeof(buf4))) { - pgagroal_log_warn("pgagroal_management_client_done: write: %d", fd); + pgagroal_log_warn("%s: write_string: %p %d %s", prefix, ssl, socket, strerror(errno)); errno = 0; goto error; } - memset(&buf, 0, sizeof(buf)); - pgagroal_write_int32(buf, pid); - - if (write_complete(NULL, fd, &buf, sizeof(buf))) + if (str != NULL) { - pgagroal_log_warn("pgagroal_management_client_done: write: %d %s", fd, strerror(errno)); - errno = 0; - goto error; + if (write_complete(ssl, socket, str, strlen(str))) + { + pgagroal_log_warn("%s: write_string: %p %d %s", prefix, ssl, socket, strerror(errno)); + errno = 0; + goto error; + } } - pgagroal_disconnect(fd); - return 0; error: - pgagroal_disconnect(fd); return 1; } -int -pgagroal_management_client_fd(int32_t slot, pid_t pid) +static int +read_complete(SSL* ssl, int socket, void* buf, size_t size) { - char p[MISC_LENGTH]; - int fd; - struct main_configuration* config; - struct cmsghdr* cmptr = NULL; - struct iovec iov[1]; - struct msghdr msg; - char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */ - - config = (struct main_configuration*)shmem; + ssize_t r; + size_t offset; + size_t needs; + int retries; - memset(&p, 0, sizeof(p)); - snprintf(&p[0], sizeof(p), ".s.%d", pid); + offset = 0; + needs = size; + retries = 0; - if (pgagroal_connect_unix_socket(config->unix_socket_dir, &p[0], &fd)) +read: + if (ssl == NULL) { - pgagroal_log_debug("pgagroal_management_client_fd: connect: %d", fd); - errno = 0; - goto unavailable; + r = read(socket, buf + offset, needs); } - - if (write_header(NULL, fd, MANAGEMENT_CLIENT_FD, slot)) + else { - pgagroal_log_warn("pgagroal_management_client_fd: write: %d", fd); - errno = 0; - goto error; + r = SSL_read(ssl, buf + offset, needs); } - /* Write file descriptor */ - iov[0].iov_base = buf; - iov[0].iov_len = 2; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - - cmptr = malloc(CMSG_LEN(sizeof(int))); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - cmptr->cmsg_len = CMSG_LEN(sizeof(int)); - msg.msg_control = cmptr; - msg.msg_controllen = CMSG_LEN(sizeof(int)); - *(int*)CMSG_DATA(cmptr) = config->connections[slot].fd; - buf[1] = 0; /* zero status means OK */ - buf[0] = 0; /* null byte flag to recv_fd() */ - - if (sendmsg(fd, &msg, 0) != 2) + if (r == -1) { - goto error; - } - - free(cmptr); - pgagroal_disconnect(fd); - - return 0; - -unavailable: - free(cmptr); - pgagroal_disconnect(fd); - - return 1; - -error: - free(cmptr); - pgagroal_disconnect(fd); - pgagroal_kill_connection(slot, NULL); - - return 1; -} - -int -pgagroal_management_switch_to(SSL* ssl, int fd, char* server) -{ - char name[MISC_LENGTH]; - - if (write_header(ssl, fd, MANAGEMENT_SWITCH_TO, -1)) - { - pgagroal_log_warn("pgagroal_management_switch_to: write: %d", fd); - errno = 0; - goto error; - } - - memset(&name[0], 0, MISC_LENGTH); - memcpy(&name[0], server, strlen(server)); - - if (write_complete(ssl, fd, &name[0], sizeof(name))) - { - pgagroal_log_warn("pgagroal_management_switch_to: write: %d %s", fd, strerror(errno)); - errno = 0; - goto error; - } - - return 0; - -error: - - return 1; -} - -int -pgagroal_management_reload(SSL* ssl, int fd) -{ - if (write_header(ssl, fd, MANAGEMENT_RELOAD, -1)) - { - pgagroal_log_warn("pgagroal_management_reload: write: %d", fd); - errno = 0; - goto error; - } - - return 0; - -error: - - return 1; -} - -int -pgagroal_management_remove_fd(int32_t slot, int socket, pid_t pid) -{ - char p[MISC_LENGTH]; - int fd; - char buf[4]; - struct main_configuration* config; - - config = (struct main_configuration*)shmem; - - if (atomic_load(&config->states[slot]) == STATE_NOTINIT) - { - return 0; - } - - memset(&p, 0, sizeof(p)); - snprintf(&p[0], sizeof(p), ".s.%d", pid); - - if (pgagroal_connect_unix_socket(config->unix_socket_dir, &p[0], &fd)) - { - pgagroal_log_debug("pgagroal_management_remove_fd: slot %d state %d database %s user %s socket %d pid %d connect: %d", - slot, atomic_load(&config->states[slot]), - config->connections[slot].database, config->connections[slot].username, socket, pid, fd); - errno = 0; - goto error; - } - - if (write_header(NULL, fd, MANAGEMENT_REMOVE_FD, slot)) - { - pgagroal_log_warn("pgagroal_management_remove_fd: write: %d", fd); - errno = 0; - goto error; - } - - pgagroal_write_int32(&buf, socket); - if (write_complete(NULL, fd, &buf, sizeof(buf))) - { - pgagroal_log_warn("pgagroal_management_remove_fd: write: %d %s", fd, strerror(errno)); - errno = 0; - goto error; - } - - pgagroal_disconnect(fd); - - return 0; - -error: - pgagroal_disconnect(fd); - - return 1; -} - -static int -read_complete(SSL* ssl, int socket, void* buf, size_t size) -{ - ssize_t r; - size_t offset; - size_t needs; - int retries; - - offset = 0; - needs = size; - retries = 0; - -read: - if (ssl == NULL) - { - r = read(socket, buf + offset, needs); - } - else - { - r = SSL_read(ssl, buf + offset, needs); - } - - if (r == -1) - { - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - errno = 0; - goto read; - } - + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + errno = 0; + goto read; + } + goto error; } else if (r < needs) @@ -1688,7 +1358,7 @@ write_socket(int socket, void* buf, size_t size) { numbytes = write(socket, buf + offset, remaining); - if (likely(numbytes == size)) + if (likely(numbytes == (ssize_t)size)) { return 0; } @@ -1698,7 +1368,7 @@ write_socket(int socket, void* buf, size_t size) totalbytes += numbytes; remaining -= numbytes; - if (totalbytes == size) + if (totalbytes == (ssize_t)size) { return 0; } @@ -1806,1333 +1476,3 @@ write_ssl(SSL* ssl, void* buf, size_t size) return 1; } - -/** - * Function to write sender application name and its version - * into header socket. - * - * @param header: the header to write sender application name and its version into - * @param command: the server application name - * @param offset: the offset at which info has to be written - * @return 0 on success - */ -static int -write_info(char* buffer, int command, int offset) -{ - int exec_version = pgagroal_executable_version_number(PGAGROAL_VERSION, sizeof(PGAGROAL_VERSION)); - if (exec_version == 1) - { - return 1; - } - - char* exec_name = pgagroal_executable_name(command); - if (exec_name == NULL) - { - return 1; - } - - struct pgagroal_version_info info = { - .command = command, - .version = exec_version, - }; - - memcpy(&(info.s), S, sizeof(info.s)); - memcpy(&(info.v), V, sizeof(info.v)); - - memcpy(&(buffer[offset]), info.s, sizeof(info.s)); - pgagroal_write_int32(&(buffer[offset + 2]), info.command); - - memcpy(&(buffer[offset + 6]), info.v, sizeof(info.v)); - pgagroal_write_int32(&(buffer[offset + 9]), info.version); - - pgagroal_log_debug("%s version %d", exec_name, exec_version); - - return 0; -} - -/* - * Utility function to convert PGAGROAL_VERSION into a number. - * The major version is represented by a single digit. - * For minor and patch, a leading 0 is added if they are single digits. - */ -static int -pgagroal_executable_version_number(char* version, size_t version_size) -{ - int major; - int minor; - int patch; - - long val; - - char* del = "."; - char* endptr; - - if (version == NULL) - { - version = PGAGROAL_VERSION; - version_size = sizeof(version); - } - - char buf[version_size]; - - memcpy(buf, version, sizeof(buf)); - - char* token = strtok(buf, del); - val = strtol(token, &endptr, 10); - if (errno == ERANGE || val <= LONG_MIN || val >= LONG_MAX) - { - goto error; - } - major = (int)val; - - token = strtok(NULL, del); - val = strtol(token, &endptr, 10); - if (errno == ERANGE || val <= LONG_MIN || val >= LONG_MAX) - { - goto error; - } - minor = (int)val; - - token = strtok(NULL, del); - val = strtol(token, &endptr, 10); - if (errno == ERANGE || val <= LONG_MIN || val >= LONG_MAX) - { - goto error; - } - patch = (int)val; - - int version_number = (major % 10) * 10000 + (minor / 10) * 1000 + (minor % 10) * 100 + patch; - - if (version_number < INT_MIN || version_number > INT_MAX || version_number < 10700) - { - goto error; - } - - return version_number; - -error: - pgagroal_log_debug("pgagroal_get_executable_number got overflowed or suspicious value: %s %s", version, strerror(errno)); - errno = 0; - return 1; -} - -/* - * Utility function to convert back version_number into a string. - */ -static int -pgagroal_executable_version_string(char** version_string, int version_number) -{ - char* v = NULL; - int major = version_number / 10000; - int minor = (version_number / 100) % 100; - int patch = version_number % 100; - - v = pgagroal_append_int(v, major); - v = pgagroal_append(v, "."); - v = pgagroal_append_int(v, minor); - v = pgagroal_append(v, "."); - v = pgagroal_append_int(v, patch); - - *version_string = v; - - return 0; -} - -/* - * Utility function to convert command into a string. - */ -static char* -pgagroal_executable_name(int command) -{ - switch (command) - { - case PGAGROAL_EXECUTABLE: - return "pgagroal"; - case PGAGROAL_EXECUTABLE_CLI: - return "pgagroal-cli"; - case PGAGROAL_EXECUTABLE_VAULT: - return "pgagroal-vault"; - default: - pgagroal_log_debug("pgagroal_get_command_name got unexpected value: %d", command); - return NULL; - } -} - -static int -write_header(SSL* ssl, int fd, signed char type, int slot) -{ - char header[MANAGEMENT_HEADER_SIZE + MANAGEMENT_INFO_SIZE]; - - pgagroal_write_byte(&(header), type); - pgagroal_write_int32(&(header[1]), slot); - - if (write_info(header, PGAGROAL_EXECUTABLE_CLI, MANAGEMENT_HEADER_SIZE)) - { - return 1; - } - - return write_complete(ssl, fd, &(header), sizeof(header)); -} - -int -pgagroal_management_config_get(SSL* ssl, int socket, char* config_key) -{ - char buf[4]; - size_t size; - - // security check: avoid writing something null or with too much stuff! - if (!config_key || !strlen(config_key)) - { - pgagroal_log_debug("pgagroal_management_config_get: no key specified"); - goto error; - } - - size = strlen(config_key) + 1; - if (size > MISC_LENGTH) - { - pgagroal_log_debug("pgagroal_management_config_get: key <%s> too big (%d bytes)", config_key, size); - goto error; - } - - // send the header for this command - if (write_header(ssl, socket, MANAGEMENT_CONFIG_GET, -1)) - { - pgagroal_log_debug("pgagroal_management_config_get: write error on header for key <%s> on socket %d", config_key, socket); - goto error; - } - - // send the size of the payload - memset(&buf, 0, sizeof(buf)); - pgagroal_write_int32(&buf, size); - if (write_complete(ssl, socket, &buf, sizeof(buf))) - { - pgagroal_log_debug("pgagroal_management_config_get: write error for the size of the payload (%d bytes for <%s>, socket %d): %s", - size, - config_key, - socket, - strerror(errno)); - goto error; - } - - // send the effective payload, i.e., the configuration parameter name to get - memset(&buf, 0, sizeof(buf)); - - if (write_complete(ssl, socket, config_key, size)) - { - pgagroal_log_debug("pgagroal_management_config_get: write error sending the configuration name <%s> over socket %d: %s", config_key, socket, strerror(errno)); - goto error; - } - - return 0; - -error: - errno = 0; - return 1; -} - -int -pgagroal_management_write_config_get(int socket, char* config_key) -{ - char data[MISC_LENGTH]; - char buf[4]; - char buf_info[MANAGEMENT_INFO_SIZE]; - - size_t size; - - if (write_info(buf_info, PGAGROAL_EXECUTABLE, 0)) - { - goto error; - } - - if (write_complete(NULL, socket, buf_info, sizeof(buf_info))) - { - pgagroal_log_debug("pgagroal_management_config_get: write: %d %s", socket, strerror(errno)); - goto error; - } - - if (!config_key || !strlen(config_key)) - { - pgagroal_log_debug("pgagroal_management_write_config_get: no key specified"); - goto error; - } - - size = strlen(config_key) + 1; - if (size > MISC_LENGTH) - { - pgagroal_log_debug("pgagroal_management_write_config_get: key <%s> too big (%d bytes)", config_key, size); - goto error; - } - - memset(&data, 0, sizeof(data)); - - if (pgagroal_write_config_value(&data[0], config_key, sizeof(data))) - { - pgagroal_log_debug("pgagroal_management_write_config_get: unknwon configuration key <%s>", config_key); - // leave the payload empty, so a zero filled payload will be sent - } - - // send the size of the payload - memset(&buf, 0, sizeof(buf)); - size = strlen(data) + 1; - pgagroal_write_int32(&buf, size); - if (write_complete(NULL, socket, &buf, sizeof(buf))) - { - pgagroal_log_debug("pgagroal_management_write_config_get: write error for the size of the payload <%s> (%d bytes for <%s>, socket %d): %s", - data, - size, - config_key, - socket, - strerror(errno)); - goto error; - } - - if (write_complete(NULL, socket, data, size)) - { - pgagroal_log_debug("pgagroal_management_write_config_get (%s): write: %d %s", config_key, socket, strerror(errno)); - goto error; - } - - return 0; - -error: - errno = 0; - return 1; - -} - -/** - * Utility method to wrap the answer about a configuration setting - * into a JSON object. - * - * @param socket the socket from which reading the data from - * @param config_key the key requested, used only to populate the json - * @param expected_value the config value expected in the case of a `config set`. - * If the expetced_value is not null, the function checks if the obtained config value and - * the expected one are equal, and in case are not set the JSON object as faulty. - * - * @return the JSON object - */ -static cJSON* -pgagroal_managment_json_read_config_get(int socket, char* config_key, char* expected_value) -{ - char buf_info[MANAGEMENT_INFO_SIZE]; - int version; - int application; - - if (read_complete(NULL, socket, &buf_info[0], sizeof(buf_info))) - { - pgagroal_log_debug("pgagroal_management_write_config_get (%s): write: %d %s", config_key, socket, strerror(errno)); - errno = 0; - return NULL; - } - - int size = MISC_LENGTH; - char* buffer = NULL; - bool is_config_set = false; - - buffer = calloc(1, size); - if (buffer == NULL) - { - goto error; - } - - if (pgagroal_management_read_payload(socket, MANAGEMENT_CONFIG_GET, &size, &buffer)) - { - goto error; - } - - // is this the answer from a 'conf set' command ? - is_config_set = (expected_value && strlen(expected_value) > 0); - - application = pgagroal_read_int32(&(buf_info[2])); - version = pgagroal_read_int32(&(buf_info[9])); - - char* version_buf = NULL; - - if (pgagroal_executable_version_string(&version_buf, version)) - { - goto error; - } - - cJSON* json = pgagroal_json_create_new_command_object(is_config_set ? "conf set" : "conf get", true, pgagroal_executable_name(application), version_buf); - cJSON* output = pgagroal_json_extract_command_output_object(json); - - cJSON_AddStringToObject(output, "key", config_key); - cJSON_AddStringToObject(output, "value", buffer); - - if (is_config_set) - { - cJSON_AddStringToObject(output, "expected", expected_value); - - // if the expected value is not what we get, this means there is an error - // (e.g., cannot apply the config set) - if (strncmp(buffer, expected_value, size)) - { - pgagroal_json_set_command_object_faulty(json, "Current and expected values are different", EXIT_STATUS_DATA_ERROR); - } - } - - free(buffer); - return json; -error: - free(buffer); - return NULL; -} - -int -pgagroal_management_read_config_get(int socket, char* config_key, char* expected_value, bool verbose, char output_format) -{ - cJSON* json = pgagroal_managment_json_read_config_get(socket, config_key, expected_value); - int status = EXIT_STATUS_OK; - - if (!json) - { - goto error; - } - - // extract the command status - status = pgagroal_json_command_object_exit_status(json); - - if (output_format == COMMAND_OUTPUT_FORMAT_JSON) - { - pgagroal_json_print_and_free_json_object(json); - json = NULL; - goto end; - } - - // if here, print out in text format - cJSON* output = pgagroal_json_extract_command_output_object(json); - cJSON* value = cJSON_GetObjectItemCaseSensitive(output, "value"); - cJSON* key = cJSON_GetObjectItemCaseSensitive(output, "key"); - if (verbose) - { - printf("%s = %s\n", key->valuestring, value->valuestring); - } - else - { - printf("%s\n", value->valuestring); - } - - goto end; - -error: - - pgagroal_log_warn("pgagroal_management_read_config_get : error retrieving configuration for <%s> : %s", config_key, strerror(errno)); - errno = 0; - status = EXIT_STATUS_DATA_ERROR; -end: - if (json) - { - cJSON_Delete(json); - } - - return status; -} - -int -pgagroal_management_config_set(SSL* ssl, int socket, char* config_key, char* config_value) -{ - char buf[4]; - size_t size; - - // security check: avoid writing something null or with too much stuff! - if (!config_key || !strlen(config_key) || !config_value || !strlen(config_value)) - { - pgagroal_log_debug("pgagroal_management_config_set: no key or value specified"); - goto error; - } - - if (strlen(config_key) > MISC_LENGTH - 1 || strlen(config_value) > MISC_LENGTH - 1) - { - pgagroal_log_debug("pgagroal_management_config_set: key <%s> or value <%s> too big (max %d bytes)", config_key, config_value, MISC_LENGTH); - goto error; - } - - // send the header for this command - if (write_header(ssl, socket, MANAGEMENT_CONFIG_SET, -1)) - { - pgagroal_log_debug("pgagroal_management_config_set: write error on header for key <%s> on socket %d", config_key, socket); - goto error; - } - - /* - * send a message with the size of the key, the key - * then the size of the value and the value - */ - - // send the size of the payload for the config key - memset(&buf, 0, sizeof(buf)); - size = strlen(config_key) + 1; - pgagroal_write_int32(&buf, size); - if (write_complete(ssl, socket, &buf, sizeof(buf))) - { - pgagroal_log_debug("pgagroal_management_config_set: write error for the size of the payload (%d bytes for <%s>, socket %d): %s", - size, - config_key, - socket, - strerror(errno)); - goto error; - } - - // send the effective payload, i.e., the configuration parameter name to get - memset(&buf, 0, sizeof(buf)); - - if (write_complete(ssl, socket, config_key, size)) - { - pgagroal_log_debug("pgagroal_management_config_set: write error sending the configuration name <%s> over socket %d: %s", config_key, socket, strerror(errno)); - goto error; - } - - // send the size of the payload for the config value - memset(&buf, 0, sizeof(buf)); - size = strlen(config_value) + 1; - pgagroal_write_int32(&buf, size); - if (write_complete(ssl, socket, &buf, sizeof(buf))) - { - pgagroal_log_debug("pgagroal_management_config_set: write error for the size of the payload (%d bytes for <%s>, socket %d): %s", - size, - config_value, - socket, - strerror(errno)); - goto error; - } - - // send the effective payload, i.e., the configuration value to set - memset(&buf, 0, sizeof(buf)); - - if (write_complete(ssl, socket, config_value, size)) - { - pgagroal_log_warn("pgagroal_management_config_set: write error sending the configuration value <%s> over socket %d: %s", config_value, socket, strerror(errno)); - goto error; - } - - return 0; - -error: - errno = 0; - return 1; -} - -int -pgagroal_management_write_config_set(int socket, char* config_key, char* config_value) -{ - if (!config_key || !strlen(config_key) || !config_value || !strlen(config_value)) - { - pgagroal_log_warn("pgagroal_management_write_config_set: no key or value specified"); - goto error; - } - - if (strlen(config_key) > MISC_LENGTH - 1 || strlen(config_value) > MISC_LENGTH - 1) - { - pgagroal_log_warn("pgagroal_management_write_config_set: key <%s> or value <%s> too big (max %d bytes)", config_key, config_value, MISC_LENGTH); - goto error; - } - - pgagroal_log_debug("pgagroal_management_write_config_set: trying to set <%s> to <%s>", config_key, config_value); - - // do set the configuration value - if (pgagroal_apply_configuration(config_key, config_value)) - { - pgagroal_log_debug("pgagroal_management_write_config_set: unable to apply changes to <%s> -> <%s>", config_key, config_value); - } - - // query back the status of the parameter - // and send it over the socket - return pgagroal_management_write_config_get(socket, config_key); - -error: - errno = 0; - return 1; - -} - -int -pgagroal_management_conf_ls(SSL* ssl, int fd) -{ - if (write_header(ssl, fd, MANAGEMENT_CONFIG_LS, -1)) - { - pgagroal_log_warn("pgagroal_management_conf_ls: write: %d", fd); - errno = 0; - goto error; - } - - return 0; - -error: - - return 1; -} - -int -pgagroal_management_read_conf_ls(SSL* ssl, int socket, char output_format) -{ - - // get the JSON output - cJSON* json = pgagroal_management_json_read_conf_ls(ssl, socket); - - // check we have an answer and it is not an error - if (!json) - { - goto error; - } - - // print out the command answer - if (output_format == COMMAND_OUTPUT_FORMAT_JSON) - { - return pgagroal_json_print_and_free_json_object(json); - } - else - { - return pgagroal_management_json_print_conf_ls(json); - } - -error: - pgagroal_log_warn("pgagroal_management_read_conf_ls: read: %d %s", socket, strerror(errno)); - errno = 0; - - return 1; -} - -int -pgagroal_management_write_conf_ls(int socket) -{ - struct main_configuration* config; - - config = (struct main_configuration*)shmem; - - char buf_info[MANAGEMENT_INFO_SIZE]; - if (write_info(buf_info, PGAGROAL_EXECUTABLE, 0)) - { - goto error; - } - - if (write_complete(NULL, socket, &buf_info, sizeof(buf_info))) - { - goto error; - } - - if (pgagroal_management_write_conf_ls_detail(socket, config->common.configuration_path)) - { - goto error; - } - - if (pgagroal_management_write_conf_ls_detail(socket, config->hba_path)) - { - goto error; - } - - if (pgagroal_management_write_conf_ls_detail(socket, config->limit_path)) - { - goto error; - } - - // 4 - if (pgagroal_management_write_conf_ls_detail(socket, config->frontend_users_path)) - { - goto error; - } - //5 - if (pgagroal_management_write_conf_ls_detail(socket, config->admins_path)) - { - goto error; - } - //6 - if (pgagroal_management_write_conf_ls_detail(socket, config->superuser_path)) - { - goto error; - } - // 7 - if (pgagroal_management_write_conf_ls_detail(socket, config->users_path)) - { - goto error; - } - - return 0; - -error: - pgagroal_log_debug("pgagroal_management_write_conf_ls: error writing out file paths"); - return 1; -} - -/** - * Utility function to write a single configuration path to the socket. - * - * @param socket the file descriptor of the open socket - * @param what the pointer to the path to send out on the socket. It cannot - * exceed in size MAX_PATH - 1. - * @returns 0 on success - */ -static int -pgagroal_management_write_conf_ls_detail(int socket, char* what) -{ - char buf[4]; - size_t size = 0; - char data[MAX_PATH]; - - if (what && strlen(what) > MAX_PATH) - { - goto error; - } - - memset(&buf, 0, sizeof(buf)); - memset(&data, 0, sizeof(data)); - - size = what ? strlen(what) + 1 : 0; - if (size > MAX_PATH) - { - errno = EMSGSIZE; - goto error; - } - - pgagroal_write_int32(&buf, size); - - if (write_complete(NULL, socket, &buf, sizeof(buf))) - { - goto error; - } - - memcpy(&data[0], what, size); - if (write_complete(NULL, socket, data, size)) - { - goto error; - } - - pgagroal_log_trace("pgagroal_management_write_conf_ls_deail: writing <%s> with %d bytes", what, size); - return 0; - -error: - pgagroal_log_debug("pgagroal_management_write_conf_ls_detail: error %d %s", errno, strerror(errno)); - errno = 0; - return 1; -} - -/** - * Utility function to read back from the socket a configuration path. - * - * It does zero fill the buffer pointed by its argument, so - * it is safe to call this function with a prefilled buffer, but its content - * will be lost. - * - * The buffer will be considered able to store MAX_PATH bytes. - * - * @param socket the file descriptor of the open socket - * @param buffer an already allocated buffer where to place the read value. Only - * MAX_PATH bytes will be read out of socket. - * @return 0 on success - */ -static int -pgagroal_management_read_conf_ls_detail(SSL* ssl, int socket, char* buffer) -{ - char buf[4]; - int size = 0; - - memset(&buf, 0, sizeof(buf)); - memset(buffer, 0, MAX_PATH); - - if (read_complete(ssl, socket, &buf[0], sizeof(buf))) - { - goto error; - } - - size = pgagroal_read_int32(&buf); - - if (size > MAX_PATH) - { - errno = EMSGSIZE; - goto error; - } - - if (read_complete(ssl, socket, buffer, size)) - { - goto error; - } - - return 0; - -error: - memset(buffer, 0, MAX_PATH); - pgagroal_log_warn("pgagroal_management_read_conf_ls_detail: read: %d %s", socket, strerror(errno)); - errno = 0; - - return 1; -} - -/** - * Utility function to print out the result of a 'status' - * or a 'status details' command already wrapped into a - * JSON object. - * The function tries to understand from the command name - * within the JSON object if the output refers to the - * 'status' or 'status details' command. - * - * If the command is faulty, this method does nothing, therefore - * printing out information about faulty commands has to be done - * at an higher level. - * - * @param json the JSON object - * - * @returns 0 on success - */ -int -pgagroal_management_json_print_status_details(cJSON* json) -{ - bool is_command_details = false; /* is this command 'status details' ? */ - int status = EXIT_STATUS_OK; - bool previous_section_printed = false; /* in 'status details', print an header bar between sections */ - - // sanity check - if (!json) - { - goto error; - } - - // the command must be 'status' or 'status details' - if (pgagroal_json_is_command_name_equals_to(json, "status")) - { - is_command_details = false; - } - else if (pgagroal_json_is_command_name_equals_to(json, "status details")) - { - is_command_details = true; - } - else - { - goto error; - } - - // now get the output and start printing it - cJSON* output = pgagroal_json_extract_command_output_object(json); - - // overall status - printf("Status: %s\n", - cJSON_GetObjectItemCaseSensitive(cJSON_GetObjectItemCaseSensitive(output, "status"), "message")->valuestring); - - // connections - cJSON* connections = cJSON_GetObjectItemCaseSensitive(output, "connections"); - if (!connections) - { - goto error; - } - - printf("Active connections: %d\n", cJSON_GetObjectItemCaseSensitive(connections, "active")->valueint); - printf("Total connections: %d\n", cJSON_GetObjectItemCaseSensitive(connections, "total")->valueint); - printf("Max connections: %d\n", cJSON_GetObjectItemCaseSensitive(connections, "max")->valueint); - - // databases - cJSON* databases = cJSON_GetObjectItemCaseSensitive(output, "databases"); - if (!databases) - { - goto error; - } - - cJSON* disabled_databases = cJSON_GetObjectItemCaseSensitive(databases, "disabled"); - if (!disabled_databases) - { - goto error; - } - - cJSON* disabled_databases_list = cJSON_GetObjectItemCaseSensitive(disabled_databases, JSON_TAG_ARRAY_NAME); - cJSON* current; - cJSON_ArrayForEach(current, disabled_databases_list) - { - printf("Disabled database: %s\n", current->valuestring); - } - - // the status command ends here - if (!is_command_details) - { - goto end; - } - - // dump the servers information - cJSON* servers = cJSON_GetObjectItemCaseSensitive(output, "servers"); - if (!servers) - { - goto error; - } - - cJSON* servers_list = cJSON_GetObjectItemCaseSensitive(servers, JSON_TAG_ARRAY_NAME); - cJSON_ArrayForEach(current, servers_list) - { - if (!previous_section_printed) - { - printf("---------------------\n"); - previous_section_printed = true; - } - printf("Server: %s\n", cJSON_GetObjectItemCaseSensitive(current, "server")->valuestring); - printf("Host: %s\n", cJSON_GetObjectItemCaseSensitive(current, "host")->valuestring); - printf("Port: %d\n", cJSON_GetObjectItemCaseSensitive(current, "port")->valueint); - printf("State: %s\n", cJSON_GetObjectItemCaseSensitive(current, "state")->valuestring); - printf("---------------------\n"); - - } - - // dump the limits information - cJSON* limits = cJSON_GetObjectItemCaseSensitive(output, "limits"); - cJSON* limits_list = cJSON_GetObjectItemCaseSensitive(limits, JSON_TAG_ARRAY_NAME); - cJSON_ArrayForEach(current, limits_list) - { - if (!previous_section_printed) - { - printf("---------------------\n"); - previous_section_printed = true; - } - printf("Database: %s\n", cJSON_GetObjectItemCaseSensitive(current, "database")->valuestring); - printf("Username: %s\n", cJSON_GetObjectItemCaseSensitive(current, "username")->valuestring); - cJSON* current_connections = cJSON_GetObjectItemCaseSensitive(current, "connections"); - printf("Active connections: %d\n", cJSON_GetObjectItemCaseSensitive(current_connections, "active")->valueint); - printf("Max connections: %d\n", cJSON_GetObjectItemCaseSensitive(current_connections, "max")->valueint); - printf("Initial connections: %d\n", cJSON_GetObjectItemCaseSensitive(current_connections, "initial")->valueint); - printf("Min connections: %d\n", cJSON_GetObjectItemCaseSensitive(current_connections, "min")->valueint); - printf("---------------------\n"); - } - - // print the connection information - cJSON_ArrayForEach(current, cJSON_GetObjectItemCaseSensitive(connections, JSON_TAG_ARRAY_NAME)) - { - if (!previous_section_printed) - { - printf("---------------------\n"); - previous_section_printed = false; - } - - printf("Connection %4d: %-15s %-19s %-6s %-6s %s %s %s\n", - cJSON_GetObjectItemCaseSensitive(current, "number")->valueint, - cJSON_GetObjectItemCaseSensitive(current, "state")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "time")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "pid")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "fd")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "user")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "database")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "detail")->valuestring); - - } - - // all done - goto end; - -error: - status = 1; -end: - if (json) - { - cJSON_Delete(json); - } - - return status; - -} - -/** - * Utility method to get the information about the `conf ls` command. - * This method produces a cJSON object that needs to be printed out in textual format. - * - * @param ssl the SSL file descriptor - * @param socket the file descriptor for the socket - * - * @returns the cJSON object, faulty if something went wrong - */ -static cJSON* -pgagroal_management_json_read_conf_ls(SSL* ssl, int socket) -{ - char buf[4]; - char* buffer; - char buf_info[MANAGEMENT_INFO_SIZE]; - int application; - int version; - - if (read_complete(ssl, socket, &buf_info[0], sizeof(buf_info))) - { - pgagroal_log_warn("pgagroal_management_json_read_conf_ls: read: %d %s", socket, strerror(errno)); - errno = 0; - return NULL; - } - - application = pgagroal_read_int32(&(buf_info[2])); - version = pgagroal_read_int32(&(buf_info[9])); - - char* version_buf = NULL; - - if (pgagroal_executable_version_string(&version_buf, version)) - { - return NULL; - } - - cJSON* json = pgagroal_json_create_new_command_object("conf ls", true, pgagroal_executable_name(application), version_buf); - cJSON* output = pgagroal_json_extract_command_output_object(json); - - // add an array that will contain the files - cJSON* files = cJSON_CreateObject(); - cJSON* files_array = cJSON_CreateArray(); - cJSON_AddItemToObject(output, "files", files); - cJSON_AddItemToObject(files, JSON_TAG_ARRAY_NAME, files_array); - - memset(&buf, 0, sizeof(buf)); - buffer = calloc(1, MAX_PATH); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the main configuration file entry - cJSON* mainConf = cJSON_CreateObject(); - cJSON_AddStringToObject(mainConf, "description", "Main Configuration file"); - cJSON_AddStringToObject(mainConf, "path", buffer); - cJSON_AddItemToArray(files_array, mainConf); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the HBA file - cJSON* hbaConf = cJSON_CreateObject(); - cJSON_AddStringToObject(hbaConf, "description", "HBA File"); - cJSON_AddStringToObject(hbaConf, "path", buffer); - cJSON_AddItemToArray(files_array, hbaConf); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the limit file - cJSON* limitConf = cJSON_CreateObject(); - cJSON_AddStringToObject(limitConf, "description", "Limit file"); - cJSON_AddStringToObject(limitConf, "path", buffer); - cJSON_AddItemToArray(files_array, limitConf); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the frontend file - cJSON* frontendConf = cJSON_CreateObject(); - cJSON_AddStringToObject(frontendConf, "description", "Frontend users file"); - cJSON_AddStringToObject(frontendConf, "path", buffer); - cJSON_AddItemToArray(files_array, frontendConf); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the admins file - cJSON* adminsConf = cJSON_CreateObject(); - cJSON_AddStringToObject(adminsConf, "description", "Admins file"); - cJSON_AddStringToObject(adminsConf, "path", buffer); - cJSON_AddItemToArray(files_array, adminsConf); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the superuser file - cJSON* superuserConf = cJSON_CreateObject(); - cJSON_AddStringToObject(superuserConf, "description", "Superuser file"); - cJSON_AddStringToObject(superuserConf, "path", buffer); - cJSON_AddItemToArray(files_array, superuserConf); - - if (pgagroal_management_read_conf_ls_detail(ssl, socket, buffer)) - { - goto error; - } - - // add the users file - cJSON* usersConf = cJSON_CreateObject(); - cJSON_AddStringToObject(usersConf, "description", "Users file"); - cJSON_AddStringToObject(usersConf, "path", buffer); - cJSON_AddItemToArray(files_array, usersConf); - - // all done - goto end; - -error: - free(buffer); - pgagroal_log_warn("pgagroal_management_json_read_conf_ls: read: %d %s", socket, strerror(errno)); - errno = 0; - pgagroal_json_set_command_object_faulty(json, strerror(errno), errno); - -end: - free(buffer); - return json; - -} - -/** - * Utility function to handle a JSON object and print it out - * as normal text. - * - * @param json the JSON object - * @returns 0 on success - */ -static int -pgagroal_management_json_print_conf_ls(cJSON* json) -{ - int status = EXIT_STATUS_OK; - - // sanity check - if (!json) - { - goto error; - } - - // now get the output and start printing it - cJSON* output = pgagroal_json_extract_command_output_object(json); - - // files - cJSON* files = cJSON_GetObjectItemCaseSensitive(output, "files"); - if (!files) - { - goto error; - } - - cJSON* files_array = cJSON_GetObjectItemCaseSensitive(files, JSON_TAG_ARRAY_NAME); - cJSON* current; - cJSON_ArrayForEach(current, files_array) - { - // the current JSON object is made by two different values - printf("%-25s : %s\n", - cJSON_GetObjectItemCaseSensitive(current, "description")->valuestring, - cJSON_GetObjectItemCaseSensitive(current, "path")->valuestring); - } - - status = pgagroal_json_command_object_exit_status(json); - goto end; - -error: - status = EXIT_STATUS_DATA_ERROR; -end: - if (json) - { - cJSON_Delete(json); - } - - return status; -} - -static cJSON* -pgagroal_json_create_new_command_object(char* command_name, bool success, char* executable_name, char* executable_version) -{ - // root of the JSON structure - cJSON* json = cJSON_CreateObject(); - - if (!json) - { - goto error; - } - - // the command structure - cJSON* command = cJSON_CreateObject(); - if (!command) - { - goto error; - } - - // insert meta-data about the command - cJSON_AddStringToObject(command, JSON_TAG_COMMAND_NAME, command_name); - cJSON_AddStringToObject(command, JSON_TAG_COMMAND_STATUS, success ? JSON_STRING_SUCCESS : JSON_STRING_ERROR); - cJSON_AddNumberToObject(command, JSON_TAG_COMMAND_ERROR, success ? JSON_BOOL_SUCCESS : JSON_BOOL_ERROR); - cJSON_AddNumberToObject(command, JSON_TAG_COMMAND_EXIT_STATUS, success ? 0 : EXIT_STATUS_DATA_ERROR); - - // the output of the command, this has to be filled by the caller - cJSON* output = cJSON_CreateObject(); - if (!output) - { - goto error; - } - - cJSON_AddItemToObject(command, JSON_TAG_COMMAND_OUTPUT, output); - - // who has launched the command ? - cJSON* application = cJSON_CreateObject(); - if (!application) - { - goto error; - } - - long minor = strtol(&executable_version[2], NULL, 10); - if (errno == ERANGE || minor <= LONG_MIN || minor >= LONG_MAX) - { - goto error; - } - long patch = strtol(&executable_version[5], NULL, 10); - if (errno == ERANGE || patch <= LONG_MIN || patch >= LONG_MAX) - { - goto error; - } - - cJSON_AddStringToObject(application, JSON_TAG_APPLICATION_NAME, executable_name); - cJSON_AddNumberToObject(application, JSON_TAG_APPLICATION_VERSION_MAJOR, executable_version[0] - '0'); - cJSON_AddNumberToObject(application, JSON_TAG_APPLICATION_VERSION_MINOR, (int)minor); - cJSON_AddNumberToObject(application, JSON_TAG_APPLICATION_VERSION_PATCH, (int)patch); - cJSON_AddStringToObject(application, JSON_TAG_APPLICATION_VERSION, executable_version); - - // add objects to the whole json thing - cJSON_AddItemToObject(json, "command", command); - cJSON_AddItemToObject(json, "application", application); - - return json; - -error: - if (json) - { - cJSON_Delete(json); - } - - return NULL; - -} - -static cJSON* -pgagroal_json_extract_command_output_object(cJSON* json) -{ - cJSON* command = cJSON_GetObjectItemCaseSensitive(json, JSON_TAG_COMMAND); - if (!command) - { - goto error; - } - - return cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_OUTPUT); - -error: - return NULL; - -} - -static bool -pgagroal_json_is_command_name_equals_to(cJSON* json, char* command_name) -{ - if (!json || !command_name || strlen(command_name) <= 0) - { - goto error; - } - - cJSON* command = cJSON_GetObjectItemCaseSensitive(json, JSON_TAG_COMMAND); - if (!command) - { - goto error; - } - - cJSON* cName = cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_NAME); - if (!cName || !cJSON_IsString(cName) || !cName->valuestring) - { - goto error; - } - - return !strncmp(command_name, - cName->valuestring, - MISC_LENGTH); - -error: - return false; -} - -static int -pgagroal_json_set_command_object_faulty(cJSON* json, char* message, int exit_status) -{ - if (!json) - { - goto error; - } - - cJSON* command = cJSON_GetObjectItemCaseSensitive(json, JSON_TAG_COMMAND); - if (!command) - { - goto error; - } - - cJSON* current = cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_STATUS); - if (!current) - { - goto error; - } - - cJSON_SetValuestring(current, message); - - current = cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_ERROR); - if (!current) - { - goto error; - } - - cJSON_SetIntValue(current, JSON_BOOL_ERROR); // cannot use cJSON_SetBoolValue unless cJSON >= 1.7.16 - - current = cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_EXIT_STATUS); - if (!current) - { - goto error; - } - - cJSON_SetIntValue(current, exit_status); - - return 0; - -error: - return 1; - -} - -static int -pgagroal_json_command_object_exit_status(cJSON* json) -{ - if (!json) - { - goto error; - } - - cJSON* command = cJSON_GetObjectItemCaseSensitive(json, JSON_TAG_COMMAND); - if (!command) - { - goto error; - } - - cJSON* status = cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_EXIT_STATUS); - if (!status || !cJSON_IsNumber(status)) - { - goto error; - } - - return status->valueint; - -error: - return EXIT_STATUS_DATA_ERROR; -} - -static const char* -pgagroal_json_get_command_object_status(cJSON* json) -{ - if (!json) - { - goto error; - } - - cJSON* command = cJSON_GetObjectItemCaseSensitive(json, JSON_TAG_COMMAND); - if (!command) - { - goto error; - } - - cJSON* status = cJSON_GetObjectItemCaseSensitive(command, JSON_TAG_COMMAND_STATUS); - if (!cJSON_IsString(status) || (status->valuestring == NULL)) - { - goto error; - } - - return status->valuestring; -error: - return NULL; - -} - -static int -pgagroal_json_print_and_free_json_object(cJSON* json) -{ - int status = pgagroal_json_command_object_exit_status(json); - printf("%s\n", cJSON_Print(json)); - cJSON_Delete(json); - return status; -} diff --git a/src/libpgagroal/memory.c b/src/libpgagroal/memory.c index 0ceb4884..d69ed16d 100644 --- a/src/libpgagroal/memory.c +++ b/src/libpgagroal/memory.c @@ -56,23 +56,26 @@ pgagroal_memory_init(void) assert(shmem != NULL); #endif - pgagroal_memory_destroy(); - - message = (struct message*)calloc(1, sizeof(struct message)); if (message == NULL) { - goto error; + message = (struct message*)calloc(1, sizeof(struct message)); + if (message == NULL) + { + goto error; + } } - - data = calloc(1, DEFAULT_BUFFER_SIZE); + if (data == NULL) { - goto error; + data = calloc(1, DEFAULT_BUFFER_SIZE); + if (data == NULL) + { + goto error; + } } message->kind = 0; message->length = 0; - message->max_length = DEFAULT_BUFFER_SIZE; message->data = data; return; @@ -108,21 +111,16 @@ pgagroal_memory_message(void) void pgagroal_memory_free(void) { - size_t length; - #ifdef DEBUG assert(message != NULL); assert(data != NULL); #endif - length = message->max_length; - memset(message, 0, sizeof(struct message)); - memset(data, 0, length); + memset(data, 0, DEFAULT_BUFFER_SIZE); message->kind = 0; message->length = 0; - message->max_length = length; message->data = data; } diff --git a/src/libpgagroal/message.c b/src/libpgagroal/message.c index 332c2a9f..19c10407 100644 --- a/src/libpgagroal/message.c +++ b/src/libpgagroal/message.c @@ -1226,7 +1226,7 @@ read_message(int socket, bool block, int timeout, struct message** msg) m = pgagroal_memory_message(); - numbytes = read(socket, m->data, m->max_length); + numbytes = read(socket, m->data, DEFAULT_BUFFER_SIZE); if (likely(numbytes > 0)) { @@ -1364,7 +1364,7 @@ ssl_read_message(SSL* ssl, int timeout, struct message** msg) m = pgagroal_memory_message(); - numbytes = SSL_read(ssl, m->data, m->max_length); + numbytes = SSL_read(ssl, m->data, DEFAULT_BUFFER_SIZE); if (likely(numbytes > 0)) { diff --git a/src/libpgagroal/network.c b/src/libpgagroal/network.c index c019fbe8..321e0a93 100644 --- a/src/libpgagroal/network.c +++ b/src/libpgagroal/network.c @@ -30,6 +30,7 @@ #include #include #include +#include /* system */ #include @@ -53,7 +54,7 @@ #include static int bind_host(const char* hostname, int port, int** fds, int* length, bool non_blocking, int* buffer_size, bool no_delay, int backlog); -static int socket_buffers(int fd, int* buffer_size); +static int socket_buffers(int fd); /** * @@ -184,7 +185,14 @@ pgagroal_bind_unix_socket(const char* directory, const char* file, int* fd) } memset(&buf, 0, sizeof(buf)); - snprintf(&buf[0], sizeof(buf), "%s/%s", directory, file); + if (!pgagroal_ends_with(&buf[0], "/")) + { + snprintf(&buf[0], sizeof(buf), "%s/%s", directory, file); + } + else + { + snprintf(&buf[0], sizeof(buf), "%s%s", directory, file); + } strncpy(addr.sun_path, &buf[0], sizeof(addr.sun_path) - 1); unlink(&buf[0]); @@ -557,18 +565,19 @@ pgagroal_write_socket(SSL* ssl, int fd, char* buffer, size_t buffer_size) } static int -socket_buffers(int fd, int* buffer_size) +socket_buffers(int fd) { + int default_buffer_size = DEFAULT_BUFFER_SIZE; socklen_t optlen = sizeof(int); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, buffer_size, optlen) == -1) + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &default_buffer_size, optlen) == -1) { pgagroal_log_warn("socket_buffers: SO_RCVBUF %d %s", fd, strerror(errno)); errno = 0; return 1; } - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, buffer_size, optlen) == -1) + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &default_buffer_size, optlen) == -1) { pgagroal_log_warn("socket_buffers: SO_SNDBUF %d %s", fd, strerror(errno)); errno = 0; @@ -655,7 +664,7 @@ bind_host(const char* hostname, int port, int** fds, int* length, bool non_block } } - if (socket_buffers(sockfd, buffer_size)) + if (socket_buffers(sockfd)) { pgagroal_disconnect(sockfd); continue; diff --git a/src/libpgagroal/pipeline_transaction.c b/src/libpgagroal/pipeline_transaction.c index 61cbcdcd..fc5e82e5 100644 --- a/src/libpgagroal/pipeline_transaction.c +++ b/src/libpgagroal/pipeline_transaction.c @@ -28,8 +28,9 @@ /* pgagroal */ #include +#include #include -#include +//#include #include #include #include @@ -118,7 +119,7 @@ transaction_start(struct ev_loop* loop, struct worker_io* w) deallocate = false; memset(&p, 0, sizeof(p)); - snprintf(&p[0], sizeof(p), ".s.%d", getpid()); + snprintf(&p[0], sizeof(p), ".s.pgagroal.%d", getpid()); if (pgagroal_bind_unix_socket(config->unix_socket_dir, &p[0], &unix_socket)) { @@ -610,11 +611,10 @@ accept_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) { struct sockaddr_in client_addr; socklen_t client_addr_length; - int client_fd; - signed char id; - int32_t payload_slot; - int payload_i; - char* payload_s = NULL; + int client_fd = -1; + int id = -1; + int32_t slot = -1; + int fd = -1; struct main_configuration* config = NULL; config = (struct main_configuration*)shmem; @@ -636,27 +636,42 @@ accept_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) } /* Process management request */ - pgagroal_management_read_header(client_fd, &id, &payload_slot); - pgagroal_management_read_payload(client_fd, id, &payload_i, &payload_s); + if (pgagroal_connection_id_read(client_fd, &id)) + { + pgagroal_log_error("pgagroal: Management client: ID: %d", id); + goto done; + } - switch (id) + if (id == CONNECTION_CLIENT_FD) { - case MANAGEMENT_CLIENT_FD: - pgagroal_log_debug("pgagroal: Management client file descriptor: Slot %d FD %d", payload_slot, payload_i); - fds[payload_slot] = payload_i; - break; - case MANAGEMENT_REMOVE_FD: - pgagroal_log_debug("pgagroal: Management remove file descriptor: Slot %d FD %d", payload_slot, payload_i); - if (fds[payload_slot] == payload_i && !config->connections[payload_slot].new && config->connections[payload_slot].fd > 0) - { - pgagroal_disconnect(payload_i); - fds[payload_slot] = 0; - } - break; - default: - pgagroal_log_debug("pgagroal: Unsupported management id: %d", id); - break; + if (pgagroal_connection_transfer_read(client_fd, &slot, &fd)) + { + pgagroal_log_error("pgagroal: Management client_fd: ID: %d Slot %d FD %d", id, slot, fd); + goto done; + } + + fds[slot] = fd; } + else if (id == CONNECTION_REMOVE_FD) + { + if (pgagroal_connection_transfer_read(client_fd, &slot, &fd)) + { + pgagroal_log_error("pgagroal: Management remove_fd: ID: %d Slot %d FD %d", id, slot, fd); + goto done; + } + + if (fds[slot] == fd && !config->connections[slot].new && config->connections[slot].fd > 0) + { + pgagroal_disconnect(fd); + fds[slot] = 0; + } + } + else + { + pgagroal_log_debug("pgagroal: Unsupported management id: %d", id); + } + +done: pgagroal_disconnect(client_fd); } diff --git a/src/libpgagroal/pool.c b/src/libpgagroal/pool.c index 539aee59..be571c0d 100644 --- a/src/libpgagroal/pool.c +++ b/src/libpgagroal/pool.c @@ -28,6 +28,7 @@ /* pgagroal */ #include +#include #include #include #include @@ -164,6 +165,7 @@ pgagroal_get_connection(char* username, char* database, bool reuse, bool transac if (!fork()) { pgagroal_flush(FLUSH_GRACEFULLY, "*"); + exit(0); } goto error; @@ -381,6 +383,7 @@ pgagroal_return_connection(int slot, SSL* ssl, bool transaction_mode) time_t now; signed char in_use; signed char age_check; + int transfer_fd = -1; config = (struct main_configuration*)shmem; @@ -398,7 +401,7 @@ pgagroal_return_connection(int slot, SSL* ssl, bool transaction_mode) { pgagroal_prometheus_connection_max_connection_age(); pgagroal_tracking_event_slot(TRACKER_MAX_CONNECTION_AGE, slot); - return pgagroal_kill_connection(slot, ssl); + goto kill_connection; } } } @@ -438,10 +441,42 @@ pgagroal_return_connection(int slot, SSL* ssl, bool transaction_mode) if (config->connections[slot].new) { - pgagroal_management_transfer_connection(slot); + if (pgagroal_connection_get(&transfer_fd)) + { + goto kill_connection; + } + + if (pgagroal_connection_id_write(transfer_fd, CONNECTION_TRANSFER)) + { + goto kill_connection; + } + + if (pgagroal_connection_transfer_write(transfer_fd, slot)) + { + goto kill_connection; + } + + pgagroal_disconnect(transfer_fd); + transfer_fd = -1; } - pgagroal_management_return_connection(slot); + if (pgagroal_connection_get(&transfer_fd)) + { + goto kill_connection; + } + + if (pgagroal_connection_id_write(transfer_fd, CONNECTION_RETURN)) + { + goto kill_connection; + } + + if (pgagroal_connection_slot_write(transfer_fd, slot)) + { + goto kill_connection; + } + + pgagroal_disconnect(transfer_fd); + transfer_fd = -1; if (config->connections[slot].limit_rule >= 0) { @@ -467,6 +502,8 @@ pgagroal_return_connection(int slot, SSL* ssl, bool transaction_mode) kill_connection: + pgagroal_disconnect(transfer_fd); + pgagroal_tracking_event_slot(TRACKER_RETURN_CONNECTION_KILL, slot); return pgagroal_kill_connection(slot, ssl); @@ -479,6 +516,7 @@ pgagroal_kill_connection(int slot, SSL* ssl) int ssl_shutdown; int result = 0; int fd; + int transfer_fd; struct main_configuration* config; config = (struct main_configuration*)shmem; @@ -492,7 +530,27 @@ pgagroal_kill_connection(int slot, SSL* ssl) fd = config->connections[slot].fd; if (fd != -1) { - pgagroal_management_kill_connection(slot, fd); + if (pgagroal_connection_get(&transfer_fd)) + { + result = 1; + } + + if (pgagroal_connection_id_write(transfer_fd, CONNECTION_KILL)) + { + result = 1; + } + + if (pgagroal_connection_slot_write(transfer_fd, slot)) + { + result = 1; + } + + if (pgagroal_connection_socket_write(transfer_fd, fd)) + { + result = 1; + } + + pgagroal_disconnect(transfer_fd); if (ssl != NULL) { @@ -882,8 +940,6 @@ pgagroal_flush(int mode, char* database) pgagroal_pool_status(); pgagroal_memory_destroy(); pgagroal_stop_logging(); - - exit(0); } void @@ -954,6 +1010,39 @@ pgagroal_flush_server(signed char server) exit(0); } +void +pgagroal_request_flush(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload) +{ + time_t start_time; + time_t end_time; + struct json* req = NULL; + int mode = 0; + char* database = NULL; + + start_time = time(NULL); + + req = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + mode = (int)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_MODE); + database = (char*)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_DATABASE); + + if (database != NULL) + { + pgagroal_flush(mode, database); + } + else + { + pgagroal_flush(mode, "*"); + } + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + + pgagroal_json_destroy(payload); + + exit(0); +} + void pgagroal_prefill(bool initial) { diff --git a/src/libpgagroal/prometheus.c b/src/libpgagroal/prometheus.c index 49ed797c..da9863c2 100644 --- a/src/libpgagroal/prometheus.c +++ b/src/libpgagroal/prometheus.c @@ -69,6 +69,7 @@ #define TWENTYFOUR_HOURS 86400 static int resolve_page(struct message* msg); +static int badrequest_page(int client_fd); static int unknown_page(int client_fd); static int home_page(int client_fd); static int home_vault_page(int client_fd); @@ -150,6 +151,8 @@ pgagroal_prometheus(int client_fd) error: + badrequest_page(client_fd); + pgagroal_log_debug("pgagroal_prometheus: disconnect %d", client_fd); pgagroal_disconnect(client_fd); @@ -905,7 +908,7 @@ pgagroal_prometheus_self_sockets_sub(void) } void -pgagroal_prometheus_reset(void) +pgagroal_prometheus_clear(void) { signed char cache_is_free; struct main_configuration* config; @@ -1102,6 +1105,40 @@ resolve_page(struct message* msg) return PAGE_UNKNOWN; } +static int +badrequest_page(int client_fd) +{ + char* data = NULL; + time_t now; + char time_buf[32]; + int status; + struct message msg; + + memset(&msg, 0, sizeof(struct message)); + memset(&data, 0, sizeof(data)); + + now = time(NULL); + + memset(&time_buf, 0, sizeof(time_buf)); + ctime_r(&now, &time_buf[0]); + time_buf[strlen(time_buf) - 1] = 0; + + data = pgagroal_append(data, "HTTP/1.1 400 Bad Request\r\n"); + data = pgagroal_append(data, "Date: "); + data = pgagroal_append(data, &time_buf[0]); + data = pgagroal_append(data, "\r\n"); + + msg.kind = 0; + msg.length = strlen(data); + msg.data = data; + + status = pgagroal_write_message(NULL, client_fd, &msg); + + free(data); + + return status; +} + static int unknown_page(int client_fd) { @@ -2969,4 +3006,4 @@ is_prometheus_enabled(void) struct prometheus* prometheus = (struct prometheus*) prometheus_shmem; struct configuration* config = (struct configuration*)shmem; return (config->metrics > 0 && prometheus != NULL); -} \ No newline at end of file +} diff --git a/src/libpgagroal/remote.c b/src/libpgagroal/remote.c index 3137196c..f84c5da4 100644 --- a/src/libpgagroal/remote.c +++ b/src/libpgagroal/remote.c @@ -48,12 +48,12 @@ void pgagroal_remote_management(int client_fd, char* address) { int server_fd = -1; - int status; int exit_code; int auth_status; - signed char type; + uint8_t compression; + uint8_t encryption; SSL* client_ssl = NULL; - struct message* msg = NULL; + struct json* payload = NULL; struct main_configuration* config; pgagroal_start_logging(); @@ -68,123 +68,32 @@ pgagroal_remote_management(int client_fd, char* address) auth_status = pgagroal_remote_management_auth(client_fd, address, &client_ssl); if (auth_status == AUTH_SUCCESS) { - status = pgagroal_read_timeout_message(client_ssl, client_fd, config->common.authentication_timeout, &msg); - if (status != MESSAGE_STATUS_OK) + if (pgagroal_connect_unix_socket(config->unix_socket_dir, MAIN_UDS, &server_fd)) { goto done; } - type = pgagroal_read_byte(msg->data); + if (pgagroal_management_read_json(client_ssl, client_fd, &compression, &encryption, &payload)) + { + goto done; + } - if (pgagroal_connect_unix_socket(config->unix_socket_dir, MAIN_UDS, &server_fd)) + if (pgagroal_management_write_json(NULL, server_fd, compression, encryption, payload)) { goto done; } - status = pgagroal_write_message(NULL, server_fd, msg); - if (status != MESSAGE_STATUS_OK) + pgagroal_json_destroy(payload); + payload = NULL; + + if (pgagroal_management_read_json(NULL, server_fd, &compression, &encryption, &payload)) { goto done; } - switch (type) + if (pgagroal_management_write_json(client_ssl, client_fd, compression, encryption, payload)) { - case MANAGEMENT_GRACEFULLY: - case MANAGEMENT_STOP: - case MANAGEMENT_CANCEL_SHUTDOWN: - case MANAGEMENT_RESET: - case MANAGEMENT_RELOAD: - break; - case MANAGEMENT_STATUS: - case MANAGEMENT_ISALIVE: - case MANAGEMENT_DETAILS: - do - { - status = pgagroal_read_timeout_message(NULL, server_fd, 1, &msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_write_message(client_ssl, client_fd, msg); - } - while (status == MESSAGE_STATUS_OK); - break; - case MANAGEMENT_FLUSH: - case MANAGEMENT_RESET_SERVER: - case MANAGEMENT_SWITCH_TO: - status = pgagroal_read_timeout_message(client_ssl, client_fd, config->common.authentication_timeout, &msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_write_message(NULL, server_fd, msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - case MANAGEMENT_ENABLEDB: - case MANAGEMENT_DISABLEDB: - status = pgagroal_read_timeout_message(client_ssl, client_fd, config->common.authentication_timeout, &msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_write_message(NULL, server_fd, msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_read_timeout_message(client_ssl, client_fd, config->common.authentication_timeout, &msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_write_message(NULL, server_fd, msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - break; - - case MANAGEMENT_GET_PASSWORD: - // Read username size from local - status = pgagroal_read_timeout_message(client_ssl, client_fd, config->common.authentication_timeout, &msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_write_message(NULL, server_fd, msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_read_timeout_message(NULL, server_fd, config->common.authentication_timeout, &msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - status = pgagroal_write_message(client_ssl, client_fd, msg); - if (status != MESSAGE_STATUS_OK) - { - goto done; - } - - break; - default: - pgagroal_log_warn("Unknown management operation: %d", type); - pgagroal_log_message(msg); - exit_code = 1; - goto done; - break; + goto done; } } else @@ -194,6 +103,9 @@ pgagroal_remote_management(int client_fd, char* address) done: + pgagroal_json_destroy(payload); + payload = NULL; + if (client_ssl != NULL) { int res; diff --git a/src/libpgagroal/security.c b/src/libpgagroal/security.c index 2ee8b85a..04a3641c 100644 --- a/src/libpgagroal/security.c +++ b/src/libpgagroal/security.c @@ -28,6 +28,7 @@ /* pgagroal */ #include +#include #include #include #include @@ -90,10 +91,6 @@ static char* get_frontend_password(char* username); static char* get_admin_password(char* username); static int get_salt(void* data, char** salt); -static int derive_key_iv(char* password, unsigned char* key, unsigned char* iv); -static int aes_encrypt(char* plaintext, unsigned char* key, unsigned char* iv, char** ciphertext, int* ciphertext_length); -static int aes_decrypt(char* ciphertext, int ciphertext_length, unsigned char* key, unsigned char* iv, char** plaintext); - static int sasl_prep(char* password, char** password_prep); static int generate_nounce(char** nounce); static int get_scram_attribute(char attribute, char* input, size_t size, char** value); @@ -639,6 +636,8 @@ pgagroal_remote_management_auth(int client_fd, char* address, SSL** client_ssl) *client_ssl = NULL; + pgagroal_memory_init(); + /* Receive client calls - at any point if client exits return AUTH_ERROR */ status = pgagroal_read_timeout_message(NULL, client_fd, config->common.authentication_timeout, &msg); if (status != MESSAGE_STATUS_OK) @@ -1036,7 +1035,7 @@ pgagroal_remote_management_scram_sha256(char* username, char* password, int serv goto error; } - pgagroal_base64_decode(base64_salt, strlen(base64_salt), &salt, &salt_length); + pgagroal_base64_decode(base64_salt, strlen(base64_salt), (void**)&salt, &salt_length); iteration = atoi(iteration_string); @@ -1085,7 +1084,7 @@ pgagroal_remote_management_scram_sha256(char* username, char* password, int serv /* Get 'v' attribute */ base64_server_signature = sasl_final->data + 11; - pgagroal_base64_decode(base64_server_signature, sasl_final->length - 11, &server_signature_received, &server_signature_received_length); + pgagroal_base64_decode(base64_server_signature, sasl_final->length - 11, (void**)&server_signature_received, &server_signature_received_length); if (server_signature(password_prep, salt, salt_length, iteration, NULL, 0, @@ -1968,7 +1967,7 @@ client_scram256(SSL* c_ssl, int client_fd, char* username, char* password, int s } get_scram_attribute('p', (char*)msg->data + 5, msg->length - 5, &base64_client_proof); - pgagroal_base64_decode(base64_client_proof, strlen(base64_client_proof), &client_proof_received, &client_proof_received_length); + pgagroal_base64_decode(base64_client_proof, strlen(base64_client_proof), (void**)&client_proof_received, &client_proof_received_length); client_final_message_without_proof = calloc(1, 58); @@ -2766,7 +2765,7 @@ server_scram256(char* username, char* password, int slot, SSL* server_ssl) goto error; } - pgagroal_base64_decode(base64_salt, strlen(base64_salt), &salt, &salt_length); + pgagroal_base64_decode(base64_salt, strlen(base64_salt), (void**)&salt, &salt_length); iteration = atoi(iteration_string); @@ -2826,7 +2825,7 @@ server_scram256(char* username, char* password, int slot, SSL* server_ssl) /* Get 'v' attribute */ base64_server_signature = sasl_final->data + 11; pgagroal_base64_decode(base64_server_signature, sasl_final->length - 11, - &server_signature_received, &server_signature_received_length); + (void**)&server_signature_received, &server_signature_received_length); if (server_signature(password_prep, salt, salt_length, iteration, NULL, 0, @@ -3282,7 +3281,7 @@ pgagroal_get_master_key(char** masterkey) goto error; } - pgagroal_base64_decode(&line[0], strlen(&line[0]), &mk, &mk_length); + pgagroal_base64_decode(&line[0], strlen(&line[0]), (void**)&mk, &mk_length); *masterkey = mk; @@ -3302,40 +3301,6 @@ pgagroal_get_master_key(char** masterkey) return 1; } -int -pgagroal_encrypt(char* plaintext, char* password, char** ciphertext, int* ciphertext_length) -{ - unsigned char key[EVP_MAX_KEY_LENGTH]; - unsigned char iv[EVP_MAX_IV_LENGTH]; - - memset(&key, 0, sizeof(key)); - memset(&iv, 0, sizeof(iv)); - - if (derive_key_iv(password, key, iv) != 0) - { - return 1; - } - - return aes_encrypt(plaintext, key, iv, ciphertext, ciphertext_length); -} - -int -pgagroal_decrypt(char* ciphertext, int ciphertext_length, char* password, char** plaintext) -{ - unsigned char key[EVP_MAX_KEY_LENGTH]; - unsigned char iv[EVP_MAX_IV_LENGTH]; - - memset(&key, 0, sizeof(key)); - memset(&iv, 0, sizeof(iv)); - - if (derive_key_iv(password, key, iv) != 0) - { - return 1; - } - - return aes_decrypt(ciphertext, ciphertext_length, key, iv, plaintext); -} - int pgagroal_md5(char* str, int length, char** md5) { @@ -3489,131 +3454,6 @@ pgagroal_tls_valid(void) return 1; } -static int -derive_key_iv(char* password, unsigned char* key, unsigned char* iv) -{ - if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, - (unsigned char*) password, strlen(password), 1, - key, iv)) - { - return 1; - } - - return 0; -} - -static int -aes_encrypt(char* plaintext, unsigned char* key, unsigned char* iv, char** ciphertext, int* ciphertext_length) -{ - EVP_CIPHER_CTX* ctx = NULL; - int length; - size_t size; - unsigned char* ct = NULL; - int ct_length; - - if (!(ctx = EVP_CIPHER_CTX_new())) - { - goto error; - } - - if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1) - { - goto error; - } - - size = strlen(plaintext) + EVP_CIPHER_block_size(EVP_aes_256_cbc()); - ct = calloc(1, size); - - if (EVP_EncryptUpdate(ctx, - ct, &length, - (unsigned char*)plaintext, strlen((char*)plaintext)) != 1) - { - goto error; - } - - ct_length = length; - - if (EVP_EncryptFinal_ex(ctx, ct + length, &length) != 1) - { - goto error; - } - - ct_length += length; - - EVP_CIPHER_CTX_free(ctx); - - *ciphertext = (char*)ct; - *ciphertext_length = ct_length; - - return 0; - -error: - if (ctx) - { - EVP_CIPHER_CTX_free(ctx); - } - - free(ct); - - return 1; -} - -static int -aes_decrypt(char* ciphertext, int ciphertext_length, unsigned char* key, unsigned char* iv, char** plaintext) -{ - EVP_CIPHER_CTX* ctx = NULL; - int plaintext_length; - int length; - size_t size; - char* pt = NULL; - - if (!(ctx = EVP_CIPHER_CTX_new())) - { - goto error; - } - - if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1) - { - goto error; - } - - size = ciphertext_length + EVP_CIPHER_block_size(EVP_aes_256_cbc()); - pt = calloc(1, size); - - if (EVP_DecryptUpdate(ctx, - (unsigned char*)pt, &length, - (unsigned char*)ciphertext, ciphertext_length) != 1) - { - goto error; - } - - plaintext_length = length; - - if (EVP_DecryptFinal_ex(ctx, (unsigned char*)pt + length, &length) != 1) - { - goto error; - } - - plaintext_length += length; - - EVP_CIPHER_CTX_free(ctx); - - pt[plaintext_length] = 0; - *plaintext = pt; - - return 0; - -error: - if (ctx) - { - EVP_CIPHER_CTX_free(ctx); - } - - free(pt); - - return 1; -} - static int sasl_prep(char* password, char** password_prep) { @@ -5053,7 +4893,7 @@ auth_query_server_scram256(char* username, char* password, int socket, SSL* serv goto error; } - pgagroal_base64_decode(base64_salt, strlen(base64_salt), &salt, &salt_length); + pgagroal_base64_decode(base64_salt, strlen(base64_salt), (void**)&salt, &salt_length); iteration = atoi(iteration_string); @@ -5113,7 +4953,7 @@ auth_query_server_scram256(char* username, char* password, int socket, SSL* serv /* Get 'v' attribute */ base64_server_signature = sasl_final->data + 11; pgagroal_base64_decode(base64_server_signature, sasl_final->length - 11, - &server_signature_received, &server_signature_received_length); + (void**)&server_signature_received, &server_signature_received_length); if (server_signature(password_prep, salt, salt_length, iteration, NULL, 0, @@ -5473,15 +5313,15 @@ auth_query_client_scram256(SSL* c_ssl, int client_fd, char* username, char* shad /* Process shadow information */ iterations = atoi(s_iterations); - if (pgagroal_base64_decode(base64_salt, strlen(base64_salt), &salt, &salt_length)) + if (pgagroal_base64_decode(base64_salt, strlen(base64_salt), (void**)&salt, &salt_length)) { goto error; } - if (pgagroal_base64_decode(base64_stored_key, strlen(base64_stored_key), &stored_key, &stored_key_length)) + if (pgagroal_base64_decode(base64_stored_key, strlen(base64_stored_key), (void**)&stored_key, &stored_key_length)) { goto error; } - if (pgagroal_base64_decode(base64_server_key, strlen(base64_server_key), &server_key, &server_key_length)) + if (pgagroal_base64_decode(base64_server_key, strlen(base64_server_key), (void**)&server_key, &server_key_length)) { goto error; } @@ -5515,7 +5355,7 @@ auth_query_client_scram256(SSL* c_ssl, int client_fd, char* username, char* shad } get_scram_attribute('p', (char*)msg->data + 5, msg->length - 5, &base64_client_proof); - pgagroal_base64_decode(base64_client_proof, strlen(base64_client_proof), &client_proof_received, &client_proof_received_length); + pgagroal_base64_decode(base64_client_proof, strlen(base64_client_proof), (void**)&client_proof_received, &client_proof_received_length); client_final_message_without_proof = calloc(1, 58); memcpy(client_final_message_without_proof, msg->data + 5, 57); diff --git a/src/libpgagroal/server.c b/src/libpgagroal/server.c index c2760906..b1542483 100644 --- a/src/libpgagroal/server.c +++ b/src/libpgagroal/server.c @@ -269,7 +269,7 @@ pgagroal_server_force_failover(int server) } int -pgagroal_server_reset(char* server) +pgagroal_server_clear(char* server) { signed char state; struct main_configuration* config = NULL; diff --git a/src/libpgagroal/status.c b/src/libpgagroal/status.c new file mode 100644 index 00000000..a82690be --- /dev/null +++ b/src/libpgagroal/status.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* pgagroal */ +#include +#include +#include +#include +#include +#include +#include +#include + +static void status_details(bool details, struct json* response); + +void +pgagroal_status(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload) +{ + char* elapsed = NULL; + time_t start_time; + time_t end_time; + int total_seconds; + struct json* response = NULL; + + pgagroal_memory_init(); + pgagroal_start_logging(); + + start_time = time(NULL); + + if (pgagroal_management_create_response(payload, -1, &response)) + { + goto error; + } + + status_details(false, 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_STATUS_NETWORK, compression, encryption, payload); + pgagroal_log_error("Status: Error sending response"); + + goto error; + } + + elapsed = pgagroal_get_timestamp_string(start_time, end_time, &total_seconds); + + pgagroal_log_info("Status (Elapsed: %s)", elapsed); + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + pgagroal_memory_destroy(); + + exit(0); + +error: + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + pgagroal_memory_destroy(); + + exit(1); +} + +void +pgagroal_status_details(SSL* ssl, int client_fd, uint8_t compression, uint8_t encryption, struct json* payload) +{ + char* elapsed = NULL; + time_t start_time; + time_t end_time; + int total_seconds; + struct json* response = NULL; + + pgagroal_memory_init(); + pgagroal_start_logging(); + + start_time = time(NULL); + + if (pgagroal_management_create_response(payload, -1, &response)) + { + goto error; + } + + status_details(true, 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_STATUS_DETAILS_NETWORK, compression, encryption, payload); + pgagroal_log_error("Status details: Error sending response"); + + goto error; + } + + elapsed = pgagroal_get_timestamp_string(start_time, end_time, &total_seconds); + + pgagroal_log_info("Status details (Elapsed: %s)", elapsed); + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + pgagroal_memory_destroy(); + + exit(0); + +error: + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_stop_logging(); + pgagroal_memory_destroy(); + + exit(1); +} + +static void +status_details(bool details, struct json* response) +{ + int active = 0; + int total = 0; + struct json* servers = NULL; + struct main_configuration* config; + + config = (struct main_configuration*)shmem; + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_STATUS, (uintptr_t)(config->gracefully ? "Graceful shutdown" : "Running"), ValueString); + + for (int i = 0; i < config->max_connections; i++) + { + int state = atomic_load(&config->states[i]); + switch (state) + { + case STATE_IN_USE: + case STATE_GRACEFULLY: + active++; + case STATE_INIT: + case STATE_FREE: + case STATE_FLUSH: + case STATE_IDLE_CHECK: + case STATE_MAX_CONNECTION_AGE: + case STATE_VALIDATION: + case STATE_REMOVE: + total++; + break; + default: + break; + } + } + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_ACTIVE_CONNECTIONS, (uintptr_t)active, ValueUInt32); + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_TOTAL_CONNECTIONS, (uintptr_t)total, ValueUInt32); + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_MAX_CONNECTIONS, (uintptr_t)config->max_connections, ValueUInt32); + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_NUMBER_OF_SERVERS, (uintptr_t)config->number_of_servers, ValueInt32); + + pgagroal_json_create(&servers); + + for (int i = 0; i < config->number_of_servers; i++) + { + struct json* js = NULL; + + pgagroal_json_create(&js); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_SERVER, (uintptr_t)config->servers[i].name, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_HOST, (uintptr_t)config->servers[i].host, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_PORT, (uintptr_t)config->servers[i].port, ValueInt32); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_STATE, (uintptr_t)pgagroal_server_state_as_string(config->servers[i].state), ValueString); + + pgagroal_json_append(servers, (uintptr_t)js, ValueJSON); + } + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_SERVERS, (uintptr_t)servers, ValueJSON); + + if (details) + { + struct json* limits = NULL; + struct json* connections = NULL; + + pgagroal_json_create(&limits); + pgagroal_json_create(&connections); + + for (int i = 0; i < config->number_of_limits; i++) + { + struct json* js = NULL; + + pgagroal_json_create(&js); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)config->limits[i].database, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_USERNAME, (uintptr_t)config->limits[i].username, ValueString); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_ACTIVE_CONNECTIONS, (uintptr_t)atomic_load(&config->limits[i].active_connections), ValueUInt32); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_MAX_CONNECTIONS, (uintptr_t)config->limits[i].max_size, ValueUInt32); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_INITIAL_CONNECTIONS, (uintptr_t)config->limits[i].initial_size, ValueUInt32); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_MIN_CONNECTIONS, (uintptr_t)config->limits[i].min_size, ValueUInt32); + + pgagroal_json_append(limits, (uintptr_t)js, ValueJSON); + } + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_LIMITS, (uintptr_t)limits, ValueJSON); + + // TODO Databases + Disabled +/* for (int i = 0; i < NUMBER_OF_DISABLED; i++) */ +/* { */ +/* if (strcmp(disabled[i], "")) */ +/* { */ +/* if (!strcmp(disabled[i], "*")) */ +/* { */ +/* cJSON_AddItemToArray(databases_array, cJSON_CreateString("ALL")); */ +/* counter = -1; */ +/* } */ +/* else */ +/* { */ +/* cJSON_AddItemToArray(databases_array, cJSON_CreateString(disabled[i])); */ +/* counter++; */ +/* } */ +/* } */ +/* } */ + + for (int i = 0; i < config->max_connections; i++) + { + struct json* js = NULL; + + pgagroal_json_create(&js); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_START_TIME, (uintptr_t)config->connections[i].start_time, ValueUInt64); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_TIMESTAMP, (uintptr_t)config->connections[i].timestamp, ValueUInt64); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_PID, (uintptr_t)config->connections[i].pid, ValueUInt32); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_FD, (uintptr_t)config->connections[i].fd, ValueUInt32); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)config->connections[i].database, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_USERNAME, (uintptr_t)config->connections[i].username, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_APPNAME, (uintptr_t)config->connections[i].appname, ValueString); + + pgagroal_json_append(connections, (uintptr_t)js, ValueJSON); + } + + pgagroal_json_put(response, MANAGEMENT_ARGUMENT_CONNECTIONS, (uintptr_t)connections, ValueJSON); + } +} diff --git a/src/libpgagroal/utils.c b/src/libpgagroal/utils.c index 062b5c4a..26fb2712 100644 --- a/src/libpgagroal/utils.c +++ b/src/libpgagroal/utils.c @@ -33,6 +33,7 @@ #include /* system */ +#include #include #include #include @@ -188,7 +189,6 @@ pgagroal_extract_message(char type, struct message* msg, struct message** extrac result->kind = pgagroal_read_byte(data); result->length = 1 + m_length; - result->max_length = 1 + m_length; result->data = data; *extracted = result; @@ -290,6 +290,12 @@ pgagroal_read_byte(void* data) return (signed char) *((char*)data); } +uint8_t +pgagroal_read_uint8(void* data) +{ + return (uint8_t) *((char*)data); +} + int16_t pgagroal_read_int16(void* data) { @@ -318,6 +324,22 @@ pgagroal_read_int32(void* data) return res; } +uint32_t +pgagroal_read_uint32(void* data) +{ + uint8_t bytes[] = {*((uint8_t*)data), + *((uint8_t*)(data + 1)), + *((uint8_t*)(data + 2)), + *((uint8_t*)(data + 3))}; + + uint32_t res = (uint32_t)(((uint32_t)bytes[0] << 24)) | + (((uint32_t)bytes[1] << 16)) | + (((uint32_t)bytes[2] << 8)) | + (((uint32_t)bytes[3])); + + return res; +} + long pgagroal_read_long(void* data) { @@ -354,6 +376,12 @@ pgagroal_write_byte(void* data, signed char b) *((char*)(data)) = b; } +void +pgagroal_write_uint8(void* data, uint8_t b) +{ + *((uint8_t*)(data)) = b; +} + void pgagroal_write_int32(void* data, int32_t i) { @@ -368,6 +396,20 @@ pgagroal_write_int32(void* data, int32_t i) *((char*)(data)) = *ptr; } +void +pgagroal_write_uint32(void* data, uint32_t i) +{ + uint8_t* ptr = (uint8_t*)&i; + + *((uint8_t*)(data + 3)) = *ptr; + ptr++; + *((uint8_t*)(data + 2)) = *ptr; + ptr++; + *((uint8_t*)(data + 1)) = *ptr; + ptr++; + *((uint8_t*)(data)) = *ptr; +} + void pgagroal_write_long(void* data, long l) { @@ -568,6 +610,34 @@ pgagroal_libev_engine(unsigned int val) return "Unknown"; } +char* +pgagroal_get_timestamp_string(time_t start_time, time_t end_time, int32_t* seconds) +{ + int32_t total_seconds; + int hours; + int minutes; + int sec; + char elapsed[128]; + char* result = NULL; + + *seconds = 0; + + total_seconds = (int32_t)difftime(end_time, start_time); + + *seconds = total_seconds; + + hours = total_seconds / 3600; + minutes = (total_seconds % 3600) / 60; + sec = total_seconds % 60; + + memset(&elapsed[0], 0, sizeof(elapsed)); + sprintf(&elapsed[0], "%02i:%02i:%02i", hours, minutes, sec); + + result = pgagroal_append(result, &elapsed[0]); + + return result; +} + char* pgagroal_get_home_directory(void) { @@ -639,7 +709,7 @@ pgagroal_exists(char* f) } int -pgagroal_base64_encode(char* raw, size_t raw_length, char** encoded, size_t* encoded_length) +pgagroal_base64_encode(void* raw, size_t raw_length, char** encoded, size_t* encoded_length) { BIO* b64_bio; BIO* mem_bio; @@ -688,7 +758,7 @@ pgagroal_base64_encode(char* raw, size_t raw_length, char** encoded, size_t* enc } int -pgagroal_base64_decode(char* encoded, size_t encoded_length, char** raw, size_t* raw_length) +pgagroal_base64_decode(char* encoded, size_t encoded_length, void** raw, size_t* raw_length) { BIO* b64_bio; BIO* mem_bio; @@ -884,6 +954,15 @@ pgagroal_version_ge(unsigned int major, unsigned int minor, unsigned int patch) } } +bool +pgagroal_ends_with(char* str, char* suffix) +{ + int str_len = strlen(str); + int suffix_len = strlen(suffix); + + return (str_len >= suffix_len) && (strcmp(str + (str_len - suffix_len), suffix) == 0); +} + char* pgagroal_append(char* orig, char* s) { @@ -1222,4 +1301,4 @@ pgagroal_escape_string(char* str) translated_ec_string[idx] = '\0'; // terminator return translated_ec_string; -} \ No newline at end of file +} diff --git a/src/libpgagroal/worker.c b/src/libpgagroal/worker.c index 56528179..173f4ab5 100644 --- a/src/libpgagroal/worker.c +++ b/src/libpgagroal/worker.c @@ -28,8 +28,8 @@ /* pgagroal */ #include +#include #include -#include #include #include #include @@ -67,6 +67,7 @@ pgagroal_worker(int client_fd, char* address, char** argv) struct pipeline p; bool tx_pool = false; int32_t slot = -1; + int transfer_fd = -1; SSL* client_ssl = NULL; SSL* server_ssl = NULL; @@ -253,7 +254,22 @@ pgagroal_worker(int client_fd, char* address, char** argv) } } - pgagroal_management_client_done(getpid()); + if (pgagroal_connection_get(&transfer_fd)) + { + pgagroal_log_error("pgagroal_workers: Unable to get a transfer connection"); + } + + if (pgagroal_connection_id_write(transfer_fd, CONNECTION_CLIENT_DONE)) + { + pgagroal_log_error("pgagroal_workers: Unable to write to a transfer connection"); + } + + if (pgagroal_connection_pid_write(transfer_fd, getpid())) + { + pgagroal_log_error("pgagroal_workers: Unable to write to a transfer connection"); + } + + pgagroal_disconnect(transfer_fd); if (client_ssl != NULL) { diff --git a/src/libpgagroal/zstandard_compression.c b/src/libpgagroal/zstandard_compression.c new file mode 100644 index 00000000..016405c5 --- /dev/null +++ b/src/libpgagroal/zstandard_compression.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2024 The pgagroal community + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may + * be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* pgagroal */ +#include +#include +#include +#include + +/* system */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZSTD_DEFAULT_NUMBER_OF_WORKERS 4 + +int +pgagroal_zstdc_string(char* s, unsigned char** buffer, size_t* buffer_size) +{ + size_t input_size; + size_t max_compressed_size; + size_t compressed_size; + + input_size = strlen(s); + max_compressed_size = ZSTD_compressBound(input_size); + + *buffer = (unsigned char*)malloc(max_compressed_size); + if (*buffer == NULL) + { + pgagroal_log_error("ZSTD: Allocation failed"); + return 1; + } + + compressed_size = ZSTD_compress(*buffer, max_compressed_size, s, input_size, 1); + if (ZSTD_isError(compressed_size)) + { + pgagroal_log_error("ZSTD: Compression error: %s", ZSTD_getErrorName(compressed_size)); + free(*buffer); + return 1; + } + + *buffer_size = compressed_size; + + return 0; +} + +int +pgagroal_zstdd_string(unsigned char* compressed_buffer, size_t compressed_size, char** output_string) +{ + size_t decompressed_size; + size_t result; + + decompressed_size = ZSTD_getFrameContentSize(compressed_buffer, compressed_size); + + if (decompressed_size == ZSTD_CONTENTSIZE_ERROR) + { + pgagroal_log_error("ZSTD: Not a valid compressed buffer"); + return 1; + } + if (decompressed_size == ZSTD_CONTENTSIZE_UNKNOWN) + { + pgagroal_log_error("ZSTD: Unknown decompressed size"); + return 1; + } + + *output_string = (char*)malloc(decompressed_size + 1); + if (*output_string == NULL) + { + pgagroal_log_error("ZSTD: Allocation failed"); + return 1; + } + + result = ZSTD_decompress(*output_string, decompressed_size, compressed_buffer, compressed_size); + if (ZSTD_isError(result)) + { + pgagroal_log_error("ZSTD: Compression error: %s", ZSTD_getErrorName(result)); + free(*output_string); + return 1; + } + + (*output_string)[decompressed_size] = '\0'; + + return 0; +} + diff --git a/src/main.c b/src/main.c index 73d550be..24dd4cdf 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,8 @@ /* pgagroal */ #include #include +#include +#include #include #include #include @@ -40,6 +42,7 @@ #include #include #include +#include #include #include @@ -71,6 +74,7 @@ static void accept_main_cb(struct ev_loop* loop, struct ev_io* watcher, int revents); static void accept_mgt_cb(struct ev_loop* loop, struct ev_io* watcher, int revents); +static void accept_transfer_cb(struct ev_loop* loop, struct ev_io* watcher, int revents); static void accept_metrics_cb(struct ev_loop* loop, struct ev_io* watcher, int revents); static void accept_management_cb(struct ev_loop* loop, struct ev_io* watcher, int revents); static void shutdown_cb(struct ev_loop* loop, ev_signal* w, int revents); @@ -82,11 +86,11 @@ static void max_connection_age_cb(struct ev_loop* loop, ev_periodic* w, int reve static void rotate_frontend_password_cb(struct ev_loop* loop, ev_periodic* w, int revents); static void validation_cb(struct ev_loop* loop, ev_periodic* w, int revents); static void disconnect_client_cb(struct ev_loop* loop, ev_periodic* w, int revents); -static void pgagroal_frontend_user_password_startup(struct main_configuration* config); +static void frontend_user_password_startup(struct main_configuration* config); static bool accept_fatal(int error); static void add_client(pid_t pid); static void remove_client(pid_t pid); -static void reload_configuration(void); +static bool reload_configuration(void); static void create_pidfile_or_exit(void); static void remove_pidfile(void); static void shutdown_ports(void); @@ -100,6 +104,7 @@ static struct accept_io io_uds; static int* main_fds = NULL; static int main_fds_length = -1; static int unix_management_socket = -1; +static int unix_transfer_socket = -1; static int unix_pgsql_socket = -1; static struct accept_io io_metrics[MAX_FDS]; static int* metrics_fds = NULL; @@ -110,6 +115,7 @@ static int management_fds_length = -1; static struct pipeline main_pipeline; static int known_fds[MAX_NUMBER_OF_CONNECTIONS]; static struct client* clients = NULL; +static struct accept_io io_transfer; static void start_mgt(void) @@ -135,6 +141,30 @@ shutdown_mgt(void) errno = 0; } +static void +start_transfer(void) +{ + memset(&io_transfer, 0, sizeof(struct accept_io)); + ev_io_init((struct ev_io*)&io_transfer, accept_transfer_cb, unix_transfer_socket, EV_READ); + io_transfer.socket = unix_transfer_socket; + io_transfer.argv = argv_ptr; + ev_io_start(main_loop, (struct ev_io*)&io_transfer); +} + +static void +shutdown_transfer(void) +{ + struct main_configuration* config; + + config = (struct main_configuration*)shmem; + + ev_io_stop(main_loop, (struct ev_io*)&io_transfer); + pgagroal_disconnect(unix_transfer_socket); + errno = 0; + pgagroal_remove_unix_socket(config->unix_socket_dir, TRANSFER_UDS); + errno = 0; +} + static void start_uds(void) { @@ -769,7 +799,7 @@ main(int argc, char** argv) errx(1, "Invalid configuration"); } - pgagroal_frontend_user_password_startup(config); + frontend_user_password_startup(config); if (pgagroal_validate_hba_configuration(shmem)) { @@ -825,6 +855,8 @@ main(int argc, char** argv) shmem = tmp_shmem; config = (struct main_configuration*)shmem; + pgagroal_memory_init(); + if (getrlimit(RLIMIT_NOFILE, &flimit) == -1) { #ifdef HAVE_LINUX @@ -886,7 +918,7 @@ main(int argc, char** argv) pgagroal_set_proc_title(argc, argv, "main", NULL); - /* Bind Unix Domain Socket for file descriptor transfers */ + /* Bind Unix Domain Socket: Main */ if (pgagroal_bind_unix_socket(config->unix_socket_dir, MAIN_UDS, &unix_management_socket)) { pgagroal_log_fatal("pgagroal: Could not bind to %s/%s", config->unix_socket_dir, MAIN_UDS); @@ -896,6 +928,16 @@ main(int argc, char** argv) goto error; } + /* Bind Unix Domain Socket: Transfer */ + if (pgagroal_bind_unix_socket(config->unix_socket_dir, TRANSFER_UDS, &unix_transfer_socket)) + { + pgagroal_log_fatal("pgagroal: Could not bind to %s/%s", config->unix_socket_dir, TRANSFER_UDS); +#ifdef HAVE_LINUX + sd_notifyf(0, "STATUS=Could not bind to %s/%s", config->unix_socket_dir, TRANSFER_UDS); +#endif + goto error; + } + if (!has_unix_socket) { char pgsql[MISC_LENGTH]; @@ -1008,6 +1050,7 @@ main(int argc, char** argv) goto error; } + start_transfer(); start_mgt(); start_uds(); start_io(); @@ -1105,6 +1148,7 @@ main(int argc, char** argv) } pgagroal_log_debug("Unix Domain Socket: %d", unix_pgsql_socket); pgagroal_log_debug("Management: %d", unix_management_socket); + pgagroal_log_debug("Transfer: %d", unix_transfer_socket); for (int i = 0; i < metrics_fds_length; i++) { pgagroal_log_debug("Metrics: %d", *(metrics_fds + i)); @@ -1170,6 +1214,7 @@ main(int argc, char** argv) shutdown_management(); shutdown_metrics(); shutdown_mgt(); + shutdown_transfer(); shutdown_io(); shutdown_uds(); @@ -1193,6 +1238,8 @@ main(int argc, char** argv) pgagroal_destroy_shared_memory(prometheus_cache_shmem, prometheus_cache_shmem_size); pgagroal_destroy_shared_memory(shmem, shmem_size); + pgagroal_memory_destroy(); + return 0; error: @@ -1221,6 +1268,8 @@ accept_main_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) ai = (struct accept_io*)watcher; config = (struct main_configuration*)shmem; + errno = 0; + memset(&address, 0, sizeof(address)); client_addr_length = sizeof(client_addr); @@ -1265,6 +1314,7 @@ accept_main_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) { shutdown_ports(); pgagroal_flush(FLUSH_GRACEFULLY, "*"); + exit(0); } start_io(); @@ -1325,11 +1375,16 @@ accept_mgt_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) struct sockaddr_in6 client_addr; socklen_t client_addr_length; int client_fd; - signed char id; - int32_t slot; - int payload_i, secondary_payload_i; - char* payload_s = NULL; - char* secondary_payload_s = NULL; + int32_t id; + pid_t pid; + char* str = NULL; + time_t start_time; + time_t end_time; + uint8_t compression = MANAGEMENT_COMPRESSION_NONE; + uint8_t encryption = MANAGEMENT_ENCRYPTION_NONE; + struct accept_io* ai; + struct json* payload = NULL; + struct json* header = NULL; struct main_configuration* config; if (EV_ERROR & revents) @@ -1339,6 +1394,9 @@ accept_mgt_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) } config = (struct main_configuration*)shmem; + ai = (struct accept_io*)watcher; + + errno = 0; client_addr_length = sizeof(client_addr); client_fd = accept(watcher->fd, (struct sockaddr*)&client_addr, &client_addr_length); @@ -1371,230 +1429,615 @@ accept_mgt_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) return; } - /* Process internal management request -- f.ex. returning a file descriptor to the pool */ - pgagroal_management_read_header(client_fd, &id, &slot); - pgagroal_management_read_payload(client_fd, id, &payload_i, &payload_s); + /* Process management request */ + if (pgagroal_management_read_json(NULL, client_fd, &compression, &encryption, &payload)) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_BAD_PAYLOAD, compression, encryption, NULL); + pgagroal_log_error("Management: Bad payload (%d)", MANAGEMENT_ERROR_BAD_PAYLOAD); + goto error; + } - switch (id) + header = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_HEADER); + id = (int32_t)pgagroal_json_get(header, MANAGEMENT_ARGUMENT_COMMAND); + + str = pgagroal_json_to_string(payload, FORMAT_JSON, NULL, 0); + pgagroal_log_debug("Management %d: %s", id, str); + + if (id == MANAGEMENT_FLUSH) + { + pgagroal_log_debug("pgagroal: Management flush"); + + pid = fork(); + if (pid == -1) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_FLUSH_NOFORK, compression, encryption, payload); + pgagroal_log_error("Flush: No fork (%d)", MANAGEMENT_ERROR_FLUSH_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, "flush", NULL); + pgagroal_request_flush(NULL, client_fd, compression, encryption, pyl); + } + } + else if (id == MANAGEMENT_ENABLEDB) { - case MANAGEMENT_TRANSFER_CONNECTION: - pgagroal_log_debug("pgagroal: Management transfer connection: Slot %d FD %d", slot, payload_i); - config->connections[slot].fd = payload_i; - known_fds[slot] = config->connections[slot].fd; + struct json* req = NULL; + struct json* res = NULL; + struct json* databases = NULL; + char* database = NULL; + + pgagroal_log_debug("pgagroal: Management enabledb: "); + pgagroal_pool_status(); + + start_time = time(NULL); + + req = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + database = (char*)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_DATABASE); + + pgagroal_management_create_response(payload, -1, &res); + pgagroal_json_create(&databases); + + for (int i = 0; i < NUMBER_OF_DISABLED; i++) + { + bool found = false; + struct json* js = NULL; - if (config->pipeline == PIPELINE_TRANSACTION) + if (!strcmp("*", database)) { - struct client* c = clients; - while (c != NULL) - { - pgagroal_management_client_fd(slot, c->pid); - c = c->next; - } + memset(&config->disabled[i], 0, MAX_DATABASE_LENGTH); + found = true; + } + else if (!strcmp(config->disabled[i], database)) + { + memset(&config->disabled[i], 0, MAX_DATABASE_LENGTH); + found = true; } - break; - case MANAGEMENT_RETURN_CONNECTION: - pgagroal_log_debug("pgagroal: Management return connection: Slot %d", slot); - break; - case MANAGEMENT_KILL_CONNECTION: - pgagroal_log_debug("pgagroal: Management kill connection: Slot %d", slot); - if (known_fds[slot] == payload_i) + if (found) { - struct client* c = clients; + pgagroal_json_create(&js); - while (c != NULL) - { - pgagroal_management_remove_fd(slot, payload_i, c->pid); - c = c->next; - } + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)database, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_ENABLED, (uintptr_t)true, ValueBool); - pgagroal_disconnect(payload_i); - known_fds[slot] = 0; + pgagroal_json_append(databases, (uintptr_t)js, ValueJSON); } - break; - case MANAGEMENT_FLUSH: - pgagroal_log_debug("pgagroal: Management flush (%d/%s)", payload_i, payload_s); - if (!fork()) + } + + pgagroal_json_put(res, MANAGEMENT_ARGUMENT_DATABASES, (uintptr_t)databases, ValueJSON); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_DISABLEDB) + { + struct json* req = NULL; + struct json* res = NULL; + struct json* databases = NULL; + char* database = NULL; + + pgagroal_log_debug("pgagroal: Management disabledb: "); + pgagroal_pool_status(); + + start_time = time(NULL); + + req = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + database = (char*)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_DATABASE); + + pgagroal_management_create_response(payload, -1, &res); + pgagroal_json_create(&databases); + + for (int i = 0; i < NUMBER_OF_DISABLED; i++) + { + bool found = false; + struct json* js = NULL; + + if (!strcmp("*", database)) { - shutdown_ports(); - pgagroal_flush(payload_i, payload_s); + memcpy(&config->disabled[i], database, strlen(database)); + } + else if (!strcmp(config->disabled[i], "")) + { + memcpy(&config->disabled[i], database, strlen(database)); + break; } - break; - case MANAGEMENT_ENABLEDB: - pgagroal_log_debug("pgagroal: Management enabledb: %s", payload_s); - pgagroal_pool_status(); - for (int i = 0; i < NUMBER_OF_DISABLED; i++) + if (found) { - if (!strcmp("*", payload_s)) + pgagroal_json_create(&js); + + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_DATABASE, (uintptr_t)database, ValueString); + pgagroal_json_put(js, MANAGEMENT_ARGUMENT_ENABLED, (uintptr_t)false, ValueBool); + + pgagroal_json_append(databases, (uintptr_t)js, ValueJSON); + } + } + + pgagroal_json_put(res, MANAGEMENT_ARGUMENT_DATABASES, (uintptr_t)databases, ValueJSON); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_GRACEFULLY) + { + pgagroal_log_debug("pgagroal: Management gracefully"); + pgagroal_pool_status(); + + start_time = time(NULL); + + config->gracefully = true; + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_SHUTDOWN) + { + pgagroal_log_debug("pgagroal: Management shutdown"); + pgagroal_pool_status(); + + start_time = time(NULL); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + + ev_break(loop, EVBREAK_ALL); + keep_running = 0; + } + else if (id == MANAGEMENT_CANCEL_SHUTDOWN) + { + pgagroal_log_debug("pgagroal: Management cancel shutdown"); + pgagroal_pool_status(); + + start_time = time(NULL); + + config->gracefully = false; + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_STATUS) + { + pgagroal_log_debug("pgagroal: Management status"); + pgagroal_pool_status(); + + pid = fork(); + if (pid == -1) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_STATUS_NOFORK, compression, encryption, payload); + pgagroal_log_error("Status: No fork %s (%d)", NULL, MANAGEMENT_ERROR_STATUS_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, "status", NULL); + pgagroal_status(NULL, client_fd, compression, encryption, pyl); + } + } + else if (id == MANAGEMENT_DETAILS) + { + pgagroal_log_debug("pgagroal: Management details"); + pgagroal_pool_status(); + + + pid = fork(); + if (pid == -1) + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_STATUS_NOFORK, compression, encryption, payload); + pgagroal_log_error("Status: No fork %s (%d)", NULL, MANAGEMENT_ERROR_STATUS_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, "status", NULL); + pgagroal_status_details(NULL, client_fd, compression, encryption, pyl); + } + } + else if (id == MANAGEMENT_PING) + { + struct json* response = NULL; + + pgagroal_log_debug("pgagroal: Management ping"); + + start_time = time(NULL); + + pgagroal_management_create_response(payload, -1, &response); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_CLEAR) + { + pgagroal_log_debug("pgagroal: Management clear"); + + start_time = time(NULL); + + pgagroal_prometheus_clear(); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + + } + else if (id == MANAGEMENT_CLEAR_SERVER) + { + pgagroal_log_debug("pgagroal: Management clear server"); + char* server = NULL; + struct json* req = NULL; + + start_time = time(NULL); + + req = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + server = (char*)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_SERVER); + + pgagroal_server_clear(server); + pgagroal_prometheus_failed_servers(); + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_SWITCH_TO) + { + pgagroal_log_debug("pgagroal: Management switch to"); + int old_primary = -1; + signed char server_state; + char* server = NULL; + struct json* req = NULL; + + start_time = time(NULL); + + req = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + server = (char*)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_SERVER); + + for (int i = 0; old_primary == -1 && i < config->number_of_servers; i++) + { + server_state = atomic_load(&config->servers[i].state); + if (server_state == SERVER_PRIMARY) + { + old_primary = i; + } + } + + if (!pgagroal_server_switch(server)) + { + if (!fork()) + { + shutdown_ports(); + if (old_primary != -1) { - memset(&config->disabled[i], 0, MAX_DATABASE_LENGTH); + pgagroal_flush_server(old_primary); } - else if (!strcmp(config->disabled[i], payload_s)) + else { - memset(&config->disabled[i], 0, MAX_DATABASE_LENGTH); + pgagroal_flush(FLUSH_GRACEFULLY, "*"); + exit(0); } } + pgagroal_prometheus_failed_servers(); + } - free(payload_s); - break; - case MANAGEMENT_DISABLEDB: - pgagroal_log_debug("pgagroal: Management disabledb: %s", payload_s); - pgagroal_pool_status(); + end_time = time(NULL); - if (!strcmp("*", payload_s)) - { - for (int i = 0; i < NUMBER_OF_DISABLED; i++) - { - memset(&config->disabled[i], 0, MAX_DATABASE_LENGTH); - } + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else if (id == MANAGEMENT_RELOAD) + { + bool restart = false; + struct json* res = NULL; - memcpy(&config->disabled[0], payload_s, 1); - } - else + pgagroal_log_debug("pgagroal: Management reload"); + + start_time = time(NULL); + + restart = reload_configuration(); + + pgagroal_management_create_response(payload, -1, &res); + + pgagroal_json_put(res, MANAGEMENT_ARGUMENT_RESTART, (uintptr_t)restart, ValueBool); + + end_time = time(NULL); + + 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_GET_PASSWORD) + { + int index = -1; + char* username = NULL; + struct json* req = NULL; + struct json* res = NULL; + + start_time = time(NULL); + + req = (struct json*)pgagroal_json_get(payload, MANAGEMENT_CATEGORY_REQUEST); + username = (char*)pgagroal_json_get(req, MANAGEMENT_ARGUMENT_USERNAME); + + for (int i = 0; index == -1 && i < config->number_of_frontend_users; i++) + { + if (!strcmp(&config->frontend_users[i].username[0], username)) { - for (int i = 0; i < NUMBER_OF_DISABLED; i++) - { - if (!strcmp(config->disabled[i], "")) - { - memcpy(&config->disabled[i], payload_s, strlen(payload_s)); - break; - } - } + index = i; } + } + + pgagroal_management_create_response(payload, -1, &res); + + pgagroal_json_put(res, MANAGEMENT_ARGUMENT_USERNAME, (uintptr_t)username, ValueString); - free(payload_s); - break; - case MANAGEMENT_GRACEFULLY: - pgagroal_log_debug("pgagroal: Management gracefully"); - pgagroal_pool_status(); - config->gracefully = true; - break; - case MANAGEMENT_STOP: - pgagroal_log_debug("pgagroal: Management stop"); + if (index != -1) + { + pgagroal_json_put(res, MANAGEMENT_ARGUMENT_PASSWORD, (uintptr_t)config->frontend_users[index].password, ValueString); + } + else + { + pgagroal_json_put(res, MANAGEMENT_ARGUMENT_PASSWORD, (uintptr_t)NULL, ValueString); + } + + end_time = time(NULL); + + pgagroal_management_response_ok(NULL, client_fd, start_time, end_time, compression, encryption, payload); + } + else + { + pgagroal_management_response_error(NULL, client_fd, NULL, MANAGEMENT_ERROR_UNKNOWN_COMMAND, compression, encryption, payload); + pgagroal_log_error("Unknown: %s (%d)", pgagroal_json_to_string(payload, FORMAT_JSON, NULL, 0), MANAGEMENT_ERROR_UNKNOWN_COMMAND); + goto error; + } + + if (keep_running && config->gracefully) + { + if (atomic_load(&config->active_connections) == 0) + { pgagroal_pool_status(); - ev_break(loop, EVBREAK_ALL); keep_running = 0; - break; - case MANAGEMENT_CANCEL_SHUTDOWN: - pgagroal_log_debug("pgagroal: Management cancel shutdown"); - pgagroal_pool_status(); - config->gracefully = false; - break; - case MANAGEMENT_STATUS: - pgagroal_log_debug("pgagroal: Management status"); - pgagroal_pool_status(); - pgagroal_management_write_status(client_fd, config->gracefully); - break; - case MANAGEMENT_DETAILS: - pgagroal_log_debug("pgagroal: Management details"); - pgagroal_pool_status(); - pgagroal_management_write_status(client_fd, config->gracefully); - pgagroal_management_write_details(client_fd); - break; - case MANAGEMENT_ISALIVE: - pgagroal_log_debug("pgagroal: Management isalive"); - pgagroal_management_write_isalive(client_fd, config->gracefully); - break; - case MANAGEMENT_CONFIG_LS: - pgagroal_log_debug("pgagroal: Management conf ls"); - pgagroal_management_write_conf_ls(client_fd); - break; - case MANAGEMENT_RESET: - pgagroal_log_debug("pgagroal: Management reset"); - pgagroal_prometheus_reset(); - break; - case MANAGEMENT_RESET_SERVER: - pgagroal_log_debug("pgagroal: Management reset server"); - pgagroal_server_reset(payload_s); - pgagroal_prometheus_failed_servers(); - break; - case MANAGEMENT_CLIENT_DONE: - pgagroal_log_debug("pgagroal: Management client done"); - pid_t p = (pid_t)payload_i; - remove_client(p); - break; - case MANAGEMENT_SWITCH_TO: - pgagroal_log_debug("pgagroal: Management switch to"); - int old_primary = -1; - signed char server_state; - for (int i = 0; old_primary == -1 && i < config->number_of_servers; i++) + ev_break(loop, EVBREAK_ALL); + } + } + + free(str); + + pgagroal_json_destroy(payload); + + pgagroal_disconnect(client_fd); + + pgagroal_prometheus_self_sockets_sub(); + + return; + +error: + + free(str); + + pgagroal_disconnect(client_fd); + + pgagroal_prometheus_self_sockets_sub(); +} + +static void +accept_transfer_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) +{ + struct sockaddr_in6 client_addr; + socklen_t client_addr_length; + int client_fd = 0; + int id = -1; + pid_t pid = 0; + int32_t slot = -1; + int fd = -1; + struct main_configuration* config; + + if (EV_ERROR & revents) + { + pgagroal_log_trace("accept_mgt_cb: got invalid event: %s", strerror(errno)); + return; + } + + config = (struct main_configuration*)shmem; + + errno = 0; + + client_addr_length = sizeof(client_addr); + client_fd = accept(watcher->fd, (struct sockaddr*)&client_addr, &client_addr_length); + + pgagroal_prometheus_self_sockets_add(); + + if (client_fd == -1) + { + if (accept_fatal(errno) && keep_running) + { + pgagroal_log_warn("Restarting transfer due to: %s (%d)", strerror(errno), watcher->fd); + + shutdown_mgt(); + + if (pgagroal_bind_unix_socket(config->unix_socket_dir, TRANSFER_UDS, &unix_transfer_socket)) { - server_state = atomic_load(&config->servers[i].state); - if (server_state == SERVER_PRIMARY) - { - old_primary = i; - } + pgagroal_log_fatal("pgagroal: Could not bind to %s/%s", config->unix_socket_dir, TRANSFER_UDS); + exit(1); } - if (!pgagroal_server_switch(payload_s)) + start_mgt(); + + pgagroal_log_debug("Transfer: %d", unix_transfer_socket); + } + else + { + pgagroal_log_debug("accept: %s (%d)", strerror(errno), watcher->fd); + } + errno = 0; + return; + } + + /* Process transfer request */ + if (pgagroal_connection_id_read(client_fd, &id)) + { + goto error; + } + + if (id == CONNECTION_TRANSFER) + { + pgagroal_log_trace("pgagroal: Transfer connection"); + + if (pgagroal_connection_transfer_read(client_fd, &slot, &fd)) + { + pgagroal_log_error("pgagroal: Transfer connection: Slot %d FD %d", slot, fd); + goto error; + } + + config->connections[slot].fd = fd; + known_fds[slot] = config->connections[slot].fd; + + if (config->pipeline == PIPELINE_TRANSACTION) + { + struct client* c = clients; + while (c != NULL) { - if (!fork()) + int c_fd = -1; + + if (pgagroal_connection_get_pid(c->pid, &c_fd)) + { + goto error; + } + + if (pgagroal_connection_id_write(c_fd, CONNECTION_CLIENT_FD)) { - shutdown_ports(); - if (old_primary != -1) - { - pgagroal_flush_server(old_primary); - } - else - { - pgagroal_flush(FLUSH_GRACEFULLY, "*"); - } + goto error; } - pgagroal_prometheus_failed_servers(); + + if (pgagroal_connection_transfer_write(c_fd, slot)) + { + goto error; + } + + pgagroal_disconnect(c_fd); + + c = c->next; } - break; - case MANAGEMENT_RELOAD: - pgagroal_log_debug("pgagroal: Management reload"); - reload_configuration(); - break; - case MANAGEMENT_CONFIG_GET: - pgagroal_log_debug("pgagroal: Management config-get for key <%s>", payload_s); - pgagroal_management_write_config_get(client_fd, payload_s); - break; - case 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); - break; - case MANAGEMENT_GET_PASSWORD: + } + + pgagroal_log_debug("pgagroal: Transfer connection: Slot %d FD %d", slot, fd); + + } + else if (id == CONNECTION_RETURN) + { + pgagroal_log_trace("pgagroal: Transfer return connection"); + + if (pgagroal_connection_slot_read(client_fd, &slot)) { - // get frontend password - char frontend_password[MAX_PASSWORD_LENGTH]; - memset(frontend_password, 0, sizeof(frontend_password)); + pgagroal_log_error("pgagroal: Transfer return connection: Slot %d", slot); + goto error; + } - for (int i = 0; i < config->number_of_frontend_users; i++) + pgagroal_log_debug("pgagroal: Transfer return connection: Slot %d", slot); + } + else if (id == CONNECTION_KILL) + { + pgagroal_log_trace("pgagroal: Transfer kill connection"); + + if (pgagroal_connection_transfer_read(client_fd, &slot, &fd)) + { + pgagroal_log_error("pgagroal: Transfer kill connection: Slot %d FD %d", slot, fd); + goto error; + } + + if (known_fds[slot] == fd) + { + struct client* c = clients; + while (c != NULL) { - if (!strcmp(&config->frontend_users[i].username[0], payload_s)) + int c_fd = -1; + + if (pgagroal_connection_get_pid(c->pid, &c_fd)) + { + goto error; + } + + if (pgagroal_connection_id_write(c_fd, CONNECTION_REMOVE_FD)) { - memcpy(frontend_password, config->frontend_users[i].password, strlen(config->frontend_users[i].password)); + goto error; } + + if (pgagroal_connection_transfer_write(c_fd, slot)) + { + goto error; + } + + pgagroal_disconnect(c_fd); + + c = c->next; } - // Send password to the vault - pgagroal_management_write_get_password(NULL, client_fd, frontend_password); - pgagroal_disconnect(client_fd); - return; + pgagroal_disconnect(fd); + known_fds[slot] = 0; } - default: - pgagroal_log_debug("pgagroal: Unknown management id: %d", id); - break; - } - if (keep_running && config->gracefully) + pgagroal_log_debug("pgagroal: Transfer kill connection: Slot %d FD %d", slot, fd); + } + else if (id == CONNECTION_CLIENT_DONE) { - if (atomic_load(&config->active_connections) == 0) + pgagroal_log_debug("pgagroal: Transfer client done"); + + if (pgagroal_connection_pid_read(client_fd, &pid)) { - pgagroal_pool_status(); - keep_running = 0; - ev_break(loop, EVBREAK_ALL); + pgagroal_log_error("pgagroal: Transfer client done: PID %d", (int)pid); + goto error; } + + remove_client(pid); + + pgagroal_log_debug("pgagroal: Transfer client done: PID %d", (int)pid); } pgagroal_disconnect(client_fd); pgagroal_prometheus_self_sockets_sub(); + + return; + +error: + + pgagroal_disconnect(client_fd); + + pgagroal_prometheus_self_sockets_sub(); } static void @@ -1614,6 +2057,8 @@ accept_metrics_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) config = (struct main_configuration*)shmem; + errno = 0; + client_addr_length = sizeof(client_addr); client_fd = accept(watcher->fd, (struct sockaddr*)&client_addr, &client_addr_length); @@ -1690,6 +2135,8 @@ accept_management_cb(struct ev_loop* loop, struct ev_io* watcher, int revents) config = (struct main_configuration*)shmem; + errno = 0; + client_addr_length = sizeof(client_addr); client_fd = accept(watcher->fd, (struct sockaddr*)&client_addr, &client_addr_length); @@ -1920,7 +2367,7 @@ accept_fatal(int error) } static void -pgagroal_frontend_user_password_startup(struct main_configuration* config) +frontend_user_password_startup(struct main_configuration* config) { char* pwd = NULL; @@ -1931,11 +2378,11 @@ pgagroal_frontend_user_password_startup(struct main_configuration* config) memcpy(&config->frontend_users[i].username, config->users[i].username, strlen(config->users[i].username)); if (pgagroal_generate_password(config->rotate_frontend_password_length, &pwd)) { - pgagroal_log_debug("pgagroal_frontend_user_password_startup: unable to generate random password at startup"); + pgagroal_log_debug("frontend_user_password_startup: unable to generate random password at startup"); return; } memcpy(&config->frontend_users[i].password, pwd, strlen(pwd) + 1); - pgagroal_log_trace("pgagroal_frontend_user_password_startup: frontend user with username=%s initiated", config->frontend_users[i].username); + pgagroal_log_trace("frontend_user_password_startup: frontend user with username=%s initiated", config->frontend_users[i].username); free(pwd); } config->number_of_frontend_users = config->number_of_users; @@ -2006,9 +2453,10 @@ remove_client(pid_t pid) } } -static void +static bool reload_configuration(void) { + bool restart = false; char pgsql[MISC_LENGTH]; struct main_configuration* config; @@ -2019,7 +2467,7 @@ reload_configuration(void) shutdown_metrics(); shutdown_management(); - pgagroal_reload_configuration(); + pgagroal_reload_configuration(&restart); memset(&pgsql, 0, sizeof(pgsql)); snprintf(&pgsql[0], sizeof(pgsql), ".s.PGSQL.%d", config->common.port); @@ -2107,7 +2555,12 @@ reload_configuration(void) pgagroal_log_debug("Remote management: %d", *(management_fds + i)); } - return; + if (restart) + { + remove_pidfile(); + } + + exit(0); error: remove_pidfile(); diff --git a/src/vault.c b/src/vault.c index 5fdf3128..543e468e 100644 --- a/src/vault.c +++ b/src/vault.c @@ -179,7 +179,9 @@ route_users(char* username, char** response, SSL* s_ssl, int client_fd) { struct vault_configuration* config = (struct vault_configuration*)shmem; int client_pgagroal_fd = -1; - char password[MAX_PASSWORD_LENGTH + 1]; + struct json* read = NULL; + struct json* res = NULL; + char* password = NULL; // Connect to pgagroal management port if (connect_pgagroal(config, config->vault_server.user.username, config->vault_server.user.password, &s_ssl, &client_pgagroal_fd)) // Change NULL to ssl @@ -190,10 +192,8 @@ route_users(char* username, char** response, SSL* s_ssl, int client_fd) return; } - memset(password, 0, MAX_PASSWORD_LENGTH); - // Call GET_PASSWORD at management port - if (pgagroal_management_get_password(s_ssl, client_pgagroal_fd, username, password)) + if (pgagroal_management_request_get_password(s_ssl, client_pgagroal_fd, username, COMPRESSION_NONE, ENCRYPTION_AES_256_CBC, MANAGEMENT_OUTPUT_FORMAT_JSON)) { pgagroal_log_error("pgagroal-vault: Couldn't get password from the management"); // Send Error Response @@ -201,16 +201,28 @@ route_users(char* username, char** response, SSL* s_ssl, int client_fd) return; } - if (strlen(password) == 0) // user not found + if (pgagroal_management_read_json(s_ssl, client_pgagroal_fd, NULL, NULL, &read)) + { + pgagroal_log_warn("pgagroal-vault: Couldn't receive the result"); + } + + if (read != NULL) + { + res = (struct json*)pgagroal_json_get(read, MANAGEMENT_CATEGORY_RESPONSE); + password = (char*)pgagroal_json_get(res, MANAGEMENT_ARGUMENT_PASSWORD); + } + + if (password == NULL || strlen(password) == 0) // user not found { pgagroal_log_warn("pgagroal-vault: Couldn't find the user: %s", username); route_not_found(response); } - else { route_found(response, password); } + + pgagroal_json_destroy(read); } static void