From 4683a4658c6a7b6a88bcf0f285db72b8c616d047 Mon Sep 17 00:00:00 2001 From: Kaitmazian Maksim <m.kaitmazian@picodata.io> Date: Fri, 22 Mar 2024 22:19:13 +0300 Subject: [PATCH] remove C implementation of pgproto --- pgproto/CMakeLists.txt | 43 --- pgproto/src/CMakeLists.txt | 28 -- pgproto/src/postgres/CMakeLists.txt | 13 - pgproto/src/postgres/attributes.c | 269 ------------------ pgproto/src/postgres/attributes.h | 67 ----- pgproto/src/postgres/auth.c | 224 --------------- pgproto/src/postgres/auth.h | 13 - pgproto/src/postgres/messages.c | 108 ------- pgproto/src/postgres/messages.h | 57 ---- pgproto/src/postgres/port.c | 316 --------------------- pgproto/src/postgres/port.h | 191 ------------- pgproto/src/postgres/postgres.c | 268 ------------------ pgproto/src/postgres/postgres.h | 18 -- pgproto/src/postgres/report.c | 87 ------ pgproto/src/postgres/report.h | 39 --- pgproto/src/postgres/startup.c | 249 ----------------- pgproto/src/postgres/startup.h | 16 -- pgproto/src/server/CMakeLists.txt | 7 - pgproto/src/server/server.c | 402 --------------------------- pgproto/src/server/server.h | 40 --- pgproto/src/tarantool/CMakeLists.txt | 7 - pgproto/src/tarantool/diag.h | 60 ---- pgproto/src/tarantool/evio.c | 131 --------- pgproto/src/tarantool/evio.h | 42 --- pgproto/src/tarantool/sio.c | 235 ---------------- pgproto/src/tarantool/sio.h | 122 -------- pgproto/src/tarantool/trivia/util.h | 100 ------- 27 files changed, 3152 deletions(-) delete mode 100644 pgproto/CMakeLists.txt delete mode 100644 pgproto/src/CMakeLists.txt delete mode 100644 pgproto/src/postgres/CMakeLists.txt delete mode 100644 pgproto/src/postgres/attributes.c delete mode 100644 pgproto/src/postgres/attributes.h delete mode 100644 pgproto/src/postgres/auth.c delete mode 100644 pgproto/src/postgres/auth.h delete mode 100644 pgproto/src/postgres/messages.c delete mode 100644 pgproto/src/postgres/messages.h delete mode 100644 pgproto/src/postgres/port.c delete mode 100644 pgproto/src/postgres/port.h delete mode 100644 pgproto/src/postgres/postgres.c delete mode 100644 pgproto/src/postgres/postgres.h delete mode 100644 pgproto/src/postgres/report.c delete mode 100644 pgproto/src/postgres/report.h delete mode 100644 pgproto/src/postgres/startup.c delete mode 100644 pgproto/src/postgres/startup.h delete mode 100644 pgproto/src/server/CMakeLists.txt delete mode 100644 pgproto/src/server/server.c delete mode 100644 pgproto/src/server/server.h delete mode 100644 pgproto/src/tarantool/CMakeLists.txt delete mode 100644 pgproto/src/tarantool/diag.h delete mode 100644 pgproto/src/tarantool/evio.c delete mode 100644 pgproto/src/tarantool/evio.h delete mode 100644 pgproto/src/tarantool/sio.c delete mode 100644 pgproto/src/tarantool/sio.h delete mode 100644 pgproto/src/tarantool/trivia/util.h diff --git a/pgproto/CMakeLists.txt b/pgproto/CMakeLists.txt deleted file mode 100644 index 4bf7a5ba4b..0000000000 --- a/pgproto/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -project(protocol C) - -add_compile_options( - -Wunused-label - # -Wunused-macros - -Wunused-parameter - -Wunused-variable -) - -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) -set(CMAKE_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_INCLUDE_PATH}) - -include(utils) - -# It's not really a target dir, it's `target/debug` or something... -# TODO: rename this variable to PICODATA_BUILD_DIR -if (NOT PICODATA_TARGET_DIR) - message(FATAL_ERROR "PICODATA_TARGET_DIR not set") -endif() - -set(TARANTOOL_PREFIX "${PICODATA_TARGET_DIR}/build/tarantool-sys/tarantool-prefix") -set(TARANTOOL_INCLUDE_DIRS "${TARANTOOL_PREFIX}/include/tarantool") - -# Set search hints and find the MsgPuck library -list(APPEND MSGPUCK_ROOT_DIR "${PICODATA_TARGET_DIR}/../../tarantool-sys/src/lib/msgpuck") -list(APPEND MSGPUCK_ROOT_DIR "${TARANTOOL_PREFIX}/src/tarantool-build/src/lib/msgpuck") -find_package(MsgPuck REQUIRED) - -# All the necessary prerequisites should be available if we've found the executable -set(PICODATA_EXECUTABLE "${PICODATA_TARGET_DIR}/picodata") -get_filename_component(PICODATA_EXECUTABLE ${PICODATA_EXECUTABLE} REALPATH) -if (NOT EXISTS "${PICODATA_EXECUTABLE}") - message(FATAL_ERROR "Can't find picodata executable at ${PICODATA_EXECUTABLE}") -endif() - -include_directories(${PROJECT_SOURCE_DIR}/src) -include_directories(${TARANTOOL_INCLUDE_DIRS}) -include_directories(${MSGPUCK_INCLUDE_DIRS}) - -add_subdirectory(src) -add_subdirectory(test) diff --git a/pgproto/src/CMakeLists.txt b/pgproto/src/CMakeLists.txt deleted file mode 100644 index 3dcd3ee5b2..0000000000 --- a/pgproto/src/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -add_compile_options(-Wall -Wextra -Werror) - -function(build_module module files) - add_library(${module} SHARED ${files}) - if(TARGET_OS_DARWIN) - set_target_properties(${module} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") - endif(TARGET_OS_DARWIN) -endfunction() - -add_compile_flags("C;CXX" - "-Wno-unused-parameter") - -# WARNING: This change affects current cmake variable scope and so -# a user should care to don't use it in a top level scope. -# The dynamic libraries will be loaded from tarantool executable -# and will use symbols from it. So it is completely okay to have -# unresolved symbols at build time. -string(REPLACE "-Wl,--no-undefined" "" - CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") - -if (APPLE) - set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} -undefined suppress -flat_namespace -lc++") -endif(APPLE) - -add_subdirectory(tarantool) -add_subdirectory(postgres) -add_subdirectory(server) diff --git a/pgproto/src/postgres/CMakeLists.txt b/pgproto/src/postgres/CMakeLists.txt deleted file mode 100644 index 7e43969db8..0000000000 --- a/pgproto/src/postgres/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(POSTGRES_SOURCES - attributes.c - auth.c - messages.c - port.c - postgres.c - report.c - startup.c -) - -add_library(postgres STATIC ${POSTGRES_SOURCES}) -set_property(TARGET postgres PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_options(postgres PRIVATE "-Wno-format-security") diff --git a/pgproto/src/postgres/attributes.c b/pgproto/src/postgres/attributes.c deleted file mode 100644 index 5c3d1737d1..0000000000 --- a/pgproto/src/postgres/attributes.c +++ /dev/null @@ -1,269 +0,0 @@ -/** - * PostgreSQL Database Management System - * (formerly known as Postgres, then as Postgres95) - * - * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group - * - * Portions Copyright (c) 1994, The Regents of the University of California - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - */ -#include <msgpuck.h> -#include <inttypes.h> - -#include "attributes.h" -#include "port.h" -#include "report.h" - -enum { - TYPEMOD_DEFAULT = -1, -}; - -static void -write_unknown(const struct pg_attribute *this, - struct pg_port *port, const char **data) -{ - uint16_t format = this->format; - assert(format == TEXT_FORMAT || format == BINARY_FORMAT); - int type = mp_typeof(**data); - assert(type == MP_NIL); - mp_decode_nil(data); - if (format == TEXT_FORMAT) { - pg_write_len_str(port, "NULL"); - } else { - assert(false && "binary format is not supported"); - } -} - -static void -pg_attribute_unknown(struct pg_attribute *att, - const char *name, uint32_t name_len, - uint16_t format, uint32_t typemod) -{ - att->name = name; - att->name_len = name_len; - att->type_oid = 705; - att->type_len = -2; - att->typemod = typemod; - att->format = format; - att->write = write_unknown; -} - -static void -write_int8(const struct pg_attribute *this, - struct pg_port *port, const char **data) -{ - uint16_t format = this->format; - if (mp_typeof(**data) == MP_NIL) - return write_unknown(this, port, data); - assert(format == TEXT_FORMAT || format == BINARY_FORMAT); - int type = mp_typeof(**data); - assert(type == MP_UINT || type == MP_INT); - if (format == TEXT_FORMAT) { - if (type == MP_INT) { - int64_t val = mp_decode_int(data); - pg_write_len_str(port, "%"PRId64, val); - } else if (type == MP_UINT) { - uint64_t val = mp_decode_uint(data); - pg_write_len_str(port, "%"PRIu64, val); - } - } else { - assert(false && "binary format is not supported"); - } -} - -static void -pg_attribute_int8(struct pg_attribute *att, - const char *name, uint32_t name_len, - uint16_t format, uint32_t typemod) -{ - att->name = name; - att->name_len = name_len; - att->type_oid = 20; - att->type_len = 8; - att->typemod = typemod; - att->format = format; - att->write = write_int8; -} - -static void -write_text(const struct pg_attribute *this, - struct pg_port *port, const char **data) -{ - uint16_t format = this->format; - if (mp_typeof(**data) == MP_NIL) - return write_unknown(this, port, data); - assert(format == TEXT_FORMAT || format == BINARY_FORMAT); - int type = mp_typeof(**data); - assert(type == MP_STR); - uint32_t len; - const char *str = mp_decode_str(data, &len); - if (format == TEXT_FORMAT) { - pg_write_len_str(port, "%.*s", len, str); - } else { - assert(false && "binary format is not supported"); - } -} - -static void -pg_attribute_text(struct pg_attribute *att, const char *name, uint32_t name_len, - uint16_t format, uint32_t typemod) -{ - att->name = name; - att->name_len = name_len; - att->type_oid = 25; - att->type_len = -1; - att->typemod = typemod; - att->format = format; - att->write = write_text; -} - -static void -write_bool(const struct pg_attribute *this, - struct pg_port *port, const char **data) -{ - uint16_t format = this->format; - if (mp_typeof(**data) == MP_NIL) - return write_unknown(this, port, data); - assert(format == TEXT_FORMAT || format == BINARY_FORMAT); - int type = mp_typeof(**data); - assert(type == MP_BOOL); - bool val = mp_decode_bool(data); - if (format == TEXT_FORMAT) { - pg_write_len_str(port, val ? "t" : "f"); - } else { - assert(false && "binary format is not supported"); - } -} - -static void -pg_attribute_bool(struct pg_attribute *att, - const char *name, uint32_t name_len, - uint16_t format, uint32_t typemod) -{ - att->name = name; - att->name_len = name_len; - att->type_oid = 16; - att->type_len = 1; - att->typemod = typemod; - att->format = format; - att->write = write_bool; -} - -static void -write_float8(const struct pg_attribute *this, - struct pg_port *port, const char **data) -{ - uint16_t format = this->format; - if (mp_typeof(**data) == MP_NIL) - return write_unknown(this, port, data); - assert(format == TEXT_FORMAT || format == BINARY_FORMAT); - int type = mp_typeof(**data); - assert(type == MP_FLOAT || type == MP_DOUBLE); - double val = type == MP_DOUBLE ? mp_decode_double(data) : - mp_decode_float(data); - if (format == TEXT_FORMAT) { - pg_write_len_str(port, "%lf" , val); - } else { - assert(false && "binary format is not supported"); - } -} - -static void -pg_attribute_float8(struct pg_attribute *att, - const char *name, uint32_t name_len, - uint16_t format, uint32_t typemod) -{ - att->name = name; - att->name_len = name_len; - att->type_oid = 701; - att->type_len = 8; - att->typemod = typemod; - att->format = format; - att->write = write_float8; -} - -/** - * Get row description from the metadata. - * Format is not mentioned in metadata so the caller must choose it him self. - */ -int -parse_metadata(const char **data, - struct row_description *row_desc, - uint16_t format) -{ - uint32_t natts = mp_decode_array(data); - if (natts >= (uint16_t)-1) { - say_error("too many attributes: %"PRIu32, natts); - return -1; - } - row_desc->natts = (uint16_t)natts; - row_desc->atts = box_region_alloc(sizeof(*row_desc->atts) * natts); - const char *str; - uint32_t len; - for (uint32_t i = 0; i < row_desc->natts; ++i) { - assert(mp_typeof(**data) == MP_MAP); - uint32_t map_size = mp_decode_map(data); - assert(map_size == 2); - str = mp_decode_str(data, &len); - assert(len == 4 && strncmp(str, "name", 4) == 0); - uint32_t name_len; - const char *name = mp_decode_str(data, &name_len); - str = mp_decode_str(data, &len); - assert(len == 4 && strncmp(str, "type", 4) == 0); - - const char *type = mp_decode_str(data, &len); - struct pg_attribute *att = &row_desc->atts[i]; - if (strncmp(type, "integer", len) == 0) { - pg_attribute_int8(att, name, name_len, format, - TYPEMOD_DEFAULT); - } else if (strncmp(type, "string", len) == 0) { - pg_attribute_text(att, name, name_len, format, - TYPEMOD_DEFAULT); - } else if (strncmp(type, "boolean", len) == 0) { - pg_attribute_bool(att, name, name_len, format, - TYPEMOD_DEFAULT); - } else if (strncmp(type, "double", len) == 0) { - pg_attribute_float8(att, name, name_len, format, - TYPEMOD_DEFAULT); - } else if (strncmp(type, "any", len) == 0) { - pg_attribute_unknown(att, name, name_len, format, - TYPEMOD_DEFAULT); - } else { - /** - * Unsigned type is not supported by postgres. - * Decimal type is supported in picodata and can be - * matched to NUMERIC type in postgres but it is not - * trivial to work with it compared to the other types. - */ - pg_error(NULL, ERRCODE_INTERNAL_ERROR, - "unknown type \'%.*s\'", len, type); - return -1; - } - } - return 0; -} - -void -row_description_explain(struct row_description *row_desc) -{ - row_desc->natts = 1; - row_desc->atts = box_region_alloc(sizeof(*row_desc->atts)); - pg_attribute_text(row_desc->atts, "QUERY PLAN", strlen("QUERY PLAN"), - TEXT_FORMAT, TYPEMOD_DEFAULT); -} diff --git a/pgproto/src/postgres/attributes.h b/pgproto/src/postgres/attributes.h deleted file mode 100644 index 4239944767..0000000000 --- a/pgproto/src/postgres/attributes.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include <stdint.h> - -enum { - TEXT_FORMAT = 0, - BINARY_FORMAT = 1, -}; - -struct pg_port; - -/** Definition of an attribute. */ -struct pg_attribute { - /** Column name. */ - const char *name; - /** Len of a name. */ - uint32_t name_len; - /** OID that defines the data type of this attribute. */ - uint32_t type_oid; - /** Len of the data type. */ - uint16_t type_len; - /** - * typmod records type-specific data supplied at table creation time - * (for example, the max length of a varchar field). The - * value will generally be -1 for types that do not need typmod. - */ - uint32_t typemod; - /** Format is used to send the attribute. */ - uint16_t format; - - /** - * Decode an attribute from msgpuck and write it to the port. - * It is used for forming a DataRow message. - */ - void (*write)(const struct pg_attribute *this, - struct pg_port *port, const char **data); -}; - -/** - * Row description message. - * Describes the format of subsequent RowData messages. - */ -struct row_description { - /** Number of attributes. */ - uint32_t natts; - /** Attribute descriptions. */ - struct pg_attribute *atts; -}; - -/** - * Get a row description from the metadata. - * Format is not mentioned in metadata so the caller must choose it him self. - * After the call metadata is consumed and the data points to the begining of - * the rows array. - * Allocates on box region. - */ -int -parse_metadata(const char **data, - struct row_description *row_desc, - uint16_t format); - -/** - * Get a row description for an explain query. - * Allocates on box region. - */ -void -row_description_explain(struct row_description *row_desc); diff --git a/pgproto/src/postgres/auth.c b/pgproto/src/postgres/auth.c deleted file mode 100644 index 09cfef4a4b..0000000000 --- a/pgproto/src/postgres/auth.c +++ /dev/null @@ -1,224 +0,0 @@ -/** - * PostgreSQL Database Management System - * (formerly known as Postgres, then as Postgres95) - * - * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group - * - * Portions Copyright (c) 1994, The Regents of the University of California - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - */ - -#include "auth.h" -#include "port.h" -#include "report.h" - -#include <module.h> -#include <msgpuck.h> -#include <inttypes.h> - -/** - * * Taken from Postgres sources * - * These are the authentication request codes sent by the backend. - */ -enum PG_AUTH_REQUEST_CODE { - AUTH_REQ_OK = 0, /* User is authenticated */ - AUTH_REQ_KRB4 = 1, /* Kerberos V4. Not supported any more. */ - AUTH_REQ_KRB5 = 2, /* Kerberos V5. Not supported any more. */ - AUTH_REQ_PASSWORD = 3, /* Password */ - AUTH_REQ_CRYPT = 4, /* crypt password. Not supported any more. */ - AUTH_REQ_MD5 = 5, /* md5 password */ - AUTH_REQ_SCM_CREDS = 6, /* transfer SCM credentials */ - AUTH_REQ_GSS = 7, /* GSSAPI without wrap() */ - AUTH_REQ_GSS_CONT = 8, /* Continue GSS exchanges */ - AUTH_REQ_SSPI = 9, /* SSPI negotiate without wrap() */ - AUTH_REQ_SASL = 10, /* Begin SASL authentication */ - AUTH_REQ_SASL_CONT = 11, /* Continue SASL authentication */ - AUTH_REQ_SASL_FIN = 12 /* Final SASL message */ -}; - -/** Send AuthRequest packet */ -static void -pg_send_auth_request(struct pg_port *port, enum PG_AUTH_REQUEST_CODE code, - const char *extra_data, size_t extra_len) -{ - pg_begin_msg(port, 'R'); - pg_write_uint32(port, code); - if (extra_data != NULL) - pg_write_bytes(port, extra_data, extra_len); - pg_end_msg(port); - - /** - * Flush the message so the client can see it and send response. - * In case of AUTH_REQ_OK flush is performed when we are ready for - * queries. - */ - if (code != AUTH_REQ_OK) - pg_flush(port); - - say_debug("sent auth request(%d) to user \'%s\'", code, port->user); -} - -/** AuthResponse packet format. */ -struct auth_response { - /* Packet type, equals to 'p' for auth response. */ - uint8_t type; - /* Packet length, including len field itself. */ - uint32_t len; - /* Authentication data. The context depends on authentication type. */ - char *data; -}; - -/** Receive AuthResponse packet. */ -static int -pg_recv_auth_response(struct pg_port *port, struct auth_response *packet) -{ - if (pg_read_uint8(port, &packet->type) < 0) { - /* - * If the client just disconnects without offering a password, - * don't make a log entry. This is legal per protocol spec and - * in fact commonly done by psql, so complaining just clutters - * the log. - * - * In case of error the corresponding message is already written - * to the log. - */ - return -1; - } - if (packet->type != 'p') { - pg_error(port, ERRCODE_PROTOCOL_VIOLATION, - "expected password response, got \'%c\' message type", - packet->type); - return -1; - } - - if (pg_read_uint32(port, &packet->len) < 0) - return -1; - - if (packet->len < sizeof(packet->len)) { - pg_error(port, ERRCODE_PROTOCOL_VIOLATION, - "invalid auth response message length: %"PRIu32, - packet->len); - return -1; - } - uint32_t to_read = packet->len - sizeof(packet->len); - - packet->data = pg_read_bytes(port, to_read); - if (packet->data == NULL) - return -1; - - say_debug("received auth response from user \'%s\'", port->user); - pg_read_gc(port); - return 0; -} - -enum { - /** - * 64 seems to be enough because it is almost twice the payload: - * 35 bytes for password and 3 bytes for method name. - */ - MD5_AUTH_PACKET_SIZE = 64, -}; - -/** - * Encode an auth_packet in format that is used in `authenticate` function. - * Packet is allocated on box region. - */ -void -encode_md5_auth_packet(char *buff, - const char *client_pass, size_t pass_len) -{ - const char *method = "md5"; - const size_t method_len = strlen("md5"); - uint32_t packet_size = mp_sizeof_array(2) + mp_sizeof_str(method_len) + - mp_sizeof_str(pass_len); - assert(packet_size < MD5_AUTH_PACKET_SIZE); - (void)packet_size; - - char *tuple = buff; - tuple = mp_encode_array(tuple, 2); - tuple = mp_encode_str(tuple, method, method_len); - tuple = mp_encode_str(tuple, client_pass, pass_len); -} - -/** - * Perform md5 authentication message exchange and try to authenticate the user. - * Return values are 0 in case of success and non-zero value in case of error. - */ -static int -pg_authenticate_md5(struct pg_port *port) -{ - uint32_t salt; - random_bytes((char *)&salt, sizeof(salt)); - pg_send_auth_request(port, AUTH_REQ_MD5, (char *)&salt, sizeof(salt)); - - struct auth_response response; - if (pg_recv_auth_response(port, &response) != 0) - return -1; - - const char *client_pass = response.data; - size_t pass_len = strlen(response.data); - - char auth_packet[MD5_AUTH_PACKET_SIZE]; - encode_md5_auth_packet(auth_packet, client_pass, pass_len); - int ret = authenticate(port->user, strlen(port->user), - (const char *)&salt, auth_packet); - return ret; -} - -/** Notify the client of successful authentication. */ -static void -pg_send_auth_ok(struct pg_port *port) -{ - pg_send_auth_request(port, AUTH_REQ_OK, NULL, 0); -} - -int -pg_authenticate(struct pg_port *port) -{ - /** - * @note: see ClientAuthentication() - * from backend/libpq/auth.c and so on in postgres sources - */ - assert(port->user != NULL && "startup message was not processed"); - - const char *auth_method = - user_auth_method_name(port->user, strlen(port->user)); - if (auth_method == NULL) - goto auth_failed; - - int rc = -1; - if (strcmp(auth_method, "md5") == 0) { - rc = pg_authenticate_md5(port); - } else { - say_debug("unknown auth method %s", auth_method); - goto auth_failed; - } - if (rc != 0) - goto auth_failed; - - pg_send_auth_ok(port); - say_info("authenticated"); - return 0; - -auth_failed: - pg_error(port, ERRCODE_INVALID_PASSWORD, - "%s authentication failed for user \'%s\'", - auth_method != NULL ? auth_method : "", port->user); - return -1; -} diff --git a/pgproto/src/postgres/auth.h b/pgproto/src/postgres/auth.h deleted file mode 100644 index 9b7627cb64..0000000000 --- a/pgproto/src/postgres/auth.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -struct pg_port; - -/** - * Authenticate the postgres client after processing a startup message. - * - * @retval 0 on success. - * @retval -1 on error, error message is written to the server log and sent - * to the client. - */ -int -pg_authenticate(struct pg_port *port); diff --git a/pgproto/src/postgres/messages.c b/pgproto/src/postgres/messages.c deleted file mode 100644 index eabbb5bc9f..0000000000 --- a/pgproto/src/postgres/messages.c +++ /dev/null @@ -1,108 +0,0 @@ -#include <msgpuck.h> - -#include "messages.h" -#include "port.h" -#include "attributes.h" - -void -send_ready_for_query(struct pg_port *port) -{ - pg_begin_msg(port, 'Z'); - /** 'I' - not in a transaction block */ - pg_write_byte(port, 'I'); - pg_end_msg(port); - /** Notify the client that we are ready for queries. */ - pg_flush(port); -} - -void -send_command_complete(struct pg_port *port, const char *tag, - bool display_row_count, size_t row_count) -{ - /** @note: see BuildQueryCompletionString - * from src/backendtcop.cmdtag.c */ - - pg_begin_msg(port, 'C'); - pg_write_bytes(port, tag, strlen(tag)); - /** Inserts are special. */ - if (strncasecmp(tag, "INSERT", 6) == 0) - pg_write_bytes(port, " 0", 2); - if (display_row_count) - pg_write_str(port, " %zu", row_count); - else - pg_write_str(port, "\0"); - pg_end_msg(port); -} - -void -send_row_description_message(struct pg_port *port, - const struct row_description *row_desc) -{ - pg_begin_msg(port, 'T'); - pg_write_uint16(port, row_desc->natts); - /** - * ** From postgres sources ** - * resorigtbl/resorigcol identify the source of the column, if it is a - * simple reference to a column of a base table (or view). If it is not - * a simple reference, these fields are zeroes. - */ - uint32_t resorigtbl = 0; - uint32_t resorigcol = 0; - const struct pg_attribute *atts = row_desc->atts; - for (uint16_t i = 0; i < row_desc->natts; ++i) { - pg_write_str(port, "%.*s", atts[i].name_len, atts[i].name); - pg_write_uint32(port, resorigtbl); - pg_write_uint16(port, resorigcol); - pg_write_uint32(port, atts[i].type_oid); - pg_write_uint16(port, atts[i].type_len); - pg_write_uint32(port, atts[i].typemod); - pg_write_uint16(port, atts[i].format); - } - pg_end_msg(port); -} - -void -send_data_row(struct pg_port *port, const char **data, - const struct row_description *row_desc, - uint16_t format) -{ - (void)format; - pg_begin_msg(port, 'D'); - pg_write_uint16(port, row_desc->natts); - const struct pg_attribute *atts = row_desc->atts; - /** - * All queries except explain return rows as arrays, - * explain returns strings, so there is no need for decoding. - */ - if (mp_typeof(**data) == MP_ARRAY) { - uint32_t row_size = mp_decode_array(data); - assert(row_size == row_desc->natts); - } else { - assert(mp_typeof(**data) == MP_STR); - } - for (uint16_t i = 0; i < row_desc->natts; ++i) - atts[i].write(&atts[i], port, data); - pg_end_msg(port); -} - -uint32_t -send_data_rows(struct pg_port *port, const char **data, - const struct row_description *row_desc) -{ - assert(mp_typeof(**data) == MP_ARRAY); - uint32_t row_count = mp_decode_array(data); - for (uint32_t i = 0; i < row_count; ++i) - send_data_row(port, data, row_desc, row_desc->atts[i].format); - return row_count; -} - -void -send_parameter_status(struct pg_port *port, - const char *name, - const char *value) -{ - pg_begin_msg(port, 'S'); - pg_write_str(port, name); - pg_write_str(port, value); - pg_end_msg(port); -} diff --git a/pgproto/src/postgres/messages.h b/pgproto/src/postgres/messages.h deleted file mode 100644 index 7b6bbbd92b..0000000000 --- a/pgproto/src/postgres/messages.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -struct pg_port; -struct row_description; - -/** - * Send ReadyForQuery message. - * ReadyForQuery informs the frontend that it can safely send a new command. - */ -void -send_ready_for_query(struct pg_port *port); - -/** - * Send CommandComplete message to the frontend. - * CommandComplete informs the frontend that the sent query has been - * completed successfully. - */ -void -send_command_complete(struct pg_port *port, const char *tag, - bool display_row_count, size_t row_count); - -/** - * Send a RowDescription message that - * describes the format of subsequent RowData messages. - */ -void -send_row_description_message(struct pg_port *port, - const struct row_description *row_desc); - -/** - * Read a msgpuck data row and sent it to the frontend according - * to the row description. - */ -void -send_data_row(struct pg_port *port, const char **data, - const struct row_description *row_desc, - uint16_t format); - -/** - * Read all the msgpuck data rows and sent them to the frontend according - * to the row description. - */ -uint32_t -send_data_rows(struct pg_port *port, const char **data, - const struct row_description *row_desc); - -/** - * Send ParameterStatus message to the frontend. - */ -void -send_parameter_status(struct pg_port *port, - const char *name, - const char *value); diff --git a/pgproto/src/postgres/port.c b/pgproto/src/postgres/port.c deleted file mode 100644 index 03a8ac57c7..0000000000 --- a/pgproto/src/postgres/port.c +++ /dev/null @@ -1,316 +0,0 @@ -#include "port.h" -#include "report.h" -#include "tarantool/trivia/util.h" -#include "tarantool/diag.h" - - -#include <arpa/inet.h> -#include <sys/socket.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -static void -buff_create(struct pg_port_buff *buff, size_t size) -{ - buff->data = xmalloc(size); - buff->size = size; - buff->pos = 0; - buff->used = 0; -} - -static void -buff_destroy(struct pg_port_buff *buff) -{ - - free(buff->data); -} - -static void -buff_resize(struct pg_port_buff *buff, size_t new_size) -{ - buff->data = xrealloc(buff->data, new_size); - buff->size = new_size; -} - -static void -buff_gc(struct pg_port_buff *buff) -{ - size_t new_used = buff->used - buff->pos; - /* Move unprocessed data to the beginning. */ - memmove(buff->data, buff->data + buff->pos, new_used); - buff->pos = 0; - buff->used = new_used; -} - -static bool -buff_verify(struct pg_port_buff *buff) -{ - return (buff->used <= buff->size) && (buff->pos <= buff->used); -} - -enum { - /** Initial size of a read buffer. The same as in Postgres. */ - READ_BUFF_INITIAL_SIZE = 1 << 13, - /** Initial size of a write buffer. The same as in Postgres. */ - WRITE_BUFF_INITIAL_SIZE = 1 << 13, - /** Size above which flushing is performed. */ - WRITE_BUFF_FLUSH_IS_NEEDED_PAYLOAD = WRITE_BUFF_INITIAL_SIZE, -}; - -void -pg_port_create(struct pg_port *port, struct iostream *io) -{ - memset(port, 0, sizeof(*port)); - iostream_move(&port->io, io); - buff_create(&port->read_buff, READ_BUFF_INITIAL_SIZE); - buff_create(&port->write_buff, WRITE_BUFF_INITIAL_SIZE); -} - -void -pg_port_close(struct pg_port *port) -{ - pg_flush(port); - buff_destroy(&port->read_buff); - buff_destroy(&port->write_buff); - iostream_close(&port->io); - free(port->user); -} - -char * -pg_read_cstr(struct pg_port *port, size_t *len) -{ - uint32_t packet_len; - if (pg_read_uint32(port, &packet_len) < 0) - return NULL; - - if (packet_len < sizeof(uint32_t) + 1) - return NULL; - - size_t cstr_len = packet_len - sizeof(uint32_t); - char *query = pg_read_bytes(port, cstr_len); - if (query == NULL || query[cstr_len - 1] != '\0') - return NULL; - - *len = cstr_len - 1; - return query; -} - -void * -pg_read_bytes(struct pg_port *port, size_t size) -{ - struct pg_port_buff *buff = &port->read_buff; - assert(buff_verify(buff)); - - if (buff->size < buff->pos + size) - buff_resize(buff, buff->pos + size); - - if (buff->used - buff->pos < size) { - char *data_used = buff->data + buff->used; - size_t to_read = buff->pos + size - buff->used; - size_t to_read_ahead = buff->size - buff->used; - ssize_t nread = coio_read_ahead(&port->io, data_used, - to_read, to_read_ahead); - if (nread == 0) { - port->status = PG_EOF; - return NULL; - } else if (nread == -1) { - if (! fiber_is_cancelled()) - /* Cancelled fiber is not an error. It - * happens when the server stops. */ - pg_error(NULL, ERRCODE_INTERNAL_ERROR, - "couldn't read data from " - "client \'%s\': %s", - port->user ? port->user : "", - box_error_message(box_error_last())); - /* In case of fiber_is_cancelled() it is not really an - * error but we want to stop this fiber. */ - port->status = PG_ERR; - return NULL; - } - buff->used += nread; - } - - char *ret = buff->data + buff->pos; - buff->pos += size; - assert(buff_verify(buff) && "pg_read has broken the buff"); - return (void *)ret; -} - -int -pg_read_uint8(struct pg_port *port, uint8_t *result) -{ - uint8_t *data = pg_read_bytes(port, sizeof(*data)); - if (data != NULL) - *result = *data; - return port->status; -} - -int -pg_read_uint16(struct pg_port *port, uint16_t *result) -{ - uint16_t *data = pg_read_bytes(port, sizeof(*data)); - if (data != NULL) - *result = ntohs(*data); - return port->status; -} - -int -pg_read_uint32(struct pg_port *port, uint32_t *result) -{ - uint32_t *data = pg_read_bytes(port, sizeof(*data)); - if (data != NULL) - *result = ntohl(*data); - return port->status; -} - -void -pg_read_gc(struct pg_port *port) -{ - struct pg_port_buff *buff = &port->read_buff; - assert(buff_verify(buff)); - buff_gc(buff); -} - -void -pg_write_bytes(struct pg_port *port, const void *bytes, size_t len) -{ - struct pg_port_buff *buff = &port->write_buff; - assert(buff_verify(buff)); - if (buff->size - buff->used < len) - buff_resize(buff, buff->used + len); - - memcpy(buff->data + buff->used, bytes, len); - buff->used += len; - assert(buff_verify(buff) && "pg_write_bytes has broken the buff"); -} - -void -pg_begin_msg(struct pg_port *port, uint8_t type) -{ - struct pg_port_buff *buff = &port->write_buff; - assert(buff_verify(buff)); - /** Type byte + size filed */ - uint8_t msg_header[1 + 4]; - /** - * We can't set the size at the moment, but we need to have room for it - * so we can set it later. - */ - msg_header[0] = type; - pg_write_bytes(port, msg_header, sizeof(msg_header)); -} - -void -pg_end_msg(struct pg_port *port) -{ - struct pg_port_buff *buff = &port->write_buff; - assert(buff_verify(buff)); - /** Exclude the type byte. */ - uint32_t net_size = htonl(buff->used - buff->pos - 1); - /** Now we can set the size that comes right after the type. */ - memcpy(buff->data + buff->pos + 1, &net_size, sizeof(net_size)); - /** Message is completed, advance current position. */ - buff->pos = buff->used; - if (buff->used >= WRITE_BUFF_FLUSH_IS_NEEDED_PAYLOAD) - pg_flush(port); -} - -void -pg_write_uint8(struct pg_port *port, uint8_t value) -{ - pg_write_bytes(port, &value, sizeof(value)); -} - -void -pg_write_uint16(struct pg_port *port, uint16_t value) -{ - value = htons(value); - pg_write_bytes(port, &value, sizeof(value)); -} - -void -pg_write_uint32(struct pg_port *port, uint32_t value) -{ - value = htonl(value); - pg_write_bytes(port, &value, sizeof(value)); -} - -void -pg_write_str(struct pg_port *port, const char *format, ...) -{ - va_list args; - va_start(args, format); - pg_write_str_va(port, format, args); - va_end(args); -} - -void -pg_write_str_va(struct pg_port *port, const char *fmt, va_list args) -{ - assert(fmt != NULL && "avoid UB"); - struct pg_port_buff *buff = &port->write_buff; - assert(buff_verify(buff)); - - va_list args_copy; - va_copy(args_copy, args); - int to_write = vsnprintf(NULL, 0, fmt, args_copy); - va_end(args_copy); - /* - * According to the ISO standards of C99 and above vsprintf may return - * a negative value ** only ** if an encoding error occurred, which does - * not apply to our case. - */ - assert(to_write >= 0 && "vsprintf failed, this should never happen"); - - if (buff->size - buff->used < (uint32_t)to_write) - /* Plus 1 for null-termination. */ - buff_resize(buff, buff->used + to_write + 1); - - int written = vsprintf(buff->data + buff->used, fmt, args); - assert(written >= 0 && "vsprintf failed, this should never happen"); - assert(buff_verify(buff) && "pg_write_str_va has broken the buff"); - /** Plus one to include a terminating byte. */ - buff->used += written + 1; -} - -void -pg_flush(struct pg_port *port) -{ - struct pg_port_buff *buff = &port->write_buff; - assert(buff_verify(buff)); - if (buff->pos != 0) - coio_write(&port->io, buff->data, buff->pos); - buff_gc(buff); -} - -void -pg_write_len_str(struct pg_port *port, const char *fmt, ...) -{ - assert(fmt != NULL && "avoid UB"); - struct pg_port_buff *buff = &port->write_buff; - assert(buff_verify(buff)); - - va_list args; - va_start(args, fmt); - int to_write = vsnprintf(NULL, 0, fmt, args); - va_end(args); - /** write len field */ - pg_write_uint32(port, to_write); - /* - * According to the ISO standards of C99 and above vsprintf may return - * a negative value ** only ** if an encoding error occurred, which does - * not apply to our case. - */ - assert(to_write >= 0 && "vsprintf failed, this should never happen"); - - if (buff->size - buff->used < (uint32_t)to_write) - buff_resize(buff, buff->used + to_write); - - va_start(args, fmt); - int written = vsprintf(buff->data + buff->used, fmt, args); - va_end(args); - assert(written == to_write); - assert(written >= 0 && "vsprintf failed, this should never happen"); - assert(buff_verify(buff) && "pg_write_str_va has broken the buff"); - buff->used += written; -} diff --git a/pgproto/src/postgres/port.h b/pgproto/src/postgres/port.h deleted file mode 100644 index a37f2565e9..0000000000 --- a/pgproto/src/postgres/port.h +++ /dev/null @@ -1,191 +0,0 @@ -#pragma once - -#include <stdint.h> - -#include <module.h> -#include <stdarg.h> - -enum PG_PORT_STATUS { - /** No error happened. */ - PG_OK = 0, - /** Connection error. */ - PG_ERR = -1, - /** EOF. */ - PG_EOF = -2, -}; - -/** - * Read and write buffer. - * It is used to avoid repeating inefficient malloc/free patterns - * while reading/writing packets and to reduce the number of I/O - * operations. - */ -struct pg_port_buff { - /** Data stored in the buffer */ - char *data; - /** Memory allocated. */ - uint32_t size; - /** - * Range from data and data + used considered as payload. - * For the read buffer it is the total amount of data read. - * For the write buffer it is the total amount of data written. - */ - uint32_t used; - /** - * Position of the first byte to be processed. - * For the read buffer it is the position of data to be read. - * For the write buffer is the beginning of the message being - * sent. - */ - uint32_t pos; -}; - -/** Port represents a single connection with a postgres frontend. */ -struct pg_port { - /** iostream allows to exchange messages with the frontend */ - struct iostream io; - - /** Read buffers. See the comment to pg_port_buff. */ - struct pg_port_buff read_buff; - /** Write buffers. See the comment to pg_port_buff. */ - struct pg_port_buff write_buff; - - /** Contains the corresponding status if the I/O operation failed. */ - enum PG_PORT_STATUS status; - - /** - * Parameters sent in startup message. - * NULL means that the value wasn't set. - */ - - /** User name. */ - char *user; -}; - -/** - * Create a port from the given iostream. - * iostream will be moved so it shouldn't be used after the call. - */ -void -pg_port_create(struct pg_port *port, struct iostream *io); - -/** - * Destroy port and close the connection. - * The client is not notified, so the reason to close the connection should - * be explained via pg_error(). - */ -void -pg_port_close(struct pg_port *port); - -/** - * Read a null-terminated string to the port buffer, set size to its length - * excluding the terminator and return a pointer to the data read. - * - * @retval not-NULL on success. - * @retval NULL on EOF or error, - * check pg_port::state to understand what happened. - */ -char * -pg_read_cstr(struct pg_port *port, size_t *size); - -/** - * Read size bytes to the port buffer and return a pointer to the data read. - * - * @retval not-NULL on success. - * @retval NULL on EOF or error, - * check pg_port::state to understand what happened. - */ -void * -pg_read_bytes(struct pg_port *port, size_t size); - -/** - * Read uint8_t from the port. - * Possible return values are PGCONN_OK, PGCONN_ERR and PGCON_EOF. - */ -int -pg_read_uint8(struct pg_port *port, uint8_t *result); - -/** - * Read uint16_t from the port. - * Possible return values are PGCONN_OK, PGCONN_ERR and PGCON_EOF. - */ -int -pg_read_uint16(struct pg_port *port, uint16_t *result); - -/** - * Read uint32_t from the port. - * Possible return values are PGCONN_OK, PGCONN_ERR and PGCON_EOF. - */ -int -pg_read_uint32(struct pg_port *port, uint32_t *result); - -/** - * Remove procced data from read buffer. After the call all pointers received - * from pg_read becomes invalid. - * Memory is ** not ** freed so there is no need to call it in case of an error. - */ -void -pg_read_gc(struct pg_port *port); - -/** - * Start forming a message to send to the frontend. - */ -void -pg_begin_msg(struct pg_port *port, uint8_t type); - -/** - * Send a formed message to the frontend. - */ -void -pg_end_msg(struct pg_port *port); - -/** Append uint8_t value at the end of the message. Never fails. */ -void -pg_write_uint8(struct pg_port *port, uint8_t value); - -/** Append uint16_t value at the end of the message. Never fails. */ -void -pg_write_uint16(struct pg_port *port, uint16_t value); - -/** Append uint32_t value at the end of the message. Never fails. */ -void -pg_write_uint32(struct pg_port *port, uint32_t value); - -/** Append n bytes at the end of the message. Never fails. */ -void -pg_write_bytes(struct pg_port *port, const void *bytes, size_t n); - -/** The same as pg_write_uint8. */ -static inline void -pg_write_byte(struct pg_port *port, uint8_t byte) -{ - return pg_write_uint8(port, byte); -} - -/** - * Append a null-terminated string according to the format at the and of the - * message using variadic arguments. Never fails. - */ -void -pg_write_str_va(struct pg_port *port, const char *fmt, va_list args); - -/** - * Append a null-terminated string according to the format at the and of the - * message. Never fails. - */ -void -pg_write_str(struct pg_port *port, const char *format, ...); - -/** - * Append the string length and the string itself without a trailing zero. - */ -void -pg_write_len_str(struct pg_port *port, const char *format, ...); - -/** - * Force sending of messages from the write buffer. - * Flushing is performed automatically when the buffer becomes too large, - * but in some cases the flushing is needed to avoid deadlock. - */ -void -pg_flush(struct pg_port *port); diff --git a/pgproto/src/postgres/postgres.c b/pgproto/src/postgres/postgres.c deleted file mode 100644 index b3f6300d73..0000000000 --- a/pgproto/src/postgres/postgres.c +++ /dev/null @@ -1,268 +0,0 @@ -#include <module.h> -#include <msgpuck.h> -#include <inttypes.h> -#include <strings.h> -#include <ctype.h> - -#include "postgres.h" -#include "messages.h" -#include "report.h" -#include "port.h" -#include "startup.h" -#include "auth.h" -#include "attributes.h" -#include "tarantool/trivia/util.h" - -/** - * Get a command tag that should be sent in CommandComplete message. - * It returns the exact command tag only if the tag must be sent with row count. - */ -static const char * -get_command_tag(const char *query, bool *display_row_count) -{ - /** skip leading spaces */ - while (isspace(*query) && *query) - query++; - - /** - * tagname is only considered in the folowing cases - * and these are actually the only cases we need to send row count - */ - static const char *tags[] = { - "SELECT", "DELETE", "UPDATE", "INSERT", - "FETCH", "MERGE", "MOVE", "COPY", - }; - for (size_t i = 0; i < lengthof(tags); ++i) { - if (strncasecmp(query, tags[i], strlen(tags[i])) == 0) { - *display_row_count = true; - return tags[i]; - } - } - - *display_row_count = false; - return "DONE"; -} - -/** Picodata's sql worker. */ -extern int -dispatch_query(struct box_function_ctx *f_ctx, - const char *args, const char *args_end); - -/** - * Call stored C routine dispatch_query and - * get the response in msgpuck format allocated on box region. - */ -static const char * -dispatch_query_wrapped(const char *query, size_t query_len) -{ - const char *tracer = "global"; - uint32_t arg_size = mp_sizeof_array(5) + mp_sizeof_str(query_len) + - 3 * mp_sizeof_nil() + - mp_sizeof_str(strlen(tracer)); - - char *args = box_region_alloc(arg_size); - char *args_end = args; - args_end = mp_encode_array(args_end, 5); - args_end = mp_encode_str(args_end, query, query_len); - args_end = mp_encode_nil(args_end); - args_end = mp_encode_nil(args_end); - args_end = mp_encode_nil(args_end); - args_end = mp_encode_str(args_end, tracer, strlen(tracer)); - struct port out; - port_c_create(&out); - struct box_function_ctx ctx = { &out }; - int rc = dispatch_query(&ctx, args, args_end); - const char *response = NULL; - uint32_t response_size; - if (rc == 0) - response = port_get_msgpack(&out, &response_size); - port_destroy(&out); - return response; -} - -/** - * Parse and send query response. - * Returns -1 in case of error, - */ -static int64_t -process_query_response(struct pg_port *port, const char **response) -{ - size_t row_count = 0; - const char **data = response; - assert(mp_typeof(**data) == MP_ARRAY); - uint32_t size = mp_decode_array(data); - assert(size == 1); - assert(mp_typeof(**data) == MP_ARRAY); - size = mp_decode_array(data); - if (mp_typeof(**data) == MP_ARRAY) { - /** Explain query */ - struct row_description row_desc; - row_description_explain(&row_desc); - send_row_description_message(port, &row_desc); - return send_data_rows(port, data, &row_desc); - } - assert(mp_typeof(**data) == MP_MAP); - size = mp_decode_map(data); - uint32_t len; - const char *str; - if (size == 2) { - /** - * { name:val, ... } - map, [a, b, ...] - array - * - * { "metadata": [{ "name":"col", "type":"integer" }, ... }], - * "rows": [[row1], ... ] } - */ - assert(mp_typeof(**data) == MP_STR); - str = mp_decode_str(data, &len); - assert(strncmp(str, "metadata", strlen("metadata")) == 0); - - /** Simple query response is always in text format. */ - struct row_description row_desc; - if (parse_metadata(data, &row_desc, TEXT_FORMAT) != 0) { - pg_error(port, ERRCODE_INTERNAL_ERROR, - "can't parse attributes description"); - return -1; - } - send_row_description_message(port, &row_desc); - assert(mp_typeof(**data) == MP_STR); - str = mp_decode_str(data, &len); - assert(strncmp(str, "rows", strlen("rows")) == 0); - row_count = send_data_rows(port, data, &row_desc); - } else if (size == 1) { - /* { "row_count": n } */ - assert(mp_typeof(**data) == MP_STR); - const char *str = mp_decode_str(data, &len); - assert(strncmp(str, "row_count", strlen("row_count")) == 0); - row_count = mp_decode_uint(data); - } - return row_count; -} - -/** - * Process a pending simple query message. - * Allocates on box region. - * Returns 0 if the query cycle can be continued, -1 otherwise. - */ -static int -process_simple_query_impl(struct pg_port *port) -{ - size_t query_len; - const char *query = pg_read_cstr(port, &query_len); - if (query == NULL) { - pg_error(port, ERRCODE_INTERNAL_ERROR, - "failed to read a query message"); - /** - * We can't restore the message borders, - * so the cycle should be stopped. - */ - return -1; - } - - say_debug("processing query \'%s\'", query); - const char *response = dispatch_query_wrapped(query, query_len); - - if (response == NULL) { - pg_error(port, ERRCODE_INTERNAL_ERROR, - "failed to execute query \'%s\': %s", - query, box_error_message(box_error_last())); - /** - * The error was properly handled, - * so we can continue the query cycle. - */ - return 0; - } - - bool display_row_count; - const char *command_tag = get_command_tag(query, &display_row_count); - int64_t row_count = process_query_response(port, &response); - /** Send CommandComplete only if no error happened. */ - if (row_count != -1) - send_command_complete(port, command_tag, display_row_count, row_count); - return 0; -} - -static int -process_simple_query(struct pg_port *port) -{ - size_t region_svp = box_region_used(); - int rc = process_simple_query_impl(port); - box_region_truncate(region_svp); - return rc; -} - -static int -start_query_cycle(struct pg_port *port) -{ - while (true) { - send_ready_for_query(port); - - uint8_t msg_type; - pg_read_uint8(port, &msg_type); - if (port->status == PG_EOF) { - say_error("unexpected EOF on client connection"); - return -1; - } else if (port->status == PG_ERR) { - /* Error has already been logged. */ - return -1; - } - - switch (msg_type) { - case 'Q': /* Query */ - if (process_simple_query(port) != 0) - return -1; - break; - case 'X': /* Terminate */ - say_debug("got Terminate message"); - return 0; - default: - pg_error(port, ERRCODE_FEATURE_NOT_SUPPORTED, - "\'%c\' message type is not supported", - msg_type); - return -1; - } - pg_read_gc(port); - } -} - -static void -pg_set_fiber_name(const char *name, size_t name_len) { - const char prefix[] = "pg."; - const size_t prefix_len = sizeof(prefix) - 1; - const size_t pg_name_len = prefix_len + name_len; - char *pg_name = xmalloc(prefix_len + name_len); - memcpy(pg_name, prefix, prefix_len); - memcpy(pg_name + prefix_len, name, name_len), - fiber_set_name_n(fiber_self(), pg_name, pg_name_len); - free(pg_name); -} - -int -postgres_main(struct iostream *iostream) -{ - struct pg_port port; - pg_port_create(&port, iostream); - - int ret = 0; - - if ((ret = pg_process_startup_message(&port)) != 0) - goto cleanup; - - pg_set_fiber_name(port.user, strlen(port.user)); - - if ((ret = pg_authenticate(&port)) != 0) - goto cleanup; - - send_parameter_status(&port, "client_encoding", "UTF8"); - send_parameter_status(&port, "server_version", "15.0"); - - if ((ret = start_query_cycle(&port)) != 0) - goto cleanup; - -cleanup: - if (fiber_is_cancelled()) - pg_notice(&port, "shutting down"); - - say_info("disconnected"); - pg_port_close(&port); - return ret; -} diff --git a/pgproto/src/postgres/postgres.h b/pgproto/src/postgres/postgres.h deleted file mode 100644 index dd31ba6ff6..0000000000 --- a/pgproto/src/postgres/postgres.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -struct iostream; - -/** - * Start postgres protocol backend message flow. The whole session, beginning - * with startup message exchange and ending with termination, is done by a - * single call of this routine. - * - * @param iostream connection to the postgres frontend, it will be moved so - * it should not be used after the call. - * - * @retval 0 on success. - * @retval -1 on error, error message is written to the server log and sent - * to the client. - */ -int -postgres_main(struct iostream *iostream); diff --git a/pgproto/src/postgres/report.c b/pgproto/src/postgres/report.c deleted file mode 100644 index b34469f86c..0000000000 --- a/pgproto/src/postgres/report.c +++ /dev/null @@ -1,87 +0,0 @@ -/** - * PostgreSQL Database Management System - * (formerly known as Postgres, then as Postgres95) - * - * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group - * - * Portions Copyright (c) 1994, The Regents of the University of California - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - */ - -#include "report.h" -#include "port.h" - -/* - * * Taken from Postgres sources * - * Identifiers of error message fields. Kept here to keep common - * between frontend and backend, and also to export them to libpq - * applications. - */ -#define PG_DIAG_SEVERITY 'S' -#define PG_DIAG_SEVERITY_NONLOCALIZED 'V' -#define PG_DIAG_SQLSTATE 'C' -#define PG_DIAG_MESSAGE_PRIMARY 'M' -#define PG_DIAG_MESSAGE_DETAIL 'D' -#define PG_DIAG_MESSAGE_HINT 'H' -#define PG_DIAG_STATEMENT_POSITION 'P' -#define PG_DIAG_INTERNAL_POSITION 'p' -#define PG_DIAG_INTERNAL_QUERY 'q' -#define PG_DIAG_CONTEXT 'W' -#define PG_DIAG_SCHEMA_NAME 's' -#define PG_DIAG_TABLE_NAME 't' -#define PG_DIAG_COLUMN_NAME 'c' -#define PG_DIAG_DATATYPE_NAME 'd' -#define PG_DIAG_CONSTRAINT_NAME 'n' -#define PG_DIAG_SOURCE_FILE 'F' -#define PG_DIAG_SOURCE_LINE 'L' -#define PG_DIAG_SOURCE_FUNCTION 'R' - -#define PG_DIAG_ERROR 'E' -#define PG_DIAG_NOTICE 'N' - -void -send_message_to_frontend(int level, struct pg_port *port, - const char *sql_error_code, const char *fmt, ...) -{ - /** - * @note: See - * https://www.postgresql.org/docs/current/protocol-error-fields.html - */ - - char type = level <= S_ERROR ? PG_DIAG_ERROR : PG_DIAG_NOTICE; - const char *severity = level <= S_ERROR ? "ERROR" : "NOTICE"; - if (port) { - pg_begin_msg(port, type); - pg_write_byte(port, PG_DIAG_SEVERITY); - pg_write_str(port, severity); - - pg_write_byte(port, PG_DIAG_SQLSTATE); - pg_write_str(port, sql_error_code); - - pg_write_byte(port, PG_DIAG_MESSAGE_PRIMARY); - va_list args; - va_start(args, fmt); - pg_write_str_va(port, fmt, args); - va_end(args); - - pg_write_byte(port, '\0'); /* terminator */ - - pg_end_msg(port); - } -} diff --git a/pgproto/src/postgres/report.h b/pgproto/src/postgres/report.h deleted file mode 100644 index 97d8d540b4..0000000000 --- a/pgproto/src/postgres/report.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include <module.h> - -#include <stdbool.h> -#include <string.h> -#include <assert.h> - -struct pg_port; - -/** - * Sqlstate error codes. - * @note: See https://www.postgresql.org/docs/current/errcodes-appendix.html for - * the list of all of them. - */ -#define ERRCODE_INTERNAL_ERROR "XX000" -#define ERRCODE_INVALID_PASSWORD "28P01" -#define ERRCODE_PROTOCOL_VIOLATION "08P01" -#define ERRCODE_FEATURE_NOT_SUPPORTED "0A000" -#define ERRCODE_CONNECTION_DOES_NOT_EXIST "08003" -#define ERRCODE_SUCCESSFUL_COMPLETION "00000" - -/** Send ErrorResponse packet to the frontend. */ -void -send_message_to_frontend(int level, struct pg_port *port, - const char *sql_error_code, const char *fmt, ...); - -/** Log an error message. Message is sent to the client if port != NULL. */ -#define pg_error(port, sql_code, ...) do { \ - say_error(__VA_ARGS__); \ - send_message_to_frontend(S_ERROR, port, sql_code, __VA_ARGS__); \ -} while (0) - -/** Send a notice message to the client. Writes only to debug server log. */ -#define pg_notice(port, ...) do { \ - say_debug(__VA_ARGS__); \ - send_message_to_frontend(S_INFO, port, ERRCODE_SUCCESSFUL_COMPLETION, \ - __VA_ARGS__); \ -} while (0) diff --git a/pgproto/src/postgres/startup.c b/pgproto/src/postgres/startup.c deleted file mode 100644 index 3e9eaf81ae..0000000000 --- a/pgproto/src/postgres/startup.c +++ /dev/null @@ -1,249 +0,0 @@ -/** - * PostgreSQL Database Management System - * (formerly known as Postgres, then as Postgres95) - * - * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group - * - * Portions Copyright (c) 1994, The Regents of the University of California - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - */ - -#include "port.h" -#include "startup.h" -#include "report.h" -#include "tarantool/diag.h" - -#include <inttypes.h> -#include <string.h> - -/** Extract major protocol version from the version number. */ -static uint16_t -pg_protocol_major(uint32_t version) -{ - return ((version) >> 16); -} -/** Extract minor protocol version from the version number. */ -static uint16_t -pg_protocol_minor(uint32_t version) -{ - return ((version) & 0x0000ffff); -} - -/** Get a protocol version from major and minor versions. */ -#define PG_PROTOCOL(m,n) (((m) << 16) | (n)) - -enum { - /** - * The earliest and latest frontend/backend protocol version supported. - * (Only protocol version 3 is currently supported) - */ - PG_PROTOCOL_EARLIEST = PG_PROTOCOL(3,0), - PG_PROTOCOL_LATEST = PG_PROTOCOL(3,0), -}; - -/** Cancel request message format. */ -struct cancel_request_message { - /** Note that each field is stored in network byte order! */ - - /** code to identify a cancel request */ - uint32_t cancel_request_code; - /** PID of client's backend */ - uint32_t backend_pid; - /** secret key to authorize cancel */ - uint32_t cancel_auth_code; -}; - -enum { - /** - * A client can also start by sending a request to get a secure channel. - */ - - /** SSL negotiation request. */ - NEGOTIATE_SSL_CODE = PG_PROTOCOL(1234,5679), - /** GSSAPI negotiation request. */ - NEGOTIATE_GSS_CODE = PG_PROTOCOL(1234,5680), - - /* - * A client can also send a cancel-current-operation request to the - * sever. - * The cancel request code must not match any protocol version number - * we're ever likely to use. This random choice should do. - */ - CANCEL_REQUEST_CODE = PG_PROTOCOL(1234,5678), - - /** - * In protocol 3.0 and later, the startup packet length is not fixed, - * but we set an arbitrary limit on it anyway. This is just to prevent - * simple denial-of-service attacks via sending enough data to run the - * server out of memory. - */ - MAX_STARTUP_PACKET_LENGTH = 10000, - - /** - * Startup message must contain at least size and request code fields. - * Both are represented by uint32_t type. The type byte is not included - * to the length, and BTW, startup packet doesn't have a type byte. - */ - MIN_STARTUP_PACKET_LENGTH = sizeof(uint32_t) + sizeof(uint32_t) - -}; -/** Structure of startup message packet. */ -struct startup_message { - /** Packet length, including len field itself. */ - uint32_t len; - /** Protocol version or request code. */ - uint32_t version; - /** - * Parameters in form of parameter name and value pairs. - * Possible names: user, database, options, replication, - * _pq_.*, etc. - * There must be at least a user parameter. - */ - char *parameters; -}; - -/** - * Receive startup packet pending on the port. - */ -static int -pg_recv_startup_message(struct pg_port *port, - struct startup_message *message) -{ - memset(message, 0, sizeof(*message)); - - if (pg_read_uint32(port, &message->len) < 0) { - /* - * If we get no data, don't clutter the log with a complaint; - * such cases often occur for legitimate reasons. - * An example is that we might be here after responding to - * NEGOTIATE_SSL_CODE, and if the client didn't like our - * response, it'll probably just drop the connection. - * Service-monitoring software also often just opens and - * closes a connection without sending anything. - */ - return -1; - } - - if (message->len < MIN_STARTUP_PACKET_LENGTH || - message->len > MAX_STARTUP_PACKET_LENGTH) { - pg_error(NULL, ERRCODE_PROTOCOL_VIOLATION, - "invalid startup message length: %"PRIu32, - message->len); - return -1; - } - - if (pg_read_uint32(port, &message->version) < 0) { - pg_error(NULL, ERRCODE_PROTOCOL_VIOLATION, - "incomplete startup message: no protocol"); - return -1; - } - - if (message->version == CANCEL_REQUEST_CODE) { - if (message->len != sizeof(struct cancel_request_message)) - pg_error(NULL, ERRCODE_PROTOCOL_VIOLATION, - "invalid length of startup packet"); - /** Not really an error, but we don't want to proceed further */ - return -1; - } - - if (message->version == NEGOTIATE_SSL_CODE) { - pg_error(port, ERRCODE_FEATURE_NOT_SUPPORTED, - "SSL is not supported"); - return -1; - } - size_t to_read = message->len - - sizeof(message->len) - sizeof(message->version); - message->parameters = pg_read_bytes(port, to_read); - if (message->parameters == NULL) { - pg_error(NULL, ERRCODE_PROTOCOL_VIOLATION, - "incomplete startup message: no parameters"); - return -1; - } - - pg_read_gc(port); - return 0; -} - -/** Check that the protocol version is in the supported range. */ -static int -pg_check_protocol_version(uint32_t version) -{ - uint16_t frontend_protocol_major = pg_protocol_major(version); - uint16_t frontend_protocol_minor = pg_protocol_minor(version); - uint16_t protocol_major_earliest = - pg_protocol_major(PG_PROTOCOL_EARLIEST); - uint16_t protocol_major_latest = - pg_protocol_major(PG_PROTOCOL_LATEST); - uint16_t protocol_minor_earliest = - pg_protocol_minor(PG_PROTOCOL_EARLIEST); - uint16_t protocol_minor_latest = - pg_protocol_minor(PG_PROTOCOL_LATEST); - return frontend_protocol_major >= protocol_major_earliest && - frontend_protocol_major <= protocol_major_latest && - frontend_protocol_minor >= protocol_minor_earliest && - frontend_protocol_minor <= protocol_minor_latest; -} - -int -pg_process_startup_message(struct pg_port *port) -{ - /** - * @note: see ProcessStartupPacket() - * from backend/postmaster/postmaster.c in postgres sources. - */ - - struct startup_message message; - if (pg_recv_startup_message(port, &message) != 0) - return -1; - - if (!pg_check_protocol_version(message.version)) { - pg_error(port, ERRCODE_FEATURE_NOT_SUPPORTED, - "unsupported frontend protocol %u.%u: " - "server supports %u.0 to %u.%u", - pg_protocol_major(message.version), - pg_protocol_minor(message.version), - pg_protocol_major(PG_PROTOCOL_EARLIEST), - pg_protocol_major(PG_PROTOCOL_LATEST), - pg_protocol_minor(PG_PROTOCOL_LATEST)); - return -1; - } - - const char *namevalue = message.parameters; - while (*namevalue != '\0') { - const char *name = namevalue; - const char *value = name + strlen(name) + 1; - if (strcmp(name, "user") == 0) - port->user = xstrdup(value); - else - pg_error(NULL, ERRCODE_FEATURE_NOT_SUPPORTED, - "%s:%s is not supported for now", - name, value); - - namevalue = value + strlen(value) + 1; - } - - if (port->user == NULL || port->user[0] == '\0') { - pg_error(port, ERRCODE_PROTOCOL_VIOLATION, - "incomplete startup message: no user"); - return -1; - } - - say_debug("processed startup message for user \"%s\"", port->user); - return 0; -} diff --git a/pgproto/src/postgres/startup.h b/pgproto/src/postgres/startup.h deleted file mode 100644 index f5cf9c039e..0000000000 --- a/pgproto/src/postgres/startup.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -struct pg_port; - -/** - * Process the pending startup message on the port. - * - * After connecting to the server, client sends a strtup message that defines - * parameters such as user name and etc. that must be processed by this routine. - * - * @retval 0 on success. - * @retval -1 on error, error message is written to the server log and sent - * to the client. - */ -int -pg_process_startup_message(struct pg_port *port); diff --git a/pgproto/src/server/CMakeLists.txt b/pgproto/src/server/CMakeLists.txt deleted file mode 100644 index b18a467e1d..0000000000 --- a/pgproto/src/server/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SERVER_SOURCES - server.c -) - -build_module(pgproto ${SERVER_SOURCES}) -target_link_libraries(pgproto - PRIVATE tarantool postgres ${MSGPUCK_LIB}) diff --git a/pgproto/src/server/server.c b/pgproto/src/server/server.c deleted file mode 100644 index 9e026f3c67..0000000000 --- a/pgproto/src/server/server.c +++ /dev/null @@ -1,402 +0,0 @@ - -#include <stdarg.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <unistd.h> -#include <sys/queue.h> - -#include <module.h> -#include "msgpuck.h" -#include "tarantool/sio.h" -#include "tarantool/diag.h" -#include "tarantool/evio.h" -#include "tarantool/trivia/util.h" -#include "postgres/postgres.h" - -enum { - SERVER_TIMEOUT_INFINITY = 3600 * 24 * 365 * 10 -}; - -/** Entry of a doubly linked list containing fibers */ -struct fiber_list_entry { - struct fiber *fiber; - LIST_ENTRY(fiber_list_entry) entries; -}; - -/** Head of a doubly linked list containing fibers. */ -LIST_HEAD(fiber_list, fiber_list_entry); - -/** Insert entry to the head of the list. Takes o(1) time */ -static void -fiber_list_insert_head(struct fiber_list *list, struct fiber_list_entry *entry) -{ - LIST_INSERT_HEAD(list, entry, entries); -} - -/** Remove entry from the list. Takes o(1) time */ -static void -fiber_list_remove(struct fiber_list_entry *entry) -{ - LIST_REMOVE(entry, entries); -} - -/** Initialize fiber list. */ -static void -fiber_list_init(struct fiber_list *list_head) -{ - LIST_INIT(list_head); -} - -/** Get the first entry from the list. */ -static struct fiber_list_entry * -fiber_list_first(struct fiber_list *list) -{ - return LIST_FIRST(list); -} - -/** Check if the list has no entries. */ -static bool -fiber_list_empty(struct fiber_list *list) -{ - return LIST_EMPTY(list); -} - -/** - * cord_on_yield is declared but not defined in the tarantool's core - * so it must be defined by core users. - */ -void -cord_on_yield() {} - -struct server { - /** Server socket. */ - int socket; - /** Fiber on which the accept loop runs. */ - struct fiber *fiber; - - /** - * Running clients. - * There is a need in stopping all the running clients before - * unmapping the library. Waking up a client running in the unmapped - * address space leads to SEGV_MAPPER. - * The list is manged by client_worker. - */ - struct fiber_list clients; -}; - -/** Server instance. */ -static struct server server; - -/** Create new server socket */ -static int -server_socket_new(const char *host, const char *service) -{ - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - /* Allow IPv4 or IPv6 */ - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - /* Loop-back address if host is not specified */ - hints.ai_flags = AI_PASSIVE; - struct addrinfo *ai; - const double delay1s = 1.; - int rc = coio_getaddrinfo(host, service, &hints, &ai, delay1s); - if (rc != 0) - return -1; - - for (; ai != NULL; ai = ai->ai_next) { - struct sockaddr *addr = ai->ai_addr; - const socklen_t addr_len = ai->ai_addrlen; - - int server_socket = sio_socket(addr->sa_family, SOCK_STREAM, 0); - if (server_socket < 0) - continue; - - if (evio_setsockopt_server(server_socket, addr->sa_family, - SOCK_STREAM) != 0) - goto cleanup_and_try_again; - - if (sio_bind(server_socket, addr, addr_len) != 0) - goto cleanup_and_try_again; - - if (sio_listen(server_socket) != 0) - goto cleanup_and_try_again; - - return server_socket; - - cleanup_and_try_again: - close(server_socket); - } - - return diag_set(IllegalParams, - "Can't create a server at the specified address: " - "%s:%s", host, service); -} - -/** - * Client worker. - * Takes 2 arguments: - * 1st: int client_socket - * 2nd: struct fiber *client_fiber - */ -static int -client_worker(va_list args) -{ - int client_socket = va_arg(args, int); - struct fiber *client_fiber = va_arg(args, struct fiber *); - - /** - * Insert the fiber to the list so that we can find it in a client - * list and cancel while stopping the server and unloading the library. - */ - struct fiber_list_entry *entry = xcalloc(1, sizeof(*entry)); - entry->fiber = client_fiber; - fiber_list_insert_head(&server.clients, entry); - - struct iostream io; - plain_iostream_create(&io, client_socket); - postgres_main(&io); - - /** Fiber is finished so it can be removed from the list. */ - fiber_list_remove(entry); - free(entry); - return 0; -} - -static int -server_start_client_worker(int client_socket) -{ - say_info("client[%d]: connected", client_socket); - - struct fiber *client_fiber = fiber_new("client", client_worker); - if (client_fiber == NULL) - return -1; - - fiber_set_joinable(client_fiber, true); - fiber_start(client_fiber, client_socket, client_fiber); - return 0; -} - -static int -server_wouldblock(int err) -{ - return sio_wouldblock(err); -} - -/** - * Yield control to other fibers until there are no pending clients - * in the backlog. - * Return 1 if there are pending connections and 0 if waiting was interrupted. - */ -static int -server_wait_for_connection() -{ - int events = coio_wait(server.socket, COIO_READ, - SERVER_TIMEOUT_INFINITY); - return (events & COIO_READ) != 0; -} - -static int -server_accept_and_setopt() -{ - struct sockaddr_storage client_addr; - socklen_t addr_len = sizeof(struct sockaddr_storage); - struct sockaddr *client_addr_ptr = (struct sockaddr *)&client_addr; - int client_socket = sio_accept(server.socket, client_addr_ptr, - &addr_len); - - if (client_socket < 0) - return -1; - - if (evio_setsockopt_client(client_socket, client_addr.ss_family, - SOCK_STREAM) != 0) { - close(client_socket); - return -1; - } - - return client_socket; -} - -/** - * Accept a pending client and run its worker. - * If serving completed successfully 0 is returned, otherwise -1. - */ -static int -server_serve_connection() -{ - int client_socket = server_accept_and_setopt(); - if (client_socket >= 0) { - if (server_start_client_worker(client_socket) == 0) { - return 0; - } else { - close(client_socket); - return -1; - } - } - return -1; -} - -#ifdef __linux__ -/** - * Check if the error is one of the network errors. - */ -static int -server_network_error(int err) -{ - return err == ENETDOWN || err == EPROTO || err == ENOPROTOOPT || - err == EHOSTDOWN || err == ENONET || err == EHOSTUNREACH || - err == EOPNOTSUPP || err == ENETUNREACH; -} -#endif /* __linux__ */ - -/** Check whether the error should be treated as EAGAIN. */ -static int -server_should_try_again(int err) -{ -#ifdef __linux__ - /* Take a look at the Error handling section - in the accept's manual page. */ - return server_wouldblock(err) || server_network_error(err); -#else - return server_wouldblock(err); -#endif -} - -/** Server accept loop. */ -static int -server_worker(va_list args) -{ - (void)args; - say_info("server has been started"); - while (! fiber_is_cancelled()) { - if (server_wait_for_connection()) { - if (server_serve_connection() != 0 && - ! server_should_try_again(errno)) { - say_info("server was stopped due to error: %s", - box_error_message(box_error_last())); - return -1; - } - } - } - say_info("server was stopped"); - return 0; -} - -static int -server_init(const char *host, const char *service) -{ - server.socket = server_socket_new(host, service); - if (server.socket < 0) - return -1; - server.fiber = fiber_new("server", server_worker); - if (server.fiber == NULL) { - close(server.socket); - return -1; - } - fiber_set_joinable(server.fiber, true); - fiber_list_init(&server.clients); - return 0; -} - -static void -server_start_accept_loop() -{ - fiber_start(server.fiber); -} - -int -server_start(box_function_ctx_t *ctx, - const char *args, const char *args_end) -{ - (void)ctx; - (void)args_end; - const char *usage = "server_start(host = <str>, service = <str>)"; - - uint32_t args_count = mp_decode_array(&args); - if (args_count != 2) - goto illegal_params; - - if (mp_typeof(*args) != MP_STR) - goto illegal_params; - uint32_t host_len = 0; - const char *host = mp_decode_str(&args, &host_len); - - if (mp_typeof(*args) != MP_STR) - goto illegal_params; - uint32_t service_len = 0; - const char *service = mp_decode_str(&args, &service_len); - - /* make host and service null-terminated */ - host = xstrndup(host, host_len); - service = xstrndup(service, service_len); - int rc = server_init(host, service); - free((void *)host); - free((void *)service); - if (rc != 0) - return -1; - - server_start_accept_loop(); - return 0; - -illegal_params: - return diag_set(IllegalParams, "Usage: %s", usage); -} - -static int -server_stop_accept_loop() -{ - fiber_cancel(server.fiber); - fiber_wakeup(server.fiber); - return fiber_join(server.fiber); -} - -/** - * Cancel all client fibers and wait for them to finish. - * See the comment to server::clients for details. - */ -static void -server_terminate_clients() -{ - struct fiber_list_entry *entry; - struct fiber *fiber; - while(! fiber_list_empty(&server.clients)) { - entry = fiber_list_first(&server.clients); - fiber = entry->fiber; - /* List entry is removed when fiber is finished */ - fiber_cancel(fiber); - fiber_join(fiber); - } -} - -static int -server_free() -{ - int server_socket = server.socket; - memset(&server, 0, sizeof(server)); - return coio_close(server_socket); -} - -int -server_stop(box_function_ctx_t *ctx, - const char *args, const char *args_end) -{ - (void)ctx; - (void)args_end; - const char *usage = "server_stop()"; - uint32_t arg_count = mp_decode_array(&args); - if (arg_count != 0) - goto illegal_params; - - server_stop_accept_loop(); - server_terminate_clients(); - assert(fiber_list_empty(&server.clients)); - server_free(); - return 0; - -illegal_params: - return diag_set(IllegalParams, "Usage: %s", usage); -} diff --git a/pgproto/src/server/server.h b/pgproto/src/server/server.h deleted file mode 100644 index 213d0b0a55..0000000000 --- a/pgproto/src/server/server.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <stdint.h> - -#include "module.h" - -#if defined(__cplusplus) -extern "C" { -#endif /* defined(__cplusplus) */ - -/** - * Stored procedure that creates a server at the specified address and starts - * accept loop. - * Server accept loop runs in a separate fiber. - * Every connected client runs in a separate fiber. - * - * It takes 2 arguments encoded in msgpuck format: - * host address represented as a string, - * service, represented as a string. - * - * @return 0 - on success, -1 in case of error. - */ -int -server_start(box_function_ctx_t *ctx, - const char *args, const char *args_end); - -/** - * Stored procedure that stops server accept loop and releases server resources. - * - * It has no arguments. - * - * @return 0 - on success, -1 in case of error. - */ -int -server_stop(box_function_ctx_t *ctx, - const char *args, const char *args_end); - -#if defined(__cplusplus) -} /* extern "C" */ -#endif /* defined(__cplusplus) */ diff --git a/pgproto/src/tarantool/CMakeLists.txt b/pgproto/src/tarantool/CMakeLists.txt deleted file mode 100644 index 784bf7b2cb..0000000000 --- a/pgproto/src/tarantool/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(TARANTOOL_MODULE_SOURCES - sio.c - evio.c -) - -add_library(tarantool STATIC ${TARANTOOL_MODULE_SOURCES}) -set_property(TARGET tarantool PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/pgproto/src/tarantool/diag.h b/pgproto/src/tarantool/diag.h deleted file mode 100644 index 567b8228df..0000000000 --- a/pgproto/src/tarantool/diag.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -/* - * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 - * <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 <module.h> -#include "sio.h" /* sio_socketname_to_buffer */ -#include "trivia/util.h" - -#include <assert.h> - -#define diag_set(type_, ...) ({ \ - int save_errno = errno; \ - say_debug("%s at %s:%i", #type_, __FILE__, __LINE__); \ - int rc = box_error_set(__FILE__, __LINE__, ER_PROC_C, \ - __VA_ARGS__); \ - errno = save_errno; \ - rc; \ -}) - -#define diag_set_SocketError(sock, ...) ({ \ - char name[SERVICE_NAME_MAXLEN + 1]; \ - name[SERVICE_NAME_MAXLEN] = '\0'; \ - char msg[256]; \ - msg[lengthof(msg) - 1] = '\0'; \ - assert(SERVICE_NAME_MAXLEN < lengthof(msg)); \ - int rc = snprintf(msg, lengthof(msg) - 1, __VA_ARGS__); \ - assert(rc != -1 && "snprintf"); \ - rc = sio_socketname_to_buffer(sock, name, SERVICE_NAME_MAXLEN); \ - assert(rc == 0 && "sio_socketname_to_buffer"); \ - rc = diag_set(SocketError, "%s, called on %s", msg, name); \ - rc; \ -}) diff --git a/pgproto/src/tarantool/evio.c b/pgproto/src/tarantool/evio.c deleted file mode 100644 index b481607cd0..0000000000 --- a/pgproto/src/tarantool/evio.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 - * <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 "evio.h" -#include "sio.h" -#include <stdio.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -static int -evio_setsockopt_keepalive(int fd) -{ - int on = 1; - /* - * SO_KEEPALIVE to ensure connections don't hang - * around for too long when a link goes away. - */ - if (sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - &on, sizeof(on))) - return -1; -#ifdef __linux__ - /* - * On Linux, we are able to fine-tune keepalive - * intervals. Set smaller defaults, since the system-wide - * defaults are in days. - */ - int keepcnt = 5; - if (sio_setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, - sizeof(int))) - return -1; - int keepidle = 30; - - if (sio_setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, - sizeof(int))) - return -1; - - int keepintvl = 60; - if (sio_setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, - sizeof(int))) - return -1; -#endif - return 0; -} - -/** Set common client socket options. */ -int -evio_setsockopt_client(int fd, int family, int type) -{ - int on = 1; - /* In case this throws, the socket is not leaked. */ - if (sio_setfl(fd, O_NONBLOCK, on)) - return -1; - if (type == SOCK_STREAM && family != AF_UNIX) { - /* - * SO_KEEPALIVE to ensure connections don't hang - * around for too long when a link goes away. - */ - if (evio_setsockopt_keepalive(fd) != 0) - return -1; - /* - * Lower latency is more important than higher - * bandwidth, and we usually write entire - * request/response in a single syscall. - */ - if (sio_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, - &on, sizeof(on))) - return -1; - } - return 0; -} - -int -evio_setsockopt_server(int fd, int family, int type) -{ - int on = 1; - /* In case this throws, the socket is not leaked. */ - if (sio_setfl(fd, O_NONBLOCK, on)) - return -1; - /* Allow reuse local addresses. */ - if (sio_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on))) - return -1; - -#ifndef TARANTOOL_WSL1_WORKAROUND_ENABLED - /* Send all buffered messages on socket before take - * control out from close(2) or shutdown(2). */ - struct linger linger = { 0, 0 }; - - if (sio_setsockopt(fd, SOL_SOCKET, SO_LINGER, - &linger, sizeof(linger))) - return -1; -#endif - if (type == SOCK_STREAM && family != AF_UNIX && - evio_setsockopt_keepalive(fd) != 0) - return -1; - return 0; -} diff --git a/pgproto/src/tarantool/evio.h b/pgproto/src/tarantool/evio.h deleted file mode 100644 index 2a1a2869b5..0000000000 --- a/pgproto/src/tarantool/evio.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -/* - * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 - * <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. - */ - -/** Set socket flags. */ -int sio_setfl(int fd, int flag, int on); - -/** Set common client socket options. */ -int -evio_setsockopt_client(int fd, int family, int type); - -/** Set options for server sockets. */ -int -evio_setsockopt_server(int fd, int family, int type); diff --git a/pgproto/src/tarantool/sio.c b/pgproto/src/tarantool/sio.c deleted file mode 100644 index b96cc8faf5..0000000000 --- a/pgproto/src/tarantool/sio.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 - * <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 <module.h> -#include "diag.h" -#include "trivia/util.h" -#include "sio.h" - -#include <limits.h> -#include <unistd.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/uio.h> -#include <stdio.h> -#include <limits.h> -#include <netinet/in.h> /* TCP_NODELAY */ -#include <netinet/tcp.h> /* TCP_NODELAY */ -#include <arpa/inet.h> -#include <netdb.h> - -#ifndef NI_MAXHOST -#define NI_MAXHOST 1025 -#endif - -/** Get a string representation of a socket option name, - * for logging. - */ -static const char * -sio_option_name(int option) -{ -#define CASE_OPTION(opt) case opt: return #opt - switch (option) { - CASE_OPTION(SO_KEEPALIVE); - CASE_OPTION(SO_LINGER); - CASE_OPTION(SO_ERROR); - CASE_OPTION(SO_REUSEADDR); - CASE_OPTION(TCP_NODELAY); -#ifdef __linux__ - CASE_OPTION(TCP_KEEPCNT); - CASE_OPTION(TCP_KEEPINTVL); -#endif - default: - return "undefined"; - } -#undef CASE_OPTION -} - -int -sio_setsockopt(int fd, int level, int optname, - const void *optval, socklen_t optlen) -{ - int rc = setsockopt(fd, level, optname, optval, optlen); - if (rc) { - diag_set_SocketError(fd, "setsockopt(%s)", - sio_option_name(optname)); - } - return rc; -} - -int -sio_getsockopt(int fd, int level, int optname, - void *optval, socklen_t *optlen) -{ - int rc = getsockopt(fd, level, optname, optval, optlen); - if (rc) { - diag_set_SocketError(fd, "getsockopt(%s)", - sio_option_name(optname)); - } - return rc; -} - -static int -sio_addr_snprintf(char *buf, size_t size, const struct sockaddr *addr, - socklen_t addrlen) -{ - int res; - if (addr->sa_family == AF_UNIX) { - struct sockaddr_un *u = (struct sockaddr_un *)addr; - if (addrlen >= sizeof(*u)) - res = snprintf(buf, size, "unix/:%s", u->sun_path); - else - res = snprintf(buf, size, "unix/:(socket)"); - } else { - char host[NI_MAXHOST], serv[NI_MAXSERV]; - int flags = NI_NUMERICHOST | NI_NUMERICSERV; - if (getnameinfo(addr, addrlen, host, sizeof(host), serv, - sizeof(serv), flags) != 0) - res = snprintf(buf, size, "(host):(port)"); - else if (addr->sa_family == AF_INET) - res = snprintf(buf, size, "%s:%s", host, serv); - else - res = snprintf(buf, size, "[%s]:%s", host, serv); - } - assert(res + 1 < SERVICE_NAME_MAXLEN); - assert(res >= 0); - return res; -} - -/** - * Safely print a socket description to the given buffer, with correct overflow - * checks and all. - */ -int -sio_socketname_to_buffer(int fd, char *buf, int size) -{ - int n = 0; - (void)n; - SNPRINT(n, snprintf, buf, size, "fd %d", fd); - if (fd < 0) - return 0; - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - struct sockaddr *base_addr = (struct sockaddr *)&addr; - int rc = getsockname(fd, base_addr, &addrlen); - if (rc == 0) { - SNPRINT(n, snprintf, buf, size, ", aka "); - SNPRINT(n, sio_addr_snprintf, buf, size, base_addr, addrlen); - } - addrlen = sizeof(addr); - rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen); - if (rc == 0) { - SNPRINT(n, snprintf, buf, size, ", peer of "); - SNPRINT(n, sio_addr_snprintf, buf, size, base_addr, addrlen); - } - return 0; -} - -int -sio_getfl(int fd) -{ - int flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) - diag_set_SocketError(fd, "fcntl(..., F_GETFL, ...)"); - return flags; -} - -int -sio_setfl(int fd, int flag, int on) -{ - int flags = sio_getfl(fd); - if (flags < 0) - return flags; - flags = fcntl(fd, F_SETFL, on ? flags | flag : flags & ~flag); - if (flags < 0) - diag_set_SocketError(fd, "fcntl(..., F_SETFL, ...)"); - return flags; -} - -/** Try to automatically configure a listen backlog. - * On Linux, use the system setting, which defaults - * to 128. This way a system administrator can tune - * the backlog as needed. On other systems, use SOMAXCONN. - */ -static int -sio_listen_backlog() -{ -#ifdef __linux__ - FILE *proc = fopen("/proc/sys/net/core/somaxconn", "r"); - if (proc) { - int backlog; - int rc = fscanf(proc, "%d", &backlog); - fclose(proc); - if (rc == 1) - return backlog; - } -#endif /* __linux__ */ - return SOMAXCONN; -} - -int -sio_socket(int domain, int type, int protocol) -{ - /* AF_UNIX can't use tcp protocol */ - if (domain == AF_UNIX) - protocol = 0; - int fd = socket(domain, type, protocol); - if (fd < 0) - diag_set_SocketError(fd, "socket"); - return fd; -} - -int -sio_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) -{ - int rc = bind(fd, addr, addrlen); - if (rc < 0) - diag_set_SocketError(fd, "bind"); - return rc; -} - -int -sio_listen(int fd) -{ - int rc = listen(fd, sio_listen_backlog()); - if (rc < 0) - diag_set_SocketError(fd, "listen"); - return rc; -} - -int -sio_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) -{ - /* Accept a connection. */ - int newfd = accept(fd, addr, addrlen); - if (newfd < 0 && !sio_wouldblock(errno)) - diag_set_SocketError(fd, "accept"); - return newfd; -} diff --git a/pgproto/src/tarantool/sio.h b/pgproto/src/tarantool/sio.h deleted file mode 100644 index a5904328e3..0000000000 --- a/pgproto/src/tarantool/sio.h +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once -/* - * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 - * <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. - */ -/** - * A thin wrapper around BSD sockets. Sets the diagnostics - * area with a nicely formatted message for most errors (some - * intermittent errors such as EWOULDBLOCK, EINTR, EINPROGRESS, - * EAGAIN are an exception to this). The API is following - * suite of BSD socket API: most functinos -1 on error, 0 or a - * valid file descriptor on success. Exceptions to this rule, once - * again, are marked explicitly. - */ -#include <stdbool.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netdb.h> -#include <fcntl.h> -#include <errno.h> - -#if defined(__cplusplus) -extern "C" { -#endif /* defined(__cplusplus) */ - -enum { - /** - * - Unix socket path is 108 bytes max; - * - IP(v4, v6) max string len is 45; - * - * Max result is rounded up just in case the numbers are a bit different - * on various platforms. - */ - SERVICE_NAME_MAXLEN = 200, -}; - -/** Get socket flags. */ -int sio_getfl(int fd); - -/** Set socket flags. */ -int sio_setfl(int fd, int flag, int on); - -/** - * Check if an errno, returned from a sio function, means a - * non-critical error: EAGAIN, EWOULDBLOCK, EINTR. - */ -static inline bool -sio_wouldblock(int err) -{ - return err == EAGAIN || err == EWOULDBLOCK || err == EINTR; -} - -/** Create a TCP or AF_UNIX socket. */ -int -sio_socket(int domain, int type, int protocol); - -/** Set an option on a socket. */ -int -sio_setsockopt(int fd, int level, int optname, - const void *optval, socklen_t optlen); - -/** Get a socket option value. */ -int -sio_getsockopt(int fd, int level, int optname, - void *optval, socklen_t *optlen); - -/** - * Bind a socket to the given address. - */ -int -sio_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); - -/** - * Mark a socket as accepting connections. - */ -int -sio_listen(int fd); - -/** - * Safely print a socket description to the given buffer, with correct overflow - * checks and all. - */ -int -sio_socketname_to_buffer(int fd, char *buf, int size); - -/** - * Accept a client connection on a server socket. The - * diagnostics is not set for inprogress errors (@sa - * sio_wouldblock()) - */ -int sio_accept(int fd, struct sockaddr *addr, socklen_t *addrlen); - -#if defined(__cplusplus) -} /* extern "C" */ -#endif /* defined(__cplusplus) */ diff --git a/pgproto/src/tarantool/trivia/util.h b/pgproto/src/tarantool/trivia/util.h deleted file mode 100644 index 334466fbdb..0000000000 --- a/pgproto/src/tarantool/trivia/util.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -/* - * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 - * <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 <stdlib.h> -#include <stdio.h> -#include <limits.h> - -#include <module.h> - -#ifndef IOV_MAX -#define IOV_MAX 1024 -#endif - -/** - * Helper macro to handle easily snprintf() result - */ -#define SNPRINT(_total, _fun, _buf, _size, ...) do { \ - int written =_fun(_buf, _size, ##__VA_ARGS__); \ - if (written < 0) \ - return -1; \ - _total += written; \ - if (written < _size) { \ - _buf += written, _size -= written; \ - } else { \ - _buf = NULL, _size = 0; \ - } \ -} while(0) - -#define nelem(x) (sizeof((x))/sizeof((x)[0])) -#define field_sizeof(compound_type, field) sizeof(((compound_type *)NULL)->field) -#ifndef lengthof -#define lengthof(array) (sizeof (array) / sizeof ((array)[0])) -#endif - -/** - * An x* variant of a memory allocation function calls the original function - * and panics if it fails (i.e. it should never return NULL). - */ -#define xalloc_impl(size, func, args...) \ - ({ \ - void *ret = func(args); \ - if (unlikely(ret == NULL)) { \ - fprintf(stderr, "Can't allocate %zu bytes at %s:%d", \ - (size_t)(size), __FILE__, __LINE__); \ - exit(EXIT_FAILURE); \ - } \ - ret; \ - }) - -#define xmalloc(size) xalloc_impl((size), malloc, (size)) -#define xcalloc(n, size) xalloc_impl((n) * (size), calloc, (n), (size)) -#define xrealloc(ptr, size) xalloc_impl((size), realloc, (ptr), (size)) -#define xstrdup(s) xalloc_impl(strlen((s)) + 1, strdup, (s)) -#define xstrndup(s, n) xalloc_impl((n) + 1, strndup, (s), (n)) - -#ifndef NDEBUG -#define TRASH(ptr) memset(ptr, '#', sizeof(*ptr)) -#else -#define TRASH(ptr) (void) (ptr) -#endif - -/** - * If control flow reaches the point of the unreachable(), the program is - * undefined. It is useful in situations where the compiler cannot deduce - * the unreachability of the code. - */ -#if __has_builtin(__builtin_unreachable) || defined(__GNUC__) -# define unreachable() (assert(0), __builtin_unreachable()) -#else -# define unreachable() (assert(0)) -#endif -- GitLab