diff --git a/Apply-upstream-patches-from-codership-galera-558.patch b/Apply-upstream-patches-from-codership-galera-558.patch new file mode 100644 index 0000000000000000000000000000000000000000..d9e25223bda87318086efc69cec5cb2669b063ad --- /dev/null +++ b/Apply-upstream-patches-from-codership-galera-558.patch @@ -0,0 +1,1965 @@ +From: https://github.com/codership/galera/commits/4.x-26.4.16-gh-558 +Forwarded: https://github.com/codership/galera/pull/588 +From cb4437f7f4b30ff1d3394fa10fa1e4168b47b61c Mon Sep 17 00:00:00 2001 +Author: Teemu Ollakka +Date: Mon, 2 Oct 2023 14:48:06 +0300 +Subject: [PATCH 1/8] Add CMake option to compile with UBSAN instrumentation + +CMake option -DGALERA_WITH_UBSAN:BOOL=ON enables UBSAN +instrumentation for the build. +--- + cmake/asan.cmake | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/cmake/asan.cmake ++++ b/cmake/asan.cmake +@@ -1,5 +1,5 @@ + # +-# Copyright (C) 2020 Codership Oy ++# Copyright (C) 2020-2023 Codership Oy + # + + if (GALERA_WITH_ASAN) +@@ -7,3 +7,21 @@ if (GALERA_WITH_ASAN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + add_definitions(-DGALERA_WITH_ASAN) + endif() ++ ++if (GALERA_WITH_UBSAN) ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") ++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") ++ add_definitions(-DGALERA_WITH_UBSAN) ++ # don't run unit tests that use outdaed unaligned record set format ++ add_definitions(-DGALERA_ONLY_ALIGNED) ++ ++ find_library(UBSAN_LIB NAMES ubsan libubsan.so.1) ++ message(STATUS ${UBSAN_LIB}) ++ set(CMAKE_REQUIRED_LIBRARIES ${UBSAN_LIB}) ++ check_c_source_compiles("int main() { return 0; }" GALERA_HAVE_UBSAN_LIB) ++ if (NOT GALERA_HAVE_UBSAN_LIB) ++ message(FATAL_ERROR "Could not find UBSAN support library") ++ endif() ++ unset(CMAKE_REQUIRED_LIBRARIES) ++ list(APPEND GALERA_SYSTEM_LIBS ${UBSAN_LIB}) ++endif() +--- a/cmake/os.cmake ++++ b/cmake/os.cmake +@@ -11,7 +11,8 @@ set(GALERA_SYSTEM_LIBS ${PTHREAD_LIB} ${ + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + # Check if linkage with atomic library is needed for 8 byte atomics + set(ATOMIC_8_TEST_C_SOURCE +- "int main() { long long val; __atomic_fetch_add_8(&val, 1, __ATOMIC_SEQ_CST); return 0;}") ++ "#include ++ int main() { atomic_llong val; atomic_fetch_add(&val, 1); return 0; }") + check_c_source_compiles("${ATOMIC_8_TEST_C_SOURCE}" GALERA_HAVE_ATOMIC) + if (NOT GALERA_HAVE_ATOMIC) + find_library(ATOMIC_LIB NAMES atomic libatomic.so.1) +--- a/cmake/compiler.cmake ++++ b/cmake/compiler.cmake +@@ -1,5 +1,5 @@ + # +-# Copyright (C) 2020 Codership Oy ++# Copyright (C) 2020-2023 Codership Oy + # + # Common compiler and preprocessor options. + # +@@ -46,7 +46,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + # To detect STD library misuse with Debug builds. +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_ASSERTIONS") ++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_ASSERTIONS -O0") ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") + # Enable debug sync points + add_definitions(-DGU_DBUG_ON) + else() +--- a/galera/tests/data_set_check.cpp ++++ b/galera/tests/data_set_check.cpp +@@ -1,10 +1,14 @@ +-/* Copyright (C) 2013-2020 Codership Oy ++/* Copyright (C) 2013-2023 Codership Oy + * + * $Id$ + */ + + #undef NDEBUG + ++#if defined(__sun) ++#define GALERA_ONLY_ALIGNED ++#endif ++ + #include "../src/data_set.hpp" + + #include "gu_logger.hpp" +--- a/galera/tests/defaults_check.cpp ++++ b/galera/tests/defaults_check.cpp +@@ -1,5 +1,5 @@ + // +-// Copyright (C) 2018-2020 Codership Oy ++// Copyright (C) 2018-2023 Codership Oy + // + + #include +@@ -307,7 +307,6 @@ START_TEST(defaults) + ret, strerror(ret)); + } + +- provider.free(&provider); + mark_point(); + + /* cleanup files */ +--- a/galerautils/src/gu_crc32c_x86.c ++++ b/galerautils/src/gu_crc32c_x86.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2020 Codership Oy ++ * Copyright (C) 2020-2023 Codership Oy + */ + + /** +@@ -19,33 +19,77 @@ + #include + #include + ++/* process data preceding the first 4-aligned byte */ + static inline gu_crc32c_t +-crc32c_x86_tail3(gu_crc32c_t state, const uint8_t* ptr, size_t len) ++crc32c_x86_head3(gu_crc32c_t state, const uint8_t* ptr, size_t len) + { ++ assert(len > 0); + assert(len < 4); + +- switch (len) ++ if (((uintptr_t)ptr) & 1) + { +- case 3: ++ /* odd address */ + state = __builtin_ia32_crc32qi(state, *ptr); + ptr++; +- /* fall through */ ++ len--; ++ } ++ ++ /* here ptr is at least 2-aligned */ ++ if (len >= 2) ++ { ++ assert(0 == ((uintptr_t)ptr)%2); ++ state = __builtin_ia32_crc32hi(state, *(uint16_t*)ptr); ++ ptr += 2; ++ len -= 2; ++ } ++ ++ if (len) ++ { ++ assert(1 == len); ++ state = __builtin_ia32_crc32qi(state, *ptr); ++ } ++ ++ return state; ++} ++ ++static inline gu_crc32c_t ++crc32c_x86_tail3(gu_crc32c_t state, const uint8_t* ptr, size_t len) ++{ ++ switch (len) ++ { ++ case 3: + case 2: ++ /* this byte is 4-aligned */ + state = __builtin_ia32_crc32hi(state, *(uint16_t*)ptr); +- break; ++ if (len == 2) break; ++ ptr += 2; ++ /* fall through */ + case 1: + state = __builtin_ia32_crc32qi(state, *ptr); ++ break; ++ default: ++ assert(0); + } +- + return state; + } + + static inline gu_crc32c_t + crc32c_x86(gu_crc32c_t state, const uint8_t* ptr, size_t len) + { ++ if (0 == len) return state; ++ + static size_t const arg_size = sizeof(uint32_t); + +- /* apparently no ptr misalignment protection is needed */ ++ size_t align_offset = ((uintptr_t)ptr) % arg_size; ++ if (align_offset) ++ { ++ align_offset = arg_size - align_offset; ++ if (align_offset > len) align_offset = len; ++ state = crc32c_x86_head3(state, ptr, align_offset); ++ len -= align_offset; ++ ptr += align_offset; ++ } ++ + while (len >= arg_size) + { + state = __builtin_ia32_crc32si(state, *(uint32_t*)ptr); +@@ -55,7 +99,7 @@ crc32c_x86(gu_crc32c_t state, const uint + + assert(len < 4); + +- return crc32c_x86_tail3(state, ptr, len); ++ return (len ? crc32c_x86_tail3(state, ptr, len) : state); + } + + gu_crc32c_t +@@ -71,7 +115,20 @@ gu_crc32c_x86_64(gu_crc32c_t state, cons + const uint8_t* ptr = (const uint8_t*)data; + + #ifdef __LP64__ ++ if (0 == len) return state; ++ + static size_t const arg_size = sizeof(uint64_t); ++ ++ size_t align_offset = ((uintptr_t)ptr) % arg_size; ++ if (align_offset) ++ { ++ align_offset = arg_size - align_offset; ++ if (align_offset > len) align_offset = len; ++ state = crc32c_x86(state, ptr, align_offset); ++ len -= align_offset; ++ ptr += align_offset; ++ } ++ + uint64_t state64 = state; + + while (len >= arg_size) +--- a/galerautils/src/gu_mmh3.c ++++ b/galerautils/src/gu_mmh3.c +@@ -257,10 +257,11 @@ static uint64_t const GU_MMH128_SEED2 = + extern void + gu_mmh128 (const void* const msg, size_t const len, void* const out) + { +- _mmh3_128_seed (msg, len, GU_MMH128_SEED1, GU_MMH128_SEED2, (uint64_t*)out); +- uint64_t* const res = (uint64_t*)out; ++ uint64_t res[2]; ++ _mmh3_128_seed (msg, len, GU_MMH128_SEED1, GU_MMH128_SEED2, res); + res[0] = gu_le64(res[0]); + res[1] = gu_le64(res[1]); ++ memcpy(out, res, sizeof(res)); + } + + /* returns hash as an integer, in host byte-order */ +@@ -378,8 +379,9 @@ gu_mmh128_get32 (const gu_mmh128_ctx_t* + void + gu_mmh3_32 (const void* const key, int const len, uint32_t const seed, void* const out) + { +- uint32_t const res = _mmh32_seed (key, len, seed); +- *((uint32_t*)out) = gu_le32(res); ++ uint32_t res = _mmh32_seed (key, len, seed); ++ res = gu_le32(res); ++ memcpy(out, &res, sizeof(res)); + } + + //----------------------------------------------------------------------------- +--- a/galerautils/src/gu_utils.c ++++ b/galerautils/src/gu_utils.c +@@ -1,4 +1,4 @@ +-// Copyright (C) 2010 Codership Oy ++// Copyright (C) 2010-2023 Codership Oy + + /** + * @file Miscellaneous utility functions +@@ -42,13 +42,20 @@ gu_str2ll (const char* str, long long* l + shift += 10; + ret++; + +- if (llret == ((llret << (shift + 1)) >> (shift + 1))) { +- llret <<= shift; +- } +- else { /* ERANGE */ +- if (llret > 0) llret = LLONG_MAX; +- else llret = LLONG_MIN; +- errno = ERANGE; ++ { ++ long long const sign = (llret < 0 ? -1 : 1); ++ unsigned long long ullret = sign * llret; ++ ++ if (ullret == ((ullret << (shift + 1)) >> (shift + 1))) { ++ ullret <<= shift; ++ llret = ullret; ++ llret *= sign; ++ } ++ else { /* ERANGE */ ++ if (llret > 0) llret = LLONG_MAX; ++ else llret = LLONG_MIN; ++ errno = ERANGE; ++ } + } + /* fall through */ + default: +--- a/galera/src/certification.cpp ++++ b/galera/src/certification.cpp +@@ -98,8 +98,8 @@ check_purge_complete(const galera::Certi + { + std::for_each( + cert_index.begin(), cert_index.end(), +- [&cert_index, &key_set, +- ts](const galera::Certification::CertIndexNG::value_type& ke) { ++ [&key_set, ts] ++ (const galera::Certification::CertIndexNG::value_type& ke) { + ke->for_each_ref([&ke, &key_set, ts](const TrxHandleSlave* ref) { + if (ts == ref) + { +--- a/gcache/src/gcache_mem_store.hpp ++++ b/gcache/src/gcache_mem_store.hpp +@@ -92,16 +92,19 @@ namespace gcache + + void* realloc (void* ptr, size_type size) + { +- BufferHeader* bh(0); +- size_type old_size(0); ++ if (!ptr) return malloc(size); + +- if (ptr) ++ BufferHeader* bh(ptr2BH(ptr)); ++ assert (SEQNO_NONE == bh->seqno_g); ++ ++ if (!size) + { +- bh = ptr2BH(ptr); +- assert (SEQNO_NONE == bh->seqno_g); +- old_size = bh->size; ++ free(bh); ++ return nullptr; + } + ++ uintptr_t const orig(reinterpret_cast(bh)); ++ size_type const old_size(bh->size); + diff_type const diff_size(size - old_size); + + if (size > max_size_ || +@@ -109,11 +112,11 @@ namespace gcache + + assert (size_ + diff_size <= max_size_); + ++ allocd_.erase(bh); + void* tmp = ::realloc (bh, size); + + if (tmp) + { +- allocd_.erase(bh); + allocd_.insert(tmp); + + bh = BH_cast(tmp); +@@ -124,6 +127,13 @@ namespace gcache + + return (bh + 1); + } ++ else ++ { ++ assert(size > 0); ++ /* orginal buffer is still allocated so we need to restore it ++ * but we can't use bh directly due to GCC warnings */ ++ allocd_.insert(reinterpret_cast(orig)); ++ } + + return 0; + } +@@ -133,8 +143,8 @@ namespace gcache + assert (BH_is_released(bh)); + + size_ -= bh->size; +- ::free (bh); + allocd_.erase(bh); ++ ::free (bh); + } + + void set_max_size (size_t size) { max_size_ = size; } +--- a/gcache/src/gcache_rb_store.cpp ++++ b/gcache/src/gcache_rb_store.cpp +@@ -798,7 +798,8 @@ namespace gcache + sizeof(cs_old))); + std::ostringstream msg; + +- msg << "Attempt to reuse the same seqno: " << seqno_g ++ msg << "Attempt (" << collision_count ++ << ") to reuse the same seqno: " << seqno_g + << ". New ptr = " << new_ptr << ", " << bh + << ", cs: " << gu::Hexdump(cs_new, sizeof(cs_new)) + << ", previous ptr = " << old_ptr; +@@ -1230,7 +1231,7 @@ namespace gcache + size_t chain_count[] = { 0, 0, 0, 0 }; + + chain_t chain(NONE); +- const uint8_t* chain_start; ++ const uint8_t* chain_start(start_); + size_t count; + + bool next(false); +--- a/gcs/src/gcs_group.cpp ++++ b/gcs/src/gcs_group.cpp +@@ -1409,7 +1409,7 @@ group_for_each_donor_in_string (const gc + * that at least one of the nodes in the list will become available. */ + if (-EAGAIN != err) err = idx; + +- begin = end + 1; /* skip comma */ ++ if (end) begin = end + 1; /* skip comma */ + + } while (end != NULL); + +@@ -1498,7 +1498,7 @@ group_find_ist_donor_by_name_in_string ( + ret = idx; + } + } +- begin = end + 1; ++ if (end) begin = end + 1; + } while (end != NULL); + + if (ret == -1) { +--- a/gcs/src/unit_tests/gcs_core_test.cpp ++++ b/gcs/src/unit_tests/gcs_core_test.cpp +@@ -506,7 +506,7 @@ START_TEST (gcs_core_test_api) + size_t act_size = sizeof(act3_str); + + action_t act_s(act, NULL, NULL, act_size, GCS_ACT_WRITESET, -1, (gu_thread_t)-1); +- action_t act_r(act, NULL, NULL, -1, (gcs_act_type_t)-1, -1, (gu_thread_t)-1); ++ action_t act_r(act, NULL, NULL, -1, (gcs_act_type_t)GCS_ACT_UNKNOWN, -1, (gu_thread_t)-1); + long i = 5; + + // test basic fragmentaiton +@@ -610,7 +610,7 @@ CORE_TEST_OWN (int gcs_proto_ver) + size_t act_size = sizeof(act2_str); + + action_t act_s(act, NULL, NULL, act_size, GCS_ACT_WRITESET, -1, (gu_thread_t)-1); +- action_t act_r(act, NULL, NULL, -1, (gcs_act_type_t)-1, -1, (gu_thread_t)-1); ++ action_t act_r(act, NULL, NULL, -1, (gcs_act_type_t)GCS_ACT_UNKNOWN, -1, (gu_thread_t)-1); + + // Create primary and non-primary component messages + gcs_comp_msg_t* prim = gcs_comp_msg_new (true, false, 0, 1, 0); +--- a/gcomm/src/evs_message2.cpp ++++ b/gcomm/src/evs_message2.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2009-2012 Codership Oy ++ * Copyright (C) 2009-2023 Codership Oy + * + * $Id$ + */ +@@ -179,8 +179,16 @@ size_t gcomm::evs::Message::serialize(gu + return offset; + } + ++gcomm::evs::Message::Type gcomm::evs::Message::get_type(const gu::byte_t* buf, ++ size_t buflen, ++ size_t offset) ++{ ++ uint8_t b; ++ gu_trace(offset = gu::unserialize1(buf, buflen, offset, b)); ++ return static_cast((b >> 2) & 0x7); ++} + +-size_t gcomm::evs::Message::unserialize(const gu::byte_t* const buf, ++size_t gcomm::evs::Message::unserialize_common(const gu::byte_t* const buf, + size_t const buflen, + size_t offset) + { +@@ -267,13 +275,9 @@ size_t gcomm::evs::UserMessage::serializ + + size_t gcomm::evs::UserMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + gu_trace(offset = gu::unserialize1(buf, buflen, offset, user_type_)); + uint8_t b; + gu_trace(offset = gu::unserialize1(buf, buflen, offset, b)); +@@ -340,13 +344,9 @@ size_t gcomm::evs::DelegateMessage::seri + + size_t gcomm::evs::DelegateMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + return offset; + } + +@@ -371,13 +371,9 @@ size_t gcomm::evs::GapMessage::serialize + + size_t gcomm::evs::GapMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); + gu_trace(offset = range_uuid_.unserialize(buf, buflen, offset)); +@@ -406,13 +402,9 @@ size_t gcomm::evs::JoinMessage::serializ + + size_t gcomm::evs::JoinMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); + node_list_.clear(); +@@ -441,13 +433,9 @@ size_t gcomm::evs::InstallMessage::seria + + size_t gcomm::evs::InstallMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); + gu_trace(offset = install_view_id_.unserialize(buf, buflen, offset)); +@@ -477,13 +465,9 @@ size_t gcomm::evs::LeaveMessage::seriali + + size_t gcomm::evs::LeaveMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, seq_)); + gu_trace(offset = gu::unserialize8(buf, buflen, offset, aru_seq_)); + return offset; +@@ -512,13 +496,9 @@ size_t gcomm::evs::DelayedListMessage::s + + size_t gcomm::evs::DelayedListMessage::unserialize(const gu::byte_t* const buf, + size_t const buflen, +- size_t offset, +- bool skip_header) ++ size_t offset) + { +- if (skip_header == false) +- { +- gu_trace(offset = Message::unserialize(buf, buflen, offset)); +- } ++ gu_trace(offset = Message::unserialize_common(buf, buflen, offset)); + delayed_list_.clear(); + uint8_t list_sz(0); + gu_trace(offset = gu::unserialize1(buf, buflen, offset, list_sz)); +--- a/gcomm/src/evs_message2.hpp ++++ b/gcomm/src/evs_message2.hpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2009-2019 Codership Oy ++ * Copyright (C) 2009-2023 Codership Oy + */ + + #ifndef EVS_MESSAGE2_HPP +@@ -290,7 +290,17 @@ public: + */ + gu::datetime::Date tstamp() const { return tstamp_; } + +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset); ++ /* Read type from message buffer. */ ++ static Type get_type(const gu::byte_t* buf, size_t buflen, size_t offset); ++ ++ /* Unserialize common header. */ ++ size_t unserialize_common(const gu::byte_t* buf, size_t buflen, ++ size_t offset); ++ ++ /* Unserialize message. */ ++ virtual size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) ++ = 0; + + bool operator==(const Message& cmp) const; + +@@ -444,8 +454,7 @@ public: + void set_aru_seq(const seqno_t as) { aru_seq_ = as; } + + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset) override; + size_t serial_size() const; + + }; +@@ -504,8 +513,8 @@ public: + fifo_seq) + { } + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) override; + size_t serial_size() const; + }; + +@@ -537,8 +546,8 @@ public: + range) + { } + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) override; + size_t serial_size() const; + }; + +@@ -569,8 +578,8 @@ public: + node_list) + { } + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) override; + size_t serial_size() const; + }; + +@@ -602,8 +611,8 @@ public: + node_list) + { } + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) override; + size_t serial_size() const; + }; + +@@ -631,8 +640,8 @@ public: + flags) + { } + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) override; + size_t serial_size() const; + }; + +@@ -662,8 +671,8 @@ public: + const DelayedList& delayed_list() const { return delayed_list_; } + + size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset) const; +- size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset, +- bool skip_header = false); ++ size_t unserialize(const gu::byte_t* buf, size_t buflen, ++ size_t offset) override; + size_t serial_size() const; + bool operator==(const DelayedListMessage& cmp) const + { +--- a/gcomm/src/evs_proto.cpp ++++ b/gcomm/src/evs_proto.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2009-2019 Codership Oy ++ * Copyright (C) 2009-2023 Codership Oy + */ + + #include "evs_proto.hpp" +@@ -2490,67 +2490,63 @@ void gcomm::evs::Proto::handle_msg(const + // Protolay interface + //////////////////////////////////////////////////////////////////////// + +-size_t gcomm::evs::Proto::unserialize_message(const UUID& source, +- const Datagram& rb, +- Message* msg) ++std::pair, size_t> ++gcomm::evs::Proto::unserialize_message(const UUID& source, const Datagram& rb) + { +- size_t offset; ++ size_t offset = 0; + const gu::byte_t* begin(gcomm::begin(rb)); + const size_t available(gcomm::available(rb)); +- gu_trace(offset = msg->unserialize(begin, +- available, +- 0)); +- if ((msg->flags() & Message::F_SOURCE) == 0) ++ std::unique_ptr ret; ++ switch (Message::get_type(begin, available, offset)) + { +- assert(source != UUID::nil()); +- gcomm_assert(source != UUID::nil()); +- msg->set_source(source); +- } +- +- switch (msg->type()) +- { +- case Message::EVS_T_NONE: +- gu_throw_fatal; +- break; ++ case Message::EVS_T_NONE: gu_throw_fatal; break; + case Message::EVS_T_USER: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new UserMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; + case Message::EVS_T_DELEGATE: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new DelegateMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; + case Message::EVS_T_GAP: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new GapMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; + case Message::EVS_T_JOIN: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new JoinMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; + case Message::EVS_T_INSTALL: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new InstallMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; + case Message::EVS_T_LEAVE: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new LeaveMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; + case Message::EVS_T_DELAYED_LIST: +- gu_trace(offset = static_cast(*msg).unserialize( +- begin, available, offset, true)); ++ ret = std::unique_ptr(new DelayedListMessage); ++ gu_trace(offset = ret->unserialize(begin, available, offset)); + break; ++ default: ++ return {std::unique_ptr{}, 0}; + } +- return (offset + rb.offset()); ++ ++ /* Message did not have source field, must be set from source reported ++ by the lower layer. */ ++ if ((ret->flags() & Message::F_SOURCE) == 0) ++ { ++ assert(source != UUID::nil()); ++ gcomm_assert(source != UUID::nil()); ++ ret->set_source(source); ++ } ++ ++ return {std::move(ret), offset + rb.offset()}; + } + +-void gcomm::evs::Proto::handle_up(const void* cid, +- const Datagram& rb, ++void gcomm::evs::Proto::handle_up(const void* cid, const Datagram& rb, + const ProtoUpMeta& um) + { +- +- Message msg; +- + if (state() == S_CLOSED || um.source() == uuid() || is_evicted(um.source())) + { + // Silent drop +@@ -2559,12 +2555,16 @@ void gcomm::evs::Proto::handle_up(const + + gcomm_assert(um.source() != UUID::nil()); + ++ std::pair, size_t> msg; + try + { +- size_t offset; +- gu_trace(offset = unserialize_message(um.source(), rb, &msg)); +- handle_msg(msg, Datagram(rb, offset), +- (msg.flags() & Message::F_RETRANS) == 0); ++ gu_trace(msg = unserialize_message(um.source(), rb)); ++ if (not msg.first) { ++ /* Message could not be serialized. */ ++ return; ++ } ++ handle_msg(*msg.first, Datagram(rb, msg.second), ++ (msg.first->flags() & Message::F_RETRANS) == 0); + } + catch (gu::Exception& e) + { +@@ -2575,11 +2575,11 @@ void gcomm::evs::Proto::handle_up(const + break; + + case EINVAL: +- log_warn << "invalid message: " << msg; ++ log_warn << "invalid message: " << *msg.first; + break; + + default: +- log_fatal << "exception caused by message: " << msg; ++ log_fatal << "exception caused by message: " << *msg.first; + std::cerr << " state after handling message: " << *this; + throw; + } +@@ -3791,20 +3791,20 @@ void gcomm::evs::Proto::handle_user(cons + } + } + +- + void gcomm::evs::Proto::handle_delegate(const DelegateMessage& msg, + NodeMap::iterator ii, + const Datagram& rb) + { + gcomm_assert(ii != known_.end()); + evs_log_debug(D_DELEGATE_MSGS) << "delegate message " << msg; +- Message umsg; +- size_t offset; +- gu_trace(offset = unserialize_message(UUID::nil(), rb, &umsg)); +- gu_trace(handle_msg(umsg, Datagram(rb, offset), false)); ++ std::pair, size_t> umsg; ++ gu_trace(umsg = unserialize_message(UUID::nil(), rb)); ++ if (not umsg.first) { ++ return; ++ } ++ gu_trace(handle_msg(*umsg.first, Datagram(rb, umsg.second), false)); + } + +- + void gcomm::evs::Proto::handle_gap(const GapMessage& msg, NodeMap::iterator ii) + { + assert(ii != known_.end()); +--- a/gcomm/src/evs_proto.hpp ++++ b/gcomm/src/evs_proto.hpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2009-2019 Codership Oy ++ * Copyright (C) 2009-2023 Codership Oy + */ + + /*! +@@ -288,11 +288,9 @@ private: + void populate_node_list(MessageNodeList*) const; + void isolate(gu::datetime::Period period); + public: +- static size_t unserialize_message(const UUID&, +- const Datagram&, +- Message*); +- void handle_msg(const Message& msg, +- const Datagram& dg = Datagram(), ++ static std::pair, size_t> ++ unserialize_message(const UUID&, const Datagram&); ++ void handle_msg(const Message& msg, const Datagram& dg = Datagram(), + bool direct = true); + // Protolay + void handle_up(const void*, const Datagram&, const ProtoUpMeta&); +--- a/gcomm/test/check_evs2.cpp ++++ b/gcomm/test/check_evs2.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2009-2020 Codership Oy ++ * Copyright (C) 2009-2023 Codership Oy + */ + + /*! +@@ -455,12 +455,13 @@ START_TEST(test_input_map_gap_range_list + } + END_TEST + +-static Datagram* get_msg(DummyTransport* tp, Message* msg, bool release = true) ++static Datagram* get_msg(DummyTransport* tp, std::unique_ptr& msg, ++ bool release = true) + { + Datagram* rb = tp->out(); + if (rb != 0) + { +- gu_trace(Proto::unserialize_message(tp->uuid(), *rb, msg)); ++ msg = Proto::unserialize_message(tp->uuid(), *rb).first; + if (release == true) + { + delete rb; +@@ -471,7 +472,7 @@ static Datagram* get_msg(DummyTransport* + + static void single_join(DummyTransport* t, Proto* p) + { +- Message jm, im, gm; ++ std::unique_ptr jm, im, gm; + + // Initial state is joining + p->shift_to(Proto::S_JOINING); +@@ -479,42 +480,42 @@ static void single_join(DummyTransport* + // Send join must produce emitted join message + p->send_join(); + +- Datagram* rb = get_msg(t, &jm); ++ Datagram* rb = get_msg(t, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); + + // Install message is emitted at the end of JOIN handling + // 'cause this is the only instance and is always consistent + // with itself +- rb = get_msg(t, &im); ++ rb = get_msg(t, im); + ck_assert(rb != 0); +- ck_assert(im.type() == Message::EVS_T_INSTALL); ++ ck_assert(im->type() == Message::EVS_T_INSTALL); + + // Handling INSTALL message emits three gap messages, + // one for receiving install message (commit gap), one for + // shift to install and one for shift to operational +- rb = get_msg(t, &gm); ++ rb = get_msg(t, gm); + ck_assert(rb != 0); +- ck_assert(gm.type() == Message::EVS_T_GAP); +- ck_assert((gm.flags() & Message::F_COMMIT) != 0); ++ ck_assert(gm->type() == Message::EVS_T_GAP); ++ ck_assert((gm->flags() & Message::F_COMMIT) != 0); + +- rb = get_msg(t, &gm); ++ rb = get_msg(t, gm); + ck_assert(rb != 0); +- ck_assert(gm.type() == Message::EVS_T_GAP); +- ck_assert((gm.flags() & Message::F_COMMIT) == 0); ++ ck_assert(gm->type() == Message::EVS_T_GAP); ++ ck_assert((gm->flags() & Message::F_COMMIT) == 0); + +- rb = get_msg(t, &gm); ++ rb = get_msg(t, gm); + ck_assert(rb != 0); +- ck_assert(gm.type() == Message::EVS_T_GAP); +- ck_assert((gm.flags() & Message::F_COMMIT) == 0); ++ ck_assert(gm->type() == Message::EVS_T_GAP); ++ ck_assert((gm->flags() & Message::F_COMMIT) == 0); + + // State must have evolved JOIN -> S_GATHER -> S_INSTALL -> S_OPERATIONAL + ck_assert(p->state() == Proto::S_OPERATIONAL); + + // Handle join message again, must stay in S_OPERATIONAL, must not + // emit anything +- p->handle_msg(jm); +- rb = get_msg(t, &gm); ++ p->handle_msg(*jm); ++ rb = get_msg(t, gm); + ck_assert(rb == 0); + ck_assert(p->state() == Proto::S_OPERATIONAL); + +@@ -553,11 +554,11 @@ static void double_join(DummyTransport* + DummyTransport* t2, Proto* p2) + { + +- Message jm; +- Message im; +- Message gm; +- Message gm2; +- Message msg; ++ std::unique_ptr jm; ++ std::unique_ptr im; ++ std::unique_ptr gm; ++ std::unique_ptr gm2; ++ std::unique_ptr msg; + + Datagram* rb; + +@@ -570,99 +571,99 @@ static void double_join(DummyTransport* + // Expected output: one join message + p2->send_join(false); + ck_assert(p2->state() == Proto::S_JOINING); +- rb = get_msg(t2, &jm); ++ rb = get_msg(t2, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(t2, &msg); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(t2, msg); + ck_assert(rb == 0); + + // Handle node 2's join on node 1 + // Expected output: shift to S_GATHER and one join message +- p1->handle_msg(jm); ++ p1->handle_msg(*jm); + ck_assert(p1->state() == Proto::S_GATHER); +- rb = get_msg(t1, &jm); ++ rb = get_msg(t1, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(t1, &msg); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(t1, msg); + ck_assert(rb == 0); + + // Handle node 1's join on node 2 + // Expected output: shift to S_GATHER and one join message +- p2->handle_msg(jm); ++ p2->handle_msg(*jm); + ck_assert(p2->state() == Proto::S_GATHER); +- rb = get_msg(t2, &jm); ++ rb = get_msg(t2, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(t2, &msg); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(t2, msg); + ck_assert(rb == 0); + + // Handle node 2's join on node 1 + // Expected output: Install and commit gap messages, state stays in S_GATHER +- p1->handle_msg(jm); ++ p1->handle_msg(*jm); + ck_assert(p1->state() == Proto::S_GATHER); +- rb = get_msg(t1, &im); ++ rb = get_msg(t1, im); + ck_assert(rb != 0); +- ck_assert(im.type() == Message::EVS_T_INSTALL); +- rb = get_msg(t1, &gm); ++ ck_assert(im->type() == Message::EVS_T_INSTALL); ++ rb = get_msg(t1, gm); + ck_assert(rb != 0); +- ck_assert(gm.type() == Message::EVS_T_GAP); +- ck_assert((gm.flags() & Message::F_COMMIT) != 0); +- rb = get_msg(t1, &msg); ++ ck_assert(gm->type() == Message::EVS_T_GAP); ++ ck_assert((gm->flags() & Message::F_COMMIT) != 0); ++ rb = get_msg(t1, msg); + ck_assert(rb == 0); + + // Handle install message on node 2 + // Expected output: commit gap message and state stays in S_RECOVERY +- p2->handle_msg(im); ++ p2->handle_msg(*im); + ck_assert(p2->state() == Proto::S_GATHER); +- rb = get_msg(t2, &gm2); ++ rb = get_msg(t2, gm2); + ck_assert(rb != 0); +- ck_assert(gm2.type() == Message::EVS_T_GAP); +- ck_assert((gm2.flags() & Message::F_COMMIT) != 0); +- rb = get_msg(t2, &msg); ++ ck_assert(gm2->type() == Message::EVS_T_GAP); ++ ck_assert((gm2->flags() & Message::F_COMMIT) != 0); ++ rb = get_msg(t2, msg); + ck_assert(rb == 0); + + // Handle gap messages + // Expected output: Both nodes shift to S_INSTALL, + // both send gap messages +- p1->handle_msg(gm2); ++ p1->handle_msg(*gm2); + ck_assert(p1->state() == Proto::S_INSTALL); +- Message gm12; +- rb = get_msg(t1, &gm12); ++ std::unique_ptr gm12; ++ rb = get_msg(t1, gm12); + ck_assert(rb != 0); +- ck_assert(gm12.type() == Message::EVS_T_GAP); +- ck_assert((gm12.flags() & Message::F_COMMIT) == 0); +- rb = get_msg(t1, &msg); ++ ck_assert(gm12->type() == Message::EVS_T_GAP); ++ ck_assert((gm12->flags() & Message::F_COMMIT) == 0); ++ rb = get_msg(t1, msg); + ck_assert(rb == 0); + +- p2->handle_msg(gm); ++ p2->handle_msg(*gm); + ck_assert(p2->state() == Proto::S_INSTALL); +- Message gm22; +- rb = get_msg(t2, &gm22); ++ std::unique_ptr gm22; ++ rb = get_msg(t2, gm22); + ck_assert(rb != 0); +- ck_assert(gm22.type() == Message::EVS_T_GAP); +- ck_assert((gm22.flags() & Message::F_COMMIT) == 0); +- rb = get_msg(t2, &msg); ++ ck_assert(gm22->type() == Message::EVS_T_GAP); ++ ck_assert((gm22->flags() & Message::F_COMMIT) == 0); ++ rb = get_msg(t2, msg); + ck_assert(rb == 0); + + // Handle final gap messages, expected output shift to operational + // and gap message + +- p1->handle_msg(gm22); ++ p1->handle_msg(*gm22); + ck_assert(p1->state() == Proto::S_OPERATIONAL); +- rb = get_msg(t1, &msg); ++ rb = get_msg(t1, msg); + ck_assert(rb != 0); +- ck_assert(msg.type() == Message::EVS_T_GAP); +- ck_assert((msg.flags() & Message::F_COMMIT) == 0); +- rb = get_msg(t1, &msg); ++ ck_assert(msg->type() == Message::EVS_T_GAP); ++ ck_assert((msg->flags() & Message::F_COMMIT) == 0); ++ rb = get_msg(t1, msg); + ck_assert(rb == 0); + +- p2->handle_msg(gm12); ++ p2->handle_msg(*gm12); + ck_assert(p2->state() == Proto::S_OPERATIONAL); +- rb = get_msg(t2, &msg); ++ rb = get_msg(t2, msg); + ck_assert(rb != 0); +- ck_assert(msg.type() == Message::EVS_T_GAP); +- ck_assert((msg.flags() & Message::F_COMMIT) == 0); +- rb = get_msg(t2, &msg); ++ ck_assert(msg->type() == Message::EVS_T_GAP); ++ ck_assert((msg->flags() & Message::F_COMMIT) == 0); ++ rb = get_msg(t2, msg); + ck_assert(rb == 0); + + } +@@ -2031,12 +2032,12 @@ START_TEST(test_gh_100) + // install message is generated. After that handle install timer + // on p1 and verify that the newly generated install message has + // greater install view id seqno than the first one. +- Message jm; +- Message im; +- Message im2; +- Message gm; +- Message gm2; +- Message msg; ++ std::unique_ptr jm; ++ std::unique_ptr im; ++ std::unique_ptr im2; ++ std::unique_ptr gm; ++ std::unique_ptr gm2; ++ std::unique_ptr msg; + + Datagram* rb; + +@@ -2049,56 +2050,56 @@ START_TEST(test_gh_100) + // Expected output: one join message + p2.send_join(false); + ck_assert(p2.state() == Proto::S_JOINING); +- rb = get_msg(&t2, &jm); ++ rb = get_msg(&t2, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(&t2, &msg); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(&t2, msg); + ck_assert(rb == 0); + + // Handle node 2's join on node 1 + // Expected output: shift to S_GATHER and one join message +- p1.handle_msg(jm); ++ p1.handle_msg(*jm); + ck_assert(p1.state() == Proto::S_GATHER); +- rb = get_msg(&t1, &jm); ++ rb = get_msg(&t1, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(&t1, &msg); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(&t1, msg); + ck_assert(rb == 0); + + // Handle node 1's join on node 2 + // Expected output: shift to S_GATHER and one join message +- p2.handle_msg(jm); ++ p2.handle_msg(*jm); + ck_assert(p2.state() == Proto::S_GATHER); +- rb = get_msg(&t2, &jm); ++ rb = get_msg(&t2, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(&t2, &msg); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(&t2, msg); + ck_assert(rb == 0); + + // Handle node 2's join on node 1 + // Expected output: Install and commit gap messages, state stays in S_GATHER +- p1.handle_msg(jm); ++ p1.handle_msg(*jm); + ck_assert(p1.state() == Proto::S_GATHER); +- rb = get_msg(&t1, &im); ++ rb = get_msg(&t1, im); + ck_assert(rb != 0); +- ck_assert(im.type() == Message::EVS_T_INSTALL); +- rb = get_msg(&t1, &gm); ++ ck_assert(im->type() == Message::EVS_T_INSTALL); ++ rb = get_msg(&t1, gm); + ck_assert(rb != 0); +- ck_assert(gm.type() == Message::EVS_T_GAP); +- ck_assert((gm.flags() & Message::F_COMMIT) != 0); +- rb = get_msg(&t1, &msg); ++ ck_assert(gm->type() == Message::EVS_T_GAP); ++ ck_assert((gm->flags() & Message::F_COMMIT) != 0); ++ rb = get_msg(&t1, msg); + ck_assert(rb == 0); + + // Handle timers to to generate shift to GATHER + p1.handle_inactivity_timer(); + p1.handle_install_timer(); +- rb = get_msg(&t1, &jm); ++ rb = get_msg(&t1, jm); + ck_assert(rb != 0); +- ck_assert(jm.type() == Message::EVS_T_JOIN); +- rb = get_msg(&t1, &im2); ++ ck_assert(jm->type() == Message::EVS_T_JOIN); ++ rb = get_msg(&t1, im2); + ck_assert(rb != 0); +- ck_assert(im2.type() == Message::EVS_T_INSTALL); +- ck_assert(im2.install_view_id().seq() > im.install_view_id().seq()); ++ ck_assert(im2->type() == Message::EVS_T_INSTALL); ++ ck_assert(im2->install_view_id().seq() > im->install_view_id().seq()); + + gcomm::Datagram* tmp; + while ((tmp = t1.out())) delete tmp; +@@ -2195,14 +2196,14 @@ START_TEST(test_gal_521) + ck_assert(t2->empty() == false); + + Datagram *d1; +- Message um1; +- ck_assert((d1 = get_msg(t1, &um1, false)) != 0); +- ck_assert(um1.type() == Message::EVS_T_USER); ++ std::unique_ptr um1; ++ ck_assert((d1 = get_msg(t1, um1, false)) != 0); ++ ck_assert(um1->type() == Message::EVS_T_USER); + ck_assert(t1->empty() == true); + Datagram *d2; +- Message um2; +- ck_assert((d2 = get_msg(t2, &um2, false)) != 0); +- ck_assert(um2.type() == Message::EVS_T_USER); ++ std::unique_ptr um2; ++ ck_assert((d2 = get_msg(t2, um2, false)) != 0); ++ ck_assert(um2->type() == Message::EVS_T_USER); + ck_assert(t2->empty() == true); + + // Both of the nodes handle each other's messages. Now due to +@@ -2211,53 +2212,53 @@ START_TEST(test_gal_521) + // must emit gap messages to make safe_seq to progress. + evs1->handle_up(0, *d2, ProtoUpMeta(dn[1]->uuid())); + delete d2; +- Message gm1; +- ck_assert(get_msg(t1, &gm1) != 0); +- ck_assert(gm1.type() == Message::EVS_T_GAP); ++ std::unique_ptr gm1; ++ ck_assert(get_msg(t1, gm1) != 0); ++ ck_assert(gm1->type() == Message::EVS_T_GAP); + ck_assert(t1->empty() == true); + + evs2->handle_up(0, *d1, ProtoUpMeta(dn[0]->uuid())); + delete d1; +- Message gm2; +- ck_assert(get_msg(t2, &gm2) != 0); +- ck_assert(gm2.type() == Message::EVS_T_GAP); ++ std::unique_ptr gm2; ++ ck_assert(get_msg(t2, gm2) != 0); ++ ck_assert(gm2->type() == Message::EVS_T_GAP); + ck_assert(t2->empty() == true); + + // Handle gap messages. The safe_seq is now incremented so the + // second user messages are now sent from output queue. +- evs1->handle_msg(gm2); +- ck_assert((d1 = get_msg(t1, &um1, false)) != 0); +- ck_assert(um1.type() == Message::EVS_T_USER); ++ evs1->handle_msg(*gm2); ++ ck_assert((d1 = get_msg(t1, um1, false)) != 0); ++ ck_assert(um1->type() == Message::EVS_T_USER); + ck_assert(t1->empty() == true); + +- evs2->handle_msg(gm1); +- ck_assert((d2 = get_msg(t2, &um2, false)) != 0); +- ck_assert(um2.type() == Message::EVS_T_USER); ++ evs2->handle_msg(*gm1); ++ ck_assert((d2 = get_msg(t2, um2, false)) != 0); ++ ck_assert(um2->type() == Message::EVS_T_USER); + ck_assert(t2->empty() == true); + + // Handle user messages. Each node should now emit gap + // because the output queue is empty. + evs1->handle_up(0, *d2, ProtoUpMeta(dn[1]->uuid())); + delete d2; +- ck_assert(get_msg(t1, &gm1) != 0); +- ck_assert(gm1.type() == Message::EVS_T_GAP); ++ ck_assert(get_msg(t1, gm1) != 0); ++ ck_assert(gm1->type() == Message::EVS_T_GAP); + ck_assert(t1->empty() == true); + + evs2->handle_up(0, *d1, ProtoUpMeta(dn[0]->uuid())); + delete d1; +- ck_assert(get_msg(t2, &gm2) != 0); +- ck_assert(gm2.type() == Message::EVS_T_GAP); ++ ck_assert(get_msg(t2, gm2) != 0); ++ ck_assert(gm2->type() == Message::EVS_T_GAP); + ck_assert(t2->empty() == true); + + // Handle gap messages. No further messages should be emitted + // since both user messages have been delivered, there are + // no pending user messages in the output queue and no timers + // have been expired. +- evs1->handle_msg(gm2); +- ck_assert((d1 = get_msg(t1, &um1, false)) == 0); ++ evs1->handle_msg(*gm2); ++ ck_assert((d1 = get_msg(t1, um1, false)) == 0); + +- evs2->handle_msg(gm1); +- ck_assert((d2 = get_msg(t2, &um2, false)) == 0); ++ evs2->handle_msg(*gm1); ++ ck_assert((d2 = get_msg(t2, um2, false)) == 0); + + + std::for_each(dn.begin(), dn.end(), DeleteObject()); +@@ -2332,70 +2333,70 @@ START_TEST(test_gap_rate_limit) + // the rest are handled by node2 for generating gap messages. + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); + gcomm::Datagram* read_dg; +- gcomm::evs::Message um1; +- read_dg = get_msg(&f.tr1, &um1); ++ std::unique_ptr um1; ++ read_dg = get_msg(&f.tr1, um1); + ck_assert(read_dg != 0); + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); +- gcomm::evs::Message um2; +- read_dg = get_msg(&f.tr1, &um2); ++ std::unique_ptr um2; ++ read_dg = get_msg(&f.tr1, um2); + ck_assert(read_dg != 0); + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); +- gcomm::evs::Message um3; +- read_dg = get_msg(&f.tr1, &um3); ++ std::unique_ptr um3; ++ read_dg = get_msg(&f.tr1, um3); + ck_assert(read_dg != 0); + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); +- gcomm::evs::Message um4; +- read_dg = get_msg(&f.tr1, &um4); ++ std::unique_ptr um4; ++ read_dg = get_msg(&f.tr1, um4); + ck_assert(read_dg != 0); + + // Make node2 handle an out of order message and verify that gap is emitted +- f.evs2.handle_msg(um2); +- gcomm::evs::Message gm1; +- read_dg = get_msg(&f.tr2, &gm1); ++ f.evs2.handle_msg(*um2); ++ std::unique_ptr gm1; ++ read_dg = get_msg(&f.tr2, gm1); + ck_assert(read_dg != 0); +- ck_assert(gm1.type() == gcomm::evs::Message::EVS_T_GAP); +- ck_assert(gm1.range_uuid() == f.uuid1); +- ck_assert(gm1.range().lu() == 0); +- ck_assert(gm1.range().hs() == 0); ++ ck_assert(gm1->type() == gcomm::evs::Message::EVS_T_GAP); ++ ck_assert(gm1->range_uuid() == f.uuid1); ++ ck_assert(gm1->range().lu() == 0); ++ ck_assert(gm1->range().hs() == 0); + // The node2 will also send an user message to complete the sequence + // number. Consume it. +- gcomm::evs::Message comp_um1; +- read_dg = get_msg(&f.tr2, &comp_um1); ++ std::unique_ptr comp_um1; ++ read_dg = get_msg(&f.tr2, comp_um1); + ck_assert(read_dg != 0); +- ck_assert(comp_um1.type() == gcomm::evs::Message::EVS_T_USER); +- ck_assert(comp_um1.seq() + comp_um1.seq_range() == 1); ++ ck_assert(comp_um1->type() == gcomm::evs::Message::EVS_T_USER); ++ ck_assert(comp_um1->seq() + comp_um1->seq_range() == 1); + // No further messages should be emitted +- read_dg = get_msg(&f.tr2, &comp_um1); ++ read_dg = get_msg(&f.tr2, comp_um1); + ck_assert(read_dg == 0); + + // Handle the second out of order message, gap should not be emitted. + // There will be a next user message which completes the um3. +- f.evs2.handle_msg(um3); +- gcomm::evs::Message comp_um2; +- read_dg = get_msg(&f.tr2, &comp_um2); ++ f.evs2.handle_msg(*um3); ++ std::unique_ptr comp_um2; ++ read_dg = get_msg(&f.tr2, comp_um2); + ck_assert(read_dg != 0); +- ck_assert(comp_um2.type() == gcomm::evs::Message::EVS_T_USER); +- ck_assert(comp_um2.seq() + comp_um2.seq_range() == 2); ++ ck_assert(comp_um2->type() == gcomm::evs::Message::EVS_T_USER); ++ ck_assert(comp_um2->seq() + comp_um2->seq_range() == 2); + + // There should not be any more gap messages. +- read_dg = get_msg(&f.tr2, &gm1); ++ read_dg = get_msg(&f.tr2, gm1); + ck_assert(read_dg == 0); + + // Move the clock forwards and handle the fourth message, gap should + // now emitted. + gu::datetime::SimClock::inc_time(100*gu::datetime::MSec); +- gcomm::evs::Message gm2; +- f.evs2.handle_msg(um4); +- read_dg = get_msg(&f.tr2, &gm2); ++ std::unique_ptr gm2; ++ f.evs2.handle_msg(*um4); ++ read_dg = get_msg(&f.tr2, gm2); + ck_assert(read_dg != 0); +- ck_assert(gm2.type() == gcomm::evs::Message::EVS_T_GAP); +- ck_assert(gm2.range().lu() == 0); +- ck_assert(gm2.range().hs() == 0); ++ ck_assert(gm2->type() == gcomm::evs::Message::EVS_T_GAP); ++ ck_assert(gm2->range().lu() == 0); ++ ck_assert(gm2->range().hs() == 0); + +- gcomm::evs::Message comp_u4; +- read_dg = get_msg(&f.tr2, &comp_u4); ++ std::unique_ptr comp_u4; ++ read_dg = get_msg(&f.tr2, comp_u4); + ck_assert(read_dg != 0); +- ck_assert(comp_u4.type() == gcomm::evs::Message::EVS_T_USER); ++ ck_assert(comp_u4->type() == gcomm::evs::Message::EVS_T_USER); + log_info << "END test_gap_rate_limit"; + } + END_TEST +@@ -2427,75 +2428,75 @@ START_TEST(test_gap_rate_limit_delayed) + // the rest are handled by node2 for generating gap messages. + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); + gcomm::Datagram* read_dg; +- gcomm::evs::Message um1; +- read_dg = get_msg(&f.tr1, &um1); ++ std::unique_ptr um1; ++ read_dg = get_msg(&f.tr1, um1); + ck_assert(read_dg != 0); + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); +- gcomm::evs::Message um2; +- read_dg = get_msg(&f.tr1, &um2); ++ std::unique_ptr um2; ++ read_dg = get_msg(&f.tr1, um2); + ck_assert(read_dg != 0); + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); +- gcomm::evs::Message um3; +- read_dg = get_msg(&f.tr1, &um3); ++ std::unique_ptr um3; ++ read_dg = get_msg(&f.tr1, um3); + ck_assert(read_dg != 0); + f.evs1.handle_down(dg, ProtoDownMeta(O_SAFE)); +- gcomm::evs::Message um4; +- read_dg = get_msg(&f.tr1, &um4); ++ std::unique_ptr um4; ++ read_dg = get_msg(&f.tr1, um4); + ck_assert(read_dg != 0); + + // Make node2 handle an out of order message and verify that gap is emitted +- f.evs2.handle_msg(um2); +- gcomm::evs::Message gm1; +- read_dg = get_msg(&f.tr2, &gm1); ++ f.evs2.handle_msg(*um2); ++ std::unique_ptr gm1; ++ read_dg = get_msg(&f.tr2, gm1); + ck_assert(read_dg != 0); +- ck_assert(gm1.type() == gcomm::evs::Message::EVS_T_GAP); +- ck_assert(gm1.range_uuid() == f.uuid1); +- ck_assert(gm1.range().lu() == 0); +- ck_assert(gm1.range().hs() == 0); ++ ck_assert(gm1->type() == gcomm::evs::Message::EVS_T_GAP); ++ ck_assert(gm1->range_uuid() == f.uuid1); ++ ck_assert(gm1->range().lu() == 0); ++ ck_assert(gm1->range().hs() == 0); + // The node2 will also send an user message to complete the sequence + // number. Consume it. +- gcomm::evs::Message comp_um1; +- read_dg = get_msg(&f.tr2, &comp_um1); ++ std::unique_ptr comp_um1; ++ read_dg = get_msg(&f.tr2, comp_um1); + ck_assert(read_dg != 0); +- ck_assert(comp_um1.type() == gcomm::evs::Message::EVS_T_USER); +- ck_assert(comp_um1.seq() + comp_um1.seq_range() == 1); ++ ck_assert(comp_um1->type() == gcomm::evs::Message::EVS_T_USER); ++ ck_assert(comp_um1->seq() + comp_um1->seq_range() == 1); + // No further messages should be emitted +- read_dg = get_msg(&f.tr2, &comp_um1); ++ read_dg = get_msg(&f.tr2, comp_um1); + ck_assert(read_dg == 0); + + // Move time forwards in 1 sec interval and make inactivity check + // in between. No gap messages should be emitted. + gu::datetime::SimClock::inc_time(gu::datetime::Sec); + f.evs2.handle_inactivity_timer(); +- gcomm::evs::Message gm_discard; +- read_dg = get_msg(&f.tr2, &gm_discard); ++ std::unique_ptr gm_discard; ++ read_dg = get_msg(&f.tr2, gm_discard); + ck_assert(read_dg == 0); + // The clock is now advanced over retrans_period + delay margin. Next + // call to handle_inactivity_timer() should fire the check. Gap message + // is emitted. + gu::datetime::SimClock::inc_time(gu::datetime::Sec); + f.evs2.handle_inactivity_timer(); +- read_dg = get_msg(&f.tr2, &gm1); ++ read_dg = get_msg(&f.tr2, gm1); + ck_assert(read_dg != 0); +- ck_assert(gm1.type() == gcomm::evs::Message::EVS_T_GAP); ++ ck_assert(gm1->type() == gcomm::evs::Message::EVS_T_GAP); + // Now call handle_inactivity_timer() again, gap message should not + // be emitted due to rate limit. + // Galera 4 will run with evs protocol version 1 and will emit + // delayed list at this point. + f.evs2.handle_inactivity_timer(); +- gcomm::evs::Message dm; +- read_dg = get_msg(&f.tr2, &dm); ++ std::unique_ptr dm; ++ read_dg = get_msg(&f.tr2, dm); + ck_assert(read_dg != 0); +- ck_assert(dm.type() == gcomm::evs::Message::EVS_T_DELAYED_LIST); +- read_dg = get_msg(&f.tr2, &gm_discard); ++ ck_assert(dm->type() == gcomm::evs::Message::EVS_T_DELAYED_LIST); ++ read_dg = get_msg(&f.tr2, gm_discard); + ck_assert(read_dg == 0); + // Move clock forward 100msec, new gap should be now emitted. + gu::datetime::SimClock::inc_time(100*gu::datetime::MSec); + f.evs2.handle_inactivity_timer(); +- gcomm::evs::Message gm2; +- read_dg = get_msg(&f.tr2, &gm2); ++ std::unique_ptr gm2; ++ read_dg = get_msg(&f.tr2, gm2); + ck_assert(read_dg != 0); +- ck_assert(gm2.type() == gcomm::evs::Message::EVS_T_GAP); ++ ck_assert(gm2->type() == gcomm::evs::Message::EVS_T_GAP); + log_info << "END test_gap_rate_limit_delayed"; + + gcomm::Datagram* tmp; +--- a/gcomm/test/check_trace.hpp ++++ b/gcomm/test/check_trace.hpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2009-2014 Codership Oy ++ * Copyright (C) 2009-2023 Codership Oy + * + * $Id$ + */ +@@ -173,7 +173,7 @@ namespace gcomm + std::deque out_; + bool queue_; + static std::unique_ptr net_; +- Protonet& get_net(); ++ static Protonet& get_net(); + public: + + DummyTransport(const UUID& uuid = UUID::nil(), bool queue = true, +--- a/galerautils/tests/CMakeLists.txt ++++ b/galerautils/tests/CMakeLists.txt +@@ -47,7 +47,6 @@ add_test( + # + # C++ galerautils tests. + # +- + add_executable(gu_tests++ + gu_atomic_test.cpp + gu_gtid_test.cpp +@@ -75,7 +74,7 @@ add_executable(gu_tests++ + + target_compile_definitions(gu_tests++ + PRIVATE +- -DGU_ASIO_TEST_CERT_DIR="${PROJECT_SOURCE_DIR}/tests/conf") ++ -DGU_ASIO_TEST_CERT_DIR="${CMAKE_CURRENT_BINARY_DIR}/certs") + + # TODO: These should be eventually fixed. + target_compile_options(gu_tests++ +@@ -93,7 +92,6 @@ add_test( + NAME gu_tests++ + COMMAND gu_tests++ + ) +- + # + # Deqmap micro benchmark. + # +--- a/galerautils/tests/gu_asio_test.cpp ++++ b/galerautils/tests/gu_asio_test.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2019-2020 Codership Oy ++ * Copyright (C) 2019-2023 Codership Oy + */ + + +@@ -920,18 +920,276 @@ END_TEST + + #ifdef GALERA_HAVE_SSL + ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + #include + +-// +-// SSL +-// ++#include + + static std::string get_cert_dir() + { +- // This will be set by CMake/preprocessor. ++ static_assert(::strlen(GU_ASIO_TEST_CERT_DIR) > 0); ++ const std::string ret{ GU_ASIO_TEST_CERT_DIR }; ++ auto* dir = opendir(ret.c_str()); ++ if (!dir) ++ { ++ if (mkdir(ret.c_str(), S_IRWXU)) ++ { ++ const auto* errstr = ::strerror(errno); ++ gu_throw_fatal << "Could not create dir " << ret << ": " << errstr; ++ } ++ } ++ else ++ { ++ closedir(dir); ++ } + return GU_ASIO_TEST_CERT_DIR; + } + ++static int password_cb(char*, int, int, void*) { return 0; } ++ ++static void throw_error(const char* msg) ++{ ++ gu_throw_fatal << msg << ": " << ERR_error_string(ERR_get_error(), nullptr); ++} ++ ++static EVP_PKEY* create_key() ++{ ++#if OPENSSL_VERSION_MAJOR < 3 ++ auto* bn = BN_new(); ++ if (!bn) ++ { ++ throw_error("could not create BN"); ++ } ++ BN_set_word(bn, 0x10001); ++ auto* rsa = RSA_new(); ++ if (!rsa) ++ { ++ BN_free(bn); ++ throw_error("could not create RSA"); ++ } ++ RSA_generate_key_ex(rsa, 2048, bn, nullptr); ++ auto* pkey = EVP_PKEY_new(); ++ if (!pkey) ++ { ++ BN_free(bn); ++ RSA_free(rsa); ++ throw_error("could not create PKEY"); ++ } ++ EVP_PKEY_set1_RSA(pkey, rsa); ++ RSA_free(rsa); ++ BN_free(bn); ++ return pkey; ++#else ++ auto* ret = EVP_RSA_gen(2048); ++ if (!ret) ++ { ++ throw_error("could not create RSA"); ++ } ++ return ret; ++#endif /* OPENSSL_VERSION_MAJOR < 3 */ ++} ++ ++static FILE* open_file(const std::string& path, const char* mode) ++{ ++ auto* ret = fopen(path.c_str(), mode); ++ if (!ret) ++ { ++ const auto* errstr = ::strerror(errno); ++ gu_throw_fatal << "Could not open file " << path << ": " ++ << errstr; ++ } ++ return ret; ++} ++ ++static void write_key(EVP_PKEY* pkey, const std::string& filename) ++{ ++ const std::string cert_dir = get_cert_dir(); ++ const std::string key_file_path = cert_dir + "/" + filename; ++ auto* key_file = open_file(key_file_path, "wb"); ++ if (!PEM_write_PrivateKey(key_file, pkey, nullptr, nullptr, 0, password_cb, ++ nullptr)) ++ { ++ throw_error("Could not write key"); ++ } ++ fclose(key_file); ++} ++ ++static void set_x509v3_extensions(X509* x509, X509* issuer) ++{ ++ auto* conf_bio = BIO_new(BIO_s_mem()); ++ std::string ext{ "[extensions]\n" ++ "authorityKeyIdentifier=keyid,issuer\n" ++ "subjectKeyIdentifier=hash\n" }; ++ if (!issuer) ++ { ++ ext += "basicConstraints=critical,CA:TRUE\n"; ++ } ++ else ++ { ++ ext += "keyUsage=digitalSignature,keyEncipherment\n"; ++ ext += "basicConstraints=CA:FALSE\n"; ++ } ++ BIO_printf(conf_bio, "%s", ext.c_str()); ++ auto* conf = NCONF_new(nullptr); ++ long errorline = -1; ++ int err; ++ if ((err = NCONF_load_bio(conf, conf_bio, &errorline)) <= 0) ++ { ++ gu_throw_fatal << "Could not load conf: " << err; ++ } ++ if (errorline != -1) ++ { ++ gu_throw_fatal << "Could not load conf, errorline: " << errorline; ++ } ++ // TODO: V3 extensions ++ X509V3_CTX ctx; ++ X509V3_set_ctx(&ctx, issuer ? issuer : x509, x509, nullptr, nullptr, 0); ++ X509V3_set_nconf(&ctx, conf); ++ if (!X509V3_EXT_add_nconf(conf, &ctx, "extensions", x509)) ++ { ++ throw_error("Could not add extension"); ++ } ++ NCONF_free(conf); ++ BIO_free(conf_bio); ++} ++ ++static X509* create_x509(EVP_PKEY* pkey, X509* issuer, const char* cn) ++{ ++ auto* x509 = X509_new(); ++ /* According to standard, value 2 means version 3. */ ++ X509_set_version(x509, 2); ++ ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); ++ X509_gmtime_adj(X509_get_notBefore(x509), 0); ++ X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); ++ X509_set_pubkey(x509, pkey); ++ ++ auto* name = X509_get_subject_name(x509); ++ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, reinterpret_cast("FI"), ++ -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, ++ reinterpret_cast("Uusimaa"), -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, ++ reinterpret_cast("Helsinki"), -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, ++ reinterpret_cast("Codership"), -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, ++ reinterpret_cast("Galera Devel"), -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast(cn), -1, ++ -1, 0); ++ if (!issuer) ++ { ++ /* Self signed */ ++ X509_set_issuer_name(x509, name); ++ } ++ else ++ { ++ X509_set_issuer_name(x509, X509_get_subject_name(issuer)); ++ } ++ ++ set_x509v3_extensions(x509, issuer); ++ ++ X509_sign(x509, pkey, EVP_sha256()); ++ ++ return x509; ++} ++ ++static void write_x509(X509* x509, const std::string& filename) ++{ ++ const std::string cert_dir = get_cert_dir(); ++ const std::string file_path = cert_dir + "/" + filename; ++ auto* file = open_file(file_path, "wb"); ++ if (!PEM_write_X509(file, x509)) ++ { ++ throw_error("Could not write x509"); ++ } ++ fclose(file); ++} ++ ++static void write_x509_list(const std::vector& certs, ++ const std::string& filename) ++{ ++ const std::string cert_dir = get_cert_dir(); ++ const std::string file_path = cert_dir + "/" + filename; ++ auto* file = open_file(file_path, "wb"); ++ for (auto* x509 : certs) ++ { ++ if (!PEM_write_X509(file, x509)) ++ { ++ throw_error("Could not write x509"); ++ } ++ } ++ fclose(file); ++} ++ ++/* Self signed CA + certificate */ ++static void generate_self_signed() ++{ ++ auto* pkey = create_key(); ++ write_key(pkey, "galera_key.pem"); ++ auto* ca = create_x509(pkey, nullptr, "Galera Root"); ++ write_x509(ca, "galera_ca.pem"); ++ ++ auto* cert = create_x509(pkey, ca, "Galera Cert"); ++ write_x509(cert, "galera_cert.pem"); ++ X509_free(cert); ++ X509_free(ca); ++ EVP_PKEY_free(pkey); ++} ++ ++/* ++ ---- Server cert 1 ++ / ++ Root CA - Intermediate CA ++ \---- Server cert 2 ++ ++ Two bundles consisting of intermediate CA and server certificate ++ are created for servers 1 and 2. ++ */ ++static void generate_chains() ++{ ++ auto* root_ca_key = create_key(); ++ auto* root_ca = create_x509(root_ca_key, nullptr, "Galera Root CA"); ++ auto* int_ca_key = create_key(); ++ auto* int_ca = create_x509(int_ca_key, root_ca, "Galera Intermediate CA"); ++ ++ auto* server_1_key = create_key(); ++ auto* server_1_cert = create_x509(server_1_key, int_ca, "Galera Server 1"); ++ auto* server_2_key = create_key(); ++ auto* server_2_cert = create_x509(server_2_key, int_ca, "Galera Server 2"); ++ ++ write_x509(root_ca, "galera-ca.pem"); ++ write_key(server_1_key, "galera-server-1.key"); ++ write_x509_list({ server_1_cert, int_ca }, "bundle-galera-server-1.pem"); ++ write_key(server_2_key, "galera-server-2.key"); ++ write_x509_list({ server_2_cert, int_ca }, "bundle-galera-server-2.pem"); ++ ++ X509_free(server_2_cert); ++ EVP_PKEY_free(server_2_key); ++ X509_free(server_1_cert); ++ EVP_PKEY_free(server_1_key); ++ X509_free(int_ca); ++ EVP_PKEY_free(int_ca_key); ++ X509_free(root_ca); ++ EVP_PKEY_free(root_ca_key); ++} ++ ++static void generate_certificates() ++{ ++ generate_self_signed(); ++ generate_chains(); ++} ++ ++// ++// SSL ++// ++ + static gu::Config get_ssl_config() + { + gu::Config ret; +@@ -1834,6 +2092,20 @@ END_TEST + // Datagram + // + ++/* Helper to determine if UDP sockets can be opened. */ ++static bool have_datagram() try ++{ ++ gu::AsioIoService io_service; ++ gu::URI uri("udp://127.0.0.1:0"); ++ auto socket(io_service.make_datagram_socket(uri)); ++ socket->open(uri); ++ return true; ++} ++catch (...) ++{ ++ return false; ++} ++ + class MockDatagramSocketHandler : public gu::AsioDatagramSocketHandler + { + public: +@@ -2173,6 +2445,7 @@ Suite* gu_asio_suite() + // + // SSL + // ++ generate_certificates(); + + tc = tcase_create("test_ssl_io_service"); + tcase_add_test(tc, test_ssl_io_service); +@@ -2339,6 +2612,7 @@ Suite* gu_asio_suite() + // + // Datagram + // ++ if (have_datagram()) { + + tc = tcase_create("test_datagram_socket"); + tcase_add_test(tc, test_datagram_socket); +@@ -2360,6 +2634,7 @@ Suite* gu_asio_suite() + tcase_add_test(tc, test_datagram_send_to_and_async_read); + suite_add_tcase(s, tc); + ++ } + #if defined(GALERA_ASIO_TEST_MULTICAST) + tc = tcase_create("test_datagram_connect_multicast"); + tcase_add_test(tc, test_datagram_connect_multicast); +--- a/galera/src/CMakeLists.txt ++++ b/galera/src/CMakeLists.txt +@@ -127,7 +127,7 @@ endif() + if (GALERA_VERSION_SCRIPT) + add_custom_command(TARGET galera_smm POST_BUILD + COMMAND +- sh -c "! ${CMAKE_OBJDUMP} -T libgalera_smm.so | grep asio 1> /dev/null" ++ sh -c "! ${CMAKE_OBJDUMP} -T libgalera_smm.so | grep asio" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + COMMENT "Checking library symbol visibility (hidden)" + VERBATIM) +@@ -135,7 +135,7 @@ else() + set(GALERA_LINK_OPTIONS "") + add_custom_command(TARGET galera_smm POST_BUILD + COMMAND +- sh -c "${CMAKE_OBJDUMP} -T libgalera_smm.so | grep asio 1> /dev/null" ++ sh -c "${CMAKE_OBJDUMP} -T libgalera_smm.so | grep asio" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + COMMENT "Checking library symbol visibility (not hidden)" + VERBATIM) +@@ -145,7 +145,7 @@ if (NOT GALERA_WITH_SSL) + message(STATUS "Building Galera without SSL") + add_custom_command(TARGET galera_smm POST_BUILD + COMMAND +- sh -c "! (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*crypto 1> /dev/null) && ! (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*ssl 1> /dev/null)" ++ sh -c "! (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*crypto) && ! (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*ssl)" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + COMMENT "Verifying that library is not linked with SSL" + VERBATIM) +@@ -154,7 +154,7 @@ else() + message(STATUS "Building Galera with static SSL") + add_custom_command(TARGET galera_smm POST_BUILD + COMMAND +- sh -c "(${CMAKE_OBJDUMP} -t libgalera_smm.so | grep OPENSSL 1> /dev/null) && (${CMAKE_OBJDUMP} -t libgalera_smm.so | grep CRYPTO 1> /dev/null)" ++ sh -c "(${CMAKE_OBJDUMP} -t libgalera_smm.so | grep OPENSSL) && (${CMAKE_OBJDUMP} -t libgalera_smm.so | grep CRYPTO)" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + COMMENT "Verifying that library has OpenSSL linked statically" + VERBATIM) +@@ -162,7 +162,7 @@ else() + message(STATUS "Building Galera with SSL") + add_custom_command(TARGET galera_smm POST_BUILD + COMMAND +- sh -c "(${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*crypto 1> /dev/null) && (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*ssl 1> /dev/null)" ++ sh -c "(${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*crypto) && (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*ssl)" + COMMENT "Verifying that library is linked with SSL" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + VERBATIM) diff --git a/galera-4-26.4.14.tar.gz b/galera-4-26.4.14.tar.gz deleted file mode 100644 index f5a6288da6d483ae95e78b1c394a1634b9c6dc6e..0000000000000000000000000000000000000000 Binary files a/galera-4-26.4.14.tar.gz and /dev/null differ diff --git a/galera-4-26.4.16.tar.gz b/galera-4-26.4.16.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..52cc33062fd597c5f37d14fc771fc6bd8c15e0c8 Binary files /dev/null and b/galera-4-26.4.16.tar.gz differ diff --git a/galera.spec b/galera.spec index d1cb0e69c26c83c42c7dd5a96c66a69f146389c3..9591ddb1404939e21839432929e628a16fb9a82d 100644 --- a/galera.spec +++ b/galera.spec @@ -1,11 +1,11 @@ %global galeradoc %{buildroot}%{_docdir}/galera Name: galera -Version: 26.4.14 +Version: 26.4.16 Release: 1 Summary: Synchronous multi-master replication library License: GPLv2 URL: http://galeracluster.com/ -Source0: http://releases.galeracluster.com/%{name}-4.14/source/%{name}-4-%{version}.tar.gz +Source0: http://releases.galeracluster.com/%{name}-4.16/source/%{name}-4-%{version}.tar.gz Source1: garbd.service #systemd startup script Source2: garbd-wrapper @@ -13,6 +13,8 @@ Source2: garbd-wrapper #Use decode to make the SConstruct Python3 compatible #https://github.com/codership/galera/commit/71685db8da72b81a0950c19269281d10ae179706.patch Patch0000: galera-python3.patch +# https://salsa.debian.org/mariadb-team/galera-4/-/commit/19f010d78a98d8c481b57de27b0b59cdad8b979d +Patch0001: Apply-upstream-patches-from-codership-galera-558.patch BuildRequires: asio-devel boost-devel check-devel gcc-c++ openssl-devel python3-scons systemd Requires: nmap-ncat @@ -69,6 +71,9 @@ install -D -m 755 libgalera_smm.so %{buildroot}%{_libdir}/galera/li %{_unitdir}/garbd.service %changelog +* Wed Dec 06 2023 yaoxin - 26.4.16-1 +- Upgrade to 26.4.16 and fix build error + * Tue Oct 31 2023 wangkai <13474090681@163.com> - 26.4.14-1 - Update to 26.4.14