From a2fa32d0758de386c7bd411c4d7c1992e9ef96c5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 21:23:59 -0800 Subject: [PATCH 001/202] Refactor: libcib: New cib__get_calldata() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 ++-------- daemons/based/based_messages.c | 4 +--- include/crm/cib/internal.h | 1 + lib/cib/cib_native.c | 4 +--- lib/cib/cib_remote.c | 4 +--- lib/cib/cib_utils.c | 17 ++++++++--------- 6 files changed, 14 insertions(+), 26 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 664210f180c..57572d4e2e4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -200,10 +200,7 @@ process_ping_reply(xmlNode *reply) uint64_t seq = 0; const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); - xmlNode *wrapper = pcmk__xe_first_child(reply, PCMK__XE_CIB_CALLDATA, NULL, - NULL); - xmlNode *pong = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - + xmlNode *pong = cib__get_calldata(reply); const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); @@ -245,10 +242,7 @@ process_ping_reply(xmlNode *reply) pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *wrapper = pcmk__xe_first_child(pong, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *remote_versions = pcmk__xe_first_child(wrapper, NULL, NULL, - NULL); + xmlNode *remote_versions = cib__get_calldata(pong); const char *admin_epoch_s = NULL; const char *epoch_s = NULL; diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 7cd608aa0ef..1f0e2043b58 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -161,7 +161,6 @@ int based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { - xmlNode *wrapper = NULL; xmlNode *data = NULL; const char *after_ver = NULL; @@ -170,8 +169,7 @@ based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); - wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL); - data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + data = cib__get_calldata(req); if (data == NULL) { pcmk__warn("No data specified in request"); return EPROTO; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 63b5eda8a6e..a3c505ec96a 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -187,6 +187,7 @@ cib__client_triggers_refresh(const char *name) } int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); +xmlNode *cib__get_calldata(const xmlNode *request); int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 57c8c87545e..8c19cdda340 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -126,9 +126,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = pcmk_ok; pcmk__xe_get_int(op_reply, PCMK__XA_CIB_CALLID, &reply_id); if (reply_id == cib->call_id) { - xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *tmp = cib__get_calldata(op_reply); pcmk__trace("Synchronous reply %d received", reply_id); if (pcmk__xe_get_int(op_reply, PCMK__XA_CIB_RC, &rc) != pcmk_rc_ok) { diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index c2d0fc63e74..a4e240f2dbb 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -201,9 +201,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, /* do nothing more */ } else if (!(call_options & cib_discard_reply)) { - xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { pcmk__trace("No output in reply to \"%s\" command %d", op, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 709a799c538..4969a821bf5 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -171,12 +171,14 @@ cib_acl_enabled(xmlNode *xml, const char *user) /*! * \internal - * \brief Get input data from a CIB request + * \brief Get call data from a CIB request * * \param[in] request CIB request XML + * + * \return Call data, or \c NULL if none is found */ -static xmlNode * -get_op_input(const xmlNode *request) +xmlNode * +cib__get_calldata(const xmlNode *request) { xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL, NULL); @@ -209,7 +211,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = get_op_input(req); + input = cib__get_calldata(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -513,7 +515,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = get_op_input(req); + input = cib__get_calldata(req); enable_acl = cib_acl_enabled(*cib, user); pcmk__trace("Processing %s for section '%s', user '%s'", op, @@ -776,12 +778,9 @@ cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc) cib_callback_client_t *blob = NULL; if (msg != NULL) { - xmlNode *wrapper = NULL; - pcmk__xe_get_int(msg, PCMK__XA_CIB_RC, &rc); pcmk__xe_get_int(msg, PCMK__XA_CIB_CALLID, &call_id); - wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL); - output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + output = cib__get_calldata(msg); } blob = cib__lookup_id(call_id); From 57ce6268aae74ff492c0025430c5d694149fa5ae Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 21:33:52 -0800 Subject: [PATCH 002/202] Refactor: libcib: New cib__set_calldata() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 8 +------- daemons/based/based_messages.c | 13 ++++--------- include/crm/cib/internal.h | 1 + lib/cib/cib_utils.c | 32 +++++++++++++++++++++++++------- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 57572d4e2e4..5dae2eb5cea 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -73,13 +73,7 @@ create_cib_reply(const char *op, const char *call_id, const char *client_id, pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, rc); - - if (call_data != NULL) { - xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CIB_CALLDATA); - - pcmk__trace("Attaching reply output"); - pcmk__xml_copy(wrapper, call_data); - } + cib__set_calldata(reply, call_data); crm_log_xml_explicit(reply, "cib:reply"); return reply; diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 1f0e2043b58..fd8fb3ad9d4 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -113,22 +113,19 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); char *digest = pcmk__digest_xml(the_cib, true); - xmlNode *wrapper = NULL; - *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); pcmk__xe_set(*answer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); pcmk__xe_set(*answer, PCMK_XA_DIGEST, digest); pcmk__xe_set(*answer, PCMK__XA_CIB_PING_ID, seq); - wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA); - if (*cib != NULL) { // Use *cib so that ACL filtering is applied to the answer - xmlNode *shallow = pcmk__xe_create(wrapper, - (const char *) (*cib)->name); + xmlNode *shallow = pcmk__xe_create(NULL, (const char *) (*cib)->name); pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); + cib__set_calldata(*answer, shallow); + pcmk__xml_free(shallow); } pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", @@ -379,7 +376,6 @@ sync_our_cib(xmlNode *request, bool all) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); pcmk__node_status_t *peer = NULL; xmlNode *replace_request = NULL; - xmlNode *wrapper = NULL; CRM_CHECK(the_cib != NULL, return EINVAL); CRM_CHECK(all || (host != NULL), return EINVAL); @@ -406,8 +402,7 @@ sync_our_cib(xmlNode *request, bool all) digest = pcmk__digest_xml(the_cib, true); pcmk__xe_set(replace_request, PCMK_XA_DIGEST, digest); - wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA); - pcmk__xml_copy(wrapper, the_cib); + cib__set_calldata(replace_request, the_cib); if (!all) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index a3c505ec96a..047712150b5 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -188,6 +188,7 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); xmlNode *cib__get_calldata(const xmlNode *request); +void cib__set_calldata(xmlNode *request, xmlNode *data); int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 4969a821bf5..14ad9a4de70 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -175,7 +175,8 @@ cib_acl_enabled(xmlNode *xml, const char *user) * * \param[in] request CIB request XML * - * \return Call data, or \c NULL if none is found + * \return Call data added by \c cib__set_calldata(), or \c NULL if none is + * found */ xmlNode * cib__get_calldata(const xmlNode *request) @@ -186,6 +187,28 @@ cib__get_calldata(const xmlNode *request) return pcmk__xe_first_child(wrapper, NULL, NULL, NULL); } +/*! + * \internal + * \brief Add call data to a CIB request + * + * Add a copy of \p data to a new \c PCMK__XE_CIB_CALLDATA child of \p request. + * + * \param[in,out] request CIB request XML + * \param[in] data Call data to add a copy of (if \c NULL, do nothing) + */ +void +cib__set_calldata(xmlNode *request, xmlNode *data) +{ + xmlNode *wrapper = NULL; + + if (data == NULL) { + return; + } + + wrapper = pcmk__xe_create(request, PCMK__XE_CIB_CALLDATA); + pcmk__xml_copy(wrapper, data); +} + int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output) @@ -688,12 +711,7 @@ cib__create_op(cib_t *cib, const char *op, const char *host, pcmk__trace("Sending call options: %.8lx, %d", (long) call_options, call_options); pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options); - - if (data != NULL) { - xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA); - - pcmk__xml_copy(wrapper, data); - } + cib__set_calldata(*op_msg, data); return pcmk_rc_ok; } From 868413846f3d9cdfe3156c0b52cca567275f60cb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 22:09:22 -0800 Subject: [PATCH 003/202] Refactor: libcib: Call xml_apply_patchset() in cib_apply_patch_event() ...directly, instead of calling cib__process_apply_patch(). This will make an upcoming commit simpler. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 14ad9a4de70..952ae30b12d 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -932,8 +932,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(event, diff, output, NULL); - rc = pcmk_rc2legacy(rc); + rc = xml_apply_patchset(*output, diff, true); if (rc == pcmk_ok) { return pcmk_ok; } From 84c9330227c16f278fd63c4f1c227c9cbe8290c8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 22:13:48 -0800 Subject: [PATCH 004/202] Refactor: libcib: Drop input arg from cib__op_fn_t The request argument provides the input value for functions that need it, via cib__get_calldata(). Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 37 ++++++++++----------------- daemons/based/based_messages.h | 46 +++++++++------------------------- include/crm/cib/internal.h | 38 ++++++++-------------------- lib/cib/cib_file.c | 4 +-- lib/cib/cib_ops.c | 33 +++++++++++------------- lib/cib/cib_utils.c | 6 ++--- 6 files changed, 54 insertions(+), 110 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index fd8fb3ad9d4..695b74d0d9e 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -38,7 +38,6 @@ xmlNode *the_cib = NULL; * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * * \param[in] req Ignored - * \param[in] input Ignored * \param[in] cib Ignored * \param[in] answer Ignored * @@ -47,8 +46,7 @@ xmlNode *the_cib = NULL; * \note This is unimplemented and simply returns an error. */ int -based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* @COMPAT Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed. Note that * external clients with Pacemaker versions < 3.0.0 can send it. @@ -57,14 +55,14 @@ based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace * notification if appropriate, and sync *cib to all nodes. On failure, our * caller will free *cib. */ int rc = pcmk_rc_ok; + xmlNode *input = cib__get_calldata(req); const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); pcmk__client_t *client = pcmk__find_client_by_id(client_id); @@ -82,8 +80,7 @@ based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -91,16 +88,14 @@ based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) { *answer = NULL; return pcmk_rc_ok; } int -based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* existing_cib and *cib should be identical. In the absence of ACL * filtering, they should also match the_cib. However, they may be copies @@ -140,8 +135,7 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -155,8 +149,7 @@ based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) { xmlNode *data = NULL; @@ -198,8 +191,7 @@ based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -213,8 +205,7 @@ based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -236,15 +227,13 @@ based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer) { return sync_our_cib(req, true); } int -based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; @@ -256,7 +245,7 @@ based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(req, input, cib, answer); + return cib__process_upgrade(req, cib, answer); } else { xmlNode *scratch = pcmk__xml_copy(NULL, *cib); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index fddb943e086..78d0d7e5218 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,41 +17,19 @@ extern bool based_is_primary; extern xmlNode *the_cib; -int based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, +int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer); - -int based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); int sync_our_cib(xmlNode *request, bool all); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 047712150b5..bd8496df193 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -103,8 +103,7 @@ enum cib__op_type { * replace *cib, but the replacement must become the root of the original * document. */ -typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode *input, xmlNode **cib, - xmlNode **output); +typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode **cib, xmlNode **output); typedef struct { const char *name; @@ -209,32 +208,15 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); -int cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_bump(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index f409b3e61d4..b60cdb55bbe 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -301,10 +301,10 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) } static int -process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib_xml, - xmlNode **answer) +process_commit_transact(xmlNode *req, xmlNode **cib_xml, xmlNode **answer) { int rc = pcmk_rc_ok; + xmlNode *input = cib__get_calldata(req); const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); cib_t *cib = NULL; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 4131f20c5ba..0b4857ecc7a 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -164,9 +164,9 @@ cib__get_operation(const char *op, const cib__operation_t **operation) } int -cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer) { + const xmlNode *input = cib__get_calldata(req); int rc = xml_apply_patchset(*cib, input, true); return pcmk_legacy2rc(rc); @@ -190,7 +190,7 @@ update_counter(xmlNode *xml, const char *field, bool reset) } int -cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_bump(xmlNode *req, xmlNode **cib, xmlNode **answer) { update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; @@ -290,11 +290,11 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, } int -cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); xmlNode *failed = NULL; int rc = pcmk_rc_ok; xmlNode *update_section = NULL; @@ -311,7 +311,7 @@ cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) || pcmk__xe_is(input, PCMK_XE_CIB)) { - return cib__process_modify(req, input, cib, answer); + return cib__process_modify(req, cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -460,10 +460,10 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) } int -cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -478,8 +478,7 @@ cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, } int -cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -606,10 +605,10 @@ process_modify_section(int options, const char *section, xmlNode *input, } int -cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -767,8 +766,7 @@ process_query_section(int options, const char *section, xmlNode *cib, } int -cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; @@ -955,10 +953,10 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } int -cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -973,8 +971,7 @@ cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, } int -cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; uint32_t options = cib_none; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 952ae30b12d..6230a29ab8c 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -218,7 +218,6 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; - xmlNode *input = NULL; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -234,7 +233,6 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = cib__get_calldata(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -256,7 +254,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, saved_cib = cib; saved_doc = cib->doc; - rc = fn(req, input, &cib, output); + rc = fn(req, &cib, output); /* Sanity check: op should be read-only (but this does not check children, * attributes, or private data) @@ -575,7 +573,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, saved_doc = (*cib)->doc; - rc = fn(req, input, cib, output); + rc = fn(req, cib, output); if (rc != pcmk_rc_ok) { goto done; } From 69f3c86c78215809843469d8edae49ddc678afb6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 22:51:43 -0800 Subject: [PATCH 005/202] Refactor: libcib: Drop input argument from cib_perform_op helpers We were passing the input argument just for logging on error. But we're already logging the request, and the request always contains the input (if non-NULL) as the child of a PCMK__XE_CIB_CALLDATA child. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 6230a29ab8c..49eed8c2ece 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -376,7 +376,6 @@ check_new_feature_set(const xmlNode *new_cib) * \param[in] old_cib \c PCMK_XE_CIB element before performing operation * \param[in] new_cib \c PCMK_XE_CIB element from result of operation * \param[in] request CIB request - * \param[in] input Input data for CIB request * * \return Standard Pacemaker return code * @@ -385,8 +384,7 @@ check_new_feature_set(const xmlNode *new_cib) */ static int check_cib_version_attr(const char *attr, const xmlNode *old_cib, - const xmlNode *new_cib, const xmlNode *request, - const xmlNode *input) + const xmlNode *new_cib, const xmlNode *request) { const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); int old_version = 0; @@ -406,7 +404,6 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, pcmk__err("%s went backwards in %s request: %d -> %d", attr, op, old_version, new_version); pcmk__log_xml_warn(request, "bad-request"); - pcmk__log_xml_warn(input, "bad-input"); return pcmk_rc_old_data; } @@ -423,7 +420,6 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, * \param[in] old_cib \c PCMK_XE_CIB element before performing operation * \param[in] new_cib \c PCMK_XE_CIB element from result of operation * \param[in] request CIB request - * \param[in] input Input data for CIB request * * \return Standard Pacemaker return code * @@ -432,18 +428,17 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, */ static int check_cib_versions(const xmlNode *old_cib, const xmlNode *new_cib, - const xmlNode *request, const xmlNode *input) + const xmlNode *request) { int rc = check_cib_version_attr(PCMK_XA_ADMIN_EPOCH, old_cib, new_cib, - request, input); + request); if (rc != pcmk_rc_undetermined) { return rc; } // @TODO Why aren't we checking PCMK_XA_NUM_UPDATES if epochs are equal? - rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request, - input); + rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request); if (rc == pcmk_rc_undetermined) { rc = pcmk_rc_ok; } @@ -512,7 +507,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; - xmlNode *input = NULL; bool enable_acl = false; bool manage_version = true; @@ -536,7 +530,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = cib__get_calldata(req); enable_acl = cib_acl_enabled(*cib, user); pcmk__trace("Processing %s for section '%s', user '%s'", op, @@ -610,7 +603,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, } } - rc = check_cib_versions(old_versions, *cib, req, input); + rc = check_cib_versions(old_versions, *cib, req); pcmk__strip_xml_text(*cib); From 397dcd26057e00e81facd9501adee4fea1ec5cc0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 01:14:58 -0800 Subject: [PATCH 006/202] Refactor: libcib: Reduce number of arguments in cib_ops.c I'm ambivalent about this, since it adds lines of code, but request contains everything needed by a lot of these helper functions. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 119 ++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 0b4857ecc7a..83393316e82 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -354,14 +354,19 @@ cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer) } static int -process_delete_xpath(const char *op, int options, const char *xpath, - xmlNode *cib) +process_delete_xpath(const xmlNode *request, xmlNode *cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s was already removed", xpath); @@ -434,8 +439,10 @@ delete_child(xmlNode *child, void *userdata) } static int -process_delete_section(const char *section, xmlNode *input, xmlNode *cib) +process_delete_section(const xmlNode *request, xmlNode *cib) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); xmlNode *obj_root = NULL; if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { @@ -462,19 +469,15 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) int cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); - xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_delete_xpath(op, options, section, *cib); + return process_delete_xpath(req, *cib); } - return process_delete_section(section, input, *cib); + return process_delete_section(req, *cib); } int @@ -506,14 +509,22 @@ cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer) } static int -process_modify_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *cib) +process_modify_xpath(const xmlNode *request, xmlNode *cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = NULL; - const bool score = pcmk__is_set(options, cib_score_update); - const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + uint32_t flags = pcmk__xaf_none; + + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_score_update)) { + flags = pcmk__xaf_score_update; + } if (xpath == NULL) { xpath = pcmk__cib_abs_xpath_for(PCMK_XE_CIB); @@ -555,13 +566,20 @@ process_modify_xpath(const char *op, int options, const char *xpath, } static int -process_modify_section(int options, const char *section, xmlNode *input, - xmlNode *cib) +process_modify_section(const xmlNode *request, xmlNode *cib) { - const bool score = pcmk__is_set(options, cib_score_update); - const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + + uint32_t flags = pcmk__xaf_none; xmlNode *obj_root = NULL; + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_score_update)) { + flags = pcmk__xaf_score_update; + } + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { input = pcmk_find_cib_element(input, section); } @@ -607,29 +625,30 @@ process_modify_section(int options, const char *section, xmlNode *input, int cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); - xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_modify_xpath(op, options, section, input, *cib); + return process_modify_xpath(req, *cib); } - return process_modify_section(options, section, input, *cib); + return process_modify_section(req, *cib); } static int -process_query_xpath(const char *op, int options, const char *xpath, - xmlNode *cib, xmlNode **answer) +process_query_xpath(const xmlNode *request, xmlNode *cib, xmlNode **answer) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -736,10 +755,13 @@ process_query_xpath(const char *op, int options, const char *xpath, } static int -process_query_section(int options, const char *section, xmlNode *cib, - xmlNode **answer) +process_query_section(const xmlNode *request, xmlNode *cib, xmlNode **answer) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); xmlNode *obj_root = NULL; + uint32_t options = cib_none; + + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; @@ -768,25 +790,23 @@ process_query_section(int options, const char *section, xmlNode *cib, int cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_query_xpath(op, options, section, *cib, answer); + return process_query_xpath(req, *cib, answer); } - return process_query_section(options, section, *cib, answer); + return process_query_section(req, *cib, answer); } static bool -replace_cib_digest_matches(xmlNode *request, xmlNode *input) +replace_cib_digest_matches(const xmlNode *request) { const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); const char *expected = pcmk__xe_get(request, PCMK_XA_DIGEST); + const xmlNode *input = cib__get_calldata(request); char *calculated = NULL; bool matches = false; @@ -811,7 +831,7 @@ replace_cib_digest_matches(xmlNode *request, xmlNode *input) } static int -replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) +replace_cib(xmlNode *request, xmlNode **cib) { int updates = 0; int epoch = 0; @@ -823,12 +843,13 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) const char *reason = NULL; const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); + xmlNode *input = cib__get_calldata(request); cib_version_details(*cib, &admin_epoch, &epoch, &updates); cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); - if (!replace_cib_digest_matches(request, input)) { + if (!replace_cib_digest_matches(request)) { pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " "digest mismatch", replace_admin_epoch, replace_epoch, replace_updates, peer, admin_epoch, epoch, updates); @@ -868,13 +889,19 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) } static int -process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *request, xmlNode *input, xmlNode **cib) +process_replace_xpath(xmlNode *request, xmlNode **cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*cib)->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -897,7 +924,7 @@ process_replace_xpath(const char *op, int options, const char *xpath, free(path); if (match == *cib) { - rc = replace_cib(request, input, cib); + rc = replace_cib(request, cib); break; } @@ -917,9 +944,11 @@ process_replace_xpath(const char *op, int options, const char *xpath, } static int -process_replace_section(const char *section, xmlNode *request, xmlNode *input, - xmlNode **cib) +process_replace_section(xmlNode *request, xmlNode **cib) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; @@ -933,7 +962,7 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - return replace_cib(request, input, cib); + return replace_cib(request, cib); } if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei) @@ -955,19 +984,15 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, int cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); - xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_replace_xpath(op, options, section, req, input, cib); + return process_replace_xpath(req, cib); } - return process_replace_section(section, req, input, cib); + return process_replace_section(req, cib); } int From 53b1cec74c059019b13671c4033f3e0b7d9c8196 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:39:10 -0800 Subject: [PATCH 007/202] Log: based: Drop redundant log from cib_process_command() We already warn for the same thing in based_process_request() if applicable. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 5dae2eb5cea..bf99cd5aad6 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -531,11 +531,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, *reply = NULL; /* Start processing the request... */ - rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, - cib_none); - if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); - } + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (!privileged && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { From cdf9a9a295e4ed8faef5d5236e41cbfb5b356241 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:42:10 -0800 Subject: [PATCH 008/202] Refactor: based: Drop redundant cib_process_command() config_changed set This is already done a few lines prior. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index bf99cd5aad6..ade42b38d8f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -569,10 +569,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { if (result_cib != the_cib) { - if (pcmk__is_set(operation->flags, cib__op_attr_writes_through)) { - config_changed = true; - } - rc = based_activate_cib(result_cib, config_changed, op); } From e352a9e3e8e5669183bf71e20288d5758e53a6d2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:46:30 -0800 Subject: [PATCH 009/202] Refactor: based: Unindent block of cib_process_command() And drop a fairly useless trace message. cib_dryrun always has to be false there, by the way. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ade42b38d8f..559caa30d06 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -606,15 +606,15 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xml_free(result_cib); } - if (!pcmk__any_flags_set(call_options, - cib_dryrun|cib_inhibit_notify|cib_transaction)) { - pcmk__trace("Sending notifications %d", - pcmk__is_set(call_options, cib_dryrun)); - based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, - client_name, originator, cib_diff); + if (pcmk__any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { + goto done; } - done: + based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, client_name, + originator, cib_diff); + +done: if (!pcmk__is_set(call_options, cib_discard_reply)) { *reply = create_cib_reply(op, call_id, client_id, call_options, pcmk_rc2legacy(rc), output); From 92f7f0c04dcc40ae5629c54d12d6efa80812785d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:54:11 -0800 Subject: [PATCH 010/202] Refactor: based: Unindent some of process_ping_reply() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 98 ++++++++++++++++----------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 559caa30d06..9df26c91d50 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -189,7 +189,7 @@ digest_timer_cb(gpointer data) } static void -process_ping_reply(xmlNode *reply) +process_ping_reply(xmlNode *reply) { uint64_t seq = 0; const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); @@ -197,72 +197,72 @@ process_ping_reply(xmlNode *reply) xmlNode *pong = cib__get_calldata(reply); const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); + long long seq_ll = 0; + int rc = pcmk_rc_ok; if (seq_s == NULL) { pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); return; + } - } else { - long long seq_ll; - int rc = pcmk__scan_ll(seq_s, &seq_ll, 0LL); - - if (rc != pcmk_rc_ok) { - pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID - " '%s': %s", - seq_s, pcmk_rc_str(rc)); - return; - } - seq = (uint64_t) seq_ll; + rc = pcmk__scan_ll(seq_s, &seq_ll, 0); + if (rc != pcmk_rc_ok) { + pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID " " + "'%s': %s", seq_s, pcmk_rc_str(rc)); + return; } - if(digest == NULL) { + // @TODO Check for overflow? + seq = (uint64_t) seq_ll; + + if (digest == NULL) { pcmk__trace("Ignoring ping reply %s from %s with no digest", seq_s, host); + return; + } - } else if(seq != ping_seq) { + if (seq != ping_seq) { pcmk__trace("Ignoring out of sequence ping reply %s from %s", seq_s, host); + return; + } - } else if(ping_modified_since) { + if (ping_modified_since) { pcmk__trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host); + return; + } - } else { - if(ping_digest == NULL) { - pcmk__trace("Calculating new digest"); - ping_digest = pcmk__digest_xml(the_cib, true); - } + if (ping_digest == NULL) { + pcmk__trace("Calculating new digest"); + ping_digest = pcmk__digest_xml(the_cib, true); + } + + pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); + if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { + xmlNode *remote_versions = cib__get_calldata(pong); - pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, - digest); - if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *remote_versions = cib__get_calldata(pong); - - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; - - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, - PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, - PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, - PCMK_XA_NUM_UPDATES); - } - - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); - - pcmk__xml_free(remote_versions); - sync_our_cib(reply, false); + const char *admin_epoch_s = NULL; + const char *epoch_s = NULL; + const char *num_updates_s = NULL; + + if (remote_versions != NULL) { + admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); + epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); + num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); } + + pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", + pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), + ping_digest, host, + pcmk__s(admin_epoch_s, "_"), + pcmk__s(epoch_s, "_"), + pcmk__s(num_updates_s, "_"), digest); + + pcmk__xml_free(remote_versions); + sync_our_cib(reply, false); } } From 3b6d9f4304f1e3927ff2f2c7dcb4ba57408cf2f6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 09:13:12 -0800 Subject: [PATCH 011/202] Refactor: based: Unindent rest of process_ping_reply() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 46 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 9df26c91d50..fbb19d15aeb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -200,6 +200,11 @@ process_ping_reply(xmlNode *reply) long long seq_ll = 0; int rc = pcmk_rc_ok; + xmlNode *remote_versions = cib__get_calldata(pong); + const char *admin_epoch_s = NULL; + const char *epoch_s = NULL; + const char *num_updates_s = NULL; + if (seq_s == NULL) { pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); return; @@ -239,31 +244,28 @@ process_ping_reply(xmlNode *reply) } pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); - if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *remote_versions = cib__get_calldata(pong); - - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); - } + if (pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { + return; + } - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); - - pcmk__xml_free(remote_versions); - sync_our_cib(reply, false); + if (remote_versions != NULL) { + admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); + epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); + num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); } + + pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", + pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), + ping_digest, host, + pcmk__s(admin_epoch_s, "_"), + pcmk__s(epoch_s, "_"), + pcmk__s(num_updates_s, "_"), digest); + + pcmk__xml_free(remote_versions); + sync_our_cib(reply, false); } static void From b1abe742079c0c46e6af48ff7de5d7d87cc287b3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 14:07:13 -0800 Subject: [PATCH 012/202] Refactor: based: Convert ping_seq to long long So that we can use pcmk__xe_get_ll(). This also avoids theoretical overflow risk, if a value is sent as uint64_t and then scanned as long long upon receiving it. That overflow should never happen in practice, as it would require sending a massive number of pings before restarting based. Also improve documentation a bit and add a note of non-understanding. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 67 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index fbb19d15aeb..3570ec4c02e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -10,7 +10,6 @@ #include #include // EACCES, ECONNREFUSED -#include // PRIu64 #include #include // NULL, size_t #include // uint32_t, uint64_t @@ -41,7 +40,7 @@ #define EXIT_ESCALATION_MS 10000 -static uint64_t ping_seq = 0; +static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; @@ -148,42 +147,53 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, * \internal * \brief Request CIB digests from all peer nodes * - * This is used as a callback that runs 5 seconds after we modify the CIB. It - * sends a ping request to all cluster nodes. They will respond by sending their - * current digests and version info, which we will validate in - * process_ping_reply(). This serves as a check of consistency across the - * cluster after a CIB update. + * This is used as a callback that runs 5 seconds after we modify the CIB on the + * DC. It sends a ping request to all cluster nodes. They will respond by + * sending their current digests and version info, which we will validate in + * process_ping_reply(). If their digest doesn't match, we'll sync our own CIB + * to them. This helps ensure consistency across the cluster after a CIB update. * * \param[in] data Ignored * * \return \c G_SOURCE_REMOVE (to destroy the timeout) + * + * \note It's not clear why we wait 5 seconds rather than sending the ping + * request immediately after a performing a modifying op. Perhaps it's to + * avoid overwhelming other nodes with ping requests when there are a lot + * of modifying requests in a short period. The timer restarts after + * every successful modifying op, so we send ping requests **at most** + * every 5 seconds. Or perhaps it's a remnant of legacy mode (pre-1.1.12). + * In any case, the other nodes shouldn't need time to process the + * modifying op before responding to the ping request. The ping request is + * sent after the op is sent, so it should also be received after the op + * is received. */ static gboolean digest_timer_cb(gpointer data) { - char *buf = NULL; xmlNode *ping = NULL; if (!based_is_primary) { + // Only the DC sends a ping return G_SOURCE_REMOVE; } - ping_seq++; + if (++ping_seq < 0) { + ping_seq = 0; + } + g_clear_pointer(&ping_digest, free); ping_modified_since = false; - buf = pcmk__assert_asprintf("%" PRIu64, ping_seq); - ping = pcmk__xe_create(NULL, PCMK__XE_PING); pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); - pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buf); + pcmk__xe_set_ll(ping, PCMK__XA_CIB_PING_ID, ping_seq); pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - pcmk__trace("Requesting peer digests (%s)", buf); + pcmk__trace("Requesting peer digests (%lld)", ping_seq); pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); - free(buf); pcmk__xml_free(ping); return G_SOURCE_REMOVE; } @@ -191,49 +201,38 @@ digest_timer_cb(gpointer data) static void process_ping_reply(xmlNode *reply) { - uint64_t seq = 0; - const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); - xmlNode *pong = cib__get_calldata(reply); - const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); + long long seq = 0; + const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); - long long seq_ll = 0; - int rc = pcmk_rc_ok; xmlNode *remote_versions = cib__get_calldata(pong); const char *admin_epoch_s = NULL; const char *epoch_s = NULL; const char *num_updates_s = NULL; - if (seq_s == NULL) { - pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); - return; - } + int rc = pcmk__xe_get_ll(pong, PCMK__XA_CIB_PING_ID, &seq); - rc = pcmk__scan_ll(seq_s, &seq_ll, 0); if (rc != pcmk_rc_ok) { - pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID " " - "'%s': %s", seq_s, pcmk_rc_str(rc)); + pcmk__debug("Ignoring ping reply with unset or invalid " + PCMK__XA_CIB_PING_ID ": %s", pcmk_rc_str(rc)); return; } - // @TODO Check for overflow? - seq = (uint64_t) seq_ll; - if (digest == NULL) { - pcmk__trace("Ignoring ping reply %s from %s with no digest", seq_s, + pcmk__trace("Ignoring ping reply %lld from %s with no digest", seq, host); return; } if (seq != ping_seq) { - pcmk__trace("Ignoring out of sequence ping reply %s from %s", seq_s, + pcmk__trace("Ignoring out-of-sequence ping reply %lld from %s", seq, host); return; } if (ping_modified_since) { - pcmk__trace("Ignoring ping reply %s from %s: cib updated since", seq_s, + pcmk__trace("Ignoring ping reply %lld from %s: CIB updated since", seq, host); return; } @@ -243,7 +242,7 @@ process_ping_reply(xmlNode *reply) ping_digest = pcmk__digest_xml(the_cib, true); } - pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); + pcmk__trace("Processing ping reply %lld from %s (%s)", seq, host, digest); if (pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { return; From af649da8f5845f6b59adf62eaf5e7ac42e6cf3d0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 14:19:04 -0800 Subject: [PATCH 013/202] Low: based: Ignore ping reply if we're no longer DC It might be possible for a new DC to be elected after we send a ping request and before we process a modifying request (which would set ping_modified_since to true and cause the ping reply to be ignored). Add this check just in case. If we're no longer DC, then we don't want to sync our CIB to other nodes. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3570ec4c02e..65d72142570 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -219,6 +219,12 @@ process_ping_reply(xmlNode *reply) return; } + if (!based_is_primary) { + pcmk__trace("Ignoring ping reply %lld from %s because we are no longer " + "DC", seq, host); + return; + } + if (digest == NULL) { pcmk__trace("Ignoring ping reply %lld from %s with no digest", seq, host); From 784ee359c8a6f14b2770700d9b4e2a25b79125cd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:49:17 -0800 Subject: [PATCH 014/202] Refactor: based: Unindent outermost else block in based_process_upgrade Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 695b74d0d9e..849efb43436 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -237,6 +237,14 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; + xmlNode *scratch = NULL; + const char *host = pcmk__xe_get(req, PCMK__XA_SRC); + const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); + const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); + const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); + const char *original_schema = NULL; + const char *new_schema = NULL; + *answer = NULL; if (pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) { @@ -246,80 +254,72 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) * performs the upgrade (and notifies its local clients) here. */ return cib__process_upgrade(req, cib, answer); + } - } else { - xmlNode *scratch = pcmk__xml_copy(NULL, *cib); - const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - const char *original_schema = NULL; - const char *new_schema = NULL; - const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); - const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); - const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); - - original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); - if (original_schema == NULL) { - pcmk__info("Rejecting upgrade request from %s: No " - PCMK_XA_VALIDATE_WITH, - host); - return pcmk_rc_cib_corrupt; - } + scratch = pcmk__xml_copy(NULL, *cib); + + original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); + if (original_schema == NULL) { + pcmk__info("Rejecting upgrade request from %s: No " + PCMK_XA_VALIDATE_WITH, host); + return pcmk_rc_cib_corrupt; + } - rc = pcmk__update_schema(&scratch, NULL, true, true); - new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); + rc = pcmk__update_schema(&scratch, NULL, true, true); + new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); - if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - xmlNode *up = pcmk__xe_create(NULL, __func__); + if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { + xmlNode *up = pcmk__xe_create(NULL, __func__); + + rc = pcmk_rc_ok; + pcmk__notice("Upgrade request from %s verified", host); + + pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); + pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); + pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); + pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); + pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); + + pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); + + pcmk__xml_free(up); + + } else if (rc == pcmk_rc_ok) { + rc = pcmk_rc_schema_unchanged; + } + + if (rc != pcmk_rc_ok) { + // Notify originating peer so it can notify its local clients + pcmk__node_status_t *origin = NULL; + + origin = pcmk__search_node_caches(0, host, NULL, + pcmk__node_search_cluster_member); - rc = pcmk_rc_ok; - pcmk__notice("Upgrade request from %s verified", host); + pcmk__info("Rejecting upgrade request from %s: %s " + QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, + ((origin != NULL)? origin->name : "lost")); + + if (origin != NULL) { + xmlNode *up = pcmk__xe_create(NULL, __func__); pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); - - pcmk__xml_free(up); - - } else if (rc == pcmk_rc_ok) { - rc = pcmk_rc_schema_unchanged; - } - - if (rc != pcmk_rc_ok) { - // Notify originating peer so it can notify its local clients - pcmk__node_status_t *origin = NULL; - - origin = pcmk__search_node_caches(0, host, NULL, - pcmk__node_search_cluster_member); - - pcmk__info("Rejecting upgrade request from %s: %s " - QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, - ((origin != NULL)? origin->name : "lost")); - - if (origin) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, - pcmk_rc2legacy(rc)); - if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { - pcmk__warn("Could not send CIB upgrade result to %s", host); - } - pcmk__xml_free(up); + pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { + pcmk__warn("Could not send CIB upgrade result to %s", host); } + pcmk__xml_free(up); } - pcmk__xml_free(scratch); } + + pcmk__xml_free(scratch); return rc; } From 196c1249c5ff764a51e717dac7306d9998172dfb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:54:11 -0800 Subject: [PATCH 015/202] Refactor: based: Unindent pcmk_rc_ok section of based_process_upgrade() Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 849efb43436..e88721977e5 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -244,6 +244,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); const char *original_schema = NULL; const char *new_schema = NULL; + pcmk__node_status_t *origin = NULL; *answer = NULL; @@ -285,40 +286,39 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); pcmk__xml_free(up); + goto done; + } - } else if (rc == pcmk_rc_ok) { + if (rc == pcmk_rc_ok) { rc = pcmk_rc_schema_unchanged; } - if (rc != pcmk_rc_ok) { - // Notify originating peer so it can notify its local clients - pcmk__node_status_t *origin = NULL; - - origin = pcmk__search_node_caches(0, host, NULL, - pcmk__node_search_cluster_member); - - pcmk__info("Rejecting upgrade request from %s: %s " - QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, - ((origin != NULL)? origin->name : "lost")); - - if (origin != NULL) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); - if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { - pcmk__warn("Could not send CIB upgrade result to %s", host); - } - pcmk__xml_free(up); + // Notify originating peer so it can notify its local clients + origin = pcmk__search_node_caches(0, host, NULL, + pcmk__node_search_cluster_member); + + pcmk__info("Rejecting upgrade request from %s: %s " QB_XS " rc=%d peer=%s", + host, pcmk_rc_str(rc), rc, + ((origin != NULL)? origin->name : "lost")); + + if (origin != NULL) { + xmlNode *up = pcmk__xe_create(NULL, __func__); + + pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); + pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); + pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); + pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); + pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); + pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { + pcmk__warn("Could not send CIB upgrade result to %s", host); } + pcmk__xml_free(up); } +done: pcmk__xml_free(scratch); return rc; } From fb753cfd608bed94c7190bd08ce5c4d844080137 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:56:51 -0800 Subject: [PATCH 016/202] Refactor: based: Don't set answer to NULL in cib__op_fn_t functions These are called only by cib_perform_op()/cib__perform_query(), which assert that *answer is NULL. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index e88721977e5..397ceeffaa7 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -90,7 +90,6 @@ based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) { - *answer = NULL; return pcmk_rc_ok; } @@ -209,8 +208,6 @@ based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - *answer = NULL; - if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { pcmk__info("Peer %s is requesting to shut down", host); return pcmk_rc_ok; @@ -246,8 +243,6 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) const char *new_schema = NULL; pcmk__node_status_t *origin = NULL; - *answer = NULL; - if (pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) { /* The originator of an upgrade request sends it to the DC, without * PCMK__XA_CIB_SCHEMA_MAX. If an upgrade is needed, the DC From da5d64cfbff905420a2928e29dfc6534212d40bf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 19:00:01 -0800 Subject: [PATCH 017/202] Refactor: libcib: Rename cib__perform_query() to cib__perform_op_ro() It's used for all non-modifying ops, not just queries. This also enables renaming cib_perform_op() to cib__perform_op_rw() next. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- include/crm/cib/internal.h | 2 +- lib/cib/cib_file.c | 2 +- lib/cib/cib_utils.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 65d72142570..89951fd5221 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -548,7 +548,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, } if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(op_function, request, &the_cib, &output); + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); goto done; } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index bd8496df193..68ce3e0390c 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -189,7 +189,7 @@ int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); xmlNode *cib__get_calldata(const xmlNode *request); void cib__set_calldata(xmlNode *request, xmlNode *data); -int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, +int cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index b60cdb55bbe..1a4cae69fec 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -164,7 +164,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); if (read_only) { - rc = cib__perform_query(op_function, request, &private->cib_xml, + rc = cib__perform_op_ro(op_function, request, &private->cib_xml, output); } else { result_cib = private->cib_xml; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 49eed8c2ece..e20252ff7b5 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -210,7 +210,7 @@ cib__set_calldata(xmlNode *request, xmlNode *data) } int -cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, +cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output) { int rc = pcmk_rc_ok; From 2fcdad2aa6e84faa8ddbc10c0fac233b77cf7564 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 19:01:48 -0800 Subject: [PATCH 018/202] Refactor: libcib: Rename cib_perform_op() to cib__perform_op_rw() Avoid using the public API prefix, and make it clearer that this is not for all CIB ops, but rather the ones that may modify something. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +++--- daemons/based/based_transaction.c | 4 ++-- include/crm/cib/internal.h | 6 +++--- lib/cib/cib_file.c | 6 +++--- lib/cib/cib_utils.c | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 89951fd5221..891712ec287 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -554,14 +554,14 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; - /* result_cib must not be modified after cib_perform_op() returns. + /* result_cib must not be modified after cib__perform_op_rw() returns. * * It's not important whether the client variant is cib_native or * cib_remote. */ result_cib = the_cib; - rc = cib_perform_op(cib_undefined, op_function, request, &config_changed, - &result_cib, &cib_diff, &output); + rc = cib__perform_op_rw(cib_undefined, op_function, request, + &config_changed, &result_cib, &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 48bbb9ac43b..cc4754daae6 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -118,7 +118,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, * \note This function is expected to be called only by * \p based_process_commit_transact(). * \note \p result_cib is expected to be a copy of the current CIB as created by - * \p cib_perform_op(). + * \p cib__perform_op_rw(). * \note The caller is responsible for activating and syncing \p result_cib on * success, and for freeing it on failure. */ @@ -130,7 +130,7 @@ based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, int rc = pcmk_rc_ok; char *source = NULL; - // *result_cib should be a copy of the_cib (created by cib_perform_op()) + // *result_cib should be a copy of the_cib (created by cib__perform_op_rw()) pcmk__assert((result_cib != NULL) && (*result_cib != NULL) && (*result_cib != the_cib)); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 68ce3e0390c..2307511d33c 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -192,9 +192,9 @@ void cib__set_calldata(xmlNode *request, xmlNode *data); int cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); -int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **cib, xmlNode **diff, - xmlNode **output); +int cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 1a4cae69fec..7136a81fdfe 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -168,8 +168,8 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) output); } else { result_cib = private->cib_xml; - rc = cib_perform_op(cib_file, op_function, request, &changed, - &result_cib, &cib_diff, output); + rc = cib__perform_op_rw(cib_file, op_function, request, &changed, + &result_cib, &cib_diff, output); } if (pcmk__is_set(call_options, cib_transaction)) { @@ -265,7 +265,7 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) xmlNode *saved_cib = private->cib_xml; /* *result_cib should be a copy of private->cib_xml (created by - * cib_perform_op()) + * cib__perform_op_rw()) */ pcmk__assert((result_cib != NULL) && (*result_cib != NULL) && (*result_cib != private->cib_xml)); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index e20252ff7b5..a72615c5e30 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -497,9 +497,9 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **cib, xmlNode **diff, - xmlNode **output) +cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output) { int rc = pcmk_rc_ok; From f9b47f8295cf44187d47ef3b2afe6a9b365d0dd1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 21:33:35 -0800 Subject: [PATCH 019/202] Refactor: based: Don't free remote_versions in process_ping_reply() This is part of request and will be freed when request is freed. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 891712ec287..8da19271f32 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -269,7 +269,6 @@ process_ping_reply(xmlNode *reply) pcmk__s(epoch_s, "_"), pcmk__s(num_updates_s, "_"), digest); - pcmk__xml_free(remote_versions); sync_our_cib(reply, false); } From b09aa85518aeb2b91ba80e97be3e9caafc371fb1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 21:36:06 -0800 Subject: [PATCH 020/202] Refactor: based: Assume the_cib is not NULL We ensure that it's non-NULL in cib_init() before we start the mainloop. After that, no CIB operation should be able to set it to NULL. If it would do so, the operation should fail, and the current (non-NULL) CIB should be kept. (See also the assertions at the beginnings of cib__perform_op_ro() and cib__perform_op_rw() -- cib cannot be NULL if we're calling one of the cib__op_fn_t functions.) When processing requests within a transaction, we don't make a copy, so the_cib may temporarily become NULL. However, we don't return to the mainloop from processing a commit-transaction request until we're finished. At that point, either all requests were processed successfully (and the_cib != NULL) or something failed and we revert to the copy we made at the beginning of the commit-transaction. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 21 ++++++--------------- daemons/based/based_messages.c | 15 ++++++--------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8da19271f32..4d1830325f9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -201,15 +201,13 @@ digest_timer_cb(gpointer data) static void process_ping_reply(xmlNode *reply) { + const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); + xmlNode *pong = cib__get_calldata(reply); long long seq = 0; - const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); xmlNode *remote_versions = cib__get_calldata(pong); - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; int rc = pcmk__xe_get_ll(pong, PCMK__XA_CIB_PING_ID, &seq); @@ -254,20 +252,13 @@ process_ping_reply(xmlNode *reply) return; } - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); - } - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), ping_digest, host, + pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(remote_versions, PCMK_XA_EPOCH), + pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES), digest); sync_our_cib(reply, false); } diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 397ceeffaa7..4fdbd00774e 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -106,6 +106,7 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); char *digest = pcmk__digest_xml(the_cib, true); + xmlNode *shallow = NULL; *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); @@ -113,14 +114,11 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) pcmk__xe_set(*answer, PCMK_XA_DIGEST, digest); pcmk__xe_set(*answer, PCMK__XA_CIB_PING_ID, seq); - if (*cib != NULL) { - // Use *cib so that ACL filtering is applied to the answer - xmlNode *shallow = pcmk__xe_create(NULL, (const char *) (*cib)->name); - - pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); - cib__set_calldata(*answer, shallow); - pcmk__xml_free(shallow); - } + // Use *cib so that ACL filtering is applied to the answer + shallow = pcmk__xe_create(NULL, (const char *) (*cib)->name); + pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); + cib__set_calldata(*answer, shallow); + pcmk__xml_free(shallow); pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", host, digest, @@ -361,7 +359,6 @@ sync_our_cib(xmlNode *request, bool all) pcmk__node_status_t *peer = NULL; xmlNode *replace_request = NULL; - CRM_CHECK(the_cib != NULL, return EINVAL); CRM_CHECK(all || (host != NULL), return EINVAL); pcmk__debug("Syncing CIB to %s", (all? "all peers" : host)); From 58dcd11e946d1855dc47b298cb815f1261e8c7a2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:26:26 -0800 Subject: [PATCH 021/202] Doc: based, libcib: Clarify cib__op_attr_modifies This TODO will be addressed in the next commit. So the following note is pretty much irrelevant, but I'm leaving it here for completeness. based_process_shutdown() can currently change the state by calling based_terminate(), and it does not have this flag. That will be remedied in a later commit (though likely not in the same pull request as this commit). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 +++++++ include/crm/cib/internal.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4d1830325f9..a2d719a308f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -542,6 +542,13 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } + /* @TODO The cib__op_attr_modifies flag means the request *may* modify + * *something*. A successful request with this flag set may not have + * modified anything (for example, a delete request when there is no match + * to delete), or it may have modified something other than the CIB (for + * example, the CIB manager's primary/secondary status). Thus we may be + * setting the ping_modified_since flag when the CIB has not been modified. + */ ping_modified_since = true; /* result_cib must not be modified after cib__perform_op_rw() returns. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 2307511d33c..1e0b0370357 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -48,7 +48,7 @@ enum cib__op_attr { //! No special attributes cib__op_attr_none = 0, - //! Modifies CIB + //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), //! Requires privileges From c48484aa0cf0ab3a3e05a16d7e9283104789a79d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 11:07:38 -0800 Subject: [PATCH 022/202] Refactor: based: Set ping_modified_since to true only if the CIB changed The CIB changed if cib_diff is not NULL and the current request is not a dry-run. If the current request is part of a transaction, then we certainly don't want to sync the current CIB to other nodes, so one might think we'd want to set ping_modified_since to true. However, we process an entire transaction at once without returning to the main loop, so we won't enter process_ping_reply() until after the commit-transaction request anyway. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a2d719a308f..5d23514e58c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -542,15 +542,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - /* @TODO The cib__op_attr_modifies flag means the request *may* modify - * *something*. A successful request with this flag set may not have - * modified anything (for example, a delete request when there is no match - * to delete), or it may have modified something other than the CIB (for - * example, the CIB manager's primary/secondary status). Thus we may be - * setting the ping_modified_since flag when the CIB has not been modified. - */ - ping_modified_since = true; - /* result_cib must not be modified after cib__perform_op_rw() returns. * * It's not important whether the client variant is cib_native or @@ -594,6 +585,10 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, sync_our_cib(request, true); } + if (cib_diff != NULL) { + ping_modified_since = true; + } + mainloop_timer_start(digest_timer); } else if (rc == pcmk_rc_schema_validation) { From b768abc1dc4245ae4a9e4b801447e7ab274848cd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:34:55 -0800 Subject: [PATCH 023/202] Doc: based: Add Doxygen for process_ping_reply() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 5d23514e58c..58603826786 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -198,6 +198,24 @@ digest_timer_cb(gpointer data) return G_SOURCE_REMOVE; } +/*! + * \internal + * \brief Process a reply to a \c CRM_OP_PING request + * + * See \c digest_timer_cb() for details on how the ping process works, and see + * \c based_process_ping() for the construction of the ping reply. + * + * We ignore the reply if we are no longer the DC, if the reply is malformed or + * received out of sequence, or if we may have modified the CIB since the last + * time we sent a ping request. + * + * Otherwise, we compare the CIB digest received in the reply against the digest + * of the local CIB. If the digests don't match, we sync our CIB to the node + * that sent the reply. This helps to ensure that all other nodes' views of the + * CIB eventually match the DC's view of the CIB. + * + * \param[in] reply Ping reply + */ static void process_ping_reply(xmlNode *reply) { @@ -242,7 +260,6 @@ process_ping_reply(xmlNode *reply) } if (ping_digest == NULL) { - pcmk__trace("Calculating new digest"); ping_digest = pcmk__digest_xml(the_cib, true); } From 9f10a474c8a689e945bb725b067146c0eb77d822 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:37:19 -0800 Subject: [PATCH 024/202] Refactor: based: sync_our_cib() takes const argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_messages.c | 4 ++-- daemons/based/based_messages.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 58603826786..a44e1b202bd 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -217,7 +217,7 @@ digest_timer_cb(gpointer data) * \param[in] reply Ping reply */ static void -process_ping_reply(xmlNode *reply) +process_ping_reply(const xmlNode *reply) { const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 4fdbd00774e..cbf2ee4ac6c 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -317,7 +317,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) } static xmlNode * -cib_msg_copy(xmlNode *msg) +cib_msg_copy(const xmlNode *msg) { static const char *field_list[] = { PCMK__XA_T, @@ -350,7 +350,7 @@ cib_msg_copy(xmlNode *msg) } int -sync_our_cib(xmlNode *request, bool all) +sync_our_cib(const xmlNode *request, bool all) { int rc = pcmk_rc_ok; char *digest = NULL; diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 78d0d7e5218..30b934a8600 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -31,6 +31,6 @@ int based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); -int sync_our_cib(xmlNode *request, bool all); +int sync_our_cib(const xmlNode *request, bool all); #endif // BASED_MESSAGES__H From 3a405e4f52e33b16d7a043e71e948698487db463 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:39:10 -0800 Subject: [PATCH 025/202] Low: based: Free ping_digest on exit Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a44e1b202bd..78469b461ad 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1029,6 +1029,7 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error From aeb42c1cd54a156d60d8a2e222ced77dd81af326 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 01:12:29 -0800 Subject: [PATCH 026/202] Refactor: based: Minor reorganization of cib_process_command() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 38 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 78469b461ad..c5a6ba48e3c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -517,33 +517,27 @@ static int cib_process_command(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **reply, bool privileged) { + static mainloop_timer_t *digest_timer = NULL; + xmlNode *cib_diff = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; - uint32_t call_options = cib_none; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - - int rc = pcmk_rc_ok; + uint32_t call_options = cib_none; bool config_changed = false; - - static mainloop_timer_t *digest_timer = NULL; - - pcmk__assert(cib_status == pcmk_rc_ok); + int rc = pcmk_rc_ok; if (digest_timer == NULL) { digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, digest_timer_cb, NULL); } - *reply = NULL; - /* Start processing the request... */ pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); @@ -568,20 +562,21 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, &output); - /* Always write to disk for successful ops with the flag set. This also - * negates the need to detect ordering changes. - */ if ((rc == pcmk_rc_ok) - && pcmk__is_set(operation->flags, cib__op_attr_writes_through)) { + && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - config_changed = true; - } + /* Always write to disk for successful ops with the writes-through flag + * set. This also avoids the need to detect ordering changes. + */ + const bool to_disk = config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through); - if ((rc == pcmk_rc_ok) - && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { + const char *feature_set = pcmk__xe_get(the_cib, + PCMK_XA_CRM_FEATURE_SET); if (result_cib != the_cib) { - rc = based_activate_cib(result_cib, config_changed, op); + rc = based_activate_cib(result_cib, to_disk, op); } /* @COMPAT Nodes older than feature set 3.19.0 don't support @@ -595,9 +590,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, */ if ((operation->type == cib__op_commit_transact) && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) - && (pcmk__compare_versions(pcmk__xe_get(the_cib, - PCMK_XA_CRM_FEATURE_SET), - "3.19.0") < 0)) { + && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { sync_our_cib(request, true); } @@ -640,7 +633,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xml_free(output); } - pcmk__trace("done"); pcmk__xml_free(cib_diff); return rc; } From 8fc7dbb4635fa760bc7d0af1c5ba4624f1cad197 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 23:47:31 -0800 Subject: [PATCH 027/202] Feature: libcrmcommon: Assert on memory error in mainloop_add_fd() This technically changes public-facing behavior. I don't see any reason why anything external should be using our mainloop functions, however, so it's only a matter of time until they're deprecated from public API. Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 1a0e8dd4a72..bd1a54ee932 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -953,11 +953,8 @@ mainloop_add_fd(const char *name, int priority, int fd, void *userdata, mainloop_io_t *client = NULL; if (fd >= 0) { - client = calloc(1, sizeof(mainloop_io_t)); - if (client == NULL) { - return NULL; - } - client->name = strdup(name); + client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); + client->name = pcmk__str_copy(name); client->userdata = userdata; if (callbacks) { From 0835f46538bab65ff2914b6622fe4a13efeb25a0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 23:57:55 -0800 Subject: [PATCH 028/202] Refactor: libcrmcommon: Unindent mainloop_add_fd() Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 59 ++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index bd1a54ee932..3ea23855e44 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -951,40 +951,41 @@ mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks * callbacks) { mainloop_io_t *client = NULL; + const GIOCondition condition = G_IO_IN|G_IO_HUP|G_IO_NVAL|G_IO_ERR; - if (fd >= 0) { - client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); - client->name = pcmk__str_copy(name); - client->userdata = userdata; - - if (callbacks) { - client->destroy_fn = callbacks->destroy; - client->dispatch_fn_io = callbacks->dispatch; - } + if (fd < 0) { + errno = EINVAL; + return NULL; + } - client->fd = fd; - client->channel = g_io_channel_unix_new(fd); - client->source = - g_io_add_watch_full(client->channel, priority, - (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR), mainloop_gio_callback, - client, mainloop_gio_destroy); + client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); + client->name = pcmk__str_copy(name); + client->userdata = userdata; - /* Now that mainloop now holds a reference to channel, - * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). - * - * This means that channel will be free'd by: - * g_main_context_dispatch() or g_source_remove() - * -> g_source_destroy_internal() - * -> g_source_callback_unref() - * shortly after mainloop_gio_destroy() completes - */ - g_io_channel_unref(client->channel); - pcmk__trace("Added connection %d for %s[%p].%d", client->source, - client->name, client, fd); - } else { - errno = EINVAL; + if (callbacks != NULL) { + client->destroy_fn = callbacks->destroy; + client->dispatch_fn_io = callbacks->dispatch; } + client->fd = fd; + client->channel = g_io_channel_unix_new(fd); + client->source = g_io_add_watch_full(client->channel, priority, condition, + mainloop_gio_callback, client, + mainloop_gio_destroy); + + /* Now that mainloop now holds a reference to channel, thanks to + * g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). + * + * This means that channel will be free'd by: + * g_main_context_dispatch() or g_source_remove() + * -> g_source_destroy_internal() + * -> g_source_callback_unref() + * shortly after mainloop_gio_destroy() completes + */ + g_io_channel_unref(client->channel); + + pcmk__trace("Added connection %d for %s[%p].%d", client->source, + client->name, client, fd); return client; } From b59c7b777e8bb69835b230a4c9345eae4480e39c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 23:39:09 -0800 Subject: [PATCH 029/202] Low: based: Free remote client if TLS session creation fails This wasn't a memory leak, because we would eventually free the client table. Now every based remote client is guaranteed to have a remote->source. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index edfb6796501..0cc025c7eca 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -597,9 +597,13 @@ cib_remote_listen(gpointer data) /* create gnutls session for the server socket */ new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); if (new_client->remote->tls_session == NULL) { + pcmk__err("Dropping remote connection from %s because we failed to " + "create a TLS session for it", ipstr); + pcmk__free_client(new_client); close(csock); return 0; } + } else { pcmk__set_client_flags(new_client, pcmk__client_tcp); new_client->remote->tcp_socket = csock; From b47ff93b29ba13db3601f73ccc73d2f54d874536 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 22:15:34 -0800 Subject: [PATCH 030/202] Refactor: based: Drop qb_ipcs_stats_get() call Other daemons don't check the number of active IPC client connections via libqb. It seems reasonable to follow the other daemons' lead and assume that the return value of pcmk__ipc_client_count() is all we need to care about. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c5a6ba48e3c..ad1e078fd44 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -20,7 +20,6 @@ #include // gboolean, gpointer, g_*, etc. #include // xmlNode -#include // QB_FALSE #include // qb_ipcs_connection_t #include // LOG_TRACE @@ -938,8 +937,6 @@ initiate_exit(void) void based_shutdown(int nsig) { - struct qb_ipcs_stats srv_stats; - if (!cib_shutdown_flag) { int disconnects = 0; qb_ipcs_connection_t *c = NULL; @@ -990,15 +987,13 @@ based_shutdown(int nsig) pcmk__info("Disconnected %d clients", disconnects); } - qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE); - if (pcmk__ipc_client_count() == 0) { - pcmk__info("All clients disconnected (%d)", srv_stats.active_connections); + pcmk__info("All clients disconnected"); initiate_exit(); } else { - pcmk__info("Waiting on %d clients to disconnect (%d)", - pcmk__ipc_client_count(), srv_stats.active_connections); + pcmk__info("Waiting on %d clients to disconnect", + pcmk__ipc_client_count()); } } From af3ef9d4cff8f55043ac4a48aa045067ea54504b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 8 Jan 2026 15:06:27 -0800 Subject: [PATCH 031/202] Doc: libcrmcommon: Add detail to mainloop_del_fd() Doxygen Somehow I got pretty confused last night about when or if the callback data was guaranteed to be destroyed. Note that the GSource itself is not guaranteed to be freed yet. There may be other references to it -- for example, if it has been dispatched but its callback hasn't run yet. This was the main reason for my confusion. However, since g_source_remove() marks the source as destroyed, nothing will happen if the source gets dispatched. Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 3ea23855e44..3293e1b66de 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -998,7 +998,12 @@ mainloop_del_fd(mainloop_io_t *client) pcmk__trace("Removing client %s[%p]", client->name, client); - // mainloop_gio_destroy() gets called during source removal + /* g_source_remove() marks the source as destroyed, unsets the source + * callback (mainloop_gio_callback()), and destroys the callback data (the + * client) via the notify function (mainloop_gio_destroy()). We can rely on + * mainloop_gio_callback() not getting called again for this source, and on + * the client being destroyed. + */ g_source_remove(client->source); } From 45b09fec253f4e6b3f03f476db52a35c50a4b4d4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:04:20 -0800 Subject: [PATCH 032/202] Low: based: Drop remote clients during shutdown Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +---------- daemons/based/based_remote.c | 35 +++++++++++++++++++++++++++++++++ daemons/based/based_remote.h | 1 + 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ad1e078fd44..b123616f846 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -899,15 +899,6 @@ cib_force_exit(gpointer data) return FALSE; } -static void -disconnect_remote_client(gpointer key, gpointer value, gpointer user_data) -{ - pcmk__client_t *a_client = value; - - pcmk__err("Can't disconnect client %s: Not implemented", - pcmk__client_name(a_client)); -} - static void initiate_exit(void) { @@ -983,7 +974,7 @@ based_shutdown(int nsig) pcmk__debug("Disconnecting %d remote clients", pcmk__ipc_client_count()); - pcmk__foreach_ipc_client(disconnect_remote_client, NULL); + based_drop_remote_clients(); pcmk__info("Disconnected %d clients", disconnects); } diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 0cc025c7eca..5d3ece67549 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -764,3 +764,38 @@ based_remote_init(void) remote_fd = init_remote_listener(port); } } + +/*! + * \internal + * \brief Disconnect and free a CIB manager client if it is a remote client + * + * If \p value is a remote client, drop it by removing its source from the + * mainloop. It will be freed by \c based_remote_client_destroy() via + * \c remote_client_fd_callbacks. + * + * \param[in] key Ignored + * \param[in,out] value CIB manager client (pcmk__client_t *) + * \param[in] user_data Ignored + */ +static void +drop_client_if_remote(gpointer key, gpointer value, gpointer user_data) +{ + pcmk__client_t *client = value; + + if (client->remote == NULL) { + return; + } + + pcmk__notice("Disconnecting remote client %s", pcmk__client_name(client)); + mainloop_del_fd(client->remote->source); +} + +/*! + * \internal + * \brief Disconnect and free all remote CIB manager clients + */ +void +based_drop_remote_clients(void) +{ + pcmk__foreach_ipc_client(drop_client_if_remote, NULL); +} diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index d2ab8f00802..1763dc04f55 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -14,5 +14,6 @@ extern int remote_fd; extern int remote_tls_fd; void based_remote_init(void); +void based_drop_remote_clients(void); #endif // BASED_REMOTE__H From b458d51e0a3e4215b2d416af30d365d0db8f77df Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 01:22:39 -0800 Subject: [PATCH 033/202] Refactor: based: Use pcmk__drop_all_clients() in based_shutdown() To align better with attrd, fenced, and schedulerd. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 47 +++++++-------------------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b123616f846..0c4992cfd8b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -929,53 +929,24 @@ void based_shutdown(int nsig) { if (!cib_shutdown_flag) { - int disconnects = 0; - qb_ipcs_connection_t *c = NULL; - cib_shutdown_flag = true; - c = qb_ipcs_connection_first_get(ipcs_rw); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_rw, last); - - pcmk__debug("Disconnecting r/w client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; + if (ipcs_ro != NULL) { + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); } - c = qb_ipcs_connection_first_get(ipcs_ro); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_ro, last); - - pcmk__debug("Disconnecting r/o client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; + if (ipcs_rw != NULL) { + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); } - c = qb_ipcs_connection_first_get(ipcs_shm); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_shm, last); - - pcmk__debug("Disconnecting non-blocking r/w client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; + if (ipcs_shm != NULL) { + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); } - disconnects += pcmk__ipc_client_count(); - - pcmk__debug("Disconnecting %d remote clients", - pcmk__ipc_client_count()); based_drop_remote_clients(); - pcmk__info("Disconnected %d clients", disconnects); } if (pcmk__ipc_client_count() == 0) { From 9500ef68eeb38c9929d620d313f0acb0dea9a939 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:26:34 -0800 Subject: [PATCH 034/202] Refactor: based: Call based_shutdown() only once All IPC and remote clients have been disconnected and freed by the time we return from based_shutdown(). There's no need to wait for their destroy callbacks, or to have those callbacks call based_shutdown() again. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 40 +++++++++++++++------------------ daemons/based/based_ipc.c | 9 -------- daemons/based/based_remote.c | 4 ---- 3 files changed, 18 insertions(+), 35 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0c4992cfd8b..ea607eb8c99 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -928,35 +928,31 @@ initiate_exit(void) void based_shutdown(int nsig) { - if (!cib_shutdown_flag) { - cib_shutdown_flag = true; + if (cib_shutdown_flag) { + // Already shutting down + return; + } - if (ipcs_ro != NULL) { - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - } + cib_shutdown_flag = true; - if (ipcs_rw != NULL) { - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - } + if (ipcs_ro != NULL) { + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); + } - if (ipcs_shm != NULL) { - pcmk__drop_all_clients(ipcs_shm); - g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); - } + if (ipcs_rw != NULL) { + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + } - based_drop_remote_clients(); + if (ipcs_shm != NULL) { + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); } - if (pcmk__ipc_client_count() == 0) { - pcmk__info("All clients disconnected"); - initiate_exit(); + based_drop_remote_clients(); - } else { - pcmk__info("Waiting on %d clients to disconnect", - pcmk__ipc_client_count()); - } + initiate_exit(); } /*! diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 7d59c025f9b..ff3f5fa316a 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -280,15 +280,6 @@ based_ipc_destroy(qb_ipcs_connection_t *c) { pcmk__trace("Destroying client connection %p", c); based_ipc_closed(c); - - /* Shut down if this was the last client to leave. - * - * @TODO Is it correct to do this for destroy but not for closed? Other - * daemons handle closed and destroyed connections in the same way. - */ - if (cib_shutdown_flag) { - based_shutdown(0); - } } struct qb_ipcs_service_handlers ipc_ro_callbacks = { diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 5d3ece67549..3746d53d887 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -544,10 +544,6 @@ based_remote_client_destroy(gpointer user_data) pcmk__free_client(client); pcmk__trace("Freed the cib client"); - - if (cib_shutdown_flag) { - based_shutdown(0); - } } static int From c361517cc3df0831fa15923355d053c3425ac05d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:34:43 -0800 Subject: [PATCH 035/202] Refactor: based: Inline initiate_exit() It no longer has a distinct role; this was no longer a logical split. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 48 +++++++++++++++------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ea607eb8c99..fd5e84a99ec 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -899,35 +899,12 @@ cib_force_exit(gpointer data) return FALSE; } -static void -initiate_exit(void) -{ - int active = 0; - xmlNode *leaving = NULL; - - active = pcmk__cluster_num_active_nodes(); - if (active < 2) { // This is the last active node - pcmk__info("Exiting without sending shutdown request (no active " - "peers)"); - based_terminate(CRM_EX_OK); - return; - } - - pcmk__info("Sending shutdown request to %d peers", active); - - leaving = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); - pcmk__xe_set(leaving, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(leaving, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, leaving); - pcmk__xml_free(leaving); - - pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); -} - void based_shutdown(int nsig) { + int active = 0; + xmlNode *notification = NULL; + if (cib_shutdown_flag) { // Already shutting down return; @@ -952,7 +929,24 @@ based_shutdown(int nsig) based_drop_remote_clients(); - initiate_exit(); + active = pcmk__cluster_num_active_nodes(); + if (active < 2) { + pcmk__info("Exiting without sending shutdown request (no active " + "peers)"); + based_terminate(CRM_EX_OK); + return; + } + + pcmk__info("Sending shutdown request to %d peers", active); + + notification = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); + pcmk__xe_set(notification, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(notification, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); + + pcmk__cluster_send_message(NULL, pcmk_ipc_based, notification); + pcmk__xml_free(notification); + + pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); } /*! From 7585473b1a2cdc2927659a0411f976a62d1c88b0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 12:58:16 -0800 Subject: [PATCH 036/202] Refactor: based: create_cib_reply/based_diff_notify take standard rc Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 16 ++++++++-------- daemons/based/based_notify.c | 4 ++-- daemons/based/based_notify.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index fd5e84a99ec..03cc29e7b4b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -51,7 +51,7 @@ static bool ping_modified_since = false; * \param[in] call_id CIB call ID * \param[in] client_id CIB client ID * \param[in] call_options Group of enum cib_call_options flags - * \param[in] rc Request return code + * \param[in] rc Request return code (standard Pacemaker return code) * \param[in] call_data Request output data * * \return Reply XML (guaranteed not to be \c NULL) @@ -70,7 +70,7 @@ create_cib_reply(const char *op, const char *call_id, const char *client_id, pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, call_id); pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); - pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, rc); + pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); cib__set_calldata(reply, call_data); crm_log_xml_explicit(reply, "cib:reply"); @@ -619,13 +619,13 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, client_name, - originator, cib_diff); + based_diff_notify(op, rc, call_id, client_id, client_name, originator, + cib_diff); done: if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), output); + *reply = create_cib_reply(op, call_id, client_id, call_options, rc, + output); } if (output != the_cib) { @@ -826,8 +826,8 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), the_cib); + reply = create_cib_reply(op, call_id, client_id, call_options, rc, + the_cib); } else if (process) { time_t start_time = time(NULL); diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 2e671b9c35e..a71f03596d8 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -199,7 +199,7 @@ cib_notify_send(const xmlNode *xml) } void -based_diff_notify(const char *op, int result, const char *call_id, +based_diff_notify(const char *op, int rc, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *diff) { @@ -219,7 +219,7 @@ based_diff_notify(const char *op, int result, const char *call_id, pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, client_name); pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, call_id); pcmk__xe_set(update_msg, PCMK__XA_SRC, origin); - pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, result); + pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE_RESULT); pcmk__xml_copy(wrapper, diff); diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index 79886f79aa1..a4641513085 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -16,7 +16,7 @@ int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); -void based_diff_notify(const char *op, int result, const char *call_id, +void based_diff_notify(const char *op, int rc, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *diff); From d0033509ea14e36fee2f121e6a0f09c166663906 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 13:06:53 -0800 Subject: [PATCH 037/202] Refactor: based: based_diff_notify() takes request argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 +--- daemons/based/based_notify.c | 27 +++++++++++++++++++-------- daemons/based/based_notify.h | 4 +--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 03cc29e7b4b..fc1fd7e41cc 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -525,7 +525,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); - const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); uint32_t call_options = cib_none; @@ -619,8 +618,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - based_diff_notify(op, rc, call_id, client_id, client_name, originator, - cib_diff); + based_diff_notify(request, rc, cib_diff); done: if (!pcmk__is_set(call_options, cib_discard_reply)) { diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index a71f03596d8..21458325b79 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -199,9 +199,7 @@ cib_notify_send(const xmlNode *xml) } void -based_diff_notify(const char *op, int rc, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *diff) +based_diff_notify(const xmlNode *request, int rc, xmlNode *diff) { xmlNode *update_msg = NULL; xmlNode *wrapper = NULL; @@ -212,13 +210,26 @@ based_diff_notify(const char *op, int rc, const char *call_id, update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY); + /* We could simplify by copying all attributes from request. We would just + * have to ensure that there are never "private" attributes that we want to + * hide from external clients with notify callbacks. + */ pcmk__xe_set(update_msg, PCMK__XA_T, PCMK__VALUE_CIB_NOTIFY); pcmk__xe_set(update_msg, PCMK__XA_SUBT, PCMK__VALUE_CIB_DIFF_NOTIFY); - pcmk__xe_set(update_msg, PCMK__XA_CIB_OP, op); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, client_name); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set(update_msg, PCMK__XA_SRC, origin); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_OP, + pcmk__xe_get(request, PCMK__XA_CIB_OP)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTID, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, + pcmk__xe_get(request, PCMK__XA_CIB_CALLID)); + + pcmk__xe_set(update_msg, PCMK__XA_SRC, pcmk__xe_get(request, PCMK__XA_SRC)); pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE_RESULT); diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index a4641513085..aec79e7b0b7 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -16,8 +16,6 @@ int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); -void based_diff_notify(const char *op, int rc, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *diff); +void based_diff_notify(const xmlNode *request, int rc, xmlNode *diff); #endif // BASED_NOTIFY__H From 67a88c9d05698ea1ee3b3f8228805b484202b9f6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 13:17:54 -0800 Subject: [PATCH 038/202] Refactor: based: create_cib_reply() takes request argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 42 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index fc1fd7e41cc..0da21eae6e9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -47,12 +47,10 @@ static bool ping_modified_since = false; * \internal * \brief Create reply XML for a CIB request * - * \param[in] op CIB operation type - * \param[in] call_id CIB call ID - * \param[in] client_id CIB client ID - * \param[in] call_options Group of enum cib_call_options flags - * \param[in] rc Request return code (standard Pacemaker return code) - * \param[in] call_data Request output data + * \param[in] request CIB request + * \param[in] rc Request return code (standard Pacemaker return code) + * \param[in] call_data Request output data (may be entire live CIB or result + * CIB in case of error) * * \return Reply XML (guaranteed not to be \c NULL) * @@ -60,16 +58,28 @@ static bool ping_modified_since = false; * \p pcmk__xml_free(). */ static xmlNode * -create_cib_reply(const char *op, const char *call_id, const char *client_id, - uint32_t call_options, int rc, xmlNode *call_data) +create_cib_reply(const xmlNode *request, int rc, xmlNode *call_data) { xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_CIB_REPLY); pcmk__xe_set(reply, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(reply, PCMK__XA_CIB_OP, op); - pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); + + /* We could simplify by copying all attributes from request. We would just + * have to ensure that there are never "private" attributes that we want to + * hide from external clients with notify callbacks. + */ + pcmk__xe_set(reply, PCMK__XA_CIB_OP, + pcmk__xe_get(request, PCMK__XA_CIB_OP)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, + pcmk__xe_get(request, PCMK__XA_CIB_CALLID)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CALLOPT, + pcmk__xe_get(request, PCMK__XA_CIB_CALLOPT)); + pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); cib__set_calldata(reply, call_data); @@ -523,8 +533,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, xmlNode *result_cib = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); - const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); uint32_t call_options = cib_none; @@ -622,8 +630,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, done: if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(op, call_id, client_id, call_options, rc, - output); + *reply = create_cib_reply(request, rc, output); } if (output != the_cib) { @@ -824,8 +831,7 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(op, call_id, client_id, call_options, rc, - the_cib); + reply = create_cib_reply(request, rc, the_cib); } else if (process) { time_t start_time = time(NULL); From ab0051da18099404446f4129d680571b06f99174 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 13:25:25 -0800 Subject: [PATCH 039/202] Doc: based: Drop irrelevant comment about legacy mode Legacy mode was removed by commit 7198fa62 (by making cib_legacy_mode() return FALSE). Then commit 061277ee made this comment's irrelevance explicit. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0da21eae6e9..47342756c08 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -813,10 +813,6 @@ based_process_request(xmlNode *request, bool privileged, } if (pcmk__is_set(call_options, cib_discard_reply)) { - /* If the request will modify the CIB, and we are in legacy mode, we - * need to build a reply so we can broadcast a diff, even if the - * requester doesn't want one. - */ needs_reply = false; local_notify = false; pcmk__trace("Client is not interested in the reply"); From 3f1fe4a11e75c3dd8df763410b86db8cc5d62bb5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:27:06 -0800 Subject: [PATCH 040/202] Refactor: based: Unindent do_local_notify() call Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 47342756c08..6fd86b1786b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -853,12 +853,14 @@ based_process_request(xmlNode *request, bool privileged, send_peer_reply(reply, originator); } - if (local_notify && (client_id != NULL)) { - do_local_notify((process? reply : request), client_id, - pcmk__is_set(call_options, cib_sync_call), - (client == NULL)); + if (!local_notify || (client_id == NULL)) { + goto done; } + do_local_notify((process? reply : request), client_id, + pcmk__is_set(call_options, cib_sync_call), + (client == NULL)); + done: pcmk__xml_free(reply); return rc; From 02c253e8e8acf715e93424b505929299ca581ab3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:33:21 -0800 Subject: [PATCH 041/202] Refactor: based: Guard create_cib_reply() call with !cib_discard_reply The other call is already guarded. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 6fd86b1786b..874262eeda3 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -827,7 +827,10 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(request, rc, the_cib); + + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, the_cib); + } } else if (process) { time_t start_time = time(NULL); From 5979c10edc09492d14cc92b127b1a79558865b23 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:37:25 -0800 Subject: [PATCH 042/202] Refactor: based: Move privilege check to based_process_request() Just an incremental change. This will simplify further in upcoming commits. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 874262eeda3..8d23f8894ae 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -524,7 +524,7 @@ forward_request(xmlNode *request) static int cib_process_command(xmlNode *request, const cib__operation_t *operation, - cib__op_fn_t op_function, xmlNode **reply, bool privileged) + cib__op_fn_t op_function, xmlNode **reply) { static mainloop_timer_t *digest_timer = NULL; @@ -547,13 +547,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, /* Start processing the request... */ pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { - - rc = EACCES; - goto done; - } - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { rc = cib__perform_op_ro(op_function, request, &the_cib, &output); goto done; @@ -835,8 +828,18 @@ based_process_request(xmlNode *request, bool privileged, } else if (process) { time_t start_time = time(NULL); - rc = cib_process_command(request, operation, op_function, &reply, - privileged); + if (!privileged + && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + + rc = EACCES; + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, NULL); + } + + } else { + rc = cib_process_command(request, operation, op_function, &reply); + } + log_op_result(request, operation, rc, difftime(time(NULL), start_time)); if ((reply == NULL) && (needs_reply || local_notify)) { From e100c088a203b0fffb61f5f80b5424ed3d03a6c7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:43:52 -0800 Subject: [PATCH 043/202] Refactor: based: Drop NULL-check of reply from cib_process_command() If needs_reply or local_notify is true, then cib_discard_reply is not set. (If cib_discard_reply is set, then we set needs_reply and local_notify to false earlier in based_process_request().) If cib_discard_reply is not set, then cib_process_command() always creates a non-NULL reply (in the done section). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8d23f8894ae..e6042f13bf9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -841,12 +841,6 @@ based_process_request(xmlNode *request, bool privileged, } log_op_result(request, operation, rc, difftime(time(NULL), start_time)); - - if ((reply == NULL) && (needs_reply || local_notify)) { - pcmk__err("Unexpected NULL reply to message"); - pcmk__log_xml_err(request, "null reply"); - goto done; - } } if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { From 9ac39e51ed2348f721157bde3b080730b1cb70ec Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:47:17 -0800 Subject: [PATCH 044/202] Refactor: based: Drop based_process_request last cib_discard_reply check It's redundant. If needs_reply is true, then cib_discard_reply is not set. (If cib_discard_reply is set, then we set needs_reply and local_notify to false earlier in based_process_request().) Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index e6042f13bf9..293d7b16764 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -848,8 +848,7 @@ based_process_request(xmlNode *request, bool privileged, pcmk__s(originator, "local"), client_name, call_id, (local_notify? " with local notification" : "")); - } else if (needs_reply && !stand_alone && (client == NULL) - && !pcmk__is_set(call_options, cib_discard_reply)) { + } else if (needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } From 82398636c67dbe6a03ba62a8abcbfcbc0ca22510 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 15:18:11 -0800 Subject: [PATCH 045/202] Refactor: libcib: Use done label in cib__perform_op_ro() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index a72615c5e30..2a888f4f660 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -268,26 +268,34 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, && (cib == xmlDocGetRootElement(cib->doc))); } - if (*output == NULL) { - // Do nothing + if (cib_filtered == *output) { + // Let the caller have this copy + return rc; + } - } else if (cib_filtered == *output) { - // Let them have this copy - cib_filtered = NULL; + if (*output == NULL) { + goto done; + } - } else if (*output == *current_cib) { - // They already know not to free it + if (*output == *current_cib) { + // Trust the caller to check this and not free *output + goto done; + } - } else if ((cib_filtered != NULL) - && ((*output)->doc == cib_filtered->doc)) { - // We're about to free the document of which *output is a part + if ((*output)->doc == (*current_cib)->doc) { + // Give the caller a copy that it can free *output = pcmk__xml_copy(NULL, *output); + goto done; + } - } else if ((*output)->doc == (*current_cib)->doc) { - // Give them a copy they can free - *output = pcmk__xml_copy(NULL, *output); + if ((cib_filtered == NULL) || ((*output)->doc != cib_filtered->doc)) { + goto done; } + // We're about to free the document of which *output is a part + *output = pcmk__xml_copy(NULL, *output); + +done: pcmk__xml_free(cib_filtered); return rc; } From a4348e027a3173a7c53c72c356a7560f6c22638c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 15:53:03 -0800 Subject: [PATCH 046/202] Low: libcib: Avoid memory leak in processing CIB file commit transaction process_transaction_requests() wasn't freeing output. Do that now. Also simplify the freeing of output. * process_request(): Make a copy of *output if it's not in the same doc as private->cib_xml. This lets us simplify file_perform_op_delegate(). * process_request(): No operation leaves result_cib set to the same value as *output unless it's also equal to private->cib_xml. (A query op can do this.) So drop a check in the done section. * file_perform_op_delegate(): Return only at the end of the function, within the done section. * file_perform_op_delegate(): Don't assign *output_data = NULL at the beginning. We'll do this in the done section if output is NULL. * file_perform_op_delegate(): Assume that output is not in the same document as private->cib_xml. We ensured this in process_request(). * file_perform_op_delegate(): Free output if and only if it has not been assigned to *output_data. And do the standard-to-legacy conversion at the end of file_perform_op_delegate(), in the done section. Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 59 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 7136a81fdfe..898ec994a92 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -191,8 +191,16 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) set_file_flags(private, file_flag_dirty); } + if (*output == NULL) { + goto done; + } + + if ((*output)->doc == private->cib_xml->doc) { + *output = pcmk__xml_copy(NULL, *output); + } + done: - if ((result_cib != private->cib_xml) && (result_cib != *output)) { + if (result_cib != private->cib_xml) { pcmk__xml_free(result_cib); } pcmk__xml_free(cib_diff); @@ -226,6 +234,8 @@ process_transaction_requests(cib_t *cib, xmlNode *transaction) int rc = process_request(cib, request, &output); + pcmk__xml_free(output); + if (rc != pcmk_rc_ok) { pcmk__err("Aborting transaction for CIB file client (%s) on file " "'%s' due to failed %s request: %s", @@ -403,38 +413,35 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, const cib__operation_t *operation = NULL; - pcmk__info("Handling %s operation for %s as %s", - pcmk__s(op, "invalid"), pcmk__s(section, "entire CIB"), + pcmk__info("Handling %s operation for %s as %s", pcmk__s(op, "invalid"), + pcmk__s(section, "entire CIB"), pcmk__s(user_name, "default user")); - if (output_data != NULL) { - *output_data = NULL; - } - if (cib->state == cib_disconnected) { - return -ENOTCONN; + rc = ENOTCONN; + goto done; } rc = cib__get_operation(op, &operation); - rc = pcmk_rc2legacy(rc); - if (rc != pcmk_ok) { + if (rc != pcmk_rc_ok) { // @COMPAT: At compatibility break, use rc directly - return -EPROTONOSUPPORT; + rc = EPROTONOSUPPORT; + goto done; } if (get_op_function(operation) == NULL) { // @COMPAT: At compatibility break, use EOPNOTSUPP pcmk__err("Operation %s is not supported by CIB file clients", op); - return -EPROTONOSUPPORT; + rc = EPROTONOSUPPORT; + goto done; } cib__set_call_options(call_options, "file operation", cib_no_mtime); rc = cib__create_op(cib, op, host, section, data, call_options, user_name, NULL, &request); - rc = pcmk_rc2legacy(rc); - if (rc != pcmk_ok) { - return rc; + if (rc != pcmk_rc_ok) { + goto done; } pcmk__xe_set(request, PCMK__XA_ACL_TARGET, user_name); @@ -442,30 +449,22 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (pcmk__is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, request); - rc = pcmk_rc2legacy(rc); goto done; } rc = process_request(cib, request, &output); - rc = pcmk_rc2legacy(rc); - - if ((output_data != NULL) && (output != NULL)) { - if (output->doc == private->cib_xml->doc) { - *output_data = pcmk__xml_copy(NULL, output); - } else { - *output_data = output; - } - } done: - if ((output != NULL) - && (output->doc != private->cib_xml->doc) - && ((output_data == NULL) || (output != *output_data))) { + pcmk__xml_free(request); + if (output_data != NULL) { + *output_data = output; + + } else { pcmk__xml_free(output); } - pcmk__xml_free(request); - return rc; + + return pcmk_rc2legacy(rc); } /*! From 58f720b1c3f44d22e995bb091b92ea544d435d43 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 10:35:36 -0700 Subject: [PATCH 047/202] Refactor: libcib: Assert to satisfy Coverity Coverity is throwing a CHECKED_RETURN error here, because we check the return value of cib__get_operation() 4 out of 5 times. However, it was already checked in file_perform_op_delegate(). Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 898ec994a92..773c0a5af48 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -151,8 +151,8 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) file_opaque_t *private = cib->variant_opaque; - // We error checked these in callers - cib__get_operation(op, &operation); + // We error checked these in callers, but make Coverity happy + pcmk__assert(cib__get_operation(op, &operation) == pcmk_rc_ok); op_function = get_op_function(operation); rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, From 1bafbe272d2404397365197ab007280b49f8e1cd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 29 May 2026 14:39:04 -0700 Subject: [PATCH 048/202] Refactor: based: Drop redundant NULL checks pcmk__drop_all_clients() returns immediately if its argument is NULL, and g_clear_pointer() is NULL-safe. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 293d7b16764..15d41c7a2ed 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -913,20 +913,14 @@ based_shutdown(int nsig) cib_shutdown_flag = true; - if (ipcs_ro != NULL) { - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - } + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - if (ipcs_rw != NULL) { - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - } + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - if (ipcs_shm != NULL) { - pcmk__drop_all_clients(ipcs_shm); - g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); - } + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); based_drop_remote_clients(); From 1107f400b8908233060238a785804fc24e296a6d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 16:35:55 -0800 Subject: [PATCH 049/202] Refactor: libcib: Avoid unnecessary copy in process_request() If file_perform_op_delegate()'s output_data argument is NULL, then we would just free the copy (output) as soon as we return from process_request(). Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 773c0a5af48..52219f2315f 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -148,9 +148,14 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) bool read_only = false; xmlNode *result_cib = NULL; xmlNode *cib_diff = NULL; + xmlNode *local_output = NULL; file_opaque_t *private = cib->variant_opaque; + if (output != NULL) { + *output = NULL; + } + // We error checked these in callers, but make Coverity happy pcmk__assert(cib__get_operation(op, &operation) == pcmk_rc_ok); op_function = get_op_function(operation); @@ -165,11 +170,11 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) if (read_only) { rc = cib__perform_op_ro(op_function, request, &private->cib_xml, - output); + &local_output); } else { result_cib = private->cib_xml; rc = cib__perform_op_rw(cib_file, op_function, request, &changed, - &result_cib, &cib_diff, output); + &result_cib, &cib_diff, &local_output); } if (pcmk__is_set(call_options, cib_transaction)) { @@ -191,12 +196,22 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) set_file_flags(private, file_flag_dirty); } - if (*output == NULL) { + if (local_output == NULL) { + goto done; + } + + if ((output != NULL) && (local_output->doc != private->cib_xml->doc)) { + *output = local_output; + goto done; + } + + if (output != NULL) { + *output = pcmk__xml_copy(NULL, local_output); goto done; } - if ((*output)->doc == private->cib_xml->doc) { - *output = pcmk__xml_copy(NULL, *output); + if (local_output->doc != private->cib_xml->doc) { + pcmk__xml_free(local_output); } done: @@ -408,7 +423,6 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, { int rc = pcmk_ok; xmlNode *request = NULL; - xmlNode *output = NULL; file_opaque_t *private = cib->variant_opaque; const cib__operation_t *operation = NULL; @@ -452,18 +466,10 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, goto done; } - rc = process_request(cib, request, &output); + rc = process_request(cib, request, output_data); done: pcmk__xml_free(request); - - if (output_data != NULL) { - *output_data = output; - - } else { - pcmk__xml_free(output); - } - return pcmk_rc2legacy(rc); } From b6f0db09372f1c3536e4f2c245af6f25c371c7a2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 17:22:34 -0800 Subject: [PATCH 050/202] Refactor: libcib: Don't copy output that's part of the current CIB's doc cib_file.c was already checking for this and would not free output if it was part of the same doc as the current CIB. The CIB manager was preserving output only if it was equal to the current CIB. So we just have to update the CIB manager to compare the docs, and update cib__perform_op_ro() to skip the copy if the docs are the same. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- lib/cib/cib_utils.c | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 15d41c7a2ed..0a15e930c0f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -626,7 +626,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, *reply = create_cib_reply(request, rc, output); } - if (output != the_cib) { + if ((output != NULL) && (output->doc != the_cib->doc)) { pcmk__xml_free(output); } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 2a888f4f660..b135eefc72f 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -277,14 +277,8 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, goto done; } - if (*output == *current_cib) { - // Trust the caller to check this and not free *output - goto done; - } - if ((*output)->doc == (*current_cib)->doc) { - // Give the caller a copy that it can free - *output = pcmk__xml_copy(NULL, *output); + // Trust the caller to check this and not free *output goto done; } @@ -866,8 +860,10 @@ cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, xmlNode ** output_data, int call_options, const char *user_name) { - /* Note: *output_data gets set only for create and query requests. There are - * a lot of opportunities to clean up, clarify, check/enforce things, etc. + /* @COMPAT *output_data gets set only for create and query requests. Setting + * it for create requests is deprecated since 2.1.8. When that behavior is + * removed, we can restrict freeing it to read-only operations + * (cib__perform_op_ro()). */ int (*delegate)(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, From a2c9bb37b8f51d4555c751986287f3dbd61a2955 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 17:46:22 -0800 Subject: [PATCH 051/202] Refactor: libcib: Minor cib__perform_op_ro() cleanup And drop an unhelpful trace message in cib__perform_op_rw(). Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index b135eefc72f..b15a083d029 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -217,7 +217,6 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, const char *op = NULL; const char *section = NULL; const char *user = NULL; - uint32_t call_options = cib_none; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -231,13 +230,11 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, op = pcmk__xe_get(req, PCMK__XA_CIB_OP); section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); - pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); cib = *current_cib; - if (cib_acl_enabled(*current_cib, user) - && xml_acl_filtered_copy(user, *current_cib, *current_cib, - &cib_filtered)) { + if (cib_acl_enabled(cib, user) + && xml_acl_filtered_copy(user, cib, cib, &cib_filtered)) { if (cib_filtered == NULL) { pcmk__debug("Pre-filtered the entire cib"); @@ -674,7 +671,6 @@ cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, } pcmk__xml_free(top); - pcmk__trace("Done"); return rc; } From 96fe87d02a81bcd5fd78b6bfd798f77cc0e2256d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 17:58:26 -0800 Subject: [PATCH 052/202] Refactor: based: based_process_request gets last create_cib_reply check The other two were already in based_process_request(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 37 +++++++++++++++------------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0a15e930c0f..a10e2a76ed7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -524,12 +524,11 @@ forward_request(xmlNode *request) static int cib_process_command(xmlNode *request, const cib__operation_t *operation, - cib__op_fn_t op_function, xmlNode **reply) + cib__op_fn_t op_function, xmlNode **output) { static mainloop_timer_t *digest_timer = NULL; xmlNode *cib_diff = NULL; - xmlNode *output = NULL; xmlNode *result_cib = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); @@ -548,7 +547,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + rc = cib__perform_op_ro(op_function, request, &the_cib, output); goto done; } @@ -559,7 +558,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, */ result_cib = the_cib; rc = cib__perform_op_rw(cib_undefined, op_function, request, - &config_changed, &result_cib, &cib_diff, &output); + &config_changed, &result_cib, &cib_diff, output); if ((rc == pcmk_rc_ok) && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { @@ -603,12 +602,12 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, } else if (rc == pcmk_rc_schema_validation) { pcmk__assert(result_cib != the_cib); - if (output != NULL) { - pcmk__log_xml_info(output, "cib:output"); - pcmk__xml_free(output); + if (*output != NULL) { + pcmk__log_xml_info(*output, "cib:output"); + pcmk__xml_free(*output); } - output = result_cib; + *output = result_cib; } else if (result_cib != the_cib) { pcmk__xml_free(result_cib); @@ -622,14 +621,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, based_diff_notify(request, rc, cib_diff); done: - if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(request, rc, output); - } - - if ((output != NULL) && (output->doc != the_cib->doc)) { - pcmk__xml_free(output); - } - pcmk__xml_free(cib_diff); return rc; } @@ -826,21 +817,27 @@ based_process_request(xmlNode *request, bool privileged, } } else if (process) { + xmlNode *output = NULL; time_t start_time = time(NULL); if (!privileged && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { rc = EACCES; - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, NULL); - } } else { - rc = cib_process_command(request, operation, op_function, &reply); + rc = cib_process_command(request, operation, op_function, &output); } log_op_result(request, operation, rc, difftime(time(NULL), start_time)); + + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, output); + } + + if ((output != NULL) && (output->doc != the_cib->doc)) { + pcmk__xml_free(output); + } } if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { From 160707f1d020f9803b6139077a3c566baa62f881 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:09:30 -0800 Subject: [PATCH 053/202] Refactor: based: Pull read-only ops out of cib_process_command() And rename to based_perform_op_rw(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a10e2a76ed7..de378f73f2d 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -523,13 +523,13 @@ forward_request(xmlNode *request) } static int -cib_process_command(xmlNode *request, const cib__operation_t *operation, +based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { static mainloop_timer_t *digest_timer = NULL; + xmlNode *result_cib = the_cib; xmlNode *cib_diff = NULL; - xmlNode *result_cib = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); @@ -538,25 +538,13 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, bool config_changed = false; int rc = pcmk_rc_ok; - if (digest_timer == NULL) { - digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, - digest_timer_cb, NULL); - } - - /* Start processing the request... */ pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, output); - goto done; - } - /* result_cib must not be modified after cib__perform_op_rw() returns. * * It's not important whether the client variant is cib_native or * cib_remote. */ - result_cib = the_cib; rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, output); @@ -597,6 +585,11 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; } + if (digest_timer == NULL) { + digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, + digest_timer_cb, NULL); + } + mainloop_timer_start(digest_timer); } else if (rc == pcmk_rc_schema_validation) { @@ -825,8 +818,11 @@ based_process_request(xmlNode *request, bool privileged, rc = EACCES; + } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + } else { - rc = cib_process_command(request, operation, op_function, &output); + rc = based_perform_op_rw(request, operation, op_function, &output); } log_op_result(request, operation, rc, difftime(time(NULL), start_time)); From af85ada017303f7312084b96c9da15c50e6900f5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:12:05 -0800 Subject: [PATCH 054/202] Low: based: Free digest_timer on exit Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index de378f73f2d..aa8512e7369 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -39,6 +39,7 @@ #define EXIT_ESCALATION_MS 10000 +static mainloop_timer_t *digest_timer = NULL; static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; @@ -526,8 +527,6 @@ static int based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { - static mainloop_timer_t *digest_timer = NULL; - xmlNode *result_cib = the_cib; xmlNode *cib_diff = NULL; @@ -956,6 +955,7 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); From 4d987cd37dffa984801eccdedfa7ba27ce3ca984 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:18:27 -0800 Subject: [PATCH 055/202] Refactor: based: Move notify to done section in based_perform_op_rw() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index aa8512e7369..cf3eab8e339 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -590,8 +590,10 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } mainloop_timer_start(digest_timer); + goto done; + } - } else if (rc == pcmk_rc_schema_validation) { + if (rc == pcmk_rc_schema_validation) { pcmk__assert(result_cib != the_cib); if (*output != NULL) { @@ -600,19 +602,20 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } *output = result_cib; + goto done; + } - } else if (result_cib != the_cib) { + if (result_cib != the_cib) { pcmk__xml_free(result_cib); } - if (pcmk__any_flags_set(call_options, - cib_dryrun|cib_inhibit_notify|cib_transaction)) { - goto done; - } +done: + if (!pcmk__any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { - based_diff_notify(request, rc, cib_diff); + based_diff_notify(request, rc, cib_diff); + } -done: pcmk__xml_free(cib_diff); return rc; } From 484bf0463ce70740f9d3614ad953113ea168cbaf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:28:08 -0800 Subject: [PATCH 056/202] Refactor: based: Unindent successful common case in based_perform_op_rw Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 92 ++++++++++++++++----------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cf3eab8e339..0950b8d1653 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -527,6 +527,8 @@ static int based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { + const char *feature_set = pcmk__xe_get(the_cib, + PCMK_XA_CRM_FEATURE_SET); xmlNode *result_cib = the_cib; xmlNode *cib_diff = NULL; @@ -547,68 +549,66 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, output); - if ((rc == pcmk_rc_ok) - && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - - /* Always write to disk for successful ops with the writes-through flag - * set. This also avoids the need to detect ordering changes. - */ - const bool to_disk = config_changed - || pcmk__is_set(operation->flags, - cib__op_attr_writes_through); - - const char *feature_set = pcmk__xe_get(the_cib, - PCMK_XA_CRM_FEATURE_SET); + if (rc == pcmk_rc_schema_validation) { + pcmk__assert(result_cib != the_cib); - if (result_cib != the_cib) { - rc = based_activate_cib(result_cib, to_disk, op); + if (*output != NULL) { + pcmk__log_xml_info(*output, "cib:output"); + pcmk__xml_free(*output); } - /* @COMPAT Nodes older than feature set 3.19.0 don't support - * transactions. In a mixed-version cluster with nodes <3.19.0, we must - * sync the updated CIB, so that the older nodes receive the changes. - * Any node that has already applied the transaction will ignore the - * synced CIB. - * - * To ensure the updated CIB is synced from only one node, we sync it - * from the originator. - */ - if ((operation->type == cib__op_commit_transact) - && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) - && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { - - sync_our_cib(request, true); - } + *output = result_cib; + goto done; + } - if (cib_diff != NULL) { - ping_modified_since = true; - } + // Discard result for failure, dry run, or within-transaction request + if ((rc != pcmk_rc_ok) + || pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - if (digest_timer == NULL) { - digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, - digest_timer_cb, NULL); + if (result_cib != the_cib) { + pcmk__xml_free(result_cib); } - mainloop_timer_start(digest_timer); goto done; } - if (rc == pcmk_rc_schema_validation) { - pcmk__assert(result_cib != the_cib); + if (result_cib != the_cib) { + /* Always write to disk for successful ops with the writes-through flag + * set. This also avoids the need to detect ordering changes. + */ + const bool to_disk = config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through); - if (*output != NULL) { - pcmk__log_xml_info(*output, "cib:output"); - pcmk__xml_free(*output); - } + rc = based_activate_cib(result_cib, to_disk, op); + } - *output = result_cib; - goto done; + /* @COMPAT Nodes older than feature set 3.19.0 don't support transactions. + * In a mixed-version cluster with nodes <3.19.0, we must sync the updated + * CIB, so that the older nodes receive the changes. Any node that has + * already applied the transaction will ignore the synced CIB. + * + * To ensure the updated CIB is synced from only one node, we sync it from + * the originator. + */ + if ((operation->type == cib__op_commit_transact) + && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) + && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { + + sync_our_cib(request, true); } - if (result_cib != the_cib) { - pcmk__xml_free(result_cib); + if (cib_diff != NULL) { + ping_modified_since = true; } + if (digest_timer == NULL) { + digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, + digest_timer_cb, NULL); + } + + mainloop_timer_start(digest_timer); + done: if (!pcmk__any_flags_set(call_options, cib_dryrun|cib_inhibit_notify|cib_transaction)) { From 9ef8a16facdb5ef12e51dd2642d579f45d0c7623 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:44:11 -0800 Subject: [PATCH 057/202] Refactor: based: Drop dead output log msg for pcmk_rc_schema_validation "Create" is the only read-write operation that may set *output. There is no path by which a create operation can return pcmk_rc_schema_validation. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0950b8d1653..1bed5147327 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -549,14 +549,11 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, output); + /* On validation error, include the schema-violating result CIB in any reply + * or notification that we will send. + */ if (rc == pcmk_rc_schema_validation) { - pcmk__assert(result_cib != the_cib); - - if (*output != NULL) { - pcmk__log_xml_info(*output, "cib:output"); - pcmk__xml_free(*output); - } - + pcmk__assert((result_cib != the_cib) && (*output == NULL)); *output = result_cib; goto done; } From 91fd2f036c56690e9f91e17900cc6d5800a13126 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:46:48 -0800 Subject: [PATCH 058/202] Fix: based: Fix handling of intermediate transaction results If a request within a transaction succeeds, we don't want to discard the result CIB. We need it for the next request in the transaction, or for the end result if this is the last request to process. So we want to activate it. However, we do NOT want to write it to disk. If the system or CIB manager crashes while committing a transaction, and we restore an intermediate result, the transaction was not atomic. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1bed5147327..f237581ab8c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -558,10 +558,8 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, goto done; } - // Discard result for failure, dry run, or within-transaction request - if ((rc != pcmk_rc_ok) - || pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - + // Discard result for failure or dry run + if ((rc != pcmk_rc_ok) || pcmk__any_flags_set(call_options, cib_dryrun)) { if (result_cib != the_cib) { pcmk__xml_free(result_cib); } @@ -572,10 +570,14 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, if (result_cib != the_cib) { /* Always write to disk for successful ops with the writes-through flag * set. This also avoids the need to detect ordering changes. + * + * An exception is a request within a transaction. Since a transaction + * is atomic, intermediate results must not be written to disk. */ - const bool to_disk = config_changed - || pcmk__is_set(operation->flags, - cib__op_attr_writes_through); + const bool to_disk = !pcmk__is_set(call_options, cib_transaction) + && (config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through)); rc = based_activate_cib(result_cib, to_disk, op); } From cd61fb8d832e9ad272d52c0afaa38d9ae4ab4528 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 19:09:15 -0800 Subject: [PATCH 059/202] Log: based: Drop redundant log in based_process_request() We get the same information from log_op_result() and do_local_notify(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f237581ab8c..2c2433aaa2c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -837,12 +837,9 @@ based_process_request(xmlNode *request, bool privileged, } } - if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - pcmk__trace("Completed pre-sync update from %s/%s/%s%s", - pcmk__s(originator, "local"), client_name, call_id, - (local_notify? " with local notification" : "")); + if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) + && needs_reply && !stand_alone && (client == NULL)) { - } else if (needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } From 78aefd17d800afae7d21acde539d08a0b260a823 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 19:13:43 -0800 Subject: [PATCH 060/202] Refactor: based: Unindent "if (process)" block of based_process_request Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 55 ++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 2c2433aaa2c..805d510d0d5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -734,6 +734,9 @@ based_process_request(xmlNode *request, bool privileged, const cib__operation_t *operation = NULL; cib__op_fn_t op_function = NULL; + xmlNode *output = NULL; + time_t start_time = 0; + rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { @@ -810,48 +813,50 @@ based_process_request(xmlNode *request, bool privileged, reply = create_cib_reply(request, rc, the_cib); } - } else if (process) { - xmlNode *output = NULL; - time_t start_time = time(NULL); + goto done; + } - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + if (!process) { + goto done; + } - rc = EACCES; + start_time = time(NULL); - } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + if (!privileged + && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { - } else { - rc = based_perform_op_rw(request, operation, op_function, &output); - } + rc = EACCES; - log_op_result(request, operation, rc, difftime(time(NULL), start_time)); + } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, output); - } + } else { + rc = based_perform_op_rw(request, operation, op_function, &output); + } - if ((output != NULL) && (output->doc != the_cib->doc)) { - pcmk__xml_free(output); - } + log_op_result(request, operation, rc, difftime(time(NULL), start_time)); + + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, output); + } + + if ((output != NULL) && (output->doc != the_cib->doc)) { + pcmk__xml_free(output); } +done: if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) && needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } - if (!local_notify || (client_id == NULL)) { - goto done; + if (local_notify && (client_id != NULL)) { + do_local_notify((process? reply : request), client_id, + pcmk__is_set(call_options, cib_sync_call), + (client == NULL)); } - do_local_notify((process? reply : request), client_id, - pcmk__is_set(call_options, cib_sync_call), - (client == NULL)); - -done: pcmk__xml_free(reply); return rc; } From 0a3aa78ec0a9358525af12a50ce20ca6939bb54f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 23:39:57 -0800 Subject: [PATCH 061/202] Refactor: based: Don't call option parser when cib_transaction is set We make this change despite the previous comment saying we wanted log messages and the target check. Some of the messages are misleading or confusing in the context of a within-transaction request, and skipping that call will facilitate some other changes. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 805d510d0d5..f56374fa557 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -770,27 +770,27 @@ based_process_request(xmlNode *request, bool privileged, return EOPNOTSUPP; } - if (client != NULL) { - parse_local_options(client, operation, host, op, &local_notify, - &needs_reply, &process, &needs_forward); - - } else if (!parse_peer_options(operation, request, &local_notify, - &needs_reply, &process)) { - return pcmk_rc_ok; - } - if (pcmk__is_set(call_options, cib_transaction)) { /* All requests in a transaction are processed locally against a working * CIB copy, and we don't notify for individual requests because the * entire transaction is atomic. - * - * We still call the option parser functions above, for the sake of log - * messages and checking whether we're the target for peer requests. */ process = true; needs_reply = false; local_notify = false; needs_forward = false; + + pcmk__trace("Processing %s op from %s/%s on %s locally because it's " + "part of a transaction", op, client_name, call_id, + pcmk__xe_get(request, PCMK__XA_SRC)); + + } else if (client != NULL) { + parse_local_options(client, operation, host, op, &local_notify, + &needs_reply, &process, &needs_forward); + + } else if (!parse_peer_options(operation, request, &local_notify, + &needs_reply, &process)) { + return pcmk_rc_ok; } if (pcmk__is_set(call_options, cib_discard_reply)) { From 538d0a8e9373911ee2bd9843d0844885c5484eb6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 23:51:58 -0800 Subject: [PATCH 062/202] Refactor: based: Drop needs_forward variable Pull the check inline. If it's true, then we can call forward_request() and return immediately. We can also drop the trace message, because we have one in forward_request(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 37 +++++++++++---------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f56374fa557..b007adef201 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -294,13 +294,12 @@ static void parse_local_options(const pcmk__client_t *client, const cib__operation_t *operation, const char *host, const char *op, bool *local_notify, - bool *needs_reply, bool *process, bool *needs_forward) + bool *needs_reply, bool *process) { // Process locally and notify local client *process = true; *needs_reply = false; *local_notify = true; - *needs_forward = false; if (pcmk__is_set(operation->flags, cib__op_attr_local)) { /* Always process locally if cib__op_attr_local is set. @@ -322,21 +321,6 @@ parse_local_options(const pcmk__client_t *client, return; } - if (pcmk__is_set(operation->flags, cib__op_attr_modifies) - || !pcmk__str_eq(host, OUR_NODENAME, - pcmk__str_casei|pcmk__str_null_matches)) { - - // Forward modifying and non-local requests via cluster - *process = false; - *needs_reply = false; - *local_notify = false; - *needs_forward = true; - - pcmk__trace("%s op from %s needs to be forwarded to %s", op, - pcmk__client_name(client), pcmk__s(host, "all nodes")); - return; - } - if (stand_alone) { pcmk__trace("Processing %s op from client %s (stand-alone)", op, pcmk__client_name(client)); @@ -717,7 +701,6 @@ based_process_request(xmlNode *request, bool privileged, bool process = true; // Whether to process request locally now bool needs_reply = true; // Whether to build a reply bool local_notify = false; // Whether to notify (local) requester - bool needs_forward = false; // Whether to forward request somewhere else xmlNode *reply = NULL; @@ -778,15 +761,24 @@ based_process_request(xmlNode *request, bool privileged, process = true; needs_reply = false; local_notify = false; - needs_forward = false; pcmk__trace("Processing %s op from %s/%s on %s locally because it's " "part of a transaction", op, client_name, call_id, pcmk__xe_get(request, PCMK__XA_SRC)); } else if (client != NULL) { + // Forward modifying and non-local requests via cluster + if (!pcmk__is_set(operation->flags, cib__op_attr_local) + && (pcmk__is_set(operation->flags, cib__op_attr_modifies) + || !pcmk__str_eq(host, OUR_NODENAME, + pcmk__str_casei|pcmk__str_null_matches))) { + + forward_request(request); + return pcmk_rc_ok; + } + parse_local_options(client, operation, host, op, &local_notify, - &needs_reply, &process, &needs_forward); + &needs_reply, &process); } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { @@ -799,11 +791,6 @@ based_process_request(xmlNode *request, bool privileged, pcmk__trace("Client is not interested in the reply"); } - if (needs_forward) { - forward_request(request); - return pcmk_rc_ok; - } - if (cib_status != pcmk_rc_ok) { rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " From 8acf708d6ef3b68b42f86d0e5d6a6c8141c0a795 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 10:31:38 -0800 Subject: [PATCH 063/202] Log: based: Drop log message from forward_request() It doesn't seem very helpful. We know that if we receive a request from a local client and it's either a modifying request or addressed to another node (or to all nodes), we'll forward it. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b007adef201..67af4cc74bb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -473,37 +473,16 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, static void forward_request(xmlNode *request) { - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); - const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); pcmk__node_status_t *peer = NULL; - int log_level = LOG_INFO; - - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_NOOP, pcmk__str_none)) { - log_level = LOG_DEBUG; - } - - do_crm_log(log_level, - "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)", - pcmk__s(op, "invalid"), - pcmk__s(section, "all"), - pcmk__s(host, "all"), - pcmk__s(originator, "local"), - pcmk__s(client_name, "unspecified"), - pcmk__s(call_id, "unspecified")); - - pcmk__xe_set(request, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); - if (host != NULL) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); } - pcmk__cluster_send_message(peer, pcmk_ipc_based, request); - // Return the request to its original state + // Set PCMK__XA_CIB_DELEGATED_FROM only temporarily + pcmk__xe_set(request, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); + pcmk__cluster_send_message(peer, pcmk_ipc_based, request); pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM); } From 4eab9a9e98d36fc11c2114e10805aaf085b8e507 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 10:45:29 -0800 Subject: [PATCH 064/202] Doc: based: Drop comment in parse_peer_options() I really don't think it's relevant now, though I could be wrong. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 67af4cc74bb..5d8dbbc767d 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -336,13 +336,6 @@ static bool parse_peer_options(const cib__operation_t *operation, xmlNode *request, bool *local_notify, bool *needs_reply, bool *process) { - /* TODO: What happens when an update comes in after node A - * requests the CIB from node B, but before it gets the reply (and - * sends out the replace operation)? - * - * (This may no longer be relevant since legacy mode was dropped; need to - * trace code more closely to check.) - */ const char *host = NULL; const char *delegated = pcmk__xe_get(request, PCMK__XA_CIB_DELEGATED_FROM); const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); From 274500de8a07128b83ae17f4b5db9c3e59e7abe0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:05:21 -0800 Subject: [PATCH 065/202] API: libcib: Deprecate cib_api_operations_t:sync Nothing uses it internally. sync_from is still available. Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 2 ++ lib/cib/cib_client.c | 1 + 2 files changed, 3 insertions(+) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index ff3a8b7e300..159725b0e3a 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -170,7 +170,9 @@ typedef struct cib_api_operations_s { int (*query_from) (cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options); + //! \deprecated Do not use int (*sync) (cib_t *cib, const char *section, int call_options); + int (*sync_from) (cib_t *cib, const char *host, const char *section, int call_options); int (*upgrade) (cib_t *cib, int call_options); diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 83f55b6f15c..89843aacecf 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -281,6 +281,7 @@ cib_client_upgrade(cib_t * cib, int call_options) NULL, call_options, cib->user); } +// @COMPAT cib_api_operations_t:sync is deprecated since 3.0.2 static int cib_client_sync(cib_t * cib, const char *section, int call_options) { From e9725b2776ed7f8a8e2c656cf6cde7cc23076810 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 00:45:33 -0800 Subject: [PATCH 066/202] Refactor: based: Minor easy changes to parse_peer_options() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 5d8dbbc767d..a14c13ee374 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -336,7 +336,7 @@ static bool parse_peer_options(const cib__operation_t *operation, xmlNode *request, bool *local_notify, bool *needs_reply, bool *process) { - const char *host = NULL; + const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); const char *delegated = pcmk__xe_get(request, PCMK__XA_CIB_DELEGATED_FROM); const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); @@ -359,7 +359,7 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { // Nothing to do - } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) { + } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { process_ping_reply(request); return false; @@ -426,24 +426,24 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - skip_is_reply: +skip_is_reply: *process = true; *needs_reply = false; - *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); - host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", op, originator); *needs_reply = true; return true; + } - } else if (host != NULL) { + if (host != NULL) { pcmk__trace("Ignoring %s request intended for CIB manager on %s", op, host); return false; + } - } else if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) { + if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { *needs_reply = true; } From 193815e7bf13d313419d848920acc08c5beacd75 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:34:35 -0800 Subject: [PATCH 067/202] Refactor: based: Move two parse_peer_options() early returns upward These are mutually exclusive with the other cases that appeared earlier in the if/else chain. * If op is CRM_OP_PING or PCMK__CIB_REQUEST_SHUTDOWN, then it isn't PCMK__CIB_REQUEST_REPLACE, PCMK__CIB_REQUEST_SYNC, or PCMK__CIB_REQUEST_UPGRADE. * If op is PCMK__CIB_REQUEST_SHUTDOWN, then the cib__op_attr_modifies flag isn't set. Also, return true explicitly from the shutdown case, and don't set *process. It's already set to true when this function is called. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a14c13ee374..48899c5c1d5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -348,6 +348,24 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, originator = "peer"; } + if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { + process_ping_reply(request); + return false; + } + + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { + *local_notify = false; + + if (reply_to == NULL) { + return true; + } + + // @TODO Is this possible? + pcmk__debug("Ignoring shutdown request from %s because reply_to=%s", + originator, reply_to); + return true; + } + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO if (reply_to) { @@ -359,10 +377,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { // Nothing to do - } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - process_ping_reply(request); - return false; - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. @@ -405,16 +419,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, pcmk__trace("Ignoring legacy %s reply sent from %s to local clients", op, originator); return false; - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - *local_notify = false; - if (reply_to == NULL) { - *process = true; - } else { // Not possible? - pcmk__debug("Ignoring shutdown request from %s because reply_to=%s", - originator, reply_to); - } - return *process; } if (is_reply) { From c71e0c71fb42ae99146df5d1181d8a0dea4792ae Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:44:11 -0800 Subject: [PATCH 068/202] Refactor: based: Drop legacy code from parse_peer_options() This was added by commit 15c4d2ac to deal with legacy mode. We haven't supported legacy mode (which was for clusters with pre-1.1.12 systems) since 3.0.0. Currently, the only modifying ops that may set PCMK__XA_CIB_ISREPLYTO are replace (as a sync reply) and upgrade. Those two cases are explicitly addressed here. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 48899c5c1d5..443072006cb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -412,13 +412,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, // Ignore broadcast client requests when we're not primary return false; } - - } else if (is_reply - && pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - - pcmk__trace("Ignoring legacy %s reply sent from %s to local clients", - op, originator); - return false; } if (is_reply) { From c8dba8a16d6fc03715d694e31c39a2be1bee3b1d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:56:57 -0800 Subject: [PATCH 069/202] Refactor: based: Move is_reply block to where it's used This is an incremental change; more is coming. Yes, this introduces duplication. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 45 +++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 443072006cb..7ad9262157a 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -375,9 +375,19 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { - // Nothing to do + if (is_reply) { + pcmk__trace("Will notify local clients for %s reply from %s", op, + originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; + } - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { + goto skip_is_reply; + } + + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. * @@ -400,27 +410,30 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, // Our upgrade request was rejected by DC, notify clients of result pcmk__xe_set(request, PCMK__XA_CIB_RC, upgrade_rc); - } else if ((max == NULL) && based_is_primary) { + if (is_reply) { + pcmk__trace("Will notify local clients for %s reply from %s", + op, originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; + } + + goto skip_is_reply; + } + + if ((max == NULL) && based_is_primary) { /* We are the DC, check if this upgrade is allowed */ goto skip_is_reply; + } - } else if(max) { + if (max != NULL) { /* Ok, go ahead and upgrade to 'max' */ goto skip_is_reply; - - } else { - // Ignore broadcast client requests when we're not primary - return false; } - } - if (is_reply) { - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); - *process = false; - *needs_reply = false; - *local_notify = true; - return true; + // Ignore broadcast client requests when we're not primary + return false; } skip_is_reply: From 5fbf386c954039b28d46d3028eec3c20907fed95 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 02:27:51 -0800 Subject: [PATCH 070/202] Refactor: based: Drop redundant check for upgrade RC Any PCMK__CIB_REQUEST_UPGRADE with PCMK__XA_CIB_UPGRADE_RC set also has PCMK__XA_CIB_ISREPLYTO set. The cluster message is addressed to the isreplyto host (see based_process_upgrade()), which must be non-NULL because origin is non-NULL. So we cannot receive a cluster message with PCMK__XA_CIB_UPGRADE_RC and a NULL host. If we receive a message that is addressed to some OTHER host, we drop it. cib_cs_dispatch() calls pcmk__cpg_message_data(), which ignores messages that aren't for the local node. Therefore, if we're processing a PCMK__CIB_REQUEST_UPGRADE request with PCMK__XA_CIB_UPGRADE_RC set, then is_reply must be true. Note that the equivalent of PCMK__XA_CIB_UPGRADE_RC was introduced by commit 1f05f5e2. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 7ad9262157a..92fa2e691bb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -408,18 +408,15 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (upgrade_rc != NULL) { // Our upgrade request was rejected by DC, notify clients of result + pcmk__assert(is_reply); pcmk__xe_set(request, PCMK__XA_CIB_RC, upgrade_rc); - if (is_reply) { - pcmk__trace("Will notify local clients for %s reply from %s", - op, originator); - *process = false; - *needs_reply = false; - *local_notify = true; - return true; - } - - goto skip_is_reply; + pcmk__trace("Will notify local clients for %s reply from %s", op, + originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; } if ((max == NULL) && based_is_primary) { From 42e765a02bd4d5e884519c0b185fd34763119a3c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 02:05:37 -0800 Subject: [PATCH 071/202] Refactor: based: Move early return to beginning of upgrade block ...in parse_peer_options(). Another incremental change for easy review. Note that the direct negation of the conditions that came before the previous "return false" would be if (((max != NULL) || !based_is_primary) && (max == NULL)) { ... } It cannot be the case that (max == NULL) and (max != NULL), so we can replace ((max != NULL) || !based_is_primary) with !based_is_primary. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 92fa2e691bb..dff80b8936e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -419,18 +419,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - if ((max == NULL) && based_is_primary) { - /* We are the DC, check if this upgrade is allowed */ - goto skip_is_reply; + if ((max == NULL) && !based_is_primary) { + // Ignore broadcast client requests when we're not the DC + return false; } - - if (max != NULL) { - /* Ok, go ahead and upgrade to 'max' */ - goto skip_is_reply; - } - - // Ignore broadcast client requests when we're not primary - return false; } skip_is_reply: From af43979fffb1b814bdfae9f97583db4569e52fb8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 02:53:28 -0800 Subject: [PATCH 072/202] Refactor: based: Drop skip_is_reply label But not the code below it. I've come to appreciate a nice "goto done" or similar, but in this case removing the label seems to make things less convoluted, at least for now. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index dff80b8936e..724ace2a5fb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -366,28 +366,22 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { - // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO - if (reply_to) { - delegated = reply_to; - } - goto skip_is_reply; + if (is_reply && pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { + pcmk__trace("Will notify local clients for %s reply from %s", op, + originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { - if (is_reply) { - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); - *process = false; - *needs_reply = false; - *local_notify = true; - return true; - } + if ((reply_to != NULL) + && pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { - goto skip_is_reply; - } + // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO + delegated = reply_to; - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { + } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. * @@ -425,7 +419,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } } -skip_is_reply: *process = true; *needs_reply = false; *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); From 83c922af4ecbcd488ba31f6b98bc38a592e09015 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 03:07:07 -0800 Subject: [PATCH 073/202] Refactor: based: parse_local_options() -> log_local_options() It doesn't really do anything now. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 39 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 724ace2a5fb..d6dd8734937 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -291,33 +291,25 @@ process_ping_reply(const xmlNode *reply) } static void -parse_local_options(const pcmk__client_t *client, - const cib__operation_t *operation, - const char *host, const char *op, bool *local_notify, - bool *needs_reply, bool *process) +log_local_options(const pcmk__client_t *client, + const cib__operation_t *operation, const char *host, + const char *op) { - // Process locally and notify local client - *process = true; - *needs_reply = false; - *local_notify = true; - if (pcmk__is_set(operation->flags, cib__op_attr_local)) { - /* Always process locally if cib__op_attr_local is set. - * - * @COMPAT: Currently host is ignored. At a compatibility break, throw - * an error (from based_process_request() or earlier) if host is not - * NULL or OUR_NODENAME. + /* @COMPAT Currently host is ignored. At a compatibility break, throw an + * error (from based_process_request() or earlier) if host is not NULL + * or OUR_NODENAME. */ pcmk__trace("Processing always-local %s op from client %s", op, pcmk__client_name(client)); - if (!pcmk__str_eq(host, OUR_NODENAME, - pcmk__str_casei|pcmk__str_null_matches)) { - - pcmk__warn("Operation '%s' is always local but its target host is " - "set to '%s'", - op, host); + if (pcmk__str_eq(host, OUR_NODENAME, + pcmk__str_casei|pcmk__str_null_matches)) { + return; } + + pcmk__warn("Operation '%s' is always local but its target host is set " + "to '%s'", op, host); return; } @@ -741,8 +733,11 @@ based_process_request(xmlNode *request, bool privileged, return pcmk_rc_ok; } - parse_local_options(client, operation, host, op, &local_notify, - &needs_reply, &process); + // Process locally and notify local client; no peer to reply to + needs_reply = false; + local_notify = true; + + log_local_options(client, operation, host, op); } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { From 01d167378a5738aace6709d524121b2c8be4f7b9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 03:11:24 -0800 Subject: [PATCH 074/202] Refactor: based: Set control variables only if they override defaults Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index d6dd8734937..436d365fdee 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -346,8 +346,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - *local_notify = false; - if (reply_to == NULL) { return true; } @@ -411,13 +409,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } } - *process = true; - *needs_reply = false; *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", op, originator); - *needs_reply = true; return true; } @@ -428,9 +423,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - *needs_reply = true; + return true; } + *needs_reply = false; pcmk__trace("Processing %s request broadcast by %s call %s on %s " "(local clients will%s be notified)", op, pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME), @@ -714,10 +710,7 @@ based_process_request(xmlNode *request, bool privileged, * CIB copy, and we don't notify for individual requests because the * entire transaction is atomic. */ - process = true; needs_reply = false; - local_notify = false; - pcmk__trace("Processing %s op from %s/%s on %s locally because it's " "part of a transaction", op, client_name, call_id, pcmk__xe_get(request, PCMK__XA_SRC)); From ff6af0cc4c18d4c86989bb9ef7410a971e00a50d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 16:50:43 -0800 Subject: [PATCH 075/202] Refactor: based: Rename crm_cluster to based_cluster Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- daemons/based/pacemaker-based.c | 16 ++++++++-------- daemons/based/pacemaker-based.h | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 436d365fdee..8f9a7a2e707 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -916,7 +916,7 @@ based_terminate(int exit_status) * messing with the peer caches). */ if (exit_status == CRM_EX_OK) { - pcmk_cluster_disconnect(crm_cluster); + pcmk_cluster_disconnect(based_cluster); } g_main_loop_quit(mainloop); return; @@ -925,7 +925,7 @@ based_terminate(int exit_status) /* Exit cleanly. Even the peer status callback can disconnect here, because * we're not returning control to the caller. */ - pcmk_cluster_disconnect(crm_cluster); + pcmk_cluster_disconnect(based_cluster); pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 4a38dd13da9..1878bd45fba 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -38,7 +38,7 @@ bool cib_shutdown_flag = false; int cib_status = pcmk_rc_ok; -pcmk_cluster_t *crm_cluster = NULL; +pcmk_cluster_t *based_cluster = NULL; GMainLoop *mainloop = NULL; gchar *cib_root = NULL; @@ -274,7 +274,7 @@ main(int argc, char **argv) /* If main loop returned, clean up and exit. We disconnect in case * based_terminate(-1) was called. */ - pcmk_cluster_disconnect(crm_cluster); + pcmk_cluster_disconnect(based_cluster); pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); done: @@ -283,7 +283,7 @@ main(int argc, char **argv) pcmk__cluster_destroy_node_caches(); pcmk__client_cleanup(); - pcmk_cluster_free(crm_cluster); + pcmk_cluster_free(based_cluster); g_free(cib_root); pcmk__output_and_clear_error(&error, out); @@ -367,20 +367,20 @@ cib_init(void) } based_remote_init(); - crm_cluster = pcmk_cluster_new(); + based_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(crm_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(crm_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(crm_cluster, pcmk__cpg_confchg_cb); + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); } #endif // SUPPORT_COROSYNC if (!stand_alone) { pcmk__cluster_set_status_callback(&cib_peer_update_callback); - if (pcmk_cluster_connect(crm_cluster) != pcmk_rc_ok) { + if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { pcmk__crit("Cannot sign in to the cluster... terminating"); crm_exit(CRM_EX_FATAL); } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index a23c983e167..917c39cddd8 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -24,10 +24,10 @@ #include "based_remote.h" #include "based_transaction.h" -#define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->priv->node_name) +#define OUR_NODENAME (stand_alone? "localhost" : based_cluster->priv->node_name) extern GMainLoop *mainloop; -extern pcmk_cluster_t *crm_cluster; +extern pcmk_cluster_t *based_cluster; extern gboolean stand_alone; extern bool cib_shutdown_flag; extern gchar *cib_root; From eb2ca403ec23fdc51b5d3de1b10960c7bc2716ec Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 16:56:33 -0800 Subject: [PATCH 076/202] Refactor: based: Don't create based_cluster if in stand-alone mode The NULL checks will get de-duplicated in an upcoming commit. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 +++++-- daemons/based/pacemaker-based.c | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8f9a7a2e707..ef4e83c2bdc 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -915,7 +915,7 @@ based_terminate(int exit_status) * main loop returns (this allows the peer status callback to avoid * messing with the peer caches). */ - if (exit_status == CRM_EX_OK) { + if ((exit_status == CRM_EX_OK) && (based_cluster != NULL)) { pcmk_cluster_disconnect(based_cluster); } g_main_loop_quit(mainloop); @@ -925,7 +925,10 @@ based_terminate(int exit_status) /* Exit cleanly. Even the peer status callback can disconnect here, because * we're not returning control to the caller. */ - pcmk_cluster_disconnect(based_cluster); + if (based_cluster != NULL) { + pcmk_cluster_disconnect(based_cluster); + } + pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 1878bd45fba..d3e9a715249 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -274,7 +274,10 @@ main(int argc, char **argv) /* If main loop returned, clean up and exit. We disconnect in case * based_terminate(-1) was called. */ - pcmk_cluster_disconnect(based_cluster); + if (based_cluster != NULL) { + pcmk_cluster_disconnect(based_cluster); + } + pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); done: @@ -367,17 +370,18 @@ cib_init(void) } based_remote_init(); - based_cluster = pcmk_cluster_new(); + + if (!stand_alone) { + based_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } #endif // SUPPORT_COROSYNC - if (!stand_alone) { pcmk__cluster_set_status_callback(&cib_peer_update_callback); if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { From 39da46cc3acaff9e164b395d6c55a8916d0260d9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 17:05:03 -0800 Subject: [PATCH 077/202] Refactor: based: New based_ipc_init() and based_ipc.h Also move the ipcs_{ro,rw,shm} extern declarations from based_callbacks.h to based_ipc.h, now that there is a more appropriate home for them. And make ipc_{ro,rw}_callbacks static to based_ipc.c, since we no longer use them in pacemaker-based.c. The goal is to look more like attrd, the fencer, and the scheduler. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_callbacks.h | 8 -------- daemons/based/based_ipc.c | 15 +++++++++++++-- daemons/based/based_ipc.h | 21 +++++++++++++++++++++ daemons/based/pacemaker-based.c | 5 ++--- daemons/based/pacemaker-based.h | 1 + 6 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 daemons/based/based_ipc.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index aee006cd118..5b9aa17dc5c 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -16,6 +16,7 @@ halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_callbacks.h noinst_HEADERS += based_io.h +noinst_HEADERS += based_ipc.h noinst_HEADERS += based_messages.h noinst_HEADERS += based_notify.h noinst_HEADERS += based_operation.h diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index d52bf52431e..c72ffa129ae 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -13,17 +13,9 @@ #include #include // xmlNode -#include // qb_* #include // pcmk__client_t -extern struct qb_ipcs_service_handlers ipc_ro_callbacks; -extern struct qb_ipcs_service_handlers ipc_rw_callbacks; - -extern qb_ipcs_service_t *ipcs_ro; -extern qb_ipcs_service_t *ipcs_rw; -extern qb_ipcs_service_t *ipcs_shm; - void based_peer_callback(xmlNode *msg, void *private_data); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index ff3f5fa316a..b3fc1db0bac 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -282,7 +282,7 @@ based_ipc_destroy(qb_ipcs_connection_t *c) based_ipc_closed(c); } -struct qb_ipcs_service_handlers ipc_ro_callbacks = { +static struct qb_ipcs_service_handlers ipc_ro_callbacks = { .connection_accept = based_ipc_accept, .connection_created = NULL, .msg_process = based_ipc_dispatch_ro, @@ -290,10 +290,21 @@ struct qb_ipcs_service_handlers ipc_ro_callbacks = { .connection_destroyed = based_ipc_destroy, }; -struct qb_ipcs_service_handlers ipc_rw_callbacks = { +static struct qb_ipcs_service_handlers ipc_rw_callbacks = { .connection_accept = based_ipc_accept, .connection_created = NULL, .msg_process = based_ipc_dispatch_rw, .connection_closed = based_ipc_closed, .connection_destroyed = based_ipc_destroy, }; + +/*! + * \internal + * \brief Set up \c based IPC communication + */ +void +based_ipc_init(void) +{ + pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, + &ipc_rw_callbacks); +} diff --git a/daemons/based/based_ipc.h b/daemons/based/based_ipc.h new file mode 100644 index 00000000000..98875d1e2a1 --- /dev/null +++ b/daemons/based/based_ipc.h @@ -0,0 +1,21 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_IPC__H +#define BASED_IPC__H + +#include // qb_* + +extern qb_ipcs_service_t *ipcs_ro; +extern qb_ipcs_service_t *ipcs_rw; +extern qb_ipcs_service_t *ipcs_shm; + +void based_ipc_init(void); + +#endif // BASED_IPC__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index d3e9a715249..ef9e5327e2f 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -265,6 +265,8 @@ main(int argc, char **argv) // Read initial CIB, connect to cluster, and start IPC servers cib_init(); + based_ipc_init(); + // Run the main loop mainloop = g_main_loop_new(NULL, FALSE); pcmk__notice("Pacemaker CIB manager successfully started and accepting " @@ -390,9 +392,6 @@ cib_init(void) } } - pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, - &ipc_rw_callbacks); - if (stand_alone) { based_is_primary = true; } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 917c39cddd8..eb2b7a3e365 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -18,6 +18,7 @@ #include "based_callbacks.h" #include "based_io.h" +#include "based_ipc.h" #include "based_messages.h" #include "based_operation.h" #include "based_notify.h" From 805b877f14cd4fb752274ae8bc975b84f2fbbd4d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 18:52:51 -0800 Subject: [PATCH 078/202] Refactor: based: Unindent cib_init() Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index ef9e5327e2f..159b919b1e8 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -373,26 +373,25 @@ cib_init(void) based_remote_init(); - if (!stand_alone) { - based_cluster = pcmk_cluster_new(); + if (stand_alone) { + based_is_primary = true; + return; + } + + based_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } #endif // SUPPORT_COROSYNC - pcmk__cluster_set_status_callback(&cib_peer_update_callback); - - if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { - pcmk__crit("Cannot sign in to the cluster... terminating"); - crm_exit(CRM_EX_FATAL); - } - } + pcmk__cluster_set_status_callback(&cib_peer_update_callback); - if (stand_alone) { - based_is_primary = true; + if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { + pcmk__crit("Cannot sign in to the cluster... terminating"); + crm_exit(CRM_EX_FATAL); } } From 18cfdd2b513e66fa73fb97345d70f0f957ab60c7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:44:07 -0800 Subject: [PATCH 079/202] Refactor: based: New based_ipc_cleanup() The goal is to look more like attrd, the fencer, and the scheduler. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 13 +------------ daemons/based/based_ipc.c | 29 +++++++++++++++++++++++++++++ daemons/based/based_ipc.h | 1 + 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ef4e83c2bdc..4bb3070245a 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -20,7 +20,6 @@ #include // gboolean, gpointer, g_*, etc. #include // xmlNode -#include // qb_ipcs_connection_t #include // LOG_TRACE #include // cib_call_options values @@ -847,17 +846,7 @@ based_shutdown(int nsig) } cib_shutdown_flag = true; - - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - - pcmk__drop_all_clients(ipcs_shm); - g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); - - based_drop_remote_clients(); + based_ipc_cleanup(); active = pcmk__cluster_num_active_nodes(); if (active < 2) { diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index b3fc1db0bac..4e139de73e1 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -308,3 +308,32 @@ based_ipc_init(void) pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, &ipc_rw_callbacks); } + +/*! + * \internal + * \brief Clean up \c based IPC communication + */ +void +based_ipc_cleanup(void) +{ + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); + + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); + + /* Drop remote clients here because they're part of the IPC client table and + * must be dropped before \c pcmk__client_cleanup() + */ + based_drop_remote_clients(); + + /* @TODO This is where we would call a based_unregister_handlers() to align + * with other daemons' IPC cleanup functions. Such a function does not yet + * exist; based doesn't use pcmk__request_t yet. + */ + + pcmk__client_cleanup(); +} diff --git a/daemons/based/based_ipc.h b/daemons/based/based_ipc.h index 98875d1e2a1..15b4cc31af8 100644 --- a/daemons/based/based_ipc.h +++ b/daemons/based/based_ipc.h @@ -17,5 +17,6 @@ extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; void based_ipc_init(void); +void based_ipc_cleanup(void); #endif // BASED_IPC__H From fca74d5ec5329246cdaa611fcb7f2c1071719a38 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:54:35 -0800 Subject: [PATCH 080/202] Low: based: Move based_ipc_cleanup() call to based_terminate() This seems like a more correct location for it. based_terminate() is where we free data structures. This also makes the calls to pcmk__stop_based_ipc() redundant, and allows making the qb_ipcs_service_t declarations static. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 5 ++--- daemons/based/based_ipc.c | 6 +++--- daemons/based/based_ipc.h | 6 ------ daemons/based/pacemaker-based.c | 2 -- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4bb3070245a..b9326eeb06f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -846,7 +846,6 @@ based_shutdown(int nsig) } cib_shutdown_flag = true; - based_ipc_cleanup(); active = pcmk__cluster_num_active_nodes(); if (active < 2) { @@ -887,13 +886,14 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + based_ipc_cleanup(); + g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error if (exit_status > CRM_EX_OK) { - pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(exit_status); return; } @@ -918,6 +918,5 @@ based_terminate(int exit_status) pcmk_cluster_disconnect(based_cluster); } - pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 4e139de73e1..7fbb1a31213 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -28,9 +28,9 @@ #include "pacemaker-based.h" -qb_ipcs_service_t *ipcs_ro = NULL; -qb_ipcs_service_t *ipcs_rw = NULL; -qb_ipcs_service_t *ipcs_shm = NULL; +static qb_ipcs_service_t *ipcs_ro = NULL; +static qb_ipcs_service_t *ipcs_rw = NULL; +static qb_ipcs_service_t *ipcs_shm = NULL; /*! * \internal diff --git a/daemons/based/based_ipc.h b/daemons/based/based_ipc.h index 15b4cc31af8..ed3cdb72981 100644 --- a/daemons/based/based_ipc.h +++ b/daemons/based/based_ipc.h @@ -10,12 +10,6 @@ #ifndef BASED_IPC__H #define BASED_IPC__H -#include // qb_* - -extern qb_ipcs_service_t *ipcs_ro; -extern qb_ipcs_service_t *ipcs_rw; -extern qb_ipcs_service_t *ipcs_shm; - void based_ipc_init(void); void based_ipc_cleanup(void); diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 159b919b1e8..ca018ab35ce 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -280,8 +280,6 @@ main(int argc, char **argv) pcmk_cluster_disconnect(based_cluster); } - pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); - done: g_strfreev(processed_args); pcmk__free_arg_context(context); From bd35bbcc132fb78a4d4159ec80f6e3b1844330d1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 02:05:00 -0800 Subject: [PATCH 081/202] Refactor: based: Functionize remote fd closure in based_remote.c Also make -1 the "uninitialized" value. 0 is a valid file descriptor, even though it's much more likely to be used for stdin. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +---------- daemons/based/based_remote.c | 26 ++++++++++++++++++++++++-- daemons/based/based_remote.h | 4 +--- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b9326eeb06f..fd310fb2f96 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -16,7 +16,6 @@ #include // free #include // LOG_INFO, LOG_DEBUG #include // time_t -#include // close #include // gboolean, gpointer, g_*, etc. #include // xmlNode @@ -877,16 +876,8 @@ based_shutdown(int nsig) void based_terminate(int exit_status) { - if (remote_fd > 0) { - close(remote_fd); - remote_fd = 0; - } - if (remote_tls_fd > 0) { - close(remote_tls_fd); - remote_tls_fd = 0; - } - based_ipc_cleanup(); + based_remote_cleanup(); g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 3746d53d887..0bd1d9fd01c 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -48,8 +48,8 @@ static pcmk__tls_t *tls = NULL; -int remote_fd = 0; -int remote_tls_fd = 0; +static int remote_fd = -1; +static int remote_tls_fd = -1; // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 @@ -761,6 +761,28 @@ based_remote_init(void) } } +/*! + * \internal + * \brief Stop remote listeners + * + * \note Remote clients are dropped in \c based_ipc_cleanup() rather than here, + * because they're part of the IPC client table and must be dropped before + * we call \c pcmk__client_cleanup(). + */ +void +based_remote_cleanup(void) +{ + if (remote_fd >= 0) { + close(remote_fd); + remote_fd = -1; + } + + if (remote_tls_fd >= 0) { + close(remote_tls_fd); + remote_tls_fd = -1; + } +} + /*! * \internal * \brief Disconnect and free a CIB manager client if it is a remote client diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index 1763dc04f55..ca8f172ae72 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -10,10 +10,8 @@ #ifndef BASED_REMOTE__H #define BASED_REMOTE__H -extern int remote_fd; -extern int remote_tls_fd; - void based_remote_init(void); +void based_remote_cleanup(void); void based_drop_remote_clients(void); #endif // BASED_REMOTE__H From bb0b86fa048faee840c4a28b42105c905c3df9b7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:11:35 -0800 Subject: [PATCH 082/202] Refactor: based: New based_cluster_connect() To look more like attrd and fenced. More change are coming. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 47 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index ca018ab35ce..291ad06bff7 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -161,6 +161,37 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } +/*! + * \internal + * \brief Initialize \c based_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ +static int +based_cluster_connect(void) +{ + int rc = pcmk_rc_ok; + + based_cluster = pcmk_cluster_new(); + +#if SUPPORT_COROSYNC + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } +#endif // SUPPORT_COROSYNC + + pcmk__cluster_set_status_callback(&cib_peer_update_callback); + + rc = pcmk_cluster_connect(based_cluster); + if (rc != pcmk_rc_ok) { + pcmk__err("Cluster connection failed"); + } + + return rc; +} + int main(int argc, char **argv) { @@ -376,20 +407,8 @@ cib_init(void) return; } - based_cluster = pcmk_cluster_new(); - -#if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } -#endif // SUPPORT_COROSYNC - - pcmk__cluster_set_status_callback(&cib_peer_update_callback); - - if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { - pcmk__crit("Cannot sign in to the cluster... terminating"); + if (based_cluster_connect() != pcmk_rc_ok) { + pcmk__crit("Could not connect to the cluster"); crm_exit(CRM_EX_FATAL); } } From 6f72f580b08ff010f4ad35bf49900dae2912445d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:22:35 -0800 Subject: [PATCH 083/202] Refactor: based: Pull cib_init() body into main() It doesn't make sense to have this handful of unrelated initializations in a separate function, when most of main() is for initializing things. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 52 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 291ad06bff7..759ca6f3844 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -45,8 +45,6 @@ gchar *cib_root = NULL; gboolean stand_alone = FALSE; -static void cib_init(void); - static crm_exit_t exit_code = CRM_EX_OK; /*! @@ -293,10 +291,33 @@ main(int argc, char **argv) pcmk__cluster_init_node_caches(); - // Read initial CIB, connect to cluster, and start IPC servers - cib_init(); + /* Read initial CIB. based_read_cib() returns new, non-NULL XML, so this + * should always succeed. + */ + if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { + exit_code = CRM_EX_SOFTWARE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Bug: failed to activate CIB. Terminating %s.", + pcmk__server_log_name(pcmk_ipc_based)); + goto done; + } based_ipc_init(); + based_remote_init(); + + if (stand_alone) { + based_is_primary = true; + + } else { + if (based_cluster_connect() != pcmk_rc_ok) { + exit_code = CRM_EX_FATAL; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not connect to the cluster"); + goto done; + } + + pcmk__info("Cluster connection active"); + } // Run the main loop mainloop = g_main_loop_new(NULL, FALSE); @@ -389,26 +410,3 @@ cib_peer_update_callback(enum pcmk__node_update type, break; } } - -static void -cib_init(void) -{ - // based_read_cib() returns new, non-NULL XML, so this should always succeed - if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { - pcmk__crit("Bug: failed to activate CIB. Terminating %s.", - pcmk__server_log_name(pcmk_ipc_based)); - crm_exit(CRM_EX_SOFTWARE); - } - - based_remote_init(); - - if (stand_alone) { - based_is_primary = true; - return; - } - - if (based_cluster_connect() != pcmk_rc_ok) { - pcmk__crit("Could not connect to the cluster"); - crm_exit(CRM_EX_FATAL); - } -} From 47dc0510a06c836e94eecb19855149a53c9ea057 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:27:41 -0800 Subject: [PATCH 084/202] Refactor: based: Move based_io_init() call just before based_read_cib() Place the call right before the first thing that needs it (based_activate_cib()). The rest of the initialization helpers are called in this region, so let's collect them. We can't make them all contiguous though -- based_remote_init() requires that the CIB be read and activated first. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 759ca6f3844..ba865726929 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -229,8 +229,6 @@ main(int argc, char **argv) mainloop_add_signal(SIGTERM, based_shutdown); - based_io_init(); - if ((g_strv_length(processed_args) >= 2) && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) { @@ -290,6 +288,7 @@ main(int argc, char **argv) } pcmk__cluster_init_node_caches(); + based_io_init(); /* Read initial CIB. based_read_cib() returns new, non-NULL XML, so this * should always succeed. From 9931e418ebf78b2a7e937e9260eb3dae437fd143 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:42:18 -0800 Subject: [PATCH 085/202] Refactor: based: Set based_is_primary in setup_stand_alone() Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index ba865726929..e40ca924b63 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -62,6 +62,8 @@ setup_stand_alone(GError **error) gid_t gid = 0; int rc = pcmk_rc_ok; + based_is_primary = true; + rc = pcmk__daemon_user(&uid, &gid); if (rc != pcmk_rc_ok) { exit_code = CRM_EX_FATAL; @@ -304,10 +306,7 @@ main(int argc, char **argv) based_ipc_init(); based_remote_init(); - if (stand_alone) { - based_is_primary = true; - - } else { + if (!stand_alone) { if (based_cluster_connect() != pcmk_rc_ok) { exit_code = CRM_EX_FATAL; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, From 102bdedbf14e76db1807db754f508849b7fd9fdd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:51:30 -0800 Subject: [PATCH 086/202] Refactor: based: New based_corosync.c for cluster-related functions To mirror attrd and fenced. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 2 + daemons/based/based_callbacks.c | 26 ------ daemons/based/based_callbacks.h | 1 - daemons/based/based_corosync.c | 146 ++++++++++++++++++++++++++++++++ daemons/based/based_corosync.h | 19 +++++ daemons/based/pacemaker-based.c | 95 --------------------- daemons/based/pacemaker-based.h | 1 + 7 files changed, 168 insertions(+), 122 deletions(-) create mode 100644 daemons/based/based_corosync.c create mode 100644 daemons/based/based_corosync.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 5b9aa17dc5c..28f7681931b 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -15,6 +15,7 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_callbacks.h +noinst_HEADERS += based_corosync.h noinst_HEADERS += based_io.h noinst_HEADERS += based_ipc.h noinst_HEADERS += based_messages.h @@ -34,6 +35,7 @@ pacemaker_based_LDADD += $(CLUSTERLIBS) $(PAM_LIBS) pacemaker_based_SOURCES = pacemaker-based.c pacemaker_based_SOURCES += based_callbacks.c +pacemaker_based_SOURCES += based_corosync.c pacemaker_based_SOURCES += based_io.c pacemaker_based_SOURCES += based_ipc.c pacemaker_based_SOURCES += based_messages.c diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index fd310fb2f96..f4a6f6168ad 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -798,32 +798,6 @@ based_process_request(xmlNode *request, bool privileged, return rc; } -void -based_peer_callback(xmlNode *msg, void *private_data) -{ - const char *reason = NULL; - const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); - - if (pcmk__peer_cache == NULL) { - reason = "membership not established"; - goto bail; - } - - if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); - } - - based_process_request(msg, true, NULL); - return; - - bail: - if (reason) { - const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); - - pcmk__warn("Discarding %s message from %s: %s", op, originator, reason); - } -} - static gboolean cib_force_exit(gpointer data) { diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index c72ffa129ae..9f7360d94ed 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -16,7 +16,6 @@ #include // pcmk__client_t -void based_peer_callback(xmlNode *msg, void *private_data); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_shutdown(int nsig); diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c new file mode 100644 index 00000000000..88027efbb05 --- /dev/null +++ b/daemons/based/based_corosync.c @@ -0,0 +1,146 @@ +/* + * Copyright 2004-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include // NULL, size_t +#include // uint32_t +#include // free + +#include // cpg_* +#include // gpointer +#include // xmlNode + +#include // SUPPORT_COROSYNC +#include // pcmk_cluster_* +#include // pcmk__cluster_*, etc. +#include // pcmk__err, pcmk__xml_free, etc. +#include // CRM_EX_DISCONNECT, pcmk_rc_ok + +#include "pacemaker-based.h" + +pcmk_cluster_t *based_cluster = NULL; + +static void +based_peer_callback(xmlNode *msg, void *private_data) +{ + const char *reason = NULL; + const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); + + if (pcmk__peer_cache == NULL) { + reason = "membership not established"; + goto bail; + } + + if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); + } + + based_process_request(msg, true, NULL); + return; + + bail: + if (reason) { + const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); + + pcmk__warn("Discarding %s message from %s: %s", op, originator, reason); + } +} + +#if SUPPORT_COROSYNC +static void +cib_cs_dispatch(cpg_handle_t handle, + const struct cpg_name *groupName, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +{ + xmlNode *xml = NULL; + const char *from = NULL; + char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); + + if(data == NULL) { + return; + } + + xml = pcmk__xml_parse(data); + if (xml == NULL) { + pcmk__err("Invalid XML: '%.120s'", data); + free(data); + return; + } + pcmk__xe_set(xml, PCMK__XA_SRC, from); + based_peer_callback(xml, NULL); + + pcmk__xml_free(xml); + free(data); +} + +static void +cib_cs_destroy(gpointer user_data) +{ + if (cib_shutdown_flag) { + pcmk__info("Corosync disconnection complete"); + } else { + pcmk__crit("Exiting immediately after losing connection to cluster " + "layer"); + based_terminate(CRM_EX_DISCONNECT); + } +} +#endif + +static void +cib_peer_update_callback(enum pcmk__node_update type, + pcmk__node_status_t *node, const void *data) +{ + switch (type) { + case pcmk__node_update_name: + case pcmk__node_update_state: + if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2) + && (pcmk__ipc_client_count() == 0)) { + + pcmk__info("Exiting after no more peers or clients remain"); + based_terminate(-1); + } + break; + + default: + break; + } +} + +/*! + * \internal + * \brief Initialize \c based_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ +int +based_cluster_connect(void) +{ + int rc = pcmk_rc_ok; + + based_cluster = pcmk_cluster_new(); + +#if SUPPORT_COROSYNC + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } +#endif // SUPPORT_COROSYNC + + pcmk__cluster_set_status_callback(&cib_peer_update_callback); + + rc = pcmk_cluster_connect(based_cluster); + if (rc != pcmk_rc_ok) { + pcmk__err("Cluster connection failed"); + } + + return rc; +} diff --git a/daemons/based/based_corosync.h b/daemons/based/based_corosync.h new file mode 100644 index 00000000000..5383fd748a8 --- /dev/null +++ b/daemons/based/based_corosync.h @@ -0,0 +1,19 @@ +/* + * Copyright 2025-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_COROSYNC__H +#define BASED_COROSYNC__H + +#include // pcmk_cluster_t + +extern pcmk_cluster_t *based_cluster; + +int based_cluster_connect(void); + +#endif // BASED_COROSYNC__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index e40ca924b63..7b9aab6f415 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -14,12 +14,10 @@ #include // SIGTERM #include #include // NULL, size_t -#include // free #include // LOG_INFO #include // gid_t, uid_t #include // setgid, setuid -#include // cpg_* #include // g_*, G_*, etc. #include // xmlNode @@ -38,8 +36,6 @@ bool cib_shutdown_flag = false; int cib_status = pcmk_rc_ok; -pcmk_cluster_t *based_cluster = NULL; - GMainLoop *mainloop = NULL; gchar *cib_root = NULL; @@ -161,37 +157,6 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } -/*! - * \internal - * \brief Initialize \c based_cluster and connect to the cluster layer - * - * \return Standard Pacemaker return code - */ -static int -based_cluster_connect(void) -{ - int rc = pcmk_rc_ok; - - based_cluster = pcmk_cluster_new(); - -#if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } -#endif // SUPPORT_COROSYNC - - pcmk__cluster_set_status_callback(&cib_peer_update_callback); - - rc = pcmk_cluster_connect(based_cluster); - if (rc != pcmk_rc_ok) { - pcmk__err("Cluster connection failed"); - } - - return rc; -} - int main(int argc, char **argv) { @@ -348,63 +313,3 @@ main(int argc, char **argv) pcmk__unregister_formats(); crm_exit(exit_code); } - -#if SUPPORT_COROSYNC -static void -cib_cs_dispatch(cpg_handle_t handle, - const struct cpg_name *groupName, - uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) -{ - xmlNode *xml = NULL; - const char *from = NULL; - char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); - - if(data == NULL) { - return; - } - - xml = pcmk__xml_parse(data); - if (xml == NULL) { - pcmk__err("Invalid XML: '%.120s'", data); - free(data); - return; - } - pcmk__xe_set(xml, PCMK__XA_SRC, from); - based_peer_callback(xml, NULL); - - pcmk__xml_free(xml); - free(data); -} - -static void -cib_cs_destroy(gpointer user_data) -{ - if (cib_shutdown_flag) { - pcmk__info("Corosync disconnection complete"); - } else { - pcmk__crit("Exiting immediately after losing connection to cluster " - "layer"); - based_terminate(CRM_EX_DISCONNECT); - } -} -#endif - -static void -cib_peer_update_callback(enum pcmk__node_update type, - pcmk__node_status_t *node, const void *data) -{ - switch (type) { - case pcmk__node_update_name: - case pcmk__node_update_state: - if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2) - && (pcmk__ipc_client_count() == 0)) { - - pcmk__info("Exiting after no more peers or clients remain"); - based_terminate(-1); - } - break; - - default: - break; - } -} diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index eb2b7a3e365..861622a3891 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -17,6 +17,7 @@ #include // pcmk_cluster_t #include "based_callbacks.h" +#include "based_corosync.h" #include "based_io.h" #include "based_ipc.h" #include "based_messages.h" From 608638b806090b1b05acf9164e3e44cd5eec2259 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:27:26 -0800 Subject: [PATCH 087/202] Refactor: libcrmcluster: Don't init caches in pcmk__corosync_connect() pcmk__corosync_connect() doesn't use the caches directly. pcmk__get_node() initializes the caches when it gets called. Signed-off-by: Reid Wahl --- lib/cluster/corosync.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index bfb4b0bdece..207a30405ce 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -466,8 +466,6 @@ pcmk__corosync_connect(pcmk_cluster_t *cluster) pcmk__node_status_t *local_node = NULL; int rc = pcmk_rc_ok; - pcmk__cluster_init_node_caches(); - if (cluster_layer != pcmk_cluster_layer_corosync) { pcmk__err("Invalid cluster layer: %s " QB_XS " cluster_layer=%d", cluster_layer_s, cluster_layer); From 1ae579907d906370b71a01998ac779f4e71db3ff Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:50:43 -0800 Subject: [PATCH 088/202] Refactor: libcrmcluster: Clean up includes in corosync.c Using include-what-you-use. Signed-off-by: Reid Wahl --- lib/cluster/corosync.c | 48 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 207a30405ce..2d0e52f556b 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -9,29 +9,33 @@ #include -#include -#include // PRIu64, etc. -#include -#include +#include // ENXIO, EINVAL +#include // PRIu32, PRIu64, PRIx32 #include -#include // uint32_t, etc. -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#include // NULL +#include // uint32_t, uint64_t +#include // sscanf +#include // free +#include // strerror, strchr +#include // gid_t, pid_t, uid_t +#include // sleep + +#include // cmap_* +#include // cs_*, CS_* +#include // quorum_* +#include // gboolean, gpointer, g_*, G_PRIORITY_HIGH +#include // xmlNode +#include // QB_XS + +#include // pcmk_cluster_*, etc. +#include // pcmk__cluster_private_t members +#include // pcmk__corosync2rc, pcmk__err, etc. +#include // crm_ipc_is_authentic_process +#include // CRM_LOG_ASSERT +#include // mainloop_* +#include // PCMK_VALUE_MEMBER +#include // CRM_EX_FATAL, crm_exit, pcmk_rc_*, etc. +#include // PCMK_XA_*, PCMK_XE_* #include "crmcluster_private.h" From bb452e817f0d39b5b7515bd13beb229ef56f0e6c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 21:13:11 -0800 Subject: [PATCH 089/202] Refactor: libcrmcluster: Destroy caches unconditionally on disconnect It shouldn't matter whether this is a Corosync cluster or not. Signed-off-by: Reid Wahl --- daemons/fenced/pacemaker-fenced.c | 1 - lib/cluster/cluster.c | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c index 26ca6f9c966..ceb3da0fc83 100644 --- a/daemons/fenced/pacemaker-fenced.c +++ b/daemons/fenced/pacemaker-fenced.c @@ -276,7 +276,6 @@ stonith_cleanup(void) { fenced_cib_cleanup(); fenced_ipc_cleanup(); - pcmk__cluster_destroy_node_caches(); free_stonith_remote_op_list(); free_topology_list(); fenced_free_device_table(); diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index 7ff5e674ade..461ea7438a3 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -117,13 +117,17 @@ pcmk_cluster_disconnect(pcmk_cluster_t *cluster) const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer); + /* @TODO Either decouple this from cluster disconnection, or move the caches + * to pcmk_cluster_t as suggested in comments in membership.c. + */ + pcmk__cluster_destroy_node_caches(); + pcmk__info("Disconnecting from %s cluster layer", cluster_layer_s); switch (cluster_layer) { #if SUPPORT_COROSYNC case pcmk_cluster_layer_corosync: pcmk__corosync_disconnect(cluster); - pcmk__cluster_destroy_node_caches(); return pcmk_rc_ok; #endif // SUPPORT_COROSYNC From cfba350d5857e0558adc06bc5422f8dcafd8f3f3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:08:29 -0800 Subject: [PATCH 090/202] Refactor: based: New based_cluster_disconnect() To mirror attrd and fenced. Notes: * Previously, we weren't freeing the cluster object on exit in based_terminate(). Now we are. * pcmk_cluster_free() calls pcmk__cluster_destroy_node_caches(), which is why we drop the call to that function. * I'm fairly certain that the reason the pcmk_cluster_disconnect() call previously occurred before the done section, is that prior to a recent commit, we weren't NULL-checking the cluster argument before disconnect. We should be able to call based_cluster_disconnect() regardless of how based_terminate() wherever we want to free the cluster object. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 +++------- daemons/based/based_corosync.c | 15 +++++++++++++++ daemons/based/based_corosync.h | 1 + daemons/based/pacemaker-based.c | 11 +---------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f4a6f6168ad..c9bd90b4370 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -23,7 +23,6 @@ #include // cib_call_options values #include // cib__* -#include // pcmk_cluster_disconnect #include // pcmk__cluster_send_message #include // pcmk__s, pcmk__str_eq #include // crm_ipc_*, pcmk_ipc_* @@ -869,8 +868,8 @@ based_terminate(int exit_status) * main loop returns (this allows the peer status callback to avoid * messing with the peer caches). */ - if ((exit_status == CRM_EX_OK) && (based_cluster != NULL)) { - pcmk_cluster_disconnect(based_cluster); + if (exit_status == CRM_EX_OK) { + based_cluster_disconnect(); } g_main_loop_quit(mainloop); return; @@ -879,9 +878,6 @@ based_terminate(int exit_status) /* Exit cleanly. Even the peer status callback can disconnect here, because * we're not returning control to the caller. */ - if (based_cluster != NULL) { - pcmk_cluster_disconnect(based_cluster); - } - + based_cluster_disconnect(); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 88027efbb05..b3d82b6b345 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -144,3 +144,18 @@ based_cluster_connect(void) return rc; } + +/*! + * \internal + * \brief Disconnect from the cluster layer and free \c based_cluster + */ +void +based_cluster_disconnect(void) +{ + if (based_cluster == NULL) { + return; + } + + pcmk_cluster_disconnect(based_cluster); + g_clear_pointer(&based_cluster, pcmk_cluster_free); +} diff --git a/daemons/based/based_corosync.h b/daemons/based/based_corosync.h index 5383fd748a8..4df1ea151b3 100644 --- a/daemons/based/based_corosync.h +++ b/daemons/based/based_corosync.h @@ -15,5 +15,6 @@ extern pcmk_cluster_t *based_cluster; int based_cluster_connect(void); +void based_cluster_disconnect(void); #endif // BASED_COROSYNC__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 7b9aab6f415..f0ccca78cf6 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -22,7 +22,6 @@ #include // xmlNode #include // CRM_CONFIG_DIR, CRM_DAEMON_USER -#include // pcmk_cluster_* #include // pcmk__node_update, etc. #include // crm_ipc_* #include // crm_log_* @@ -288,20 +287,12 @@ main(int argc, char **argv) "connections"); g_main_loop_run(mainloop); - /* If main loop returned, clean up and exit. We disconnect in case - * based_terminate(-1) was called. - */ - if (based_cluster != NULL) { - pcmk_cluster_disconnect(based_cluster); - } - done: g_strfreev(processed_args); pcmk__free_arg_context(context); - pcmk__cluster_destroy_node_caches(); pcmk__client_cleanup(); - pcmk_cluster_free(based_cluster); + based_cluster_disconnect(); g_free(cib_root); pcmk__output_and_clear_error(&error, out); From 33becc024d97f8a4cca73b5a480b7ca412341eff Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:14:49 -0800 Subject: [PATCH 091/202] Refactor: attrd, fencer: Set cluster pointer to NULL after freeing And NULL-check the attrd_cluster variable. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_corosync.c | 16 +++++++++++++++- daemons/fenced/fenced_corosync.c | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c index 59ea5bcc815..b357e1c3980 100644 --- a/daemons/attrd/attrd_corosync.c +++ b/daemons/attrd/attrd_corosync.c @@ -487,6 +487,12 @@ broadcast_unseen_local_values(void) } } +/*! + * \internal + * \brief Initialize \c attrd_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ int attrd_cluster_connect(void) { @@ -512,11 +518,19 @@ attrd_cluster_connect(void) return rc; } +/*! + * \internal + * \brief Disconnect from the cluster layer and free \c attrd_cluster + */ void attrd_cluster_disconnect(void) { + if (attrd_cluster == NULL) { + return; + } + pcmk_cluster_disconnect(attrd_cluster); - pcmk_cluster_free(attrd_cluster); + g_clear_pointer(&attrd_cluster, pcmk_cluster_free); } void diff --git a/daemons/fenced/fenced_corosync.c b/daemons/fenced/fenced_corosync.c index 6a1977b16c7..56e5ecbd6cf 100644 --- a/daemons/fenced/fenced_corosync.c +++ b/daemons/fenced/fenced_corosync.c @@ -174,6 +174,12 @@ fenced_cpg_destroy(gpointer unused) } #endif // SUPPORT_COROSYNC +/*! + * \internal + * \brief Initialize \c fenced_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ int fenced_cluster_connect(void) { @@ -199,6 +205,10 @@ fenced_cluster_connect(void) return rc; } +/*! + * \internal + * \brief Disconnect from the cluster layer and free \c fenced_cluster + */ void fenced_cluster_disconnect(void) { From c6c14a3b2be9834d8dc71085d18f67682eb40db2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 22:12:00 -0800 Subject: [PATCH 092/202] Fix: libcrmcluster: NULL to pcmk_cluster_disconnect() returns EINVAL pcmk_cluster_disconnect() now returns EINVAL if passed a NULL argument. Previously, if given a NULL argument in a Corosync cluster, pcmk_cluster_disconnect() would call down to pcmk__cpg_disconnect(), which would dereference the NULL pointer. Signed-off-by: Reid Wahl --- lib/cluster/cluster.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index 461ea7438a3..ae51f183c3a 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -122,6 +122,10 @@ pcmk_cluster_disconnect(pcmk_cluster_t *cluster) */ pcmk__cluster_destroy_node_caches(); + if (cluster == NULL) { + return EINVAL; + } + pcmk__info("Disconnecting from %s cluster layer", cluster_layer_s); switch (cluster_layer) { From 46085fddd969cb33328974cdcd3531b096c0820d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 15:29:14 -0700 Subject: [PATCH 093/202] Refactor: various: Use g_clear_pointer() with mainloop_del_*() functions Signed-off-by: Reid Wahl --- daemons/controld/controld_control.c | 6 ++---- daemons/execd/remoted_tls.c | 3 +-- lib/services/services.c | 11 ++--------- lib/services/services_linux.c | 3 +-- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 69cc4906c69..9a858304d62 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -177,8 +177,7 @@ crmd_exit(crm_exit_t exit_code) if(ipcs) { pcmk__trace("Closing IPC server"); - mainloop_del_ipc_server(ipcs); - ipcs = NULL; + g_clear_pointer(&ipcs, mainloop_del_ipc_server); } controld_close_attrd_ipc(); @@ -443,8 +442,7 @@ do_stop(long long action, enum crmd_fsa_cause cause, fsa_data_t *msg_data) { pcmk__trace("Stopping IPC server"); - mainloop_del_ipc_server(ipcs); - ipcs = NULL; + g_clear_pointer(&ipcs, mainloop_del_ipc_server); controld_fsa_append(C_FSA_INTERNAL, I_TERMINATE, NULL); } diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c index d250b232881..c5a7f1e2678 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -198,8 +198,7 @@ lrmd_auth_timeout_cb(gpointer data) return FALSE; } - mainloop_del_fd(client->remote->source); - client->remote->source = NULL; + g_clear_pointer(&client->remote->source, mainloop_del_fd); pcmk__err("Remote client authentication timed out"); return FALSE; diff --git a/lib/services/services.c b/lib/services/services.c index e752fdac75f..4c19de09190 100644 --- a/lib/services/services.c +++ b/lib/services/services.c @@ -524,15 +524,8 @@ services_action_cleanup(svc_action_t * op) } #endif - if (op->opaque->stderr_gsource) { - mainloop_del_fd(op->opaque->stderr_gsource); - op->opaque->stderr_gsource = NULL; - } - - if (op->opaque->stdout_gsource) { - mainloop_del_fd(op->opaque->stdout_gsource); - op->opaque->stdout_gsource = NULL; - } + g_clear_pointer(&op->opaque->stderr_gsource, mainloop_del_fd); + g_clear_pointer(&op->opaque->stdout_gsource, mainloop_del_fd); } /*! diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index ff18c9f5e9c..cb567f0a179 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -645,8 +645,7 @@ finish_op_output(svc_action_t *op, bool is_stderr) if (op->synchronous) { close(fd); } else { - mainloop_del_fd(*source); - *source = NULL; + g_clear_pointer(source, mainloop_del_fd); } } } From ea05a31233398d3edb9bd783af3f59678c66a176 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 22:43:55 -0800 Subject: [PATCH 094/202] Refactor: based: Rename corosync callback functions To mirror attrd and the fencer. Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index b3d82b6b345..a600df9063b 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -56,9 +56,9 @@ based_peer_callback(xmlNode *msg, void *private_data) #if SUPPORT_COROSYNC static void -cib_cs_dispatch(cpg_handle_t handle, - const struct cpg_name *groupName, - uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +based_cpg_dispatch(cpg_handle_t handle, + const struct cpg_name *groupName, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) { xmlNode *xml = NULL; const char *from = NULL; @@ -82,7 +82,7 @@ cib_cs_dispatch(cpg_handle_t handle, } static void -cib_cs_destroy(gpointer user_data) +based_cpg_destroy(gpointer user_data) { if (cib_shutdown_flag) { pcmk__info("Corosync disconnection complete"); @@ -95,8 +95,8 @@ cib_cs_destroy(gpointer user_data) #endif static void -cib_peer_update_callback(enum pcmk__node_update type, - pcmk__node_status_t *node, const void *data) +based_peer_change_cb(enum pcmk__node_update type, pcmk__node_status_t *node, + const void *data) { switch (type) { case pcmk__node_update_name: @@ -107,10 +107,10 @@ cib_peer_update_callback(enum pcmk__node_update type, pcmk__info("Exiting after no more peers or clients remain"); based_terminate(-1); } - break; + return; default: - break; + return; } } @@ -129,13 +129,13 @@ based_cluster_connect(void) #if SUPPORT_COROSYNC if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cluster_set_destroy_fn(based_cluster, based_cpg_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, based_cpg_dispatch); pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); } #endif // SUPPORT_COROSYNC - pcmk__cluster_set_status_callback(&cib_peer_update_callback); + pcmk__cluster_set_status_callback(based_peer_change_cb); rc = pcmk_cluster_connect(based_cluster); if (rc != pcmk_rc_ok) { From 0d5328d2b4577a315fa76fe13a04a0bc3796a95a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 13:51:07 -0800 Subject: [PATCH 095/202] Refactor: based: Don't send shutdown request The CIB manager started sending a shutdown request with commit 1f613581 (2006). There is not much detail about the issue this aimed to fix. However, the commit message says the following: """ Fix for a timing issue where a remote CCM registered the DC exited before the last CIB update(s) from the DC were applied. ... As long as one node has the update, the join process will make sure it ends up on the new DC. If for some reason *none* of our peers reply, we still exit in at most N seconds and we're no worse off than we were before. """ It very much sounds like the problem was tied to legacy mode and however we handled CIB updates back then. As of Pacemaker 1.1.12, all modifying operations get forwarded and processed on all nodes. Commit 15c4d2ac: """ Feature: cib: Send all r/w operations via the cluster connection and have all nodes process them The idea of a master cib instance goes away, and all instances apply all raw (and now ordered thanks to corosync) updates (not diffs) This ensures updates are never lost, even if there is no DC """ That was the replacement for legacy mode behavior. Given that modifying ops get forwarded to all nodes, I don't see how "...a remote CCM registered the DC exited before the last CIB update(s) from the DC were applied" would present a problem. If the DC's CIB manager receives a modifying request, it will forward it via the cluster layer to all nodes for processing. A request could always be dropped if a node abruptly leaves the cluster before it's able to forward a request that a client sent, but there's nothing we can do about that. At least the CIB would remain in a consistent state. Note that this also makes the peer status callback pointless. So we drop it, and we also drop the associated "-1" logic from based_terminate(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 70 +++++-------------------- daemons/based/based_callbacks.h | 3 +- daemons/based/based_corosync.c | 22 -------- daemons/based/based_messages.c | 18 ++----- include/crm/common/xml_names_internal.h | 1 - lib/cib/cib_utils.c | 10 +--- 6 files changed, 21 insertions(+), 103 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c9bd90b4370..d4fc41b3dd0 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -28,14 +28,12 @@ #include // crm_ipc_*, pcmk_ipc_* #include // CRM_LOG_ASSERT, CRM_CHECK #include // mainloop_* -#include // pcmk_rc_* +#include // CRM_EX_OK, crm_exit_t, pcmk_rc_* #include // PCMK_XA_*, PCMK_XE_* #include // CRM_OP_* #include "pacemaker-based.h" -#define EXIT_ESCALATION_MS 10000 - static mainloop_timer_t *digest_timer = NULL; static long long ping_seq = 0; static char *ping_digest = NULL; @@ -343,13 +341,12 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - if (reply_to == NULL) { - return true; - } - - // @TODO Is this possible? - pcmk__debug("Ignoring shutdown request from %s because reply_to=%s", - originator, reply_to); + /* @COMPAT We stopped sending shutdown requests as of 3.0.2. During a + * rolling upgrade, the requesting node expects a reply. We might as + * well continue sending one until we no longer support rolling upgrades + * from versions earlier than 3.0.2. (If the requesting node doesn't + * receive a reply, it simply exits with CRM_EX_ERROR after 10 seconds.) + */ return true; } @@ -797,57 +794,26 @@ based_process_request(xmlNode *request, bool privileged, return rc; } -static gboolean -cib_force_exit(gpointer data) -{ - pcmk__notice("Exiting immediately after %s without shutdown acknowledgment", - pcmk__readable_interval(EXIT_ESCALATION_MS)); - based_terminate(CRM_EX_ERROR); - return FALSE; -} - void based_shutdown(int nsig) { - int active = 0; - xmlNode *notification = NULL; - if (cib_shutdown_flag) { // Already shutting down return; } cib_shutdown_flag = true; - - active = pcmk__cluster_num_active_nodes(); - if (active < 2) { - pcmk__info("Exiting without sending shutdown request (no active " - "peers)"); - based_terminate(CRM_EX_OK); - return; - } - - pcmk__info("Sending shutdown request to %d peers", active); - - notification = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); - pcmk__xe_set(notification, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(notification, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, notification); - pcmk__xml_free(notification); - - pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); + based_terminate(CRM_EX_OK); } /*! * \internal * \brief Close remote sockets, free the global CIB and quit * - * \param[in] exit_status What exit status to use (if -1, use CRM_EX_OK, but - * skip disconnecting from the cluster layer) + * \param[in] exit_status Exit code */ void -based_terminate(int exit_status) +based_terminate(crm_exit_t exit_status) { based_ipc_cleanup(); based_remote_cleanup(); @@ -857,27 +823,17 @@ based_terminate(int exit_status) g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error - if (exit_status > CRM_EX_OK) { + if (exit_status != CRM_EX_OK) { crm_exit(exit_status); return; } + based_cluster_disconnect(); + if ((mainloop != NULL) && g_main_loop_is_running(mainloop)) { - /* Quit via returning from the main loop. If exit_status has the special - * value -1, we skip the disconnect here, and it will be done when the - * main loop returns (this allows the peer status callback to avoid - * messing with the peer caches). - */ - if (exit_status == CRM_EX_OK) { - based_cluster_disconnect(); - } g_main_loop_quit(mainloop); return; } - /* Exit cleanly. Even the peer status callback can disconnect here, because - * we're not returning control to the caller. - */ - based_cluster_disconnect(); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 9f7360d94ed..1121cb4c823 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -15,10 +15,11 @@ #include // xmlNode #include // pcmk__client_t +#include // crm_exit_t int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_shutdown(int nsig); -void based_terminate(int exit_status); +void based_terminate(crm_exit_t exit_status); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index a600df9063b..a3be2613512 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -94,26 +94,6 @@ based_cpg_destroy(gpointer user_data) } #endif -static void -based_peer_change_cb(enum pcmk__node_update type, pcmk__node_status_t *node, - const void *data) -{ - switch (type) { - case pcmk__node_update_name: - case pcmk__node_update_state: - if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2) - && (pcmk__ipc_client_count() == 0)) { - - pcmk__info("Exiting after no more peers or clients remain"); - based_terminate(-1); - } - return; - - default: - return; - } -} - /*! * \internal * \brief Initialize \c based_cluster and connect to the cluster layer @@ -135,8 +115,6 @@ based_cluster_connect(void) } #endif // SUPPORT_COROSYNC - pcmk__cluster_set_status_callback(based_peer_change_cb); - rc = pcmk_cluster_connect(based_cluster); if (rc != pcmk_rc_ok) { pcmk__err("Cluster connection failed"); diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index cbf2ee4ac6c..328bf655dd2 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -204,20 +204,10 @@ based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - - if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { - pcmk__info("Peer %s is requesting to shut down", host); - return pcmk_rc_ok; - } - - if (!cib_shutdown_flag) { - pcmk__err("Peer %s mistakenly thinks we wanted to shut down", host); - return EINVAL; - } - - pcmk__info("Exiting after %s acknowledged our shutdown request", host); - based_terminate(CRM_EX_OK); + /* @COMPAT Remove when PCMK__CIB_REQUEST_SHUTDOWN is removed. Nodes with + * Pacemaker versions earlier than 3.0.2 send a shutdown request and expect + * a reply. + */ return pcmk_rc_ok; } diff --git a/include/crm/common/xml_names_internal.h b/include/crm/common/xml_names_internal.h index 7fa63b67987..b880977badd 100644 --- a/include/crm/common/xml_names_internal.h +++ b/include/crm/common/xml_names_internal.h @@ -37,7 +37,6 @@ extern "C" { #define PCMK__XE_CRM_XML "crm_xml" #define PCMK__XE_DIV "div" #define PCMK__XE_DOWNED "downed" -#define PCMK__XE_EXIT_NOTIFICATION "exit-notification" #define PCMK__XE_FAILED_UPDATE "failed_update" #define PCMK__XE_FILE "file" #define PCMK__XE_GENERATION_TUPLE "generation_tuple" diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index b15a083d029..ea9f3e169a6 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -256,14 +256,8 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, /* Sanity check: op should be read-only (but this does not check children, * attributes, or private data) */ - if (!pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - /* @TODO based_terminate() frees the_cib during - * based_process_shutdown(). Consolidate the based cleanup logic and - * remove the if guard here. - */ - pcmk__assert((cib == saved_cib) && (cib->doc == saved_doc) - && (cib == xmlDocGetRootElement(cib->doc))); - } + pcmk__assert((cib == saved_cib) && (cib->doc == saved_doc) + && (cib == xmlDocGetRootElement(cib->doc))); if (cib_filtered == *output) { // Let the caller have this copy From 9afa4409dce43af5b23ed55c6cfe198be3f035db Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 23:21:07 -0800 Subject: [PATCH 096/202] Refactor: based: Move based_shutdown() to pacemaker-based.c And make it static. It seems logical to have it in the same file as the main() function. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 12 ------------ daemons/based/based_callbacks.h | 1 - daemons/based/pacemaker-based.c | 12 ++++++++++++ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index d4fc41b3dd0..1d5ac86559a 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -794,18 +794,6 @@ based_process_request(xmlNode *request, bool privileged, return rc; } -void -based_shutdown(int nsig) -{ - if (cib_shutdown_flag) { - // Already shutting down - return; - } - - cib_shutdown_flag = true; - based_terminate(CRM_EX_OK); -} - /*! * \internal * \brief Close remote sockets, free the global CIB and quit diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 1121cb4c823..3e71e2f2984 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -19,7 +19,6 @@ int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); -void based_shutdown(int nsig); void based_terminate(crm_exit_t exit_status); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index f0ccca78cf6..3c5b13964ab 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -156,6 +156,18 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } +static void +based_shutdown(int nsig) +{ + if (cib_shutdown_flag) { + // Already shutting down + return; + } + + cib_shutdown_flag = true; + based_terminate(CRM_EX_OK); +} + int main(int argc, char **argv) { From 9b14ebbc8c97e5ebd605a1c03bb28e9ea704c7a0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 00:33:42 -0800 Subject: [PATCH 097/202] Refactor: based: New based_cluster_node_name() This gets rid of the last use of based_cluster outside of based_corosync.c. Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 13 +++++++++++++ daemons/based/based_corosync.h | 1 + daemons/based/pacemaker-based.h | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index a3be2613512..187fd03ffad 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -137,3 +137,16 @@ based_cluster_disconnect(void) pcmk_cluster_disconnect(based_cluster); g_clear_pointer(&based_cluster, pcmk_cluster_free); } + +/*! + * \internal + * \brief Get the local node name at the cluster layer + * + * \return Local cluster-layer node name, or \c NULL if there is no active + * cluster connection + */ +const char * +based_cluster_node_name(void) +{ + return (based_cluster != NULL)? based_cluster->priv->node_name : NULL; +} diff --git a/daemons/based/based_corosync.h b/daemons/based/based_corosync.h index 4df1ea151b3..c7df2ab318b 100644 --- a/daemons/based/based_corosync.h +++ b/daemons/based/based_corosync.h @@ -16,5 +16,6 @@ extern pcmk_cluster_t *based_cluster; int based_cluster_connect(void); void based_cluster_disconnect(void); +const char *based_cluster_node_name(void); #endif // BASED_COROSYNC__H diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 861622a3891..b6992723d42 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -26,7 +26,7 @@ #include "based_remote.h" #include "based_transaction.h" -#define OUR_NODENAME (stand_alone? "localhost" : based_cluster->priv->node_name) +#define OUR_NODENAME (stand_alone? "localhost" : based_cluster_node_name()) extern GMainLoop *mainloop; extern pcmk_cluster_t *based_cluster; From ded7858033a53fe1888c28794116e3efd45b2be3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 00:38:34 -0800 Subject: [PATCH 098/202] Refactor: based: Make based_cluster static And rename to cluster. We don't typically care much about making variable names distinct, although we try to make function names distinct. Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 24 ++++++++++++------------ daemons/based/based_corosync.h | 4 ---- daemons/based/pacemaker-based.h | 3 --- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 187fd03ffad..d9390544f20 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -26,7 +26,7 @@ #include "pacemaker-based.h" -pcmk_cluster_t *based_cluster = NULL; +static pcmk_cluster_t *cluster = NULL; static void based_peer_callback(xmlNode *msg, void *private_data) @@ -96,7 +96,7 @@ based_cpg_destroy(gpointer user_data) /*! * \internal - * \brief Initialize \c based_cluster and connect to the cluster layer + * \brief Initialize the cluster object and connect to the cluster layer * * \return Standard Pacemaker return code */ @@ -105,17 +105,17 @@ based_cluster_connect(void) { int rc = pcmk_rc_ok; - based_cluster = pcmk_cluster_new(); + cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, based_cpg_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, based_cpg_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + pcmk_cluster_set_destroy_fn(cluster, based_cpg_destroy); + pcmk_cpg_set_deliver_fn(cluster, based_cpg_dispatch); + pcmk_cpg_set_confchg_fn(cluster, pcmk__cpg_confchg_cb); } #endif // SUPPORT_COROSYNC - rc = pcmk_cluster_connect(based_cluster); + rc = pcmk_cluster_connect(cluster); if (rc != pcmk_rc_ok) { pcmk__err("Cluster connection failed"); } @@ -125,17 +125,17 @@ based_cluster_connect(void) /*! * \internal - * \brief Disconnect from the cluster layer and free \c based_cluster + * \brief Disconnect from the cluster layer and free the cluster object */ void based_cluster_disconnect(void) { - if (based_cluster == NULL) { + if (cluster == NULL) { return; } - pcmk_cluster_disconnect(based_cluster); - g_clear_pointer(&based_cluster, pcmk_cluster_free); + pcmk_cluster_disconnect(cluster); + g_clear_pointer(&cluster, pcmk_cluster_free); } /*! @@ -148,5 +148,5 @@ based_cluster_disconnect(void) const char * based_cluster_node_name(void) { - return (based_cluster != NULL)? based_cluster->priv->node_name : NULL; + return (cluster != NULL)? cluster->priv->node_name : NULL; } diff --git a/daemons/based/based_corosync.h b/daemons/based/based_corosync.h index c7df2ab318b..d48e55b783e 100644 --- a/daemons/based/based_corosync.h +++ b/daemons/based/based_corosync.h @@ -10,10 +10,6 @@ #ifndef BASED_COROSYNC__H #define BASED_COROSYNC__H -#include // pcmk_cluster_t - -extern pcmk_cluster_t *based_cluster; - int based_cluster_connect(void); void based_cluster_disconnect(void); const char *based_cluster_node_name(void); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index b6992723d42..024fc727ac8 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -14,8 +14,6 @@ #include // gboolean, gchar, GMainLoop -#include // pcmk_cluster_t - #include "based_callbacks.h" #include "based_corosync.h" #include "based_io.h" @@ -29,7 +27,6 @@ #define OUR_NODENAME (stand_alone? "localhost" : based_cluster_node_name()) extern GMainLoop *mainloop; -extern pcmk_cluster_t *based_cluster; extern gboolean stand_alone; extern bool cib_shutdown_flag; extern gchar *cib_root; From 9510f3cd840a7cd9878e53f10cbfc9766503352a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 00:49:49 -0800 Subject: [PATCH 099/202] Refactor: based: New based_shutting_down() To replace cib_shutdown_flag. Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 10 +++++----- daemons/based/based_ipc.c | 2 +- daemons/based/pacemaker-based.c | 19 ++++++++++++++++--- daemons/based/pacemaker-based.h | 3 ++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index d9390544f20..67ff02e5394 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -84,13 +84,13 @@ based_cpg_dispatch(cpg_handle_t handle, static void based_cpg_destroy(gpointer user_data) { - if (cib_shutdown_flag) { + if (based_shutting_down()) { pcmk__info("Corosync disconnection complete"); - } else { - pcmk__crit("Exiting immediately after losing connection to cluster " - "layer"); - based_terminate(CRM_EX_DISCONNECT); + return; } + + pcmk__crit("Exiting immediately after losing connection to cluster layer"); + based_terminate(CRM_EX_DISCONNECT); } #endif diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 7fbb1a31213..9f1af42b97b 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -45,7 +45,7 @@ static qb_ipcs_service_t *ipcs_shm = NULL; static int32_t based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - if (cib_shutdown_flag) { + if (based_shutting_down()) { pcmk__info("Ignoring new IPC client [%d] during shutdown", pcmk__client_pid(c)); return -ECONNREFUSED; diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 3c5b13964ab..d79542ca378 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -32,7 +32,6 @@ #define SUMMARY "daemon for managing the configuration of a Pacemaker cluster" -bool cib_shutdown_flag = false; int cib_status = pcmk_rc_ok; GMainLoop *mainloop = NULL; @@ -40,8 +39,22 @@ gchar *cib_root = NULL; gboolean stand_alone = FALSE; +static bool shutting_down = false; static crm_exit_t exit_code = CRM_EX_OK; +/*! + * \internal + * \brief Check whether local CIB manager is shutting down + * + * \return \c true if local CIB manager has begun shutting down, or \c false + * otherwise + */ +bool +based_shutting_down(void) +{ + return shutting_down; +} + /*! * \internal * \brief Set up options, users, and groups for stand-alone mode @@ -159,12 +172,12 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) static void based_shutdown(int nsig) { - if (cib_shutdown_flag) { + if (based_shutting_down()) { // Already shutting down return; } - cib_shutdown_flag = true; + shutting_down = true; based_terminate(CRM_EX_OK); } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 024fc727ac8..52606aa6f6f 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -28,8 +28,9 @@ extern GMainLoop *mainloop; extern gboolean stand_alone; -extern bool cib_shutdown_flag; extern gchar *cib_root; extern int cib_status; +bool based_shutting_down(void); + #endif // PACEMAKER_BASED__H From 7240e1ec9814b8dd895706c06aff5b5a4b1a4778 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 00:55:40 -0800 Subject: [PATCH 100/202] Refactor: based: New based_stand_alone() To replace global stand_alone variable. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- daemons/based/based_io.c | 6 +++--- daemons/based/pacemaker-based.c | 20 ++++++++++++++++---- daemons/based/pacemaker-based.h | 7 ++++--- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1d5ac86559a..bfdc229e594 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -308,7 +308,7 @@ log_local_options(const pcmk__client_t *client, return; } - if (stand_alone) { + if (based_stand_alone()) { pcmk__trace("Processing %s op from client %s (stand-alone)", op, pcmk__client_name(client)); @@ -779,7 +779,7 @@ based_process_request(xmlNode *request, bool privileged, done: if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) - && needs_reply && !stand_alone && (client == NULL)) { + && needs_reply && !based_stand_alone() && (client == NULL)) { send_peer_reply(reply, originator); } diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 623603d6013..f24365e1f26 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -181,7 +181,7 @@ based_enable_writes(int nsig) void based_io_init(void) { - writes_enabled = !stand_alone; + writes_enabled = !based_stand_alone(); if (writes_enabled && pcmk__env_option_enabled(PCMK__SERVER_BASED, PCMK__ENV_VALGRIND_ENABLED)) { @@ -501,7 +501,7 @@ set_empty_status(xmlNode *cib_xml) { xmlNode *status = pcmk__xe_first_child(cib_xml, PCMK_XE_STATUS, NULL, NULL); - if (!stand_alone) { + if (!based_stand_alone()) { g_clear_pointer(&status, pcmk__xml_free); } @@ -584,7 +584,7 @@ based_read_cib(void) // The DC should set appropriate value for PCMK_XA_DC_UUID pcmk__xe_remove_attr(cib_xml, PCMK_XA_DC_UUID); - if (!stand_alone) { + if (!based_stand_alone()) { pcmk__log_xml_trace(cib_xml, "on-disk"); } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index d79542ca378..10c705b2a65 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -37,9 +37,8 @@ int cib_status = pcmk_rc_ok; GMainLoop *mainloop = NULL; gchar *cib_root = NULL; -gboolean stand_alone = FALSE; - static bool shutting_down = false; +static gboolean stand_alone = FALSE; static crm_exit_t exit_code = CRM_EX_OK; /*! @@ -55,6 +54,19 @@ based_shutting_down(void) return shutting_down; } +/*! + * \internal + * \brief Check whether local CIB manager is running in stand-alone mode + * + * \return \c true if local CIB manager is in stand-alone mode, or \c false + * otherwise + */ +bool +based_stand_alone(void) +{ + return stand_alone; +} + /*! * \internal * \brief Set up options, users, and groups for stand-alone mode @@ -257,7 +269,7 @@ main(int argc, char **argv) // Not up or not authentic; we'll proceed either way g_clear_pointer(&old_instance, crm_ipc_destroy); - if (stand_alone) { + if (based_stand_alone()) { rc = setup_stand_alone(&error); if (rc != pcmk_rc_ok) { goto done; @@ -295,7 +307,7 @@ main(int argc, char **argv) based_ipc_init(); based_remote_init(); - if (!stand_alone) { + if (!based_stand_alone()) { if (based_cluster_connect() != pcmk_rc_ok) { exit_code = CRM_EX_FATAL; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 52606aa6f6f..ccee070b600 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -12,7 +12,7 @@ #include -#include // gboolean, gchar, GMainLoop +#include // gchar, GMainLoop #include "based_callbacks.h" #include "based_corosync.h" @@ -24,13 +24,14 @@ #include "based_remote.h" #include "based_transaction.h" -#define OUR_NODENAME (stand_alone? "localhost" : based_cluster_node_name()) +#define OUR_NODENAME \ + (based_stand_alone()? "localhost" : based_cluster_node_name()) extern GMainLoop *mainloop; -extern gboolean stand_alone; extern gchar *cib_root; extern int cib_status; bool based_shutting_down(void); +bool based_stand_alone(void); #endif // PACEMAKER_BASED__H From e6ec00a305e94b459bcb2ef3b6f2f086d8aecb24 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 01:07:44 -0800 Subject: [PATCH 101/202] Refactor: based: New based_{get,set}_local_node_dc() To drop a global. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 8 ++++---- daemons/based/based_messages.c | 12 +++++------- daemons/based/based_messages.h | 1 - daemons/based/pacemaker-based.c | 27 ++++++++++++++++++++++++++- daemons/based/pacemaker-based.h | 3 +++ 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index bfdc229e594..b8ac5abb120 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -178,7 +178,7 @@ digest_timer_cb(gpointer data) { xmlNode *ping = NULL; - if (!based_is_primary) { + if (!based_get_local_node_dc()) { // Only the DC sends a ping return G_SOURCE_REMOVE; } @@ -240,7 +240,7 @@ process_ping_reply(const xmlNode *reply) return; } - if (!based_is_primary) { + if (!based_get_local_node_dc()) { pcmk__trace("Ignoring ping reply %lld from %s because we are no longer " "DC", seq, host); return; @@ -381,7 +381,7 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, pcmk__trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s", (is_reply? "reply" : "request"), - (based_is_primary? "primary" : "secondary"), + (based_get_local_node_dc()? "DC" : "non-DC node"), pcmk__s(max, "none"), pcmk__s(upgrade_rc, "none")); if (upgrade_rc != NULL) { @@ -397,7 +397,7 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - if ((max == NULL) && !based_is_primary) { + if ((max == NULL) && !based_get_local_node_dc()) { // Ignore broadcast client requests when we're not the DC return false; } diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 328bf655dd2..467cecc9f84 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -29,8 +29,6 @@ #include "pacemaker-based.h" -bool based_is_primary = false; - xmlNode *the_cib = NULL; /*! @@ -83,7 +81,7 @@ int based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this - return (based_is_primary? pcmk_rc_ok : EPERM); + return (based_get_local_node_dc()? pcmk_rc_ok : EPERM); } // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed @@ -134,9 +132,9 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { - if (!based_is_primary) { + if (!based_get_local_node_dc()) { pcmk__info("We are now in R/W mode"); - based_is_primary = true; + based_set_local_node_dc(true); } else { pcmk__debug("We are still in R/W mode"); @@ -190,9 +188,9 @@ based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) { - if (based_is_primary) { + if (based_get_local_node_dc()) { pcmk__info("We are now in R/O mode"); - based_is_primary = false; + based_set_local_node_dc(false); } else { pcmk__debug("We are still in R/O mode"); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 30b934a8600..c2563fdc3d6 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -14,7 +14,6 @@ #include // xmlNode * -extern bool based_is_primary; extern xmlNode *the_cib; int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 10c705b2a65..5d54173d984 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -37,10 +37,35 @@ int cib_status = pcmk_rc_ok; GMainLoop *mainloop = NULL; gchar *cib_root = NULL; +static bool local_node_dc = false; static bool shutting_down = false; static gboolean stand_alone = FALSE; static crm_exit_t exit_code = CRM_EX_OK; +/*! + * \internal + * \brief Check whether local node is DC + * + * \return \c true if local node is DC, or \c false otherwise + */ +bool +based_get_local_node_dc(void) +{ + return local_node_dc; +} + +/*! + * \internal + * \brief Record whether local node is DC + * + * \param[in] value \c true if local node is DC, or \c false otherwise + */ +void +based_set_local_node_dc(bool value) +{ + local_node_dc = value; +} + /*! * \internal * \brief Check whether local CIB manager is shutting down @@ -82,7 +107,7 @@ setup_stand_alone(GError **error) gid_t gid = 0; int rc = pcmk_rc_ok; - based_is_primary = true; + based_set_local_node_dc(true); rc = pcmk__daemon_user(&uid, &gid); if (rc != pcmk_rc_ok) { diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index ccee070b600..241852d0913 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -31,6 +31,9 @@ extern GMainLoop *mainloop; extern gchar *cib_root; extern int cib_status; +bool based_get_local_node_dc(void); +void based_set_local_node_dc(bool value); + bool based_shutting_down(void); bool based_stand_alone(void); From 8097bdb50b897c401723ed33723dd42f2e769d26 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 13:28:41 -0800 Subject: [PATCH 102/202] Refactor: based: Rename the_cib to based_cib Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 37 ++++++++++++++++--------------- daemons/based/based_io.c | 18 +++++++-------- daemons/based/based_messages.c | 16 ++++++------- daemons/based/based_messages.h | 2 -- daemons/based/based_remote.c | 4 ++-- daemons/based/based_transaction.c | 20 +++++++++-------- daemons/based/pacemaker-based.c | 15 +++++++++++++ daemons/based/pacemaker-based.h | 2 ++ 8 files changed, 65 insertions(+), 49 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b8ac5abb120..80db0f47529 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -265,7 +265,7 @@ process_ping_reply(const xmlNode *reply) } if (ping_digest == NULL) { - ping_digest = pcmk__digest_xml(the_cib, true); + ping_digest = pcmk__digest_xml(based_cib, true); } pcmk__trace("Processing ping reply %lld from %s (%s)", seq, host, digest); @@ -275,9 +275,10 @@ process_ping_reply(const xmlNode *reply) } pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), ping_digest, host, + pcmk__xe_get(based_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(based_cib, PCMK_XA_EPOCH), + pcmk__xe_get(based_cib, PCMK_XA_NUM_UPDATES), + ping_digest, host, pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH), pcmk__xe_get(remote_versions, PCMK_XA_EPOCH), pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES), digest); @@ -457,9 +458,9 @@ static int based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { - const char *feature_set = pcmk__xe_get(the_cib, + const char *feature_set = pcmk__xe_get(based_cib, PCMK_XA_CRM_FEATURE_SET); - xmlNode *result_cib = the_cib; + xmlNode *result_cib = based_cib; xmlNode *cib_diff = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); @@ -483,21 +484,21 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, * or notification that we will send. */ if (rc == pcmk_rc_schema_validation) { - pcmk__assert((result_cib != the_cib) && (*output == NULL)); + pcmk__assert((result_cib != based_cib) && (*output == NULL)); *output = result_cib; goto done; } // Discard result for failure or dry run if ((rc != pcmk_rc_ok) || pcmk__any_flags_set(call_options, cib_dryrun)) { - if (result_cib != the_cib) { + if (result_cib != based_cib) { pcmk__xml_free(result_cib); } goto done; } - if (result_cib != the_cib) { + if (result_cib != based_cib) { /* Always write to disk for successful ops with the writes-through flag * set. This also avoids the need to detect ordering changes. * @@ -585,13 +586,13 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, originator = pcmk__s(originator, "local"); client_name = pcmk__s(client_name, "client"); - /* @FIXME the_cib should always be non-NULL, but that's currently not the + /* @FIXME based_cib should always be non-NULL, but that's currently not the * case during shutdown */ - if (the_cib != NULL) { - pcmk__xe_get_int(the_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); - pcmk__xe_get_int(the_cib, PCMK_XA_EPOCH, &epoch); - pcmk__xe_get_int(the_cib, PCMK_XA_NUM_UPDATES, &num_updates); + if (based_cib != NULL) { + pcmk__xe_get_int(based_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); + pcmk__xe_get_int(based_cib, PCMK_XA_EPOCH, &epoch); + pcmk__xe_get_int(based_cib, PCMK_XA_NUM_UPDATES, &num_updates); } do_crm_log(level, @@ -743,7 +744,7 @@ based_process_request(xmlNode *request, bool privileged, "(please repair and restart): %s", pcmk_rc_str(rc)); if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, the_cib); + reply = create_cib_reply(request, rc, based_cib); } goto done; @@ -761,7 +762,7 @@ based_process_request(xmlNode *request, bool privileged, rc = EACCES; } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + rc = cib__perform_op_ro(op_function, request, &based_cib, &output); } else { rc = based_perform_op_rw(request, operation, op_function, &output); @@ -773,7 +774,7 @@ based_process_request(xmlNode *request, bool privileged, reply = create_cib_reply(request, rc, output); } - if ((output != NULL) && (output->doc != the_cib->doc)) { + if ((output != NULL) && (output->doc != based_cib->doc)) { pcmk__xml_free(output); } @@ -808,7 +809,7 @@ based_terminate(crm_exit_t exit_status) g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); - g_clear_pointer(&the_cib, pcmk__xml_free); + g_clear_pointer(&based_cib, pcmk__xml_free); // Exit immediately on error if (exit_status != CRM_EX_OK) { diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index f24365e1f26..000d7610607 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -125,10 +125,10 @@ write_cib_async(gpointer user_data) return -1; } - /* Write the CIB. Note that this modifies the_cib, but this child is about - * to exit. The parent's copy of the_cib won't be affected. + /* Write the CIB. Note that this modifies based_cib, but this child is about + * to exit. The parent's copy of based_cib won't be affected. */ - rc = cib_file_write_with_digest(the_cib, cib_root, "cib.xml"); + rc = cib_file_write_with_digest(based_cib, cib_root, "cib.xml"); rc = pcmk_legacy2rc(rc); pcmk_common_cleanup(); @@ -599,10 +599,10 @@ based_read_cib(void) * \internal * \brief Activate new CIB XML * - * This function frees the existing \c the_cib and points it to \p new_cib. + * This function frees the existing \c based_cib and points it to \p new_cib. * * \param[in] new_cib CIB XML to activate (must not be \c NULL or equal to - * \c the_cib) + * \c based_cib) * \param[in] to_disk If \c true and if the CIB status is OK and writes are * enabled, trigger the new CIB to be written to disk * \param[in] op Operation that triggered the activation (for logging @@ -611,15 +611,15 @@ based_read_cib(void) * \return Standard Pacemaker return code * * \note This function takes ownership of \p new_cib by assigning it to - * \c the_cib. The caller should not free it. + * \c based_cib. The caller should not free it. */ int based_activate_cib(xmlNode *new_cib, bool to_disk, const char *op) { - CRM_CHECK((new_cib != NULL) && (new_cib != the_cib), return ENODATA); + CRM_CHECK((new_cib != NULL) && (new_cib != based_cib), return ENODATA); - pcmk__xml_free(the_cib); - the_cib = new_cib; + pcmk__xml_free(based_cib); + based_cib = new_cib; if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) { pcmk__debug("Triggering CIB write for %s op", op); diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 467cecc9f84..a77344d92d1 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -29,8 +29,6 @@ #include "pacemaker-based.h" -xmlNode *the_cib = NULL; - /*! * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE @@ -95,7 +93,7 @@ int based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* existing_cib and *cib should be identical. In the absence of ACL - * filtering, they should also match the_cib. However, they may be copies + * filtering, they should also match based_cib. However, they may be copies * filtered based on the current CIB user's ACLs. In that case, our log * messages can use info from the full CIB, and the answer can include the * digest of the full CIB. But the answer should hide the version attributes @@ -103,7 +101,7 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) */ const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); - char *digest = pcmk__digest_xml(the_cib, true); + char *digest = pcmk__digest_xml(based_cib, true); xmlNode *shallow = NULL; *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); @@ -120,9 +118,9 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", host, digest, - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES)); + pcmk__xe_get(based_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(based_cib, PCMK_XA_EPOCH), + pcmk__xe_get(based_cib, PCMK_XA_NUM_UPDATES)); free(digest); @@ -368,10 +366,10 @@ sync_our_cib(const xmlNode *request, bool all) pcmk__xe_set_bool(replace_request, PCMK__XA_CIB_UPDATE, true); pcmk__xe_set(replace_request, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - digest = pcmk__digest_xml(the_cib, true); + digest = pcmk__digest_xml(based_cib, true); pcmk__xe_set(replace_request, PCMK_XA_DIGEST, digest); - cib__set_calldata(replace_request, the_cib); + cib__set_calldata(replace_request, based_cib); if (!all) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index c2563fdc3d6..f06873920a5 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -14,8 +14,6 @@ #include // xmlNode * -extern xmlNode *the_cib; - int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_commit_transact(xmlNode *req, xmlNode **cib, diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 0bd1d9fd01c..99a705fb59e 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -697,7 +697,7 @@ based_remote_init(void) int rc = pcmk_rc_ok; bool have_psk = false; - port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT); + port_s = pcmk__xe_get(based_cib, PCMK_XA_REMOTE_TLS_PORT); if ((pcmk__scan_port(port_s, &port) != pcmk_rc_ok) || (port <= 0)) { goto try_clear_port; @@ -751,7 +751,7 @@ based_remote_init(void) /* Regardless of whether or not we successfully enabled remote-tls-port, * we also want to try to enable remote-clear-port as well. */ - port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT); + port_s = pcmk__xe_get(based_cib, PCMK_XA_REMOTE_CLEAR_PORT); if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { pcmk__warn("Starting clear-text listener on port %d. This is insecure " diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index cc4754daae6..61f84d4b331 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -126,13 +126,15 @@ int based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, const char *origin, xmlNode **result_cib) { - xmlNode *saved_cib = the_cib; + xmlNode *saved_cib = based_cib; int rc = pcmk_rc_ok; char *source = NULL; - // *result_cib should be a copy of the_cib (created by cib__perform_op_rw()) + /* *result_cib should be a copy of based_cib (created by + * cib__perform_op_rw()) + */ pcmk__assert((result_cib != NULL) && (*result_cib != NULL) - && (*result_cib != the_cib)); + && (*result_cib != based_cib)); CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION), return pcmk_rc_no_transaction); @@ -141,23 +143,23 @@ based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, pcmk__trace("Committing transaction for %s to working CIB", source); // Apply all changes to a working copy of the CIB - the_cib = *result_cib; + based_cib = *result_cib; rc = process_transaction_requests(transaction, client, origin); pcmk__trace("Transaction commit %s for %s", ((rc == pcmk_rc_ok)? "succeeded" : "failed"), source); - /* Some request types (for example, erase) may have freed the_cib (the + /* Some request types (for example, erase) may have freed based_cib (the * working copy) and pointed it at a new XML object. In that case, it * follows that *result_cib (the working copy) was freed. * - * Point *result_cib at the updated working copy stored in the_cib. + * Point *result_cib at the updated working copy stored in based_cib. */ - *result_cib = the_cib; + *result_cib = based_cib; - // Point the_cib back to the unchanged original copy - the_cib = saved_cib; + // Point based_cib back to the unchanged original copy + based_cib = saved_cib; free(source); return rc; diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 5d54173d984..b98db539103 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -32,6 +32,21 @@ #define SUMMARY "daemon for managing the configuration of a Pacemaker cluster" +/* + * \internal + * \brief The CIB manager's global, in-memory copy of the current CIB + * + * This should reflect our most current, authoritative view of the cluster + * state. It may point to a tentative, "working" CIB copy while committing a + * transaction, but transactions are atomic. Either the transaction succeeds and + * we replace \c based_cib with the resulting CIB, or the transaction fails and + * we restore a saved version of the pre-transaction CIB. + * + * We write this in-memory CIB to disk during CIB manager startup and after a + * successful CIB operation that modifies the \c PCMK_XE_CONFIGURATION section. + */ +xmlNode *based_cib = NULL; + int cib_status = pcmk_rc_ok; GMainLoop *mainloop = NULL; diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 241852d0913..39cd9653da5 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -27,6 +27,8 @@ #define OUR_NODENAME \ (based_stand_alone()? "localhost" : based_cluster_node_name()) +extern xmlNode *based_cib; + extern GMainLoop *mainloop; extern gchar *cib_root; extern int cib_status; From 47ac3cb798fa1131fd5fc37a5e0ad6838dbf3895 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 15:55:42 -0800 Subject: [PATCH 103/202] Feature: libcrmcommon: Drop support for PCMK_ipc_type env variable There were four allowed values: shared-mem, socket, posix, and sysv. Libqb removed support for posix and sysv (message queues) in 2012 via commit 70a9623a. It simply returns an error if you try to set up those types of IPC. I spoke with the libqb maintainers last night, and they said that socket IPC has never worked correctly and that they are considering removing it. Sockets were mainly intended for systems where shared memory was not available. That is believed to be no longer applicable -- shared memory should be available on all the platforms that Pacemaker and libqb target for support. That leaves only shared memory as a working option. We are dropping this option rather than deprecating it. As discussed above, two of the values (posix and sysv) will fail immediately during IPC setup, and socket is very buggy. So user-facing behavior should not be affected. If the option is configured, it will simply be ignored. Signed-off-by: Reid Wahl --- etc/sysconfig/pacemaker.in | 14 -------------- include/crm/common/options_internal.h | 1 - lib/common/mainloop.c | 12 +----------- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/etc/sysconfig/pacemaker.in b/etc/sysconfig/pacemaker.in index a7763f6d180..fe65ee30576 100644 --- a/etc/sysconfig/pacemaker.in +++ b/etc/sysconfig/pacemaker.in @@ -337,20 +337,6 @@ # Default: PCMK_dh_max_bits="0" (no maximum) -## Inter-process Communication - -# PCMK_ipc_type (Advanced Use Only) -# -# Force use of a particular IPC method. Allowed values: -# -# shared-mem -# socket -# posix -# sysv -# -# Default: PCMK_ipc_type="shared-mem" - - ## Cluster type # PCMK_cluster_type (Advanced Use Only) diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h index 22b8e303a21..b1b2085db7f 100644 --- a/include/crm/common/options_internal.h +++ b/include/crm/common/options_internal.h @@ -152,7 +152,6 @@ bool pcmk__valid_fencing_watchdog_timeout(const char *value); #define PCMK__ENV_CRL_FILE "crl_file" #define PCMK__ENV_DEBUG "debug" #define PCMK__ENV_FAIL_FAST "fail_fast" -#define PCMK__ENV_IPC_TYPE "ipc_type" #define PCMK__ENV_KEY_FILE "key_file" #define PCMK__ENV_LOGFACILITY "logfacility" #define PCMK__ENV_LOGFILE "logfile" diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 3293e1b66de..8c637d73d2b 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -595,17 +595,7 @@ struct qb_ipcs_poll_handlers gio_poll_funcs = { static enum qb_ipc_type pick_ipc_type(enum qb_ipc_type requested) { - const char *env = pcmk__env_option(PCMK__ENV_IPC_TYPE); - - if (env && strcmp("shared-mem", env) == 0) { - return QB_IPC_SHM; - } else if (env && strcmp("socket", env) == 0) { - return QB_IPC_SOCKET; - } else if (env && strcmp("posix", env) == 0) { - return QB_IPC_POSIX_MQ; - } else if (env && strcmp("sysv", env) == 0) { - return QB_IPC_SYSV_MQ; - } else if (requested == QB_IPC_NATIVE) { + if (requested == QB_IPC_NATIVE) { /* We prefer shared memory because the server never blocks on * send. If part of a message fits into the socket, libqb * needs to block until the remainder can be sent also. From a7e264c795533d559575faf9efa041a641b31575 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 16:06:02 -0800 Subject: [PATCH 104/202] Refactor: libcrmcommon: Use QB_IPC_SHM in pcmk__serve_DAEMON_ipc() QB_IPC_NATIVE tells libqb to use shared memory (QB_IPC_SHM) unless it's shared-memory IPC is specifically disabled at build time because it's unavailable on the system. In that case, libqb will use sockets (QB_IPC_SOCKET). I spoke with the libqb maintainers last night, and they said that socket IPC has never worked correctly and that they are considering removing it. They told me never to use libqb socket IPC. Libqb socket IPC was mainly intended for systems where shared memory was not available. That is believed to be no longer applicable -- shared memory should be available on all the platforms that Pacemaker and libqb target for support. The maintainers weakly suggested using QB_IPC_NATIVE (rather than QB_IPC_SHM). I suppose this was for future-proofing ("let libqb decide what to use"). Nonetheless, since we have a couple of use cases where we MUST avoid blocking, and QB_IPC_SHM is non-blocking, we'll just use QB_IPC_SHM everywhere until we have a reason not to. Besides, this makes things explicit. Signed-off-by: Reid Wahl --- lib/common/ipc_server.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index 76e16dc6f77..031289a4441 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1033,14 +1033,14 @@ void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb) { - *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO, - QB_IPC_NATIVE, ro_cb); + *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO, QB_IPC_SHM, + ro_cb); - *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW, - QB_IPC_NATIVE, rw_cb); + *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW, QB_IPC_SHM, + rw_cb); - *ipcs_shm = mainloop_add_ipc_server(PCMK__SERVER_BASED_SHM, - QB_IPC_SHM, rw_cb); + *ipcs_shm = mainloop_add_ipc_server(PCMK__SERVER_BASED_SHM, QB_IPC_SHM, + rw_cb); if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", @@ -1083,7 +1083,7 @@ pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t * pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb) { - return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb); + return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_SHM, cb); } /*! @@ -1100,7 +1100,7 @@ pcmk__serve_attrd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_attrd), - QB_IPC_NATIVE, cb); + QB_IPC_SHM, cb); if (*ipcs == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", @@ -1148,7 +1148,7 @@ pcmk__serve_fenced_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server_with_prio(pcmk__server_ipc_name(pcmk_ipc_fenced), - QB_IPC_NATIVE, cb, QB_LOOP_HIGH); + QB_IPC_SHM, cb, QB_LOOP_HIGH); if (*ipcs == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", @@ -1173,7 +1173,7 @@ pcmk__serve_pacemakerd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_pacemakerd), - QB_IPC_NATIVE, cb); + QB_IPC_SHM, cb); if (*ipcs == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", @@ -1204,7 +1204,7 @@ pcmk__serve_schedulerd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_schedulerd), - QB_IPC_NATIVE, cb); + QB_IPC_SHM, cb); if (*ipcs == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", From f8dca5914bcfc552905c8673c032b02e55b58e83 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 16:12:41 -0800 Subject: [PATCH 105/202] Feature: libcrmcommon: mainloop_add_ipc_server{,_with_prio} ignore type ...argument. QB_IPC_NATIVE tells libqb to use shared memory (QB_IPC_SHM) unless shared-memory IPC is specifically disabled at build time because it's unavailable on the system. In that case, libqb will use sockets (QB_IPC_SOCKET). I spoke with the libqb maintainers last night, and they said that socket IPC has never worked correctly and that they are considering removing it. They told me never to use libqb socket IPC. Libqb socket IPC was mainly intended for systems where shared memory was not available. That is believed to be no longer applicable -- shared memory should be available on all the platforms that Pacemaker and libqb target for support. The maintainers weakly suggested using QB_IPC_NATIVE (rather than QB_IPC_SHM). I suppose this was for future-proofing ("let libqb decide what to use"). Nonetheless, since we have a couple of use cases where we MUST avoid blocking, and QB_IPC_SHM is non-blocking, we'll just use QB_IPC_SHM everywhere until we have a reason not to. Signed-off-by: Reid Wahl --- include/crm/common/mainloop.h | 4 ++-- lib/common/mainloop.c | 17 +---------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h index e10e0625976..f261f73f29f 100644 --- a/include/crm/common/mainloop.h +++ b/include/crm/common/mainloop.h @@ -1,5 +1,5 @@ /* - * Copyright 2009-2025 the Pacemaker project contributors + * Copyright 2009-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -114,7 +114,7 @@ qb_ipcs_service_t *mainloop_add_ipc_server(const char *name, enum qb_ipc_type ty * \brief Start server-side API end-point, hooked into the internal event loop * * \param[in] name name of the IPC end-point ("address" for the client) - * \param[in] type selects libqb's IPC back-end (or use #QB_IPC_NATIVE) + * \param[in] type Ignored * \param[in] callbacks defines libqb's IPC service-level handlers * \param[in] priority priority relative to other events handled in the * abstract handling loop, use #QB_LOOP_MED when unsure diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 8c637d73d2b..adf8f7d2355 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -592,21 +592,6 @@ struct qb_ipcs_poll_handlers gio_poll_funcs = { .dispatch_del = gio_poll_dispatch_del, }; -static enum qb_ipc_type -pick_ipc_type(enum qb_ipc_type requested) -{ - if (requested == QB_IPC_NATIVE) { - /* We prefer shared memory because the server never blocks on - * send. If part of a message fits into the socket, libqb - * needs to block until the remainder can be sent also. - * Otherwise the client will wait forever for the remaining - * bytes. - */ - return QB_IPC_SHM; - } - return requested; -} - qb_ipcs_service_t * mainloop_add_ipc_server(const char *name, enum qb_ipc_type type, struct qb_ipcs_service_handlers *callbacks) @@ -626,7 +611,7 @@ mainloop_add_ipc_server_with_prio(const char *name, enum qb_ipc_type type, gio_map = qb_array_create_2(64, sizeof(struct gio_to_qb_poll), 1); } - server = qb_ipcs_create(name, 0, pick_ipc_type(type), callbacks); + server = qb_ipcs_create(name, 0, QB_IPC_NATIVE, callbacks); if (server == NULL) { pcmk__err("Could not create %s IPC server: %s (%d)", name, From 0531474b6da40e0e1ac89790562f62cf80629a3f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 17:37:56 -0800 Subject: [PATCH 106/202] API: libcib: Deprecate cib_command_nonblocking It behaves identically to cib_command. This became even more clear with the recent "Use QB_IPC_SHM in pcmk__serve_DAEMON_ipc()" commit. Signed-off-by: Reid Wahl --- daemons/controld/controld_cib.c | 6 ++---- include/crm/cib/cib_types.h | 2 ++ lib/cib/cib_native.c | 25 +++++++++++++------------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/daemons/controld/controld_cib.c b/daemons/controld/controld_cib.c index 017bdab4516..632198ab1fb 100644 --- a/daemons/controld/controld_cib.c +++ b/daemons/controld/controld_cib.c @@ -155,13 +155,11 @@ do_cib_control(long long action, enum crmd_fsa_cause cause, return; } - rc = cib_conn->cmds->signon(cib_conn, crm_system_name, - cib_command_nonblocking); + rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); if (rc != pcmk_ok) { // A short wait that usually avoids stalling the FSA sleep(1); - rc = cib_conn->cmds->signon(cib_conn, crm_system_name, - cib_command_nonblocking); + rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); } if (rc != pcmk_ok) { diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 159725b0e3a..7f91210ac5c 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -53,6 +53,8 @@ enum cib_conn_type { cib_query, cib_no_connection, + + //! \deprecated Use \c cib_command instead cib_command_nonblocking, }; diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 8c19cdda340..a8bf366ca4f 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -298,20 +298,21 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) cib->call_timeout = PCMK__IPC_TIMEOUT; - if (type == cib_command) { - cib->state = cib_connected_command; - channel = PCMK__SERVER_BASED_RW; - - } else if (type == cib_command_nonblocking) { - cib->state = cib_connected_command; - channel = PCMK__SERVER_BASED_SHM; + switch (type) { + case cib_command: + case cib_command_nonblocking: + // @COMPAT cib_command_nonblocking is deprecated since 3.0.2 + cib->state = cib_connected_command; + channel = PCMK__SERVER_BASED_RW; + break; - } else if (type == cib_query) { - cib->state = cib_connected_query; - channel = PCMK__SERVER_BASED_RO; + case cib_query: + cib->state = cib_connected_query; + channel = PCMK__SERVER_BASED_RO; + break; - } else { - return -ENOTCONN; + default: + return -ENOTCONN; } pcmk__trace("Connecting %s channel", channel); From 92119fcf30270853f0839602f4b03f6b1a9d4a49 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 17:52:06 -0800 Subject: [PATCH 107/202] Refactor: various: Drop PCMK__SERVER_BASED_SHM And associated code. It behaves identically to PCMK__SERVER_BASED_RW. A key point to note is that only non-proxied cib_native clients connected to PCMK__SERVER_BASED_SHM (prior to deprecation of cib_command_nonblocking in a previous commit). cib_remote clients and proxied connections used only PCMK__SERVER_BASED_RO and PCMK__SERVER_BASED_RW. So backward compatibility should not be a concern -- this affects only local clients on a cluster node. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 6 +----- daemons/execd/remoted_proxy.c | 6 ++---- include/crm/common/ipc_internal.h | 4 +--- include/crm_internal.h | 3 +-- lib/common/ipc_server.c | 27 ++++++++------------------- lib/common/servers.c | 23 +++++++++++------------ 6 files changed, 24 insertions(+), 45 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 9f1af42b97b..9e32dfdbd5f 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -30,7 +30,6 @@ static qb_ipcs_service_t *ipcs_ro = NULL; static qb_ipcs_service_t *ipcs_rw = NULL; -static qb_ipcs_service_t *ipcs_shm = NULL; /*! * \internal @@ -305,7 +304,7 @@ static struct qb_ipcs_service_handlers ipc_rw_callbacks = { void based_ipc_init(void) { - pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, + pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipc_ro_callbacks, &ipc_rw_callbacks); } @@ -322,9 +321,6 @@ based_ipc_cleanup(void) pcmk__drop_all_clients(ipcs_rw); g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - pcmk__drop_all_clients(ipcs_shm); - g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); - /* Drop remote clients here because they're part of the IPC client table and * must be dropped before \c pcmk__client_cleanup() */ diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c index 6bcd0fa3940..460fc950fd4 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -30,7 +30,6 @@ static qb_ipcs_service_t *cib_ro = NULL; static qb_ipcs_service_t *cib_rw = NULL; -static qb_ipcs_service_t *cib_shm = NULL; static qb_ipcs_service_t *attrd_ipcs = NULL; static qb_ipcs_service_t *crmd_ipcs = NULL; @@ -519,7 +518,7 @@ ipc_proxy_init(void) { ipc_clients = pcmk__strkey_table(NULL, NULL); - pcmk__serve_based_ipc(&cib_ro, &cib_rw, &cib_shm, &cib_proxy_callbacks_ro, + pcmk__serve_based_ipc(&cib_ro, &cib_rw, &cib_proxy_callbacks_ro, &cib_proxy_callbacks_rw); pcmk__serve_attrd_ipc(&attrd_ipcs, &attrd_proxy_callbacks); pcmk__serve_fenced_ipc(&fencer_ipcs, &fencer_proxy_callbacks); @@ -540,7 +539,7 @@ ipc_proxy_cleanup(void) g_clear_pointer(&ipc_providers, g_list_free); g_clear_pointer(&ipc_clients, g_hash_table_destroy); - pcmk__stop_based_ipc(cib_ro, cib_rw, cib_shm); + pcmk__stop_based_ipc(cib_ro, cib_rw); g_clear_pointer(&attrd_ipcs, qb_ipcs_destroy); g_clear_pointer(&fencer_ipcs, qb_ipcs_destroy); @@ -549,5 +548,4 @@ ipc_proxy_cleanup(void) cib_ro = NULL; cib_rw = NULL; - cib_shm = NULL; } diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h index 76a7b7f40b4..1133c6c7e0d 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -251,13 +251,11 @@ qb_ipcs_service_t *pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb) void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, - qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb); void pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, - qb_ipcs_service_t *ipcs_rw, - qb_ipcs_service_t *ipcs_shm); + qb_ipcs_service_t *ipcs_rw); static inline const char * pcmk__ipc_sys_name(const char *ipc_name, const char *fallback) diff --git a/include/crm_internal.h b/include/crm_internal.h index 277fe824830..407737bb7d9 100644 --- a/include/crm_internal.h +++ b/include/crm_internal.h @@ -1,5 +1,5 @@ /* - * Copyright 2006-2025 the Pacemaker project contributors + * Copyright 2006-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -61,7 +61,6 @@ extern "C" { #define PCMK__SERVER_BASED_RO "cib_ro" #define PCMK__SERVER_BASED_RW "cib_rw" -#define PCMK__SERVER_BASED_SHM "cib_shm" /* * IPC commands that can be sent to Pacemaker daemons diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index 031289a4441..794eedc8318 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1019,19 +1019,15 @@ pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c, * * \param[out] ipcs_ro New IPC server for read-only CIB manager API * \param[out] ipcs_rw New IPC server for read/write CIB manager API - * \param[out] ipcs_shm New IPC server for shared-memory CIB manager API * \param[in] ro_cb IPC callbacks for read-only API * \param[in] rw_cb IPC callbacks for read/write and shared-memory APIs * * \note This function exits fatally on error. - * \note There is no actual difference between the three IPC endpoints other - * than their names. */ -void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, - qb_ipcs_service_t **ipcs_rw, - qb_ipcs_service_t **ipcs_shm, - struct qb_ipcs_service_handlers *ro_cb, - struct qb_ipcs_service_handlers *rw_cb) +void +pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, + struct qb_ipcs_service_handlers *ro_cb, + struct qb_ipcs_service_handlers *rw_cb) { *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO, QB_IPC_SHM, ro_cb); @@ -1039,10 +1035,7 @@ void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW, QB_IPC_SHM, rw_cb); - *ipcs_shm = mainloop_add_ipc_server(PCMK__SERVER_BASED_SHM, QB_IPC_SHM, - rw_cb); - - if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) { + if ((*ipcs_ro == NULL) || (*ipcs_rw == NULL)) { pcmk__crit("Failed to create %s IPC server; shutting down", pcmk__server_log_name(pcmk_ipc_based)); pcmk__crit("Verify pacemaker and pacemaker_remote are not both " @@ -1055,21 +1048,17 @@ void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, * \internal * \brief Destroy IPC servers for the CIB manager API * - * \param[out] ipcs_ro IPC server for read-only the CIB manager API - * \param[out] ipcs_rw IPC server for read/write the CIB manager API - * \param[out] ipcs_shm IPC server for shared-memory the CIB manager API + * \param[in,out] ipcs_ro IPC server for read-only the CIB manager API + * \param[in,out] ipcs_rw IPC server for read/write the CIB manager API * * \note This is a convenience function for calling qb_ipcs_destroy() for each * argument. */ void -pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, - qb_ipcs_service_t *ipcs_rw, - qb_ipcs_service_t *ipcs_shm) +pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw) { qb_ipcs_destroy(ipcs_ro); qb_ipcs_destroy(ipcs_rw); - qb_ipcs_destroy(ipcs_shm); } /*! diff --git a/lib/common/servers.c b/lib/common/servers.c index f2e6d79ef50..6a4fdc7405b 100644 --- a/lib/common/servers.c +++ b/lib/common/servers.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 the Pacemaker project contributors + * Copyright 2024-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -33,63 +33,62 @@ static struct { const char *log_name; // Readable server name for use in logs const char *system_names[2]; // crm_system_name values (subdaemon names) - const char *ipc_names[3]; // libqb IPC names used to contact server + const char *ipc_names[2]; // libqb IPC names used to contact server const char *message_types[3]; // IPC/cluster message types sent to server } server_info[] = { [pcmk_ipc_unknown] = { NULL, { NULL, NULL, }, - { NULL, NULL, NULL, }, + { NULL, NULL, }, { NULL, NULL, NULL, }, }, [pcmk_ipc_attrd] = { "attribute manager", { PCMK__SERVER_ATTRD, NULL, }, - { PCMK__VALUE_ATTRD, NULL, NULL, }, + { PCMK__VALUE_ATTRD, NULL, }, { PCMK__VALUE_ATTRD, NULL, NULL, }, }, [pcmk_ipc_based] = { "CIB manager", { PCMK__SERVER_BASED, NULL, }, - { PCMK__SERVER_BASED_RW, PCMK__SERVER_BASED_RO, - PCMK__SERVER_BASED_SHM, }, + { PCMK__SERVER_BASED_RW, PCMK__SERVER_BASED_RO, }, { CRM_SYSTEM_CIB, NULL, NULL, }, }, [pcmk_ipc_controld] = { "controller", { PCMK__SERVER_CONTROLD, NULL, }, - { PCMK__VALUE_CRMD, NULL, NULL, }, + { PCMK__VALUE_CRMD, NULL, }, { PCMK__VALUE_CRMD, CRM_SYSTEM_DC, CRM_SYSTEM_TENGINE, }, }, [pcmk_ipc_execd] = { "executor", { PCMK__SERVER_EXECD, PCMK__SERVER_REMOTED, }, - { PCMK__VALUE_LRMD, NULL, NULL, }, + { PCMK__VALUE_LRMD, NULL, }, { PCMK__VALUE_LRMD, NULL, NULL, }, }, [pcmk_ipc_fenced] = { "fencer", { PCMK__SERVER_FENCED, NULL, }, - { PCMK__VALUE_STONITH_NG, NULL, NULL, }, + { PCMK__VALUE_STONITH_NG, NULL, }, { PCMK__VALUE_STONITH_NG, NULL, NULL, }, }, [pcmk_ipc_pacemakerd] = { "launcher", { PCMK__SERVER_PACEMAKERD, NULL, }, - { CRM_SYSTEM_MCP, NULL, NULL, }, + { CRM_SYSTEM_MCP, NULL, }, { CRM_SYSTEM_MCP, NULL, NULL, }, }, [pcmk_ipc_schedulerd] = { "scheduler", { PCMK__SERVER_SCHEDULERD, NULL, }, - { CRM_SYSTEM_PENGINE, NULL, NULL, }, + { CRM_SYSTEM_PENGINE, NULL, }, { CRM_SYSTEM_PENGINE, NULL, NULL, }, }, }; @@ -193,7 +192,7 @@ pcmk__parse_server(const char *text) } } for (name = 0; - (name < 3) && (server_info[server].ipc_names[name] != NULL); + (name < 2) && (server_info[server].ipc_names[name] != NULL); ++name) { if (strcmp(text, server_info[server].ipc_names[name]) == 0) { return server; From de2f548fc2df3a14e4e68472f919434912db28a1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 17:56:13 -0800 Subject: [PATCH 108/202] Refactor: libcrmcommon: Drop pcmk__stop_based_ipc() It has one caller and saves one line at most. Signed-off-by: Reid Wahl --- daemons/execd/remoted_proxy.c | 9 +++------ include/crm/common/ipc_internal.h | 3 --- lib/common/ipc_server.c | 17 ----------------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c index 460fc950fd4..0f74d705401 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -539,13 +539,10 @@ ipc_proxy_cleanup(void) g_clear_pointer(&ipc_providers, g_list_free); g_clear_pointer(&ipc_clients, g_hash_table_destroy); - pcmk__stop_based_ipc(cib_ro, cib_rw); - g_clear_pointer(&attrd_ipcs, qb_ipcs_destroy); + g_clear_pointer(&cib_ro, qb_ipcs_destroy); + g_clear_pointer(&cib_rw, qb_ipcs_destroy); + g_clear_pointer(&crmd_ipcs, qb_ipcs_destroy); g_clear_pointer(&fencer_ipcs, qb_ipcs_destroy); g_clear_pointer(&pacemakerd_ipcs, qb_ipcs_destroy); - g_clear_pointer(&crmd_ipcs, qb_ipcs_destroy); - - cib_ro = NULL; - cib_rw = NULL; } diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h index 1133c6c7e0d..db1bec65fe5 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -254,9 +254,6 @@ void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb); -void pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, - qb_ipcs_service_t *ipcs_rw); - static inline const char * pcmk__ipc_sys_name(const char *ipc_name, const char *fallback) { diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index 794eedc8318..2e213dc4d73 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1044,23 +1044,6 @@ pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, } } -/*! - * \internal - * \brief Destroy IPC servers for the CIB manager API - * - * \param[in,out] ipcs_ro IPC server for read-only the CIB manager API - * \param[in,out] ipcs_rw IPC server for read/write the CIB manager API - * - * \note This is a convenience function for calling qb_ipcs_destroy() for each - * argument. - */ -void -pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw) -{ - qb_ipcs_destroy(ipcs_ro); - qb_ipcs_destroy(ipcs_rw); -} - /*! * \internal * \brief Add an IPC server to the main loop for the controller API From f3cd40d62604fdba8f7842b24ab02588c9c9ebdf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 18:00:39 -0800 Subject: [PATCH 109/202] Refactor: libcrmcommon: Assert for arguments in pcmk__serve_DAEMON_ipc() Signed-off-by: Reid Wahl --- lib/common/ipc_server.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index 2e213dc4d73..b0d13eb85d9 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1029,6 +1029,9 @@ pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb) { + pcmk__assert((ipcs_ro != NULL) && (*ipcs_ro == NULL) && (ro_cb != NULL) + && (ipcs_rw != NULL) && (*ipcs_rw == NULL) && (rw_cb != NULL)); + *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO, QB_IPC_SHM, ro_cb); @@ -1055,6 +1058,8 @@ pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t * pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb) { + pcmk__assert(cb != NULL); + return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_SHM, cb); } @@ -1071,6 +1076,8 @@ void pcmk__serve_attrd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); + *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_attrd), QB_IPC_SHM, cb); @@ -1119,6 +1126,8 @@ void pcmk__serve_fenced_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); + *ipcs = mainloop_add_ipc_server_with_prio(pcmk__server_ipc_name(pcmk_ipc_fenced), QB_IPC_SHM, cb, QB_LOOP_HIGH); @@ -1144,6 +1153,8 @@ void pcmk__serve_pacemakerd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); + *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_pacemakerd), QB_IPC_SHM, cb); @@ -1175,6 +1186,8 @@ void pcmk__serve_schedulerd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); + *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_schedulerd), QB_IPC_SHM, cb); From d41ae02abeedc2ddd0f5d07096d47d133df4fcdd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 16:50:32 -0800 Subject: [PATCH 110/202] Refactor: based: New based_{callbacks,io}_cleanup() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 143 ++++++++++++++++++-------------- daemons/based/based_callbacks.h | 3 + daemons/based/based_io.c | 14 +++- daemons/based/based_io.h | 2 + lib/common/mainloop.c | 2 + 5 files changed, 100 insertions(+), 64 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 80db0f47529..cca92a727c1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -39,6 +39,85 @@ static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; +/*! + * \internal + * \brief Request CIB digests from all peer nodes + * + * This is used as a callback that runs 5 seconds after we modify the CIB on the + * DC. It sends a ping request to all cluster nodes. They will respond by + * sending their current digests and version info, which we will validate in + * process_ping_reply(). If their digest doesn't match, we'll sync our own CIB + * to them. This helps ensure consistency across the cluster after a CIB update. + * + * \param[in] data Ignored + * + * \return \c G_SOURCE_REMOVE (to destroy the timeout) + * + * \note It's not clear why we wait 5 seconds rather than sending the ping + * request immediately after a performing a modifying op. Perhaps it's to + * avoid overwhelming other nodes with ping requests when there are a lot + * of modifying requests in a short period. The timer restarts after + * every successful modifying op, so we send ping requests **at most** + * every 5 seconds. Or perhaps it's a remnant of legacy mode (pre-1.1.12). + * In any case, the other nodes shouldn't need time to process the + * modifying op before responding to the ping request. The ping request is + * sent after the op is sent, so it should also be received after the op + * is received. + */ +static gboolean +digest_timer_cb(gpointer data) +{ + xmlNode *ping = NULL; + + if (!based_get_local_node_dc()) { + // Only the DC sends a ping + return G_SOURCE_REMOVE; + } + + if (++ping_seq < 0) { + ping_seq = 0; + } + + g_clear_pointer(&ping_digest, free); + ping_modified_since = false; + + ping = pcmk__xe_create(NULL, PCMK__XE_PING); + pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); + pcmk__xe_set_ll(ping, PCMK__XA_CIB_PING_ID, ping_seq); + pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); + + pcmk__trace("Requesting peer digests (%lld)", ping_seq); + pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); + + pcmk__xml_free(ping); + return G_SOURCE_REMOVE; +} + +/*! + * \internal + * \brief Initialize data structures used for CIB manager callbacks + */ +void +based_callbacks_init(void) +{ + if (digest_timer == NULL) { + digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, + digest_timer_cb, NULL); + } +} + +/*! + * \internal + * \brief Free data structures used for CIB manager callbacks + */ +void +based_callbacks_cleanup(void) +{ + g_clear_pointer(&digest_timer, mainloop_timer_del); + g_clear_pointer(&ping_digest, free); +} + /*! * \internal * \brief Create reply XML for a CIB request @@ -148,61 +227,6 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, } } -/*! - * \internal - * \brief Request CIB digests from all peer nodes - * - * This is used as a callback that runs 5 seconds after we modify the CIB on the - * DC. It sends a ping request to all cluster nodes. They will respond by - * sending their current digests and version info, which we will validate in - * process_ping_reply(). If their digest doesn't match, we'll sync our own CIB - * to them. This helps ensure consistency across the cluster after a CIB update. - * - * \param[in] data Ignored - * - * \return \c G_SOURCE_REMOVE (to destroy the timeout) - * - * \note It's not clear why we wait 5 seconds rather than sending the ping - * request immediately after a performing a modifying op. Perhaps it's to - * avoid overwhelming other nodes with ping requests when there are a lot - * of modifying requests in a short period. The timer restarts after - * every successful modifying op, so we send ping requests **at most** - * every 5 seconds. Or perhaps it's a remnant of legacy mode (pre-1.1.12). - * In any case, the other nodes shouldn't need time to process the - * modifying op before responding to the ping request. The ping request is - * sent after the op is sent, so it should also be received after the op - * is received. - */ -static gboolean -digest_timer_cb(gpointer data) -{ - xmlNode *ping = NULL; - - if (!based_get_local_node_dc()) { - // Only the DC sends a ping - return G_SOURCE_REMOVE; - } - - if (++ping_seq < 0) { - ping_seq = 0; - } - - g_clear_pointer(&ping_digest, free); - ping_modified_since = false; - - ping = pcmk__xe_create(NULL, PCMK__XE_PING); - pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); - pcmk__xe_set_ll(ping, PCMK__XA_CIB_PING_ID, ping_seq); - pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - - pcmk__trace("Requesting peer digests (%lld)", ping_seq); - pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); - - pcmk__xml_free(ping); - return G_SOURCE_REMOVE; -} - /*! * \internal * \brief Process a reply to a \c CRM_OP_PING request @@ -532,11 +556,6 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; } - if (digest_timer == NULL) { - digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, - digest_timer_cb, NULL); - } - mainloop_timer_start(digest_timer); done: @@ -804,11 +823,11 @@ based_process_request(xmlNode *request, bool privileged, void based_terminate(crm_exit_t exit_status) { + based_callbacks_cleanup(); + based_io_cleanup(); based_ipc_cleanup(); based_remote_cleanup(); - g_clear_pointer(&digest_timer, mainloop_timer_del); - g_clear_pointer(&ping_digest, free); g_clear_pointer(&based_cib, pcmk__xml_free); // Exit immediately on error diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 3e71e2f2984..2a1387fb275 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -17,6 +17,9 @@ #include // pcmk__client_t #include // crm_exit_t +void based_callbacks_init(void); +void based_callbacks_cleanup(void); + int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_terminate(crm_exit_t exit_status); diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 000d7610607..abb811895fb 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -31,7 +31,7 @@ #include // createEmptyCib #include // pcmk__assert_asprintf, PCMK__XE_*, etc. #include // CRM_CHECK -#include // mainloop_add_signal +#include // mainloop_* #include // pcmk_legacy2rc, pcmk_rc_* #include // pcmk_common_cleanup #include // PCMK_XA_*, PCMK_XE_* @@ -176,7 +176,7 @@ based_enable_writes(int nsig) /*! * \internal - * \brief Initialize data structures for \c pacemaker-based I/O + * \brief Initialize data structures used for CIB manager I/O */ void based_io_init(void) @@ -198,6 +198,16 @@ based_io_init(void) write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL); } +/*! + * \internal + * \brief Free data structures used for CIB manager I/O + */ +void +based_io_cleanup(void) +{ + g_clear_pointer(&write_trigger, mainloop_destroy_trigger); +} + /*! * \internal * \brief Rename a CIB or digest file after digest mismatch diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h index f9bb5011a1a..706d476d026 100644 --- a/daemons/based/based_io.h +++ b/daemons/based/based_io.h @@ -15,6 +15,8 @@ #include // xmlNode void based_io_init(void); +void based_io_cleanup(void); + void based_enable_writes(int nsig); xmlNode *based_read_cib(void); int based_activate_cib(xmlNode *new_cib, bool to_disk, const char *op); diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index adf8f7d2355..fa83dc961ac 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -335,6 +335,8 @@ mainloop_destroy_signal_entry(int sig) * \note The true signal handler merely sets a mainloop trigger to call this * dispatch function via the mainloop. Therefore, the dispatch function * does not need to be async-safe. + * \note The added signal handler gets freed by \c mainloop_cleanup() if it is + * not freed manually using \c mainloop_destroy_signal(). */ gboolean mainloop_add_signal(int sig, void (*dispatch) (int sig)) From 1ebc4ca38cb3bbdeaf51add1647245592a3b3c36 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 17:01:03 -0800 Subject: [PATCH 111/202] Refactor: based: Move based_terminate() to pacemaker-based.c And make the mainloop variable static. No other code changes. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 34 +----------------------------- daemons/based/based_callbacks.h | 2 -- daemons/based/pacemaker-based.c | 37 ++++++++++++++++++++++++++++++--- daemons/based/pacemaker-based.h | 8 ++++--- 4 files changed, 40 insertions(+), 41 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cca92a727c1..92689ad6be9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -28,7 +28,7 @@ #include // crm_ipc_*, pcmk_ipc_* #include // CRM_LOG_ASSERT, CRM_CHECK #include // mainloop_* -#include // CRM_EX_OK, crm_exit_t, pcmk_rc_* +#include // pcmk_rc_* #include // PCMK_XA_*, PCMK_XE_* #include // CRM_OP_* @@ -813,35 +813,3 @@ based_process_request(xmlNode *request, bool privileged, pcmk__xml_free(reply); return rc; } - -/*! - * \internal - * \brief Close remote sockets, free the global CIB and quit - * - * \param[in] exit_status Exit code - */ -void -based_terminate(crm_exit_t exit_status) -{ - based_callbacks_cleanup(); - based_io_cleanup(); - based_ipc_cleanup(); - based_remote_cleanup(); - - g_clear_pointer(&based_cib, pcmk__xml_free); - - // Exit immediately on error - if (exit_status != CRM_EX_OK) { - crm_exit(exit_status); - return; - } - - based_cluster_disconnect(); - - if ((mainloop != NULL) && g_main_loop_is_running(mainloop)) { - g_main_loop_quit(mainloop); - return; - } - - crm_exit(CRM_EX_OK); -} diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 2a1387fb275..7363e7ab2d6 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -15,13 +15,11 @@ #include // xmlNode #include // pcmk__client_t -#include // crm_exit_t void based_callbacks_init(void); void based_callbacks_cleanup(void); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); -void based_terminate(crm_exit_t exit_status); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index b98db539103..a88d308b0c9 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -26,7 +26,7 @@ #include // crm_ipc_* #include // crm_log_* #include // mainloop_add_signal -#include // CRM_EX_*, pcmk_rc_* +#include // CRM_EX_*, crm_exit_t, pcmk_rc_* #include "pacemaker-based.h" @@ -48,14 +48,13 @@ xmlNode *based_cib = NULL; int cib_status = pcmk_rc_ok; - -GMainLoop *mainloop = NULL; gchar *cib_root = NULL; static bool local_node_dc = false; static bool shutting_down = false; static gboolean stand_alone = FALSE; static crm_exit_t exit_code = CRM_EX_OK; +static GMainLoop *mainloop = NULL; /*! * \internal @@ -221,6 +220,38 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } +/*! + * \internal + * \brief Close remote sockets, free the global CIB and quit + * + * \param[in] exit_status Exit code + */ +void +based_terminate(crm_exit_t exit_status) +{ + based_callbacks_cleanup(); + based_io_cleanup(); + based_ipc_cleanup(); + based_remote_cleanup(); + + g_clear_pointer(&based_cib, pcmk__xml_free); + + // Exit immediately on error + if (exit_status != CRM_EX_OK) { + crm_exit(exit_status); + return; + } + + based_cluster_disconnect(); + + if ((mainloop != NULL) && g_main_loop_is_running(mainloop)) { + g_main_loop_quit(mainloop); + return; + } + + crm_exit(CRM_EX_OK); +} + static void based_shutdown(int nsig) { diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 39cd9653da5..6a4a20238b7 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -12,7 +12,9 @@ #include -#include // gchar, GMainLoop +#include // gchar + +#include // crm_exit_t #include "based_callbacks.h" #include "based_corosync.h" @@ -28,8 +30,6 @@ (based_stand_alone()? "localhost" : based_cluster_node_name()) extern xmlNode *based_cib; - -extern GMainLoop *mainloop; extern gchar *cib_root; extern int cib_status; @@ -39,4 +39,6 @@ void based_set_local_node_dc(bool value); bool based_shutting_down(void); bool based_stand_alone(void); +void based_terminate(crm_exit_t exit_status); + #endif // PACEMAKER_BASED__H From 067ad76256c973371fa12e1824bc7c15c7206398 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 17:57:28 -0800 Subject: [PATCH 112/202] Refactor: based: New based_cleanup() There is still more work to do to consolidate/reorganize the based exit/shutdown-related code. I'm keeping the pieces small to try to make it easier to reason about. based_terminate() seems too complicated. Note that based_ipc_cleanup() calls pcmk__client_cleanup(), which is why we drop the call from main. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index a88d308b0c9..202cff4aad2 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -222,12 +222,10 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) /*! * \internal - * \brief Close remote sockets, free the global CIB and quit - * - * \param[in] exit_status Exit code + * \brief Clean up CIB manager data structures */ -void -based_terminate(crm_exit_t exit_status) +static void +based_cleanup(void) { based_callbacks_cleanup(); based_io_cleanup(); @@ -235,6 +233,19 @@ based_terminate(crm_exit_t exit_status) based_remote_cleanup(); g_clear_pointer(&based_cib, pcmk__xml_free); + g_clear_pointer(&cib_root, g_free); +} + +/*! + * \internal + * \brief Clean up data structures and exit + * + * \param[in] exit_status Exit code + */ +void +based_terminate(crm_exit_t exit_status) +{ + based_cleanup(); // Exit immediately on error if (exit_status != CRM_EX_OK) { @@ -399,9 +410,8 @@ main(int argc, char **argv) g_strfreev(processed_args); pcmk__free_arg_context(context); - pcmk__client_cleanup(); based_cluster_disconnect(); - g_free(cib_root); + based_cleanup(); pcmk__output_and_clear_error(&error, out); From 3f23a0c465752eb11a1972c6cc9d36606a4e27e4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 7 Jan 2026 23:36:12 -0800 Subject: [PATCH 113/202] Refactor: based: Assume main loop is running in based_terminate() There are two call sites. * based_cpg_destroy: This is called only via the main loop. See pcmk_cluster_set_destroy_fn() and pcmk__cpg_connect(). * based_shutdown: We can reach this only through the main loop. It's set up as a main loop signal handler via mainloop_add_signal(). The true signal handler is mainloop_signal_handler(), which sets a main loop trigger to call based_shutdown(). That trigger can't do anything unless the main loop is running. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 202cff4aad2..813fdbc6140 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -255,12 +255,11 @@ based_terminate(crm_exit_t exit_status) based_cluster_disconnect(); - if ((mainloop != NULL) && g_main_loop_is_running(mainloop)) { - g_main_loop_quit(mainloop); - return; - } + // There should be no way to get here without the main loop running + CRM_CHECK((mainloop != NULL) && g_main_loop_is_running(mainloop), + crm_exit(exit_status)); - crm_exit(CRM_EX_OK); + g_main_loop_quit(mainloop); } static void From 72a7b4e7390878306aa68184298f076d520dd079 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 8 Jan 2026 01:00:21 -0800 Subject: [PATCH 114/202] Doc: based: Add notes to based_terminate() Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 813fdbc6140..2a785d00ded 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -247,10 +247,17 @@ based_terminate(crm_exit_t exit_status) { based_cleanup(); - // Exit immediately on error if (exit_status != CRM_EX_OK) { + /* After calling g_main_loop_quit(), sources that have already been + * dispatched are still executed. On error, skip that and exit + * immediately after cleaning up data structures. + * + * @TODO Is this necessary? It would be nice to do the cleanup at the + * end of main(). If so, then one (complicated) option would be to keep + * track of all main loop sources and destroy them so that + * g_main_dispatch() ignores them. + */ crm_exit(exit_status); - return; } based_cluster_disconnect(); From b657983a6a6d1e773e329a596dfe5a7a5ee9b1cc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 8 Jan 2026 01:08:42 -0800 Subject: [PATCH 115/202] Refactor: based: Set shutting_down to true in based_terminate() Mostly for sanity. Between the g_main_loop_quit() call and the main loop actually stopping, the main loop runs callbacks for any sources that have already been dispatched. In case some callback actually runs, it should be able to call based_shutting_down() and get a true return value. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 1 + 1 file changed, 1 insertion(+) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 2a785d00ded..5af9a464771 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -245,6 +245,7 @@ based_cleanup(void) void based_terminate(crm_exit_t exit_status) { + shutting_down = true; based_cleanup(); if (exit_status != CRM_EX_OK) { From 7008b203fd50223f41a019b5c82d76d558775755 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 00:01:48 -0800 Subject: [PATCH 116/202] Low: based: Fix memory leak in remote listeners We were allocating an int to hold the server socket file descriptor, and we were never freeing it. (Its destroy callback, based_remote_listener_destroy(), did not free it.) Don't allocate an int, but instead call GINT_TO_POINTER() and GPOINTER_TO_INT() to cast it to and from a pointer. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 99a705fb59e..8bd7bd750cf 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -547,13 +547,13 @@ based_remote_client_destroy(gpointer user_data) } static int -cib_remote_listen(gpointer data) +cib_remote_listen(gpointer user_data) { + int ssock = GPOINTER_TO_INT(user_data); int csock = -1; unsigned laddr; struct sockaddr_storage addr; char ipstr[INET6_ADDRSTRLEN]; - int ssock = *(int *)data; int rc; pcmk__client_t *new_client = NULL; @@ -630,7 +630,7 @@ static int init_remote_listener(int port) { int rc; - int *ssock = NULL; + int ssock = -1; struct sockaddr_in saddr; int optval; @@ -645,17 +645,15 @@ init_remote_listener(int port) #endif /* create server socket */ - ssock = pcmk__assert_alloc(1, sizeof(int)); - *ssock = socket(AF_INET, SOCK_STREAM, 0); - if (*ssock == -1) { + ssock = socket(AF_INET, SOCK_STREAM, 0); + if (ssock == -1) { pcmk__err("Listener socket creation failed: %s", pcmk_rc_str(errno)); - free(ssock); return -1; } /* reuse address */ optval = 1; - rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + rc = setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (rc < 0) { pcmk__err("Local address reuse not allowed on listener socket: %s", pcmk_rc_str(errno)); @@ -666,23 +664,22 @@ init_remote_listener(int port) saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(port); - if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { + if (bind(ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { pcmk__err("Cannot bind to listener socket: %s", pcmk_rc_str(errno)); - close(*ssock); - free(ssock); + close(ssock); return -2; } - if (listen(*ssock, 10) == -1) { + if (listen(ssock, 10) == -1) { pcmk__err("Cannot listen on socket: %s", pcmk_rc_str(errno)); - close(*ssock); - free(ssock); + close(ssock); return -3; } - mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, *ssock, ssock, &remote_listen_fd_callbacks); + mainloop_add_fd("based-remote-listener", G_PRIORITY_DEFAULT, ssock, + GINT_TO_POINTER(ssock), &remote_listen_fd_callbacks); pcmk__debug("Started listener on port %d", port); - return *ssock; + return ssock; } /*! From 2d328efd9a823fe26a5a40bc643b226c6e132eca Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 00:22:39 -0800 Subject: [PATCH 117/202] Refactor: libcrmcommon: Expose struct mainloop_io_s internally We will use this soon. Signed-off-by: Reid Wahl --- include/crm/common/mainloop_internal.h | 15 +++++++++++++++ lib/common/mainloop.c | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/crm/common/mainloop_internal.h b/include/crm/common/mainloop_internal.h index db3a49b64b4..1546c857c68 100644 --- a/include/crm/common/mainloop_internal.h +++ b/include/crm/common/mainloop_internal.h @@ -38,6 +38,21 @@ struct mainloop_child_s { pcmk__mainloop_child_exit_fn_t exit_fn; }; +struct mainloop_io_s { + char *name; + void *userdata; + + int fd; + guint source; + crm_ipc_t *ipc; + GIOChannel *channel; + + int (*dispatch_fn_ipc)(const char *buffer, ssize_t length, + gpointer user_data); + int (*dispatch_fn_io)(gpointer user_data); + void (*destroy_fn)(gpointer user_data); +}; + int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, const struct ipc_client_callbacks *callbacks, mainloop_io_t **source); diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index fa83dc961ac..ed0a43ab75a 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -647,21 +647,6 @@ mainloop_del_ipc_server(qb_ipcs_service_t * server) } } -struct mainloop_io_s { - char *name; - void *userdata; - - int fd; - guint source; - crm_ipc_t *ipc; - GIOChannel *channel; - - int (*dispatch_fn_ipc) (const char *buffer, ssize_t length, gpointer userdata); - int (*dispatch_fn_io) (gpointer userdata); - void (*destroy_fn) (gpointer userdata); - -}; - /*! * \internal * \brief I/O watch callback function (GIOFunc) From ff0002f360225db560fd6870562f61e65f35c1f2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 00:15:32 -0800 Subject: [PATCH 118/202] Low: based: Destroy remote listeners in based_remote_cleanup() Previously we were only closing the file descriptors. Instead we destroy the mainloop_io_t objects returned by mainloop_add_fd(). In order to do that, we need to start storing those objects in the first place. Previously we were ignoring the return value of mainloop_add_fd() in init_remote_listener(). Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 59 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 8bd7bd750cf..3eb59495b3a 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -48,8 +48,8 @@ static pcmk__tls_t *tls = NULL; -static int remote_fd = -1; -static int remote_tls_fd = -1; +static mainloop_io_t *tcp_listener = NULL; +static mainloop_io_t *tls_listener = NULL; // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 @@ -550,6 +550,8 @@ static int cib_remote_listen(gpointer user_data) { int ssock = GPOINTER_TO_INT(user_data); + const bool is_tls = (tls_listener != NULL) && (ssock == tls_listener->fd); + int csock = -1; unsigned laddr; struct sockaddr_storage addr; @@ -587,7 +589,7 @@ cib_remote_listen(gpointer user_data) new_client = pcmk__new_unauth_client(NULL); new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t)); - if (ssock == remote_tls_fd) { + if (is_tls) { pcmk__set_client_flags(new_client, pcmk__client_tls); /* create gnutls session for the server socket */ @@ -610,8 +612,7 @@ cib_remote_listen(gpointer user_data) remote_auth_timeout_cb, new_client); pcmk__info("%s connection from %s pending authentication for client %s", - ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"), ipstr, - new_client->id); + (is_tls? "Encrypted" : "Clear-text"), ipstr, new_client->id); new_client->remote->source = mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client, @@ -626,13 +627,14 @@ based_remote_listener_destroy(gpointer user_data) pcmk__info("No longer listening for remote connections"); } -static int +static mainloop_io_t * init_remote_listener(int port) { int rc; int ssock = -1; struct sockaddr_in saddr; int optval; + mainloop_io_t *listener = NULL; static struct mainloop_fd_callbacks remote_listen_fd_callbacks = { .dispatch = cib_remote_listen, @@ -647,8 +649,8 @@ init_remote_listener(int port) /* create server socket */ ssock = socket(AF_INET, SOCK_STREAM, 0); if (ssock == -1) { - pcmk__err("Listener socket creation failed: %s", pcmk_rc_str(errno)); - return -1; + pcmk__err("Listener socket creation failed: %s", strerror(errno)); + return NULL; } /* reuse address */ @@ -656,7 +658,7 @@ init_remote_listener(int port) rc = setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (rc < 0) { pcmk__err("Local address reuse not allowed on listener socket: %s", - pcmk_rc_str(errno)); + strerror(errno)); } /* bind server socket */ @@ -665,21 +667,27 @@ init_remote_listener(int port) saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(port); if (bind(ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { - pcmk__err("Cannot bind to listener socket: %s", pcmk_rc_str(errno)); + pcmk__err("Cannot bind to listener socket: %s", strerror(errno)); close(ssock); - return -2; + return NULL; } if (listen(ssock, 10) == -1) { - pcmk__err("Cannot listen on socket: %s", pcmk_rc_str(errno)); + pcmk__err("Cannot listen on socket: %s", strerror(errno)); close(ssock); - return -3; + return NULL; } - mainloop_add_fd("based-remote-listener", G_PRIORITY_DEFAULT, ssock, - GINT_TO_POINTER(ssock), &remote_listen_fd_callbacks); - pcmk__debug("Started listener on port %d", port); + listener = mainloop_add_fd("based-remote-listener", G_PRIORITY_DEFAULT, + ssock, GINT_TO_POINTER(ssock), + &remote_listen_fd_callbacks); + if (listener == NULL) { + pcmk__err("Cannot add pacemaker-based remote listener (port %d) to " + "mainloop: %s", port, strerror(errno)); + return NULL; + } - return ssock; + pcmk__debug("Started pacemaker-based remote listener on port %d", port); + return listener; } /*! @@ -731,8 +739,6 @@ based_remote_init(void) if (rc != pcmk_rc_ok) { pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ", "on port %d", pcmk_rc_str(rc), port); - - remote_tls_fd = -1; goto try_clear_port; } @@ -742,7 +748,7 @@ based_remote_init(void) } pcmk__notice("Starting TLS listener on port %d", port); - remote_tls_fd = init_remote_listener(port); + tls_listener = init_remote_listener(port); try_clear_port: /* Regardless of whether or not we successfully enabled remote-tls-port, @@ -754,7 +760,7 @@ based_remote_init(void) pcmk__warn("Starting clear-text listener on port %d. This is insecure " "and will be removed in a future release. Use " PCMK_XA_REMOTE_TLS_PORT " instead.", port); - remote_fd = init_remote_listener(port); + tcp_listener = init_remote_listener(port); } } @@ -769,15 +775,8 @@ based_remote_init(void) void based_remote_cleanup(void) { - if (remote_fd >= 0) { - close(remote_fd); - remote_fd = -1; - } - - if (remote_tls_fd >= 0) { - close(remote_tls_fd); - remote_tls_fd = -1; - } + g_clear_pointer(&tcp_listener, mainloop_del_fd); + g_clear_pointer(&tls_listener, mainloop_del_fd); } /*! From 152c954b6f1a42092e2c938d8674a32e2eef9063 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 21:19:23 -0800 Subject: [PATCH 119/202] Refactor: libcrmcommon: Free mainloop child list at exit Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index ed0a43ab75a..ae78c92c6e7 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -29,7 +29,6 @@ struct trigger_s { gboolean trigger; void *user_data; guint id; - }; struct mainloop_timer_s { @@ -41,6 +40,21 @@ struct mainloop_timer_s { void *userdata; }; +static GList *child_list = NULL; +static qb_array_t *gio_map = NULL; + +static void +child_free(mainloop_child_t *child) +{ + if (child->timerid != 0) { + pcmk__trace("Removing timer %d", child->timerid); + g_source_remove(child->timerid); + child->timerid = 0; + } + free(child->desc); + free(child); +} + static gboolean crm_trigger_prepare(GSource * source, gint * timeout) { @@ -400,11 +414,18 @@ mainloop_destroy_signal(int sig) return TRUE; } -static qb_array_t *gio_map = NULL; - +/*! + * \internal + * \brief Free data structures used for the mainloop + * + * \todo This is incomplete. Free other data structures created in this file. + */ void mainloop_cleanup(void) { + g_list_free_full(child_list, (GDestroyNotify) child_free); + child_list = NULL; + g_clear_pointer(&gio_map, qb_array_free); for (int sig = 0; sig < NSIG; ++sig) { @@ -969,8 +990,6 @@ mainloop_del_fd(mainloop_io_t *client) g_source_remove(client->source); } -static GList *child_list = NULL; - pid_t mainloop_child_pid(mainloop_child_t * child) { @@ -1001,20 +1020,6 @@ mainloop_clear_child_userdata(mainloop_child_t * child) child->privatedata = NULL; } -/* good function name */ -static void -child_free(mainloop_child_t *child) -{ - if (child->timerid != 0) { - pcmk__trace("Removing timer %d", child->timerid); - g_source_remove(child->timerid); - child->timerid = 0; - } - free(child->desc); - free(child); -} - -/* terrible function name */ static int child_kill_helper(mainloop_child_t *child) { From cc7a526683d18907ce0b4b4c15699b076087537d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 22:09:34 -0800 Subject: [PATCH 120/202] Refactor: libcrmcommon: Drop callback_needed variable Return false early where possible instead. Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index ae78c92c6e7..1612b5070c1 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -1078,15 +1078,16 @@ child_waitpid(mainloop_child_t *child, int flags) int signo = 0; int status = 0; int exitcode = 0; - bool callback_needed = true; rc = waitpid(child->pid, &status, flags); + if (rc == 0) { // WNOHANG in flags, and child status is not available pcmk__trace("Child process %lld (%s) still active", (long long) child->pid, child->desc); - callback_needed = false; + return false; + } - } else if (rc != child->pid) { + if (rc != child->pid) { /* According to POSIX, possible conditions: * - child->pid was non-positive (process group or any child), * and rc is specific child @@ -1098,8 +1099,8 @@ child_waitpid(mainloop_child_t *child, int flags) */ signo = SIGCHLD; exitcode = 1; - pcmk__notice("Wait for child process %d (%s) interrupted: %s", - child->pid, child->desc, pcmk_rc_str(errno)); + pcmk__notice("Wait for child process %lld (%s) interrupted: %s", + (long long) child->pid, child->desc, strerror(errno)); } else if (WIFEXITED(status)) { exitcode = WEXITSTATUS(status); @@ -1115,19 +1116,21 @@ child_waitpid(mainloop_child_t *child, int flags) #ifdef WCOREDUMP // AIX, SunOS, maybe others } else if (WCOREDUMP(status)) { core = 1; - pcmk__err("Child process %d (%s) dumped core", child->pid, child->desc); + pcmk__err("Child process %lld (%s) dumped core", (long long) child->pid, + child->desc); #endif } else { // flags must contain WUNTRACED and/or WCONTINUED to reach this pcmk__trace("Child process %lld (%s) stopped or continued", (long long) child->pid, child->desc); - callback_needed = false; + return false; } - if (callback_needed && child->exit_fn) { + if (child->exit_fn != NULL) { child->exit_fn(child, core, signo, exitcode); } - return callback_needed; + + return true; } static void From 61d19e13e2276057ca37ab4e9ef5eecab070986f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 22:20:52 -0800 Subject: [PATCH 121/202] Refactor: libcrmcommon: child_kill_helper() returns standard code Also take a const argument. This function may kill a process, but it doesn't modify the mainloop_child_t. Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 1612b5070c1..2d664e7f7e4 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -1021,33 +1021,40 @@ mainloop_clear_child_userdata(mainloop_child_t * child) } static int -child_kill_helper(mainloop_child_t *child) +child_kill_helper(const mainloop_child_t *child) { - int rc; - if (child->flags & mainloop_leave_pid_group) { + int rc = 0; + + if (pcmk__is_set(child->flags, mainloop_leave_pid_group)) { pcmk__debug("Killing PID %lld only. Leaving its process group intact.", (long long) child->pid); rc = kill(child->pid, SIGKILL); + } else { pcmk__debug("Killing PID %lld's entire process group", (long long) child->pid); rc = kill(-child->pid, SIGKILL); } - if (rc < 0) { - if (errno != ESRCH) { - pcmk__err("kill(%d, KILL) failed: %s", child->pid, strerror(errno)); - } - return -errno; + if (rc == 0) { + return pcmk_rc_ok; } - return 0; + + rc = errno; + if (rc == ESRCH) { + return rc; + } + + pcmk__err("kill(%lld, KILL) failed: %s", (long long) child->pid, + strerror(rc)); + return rc; } static gboolean child_timeout_callback(gpointer p) { mainloop_child_t *child = p; - int rc = 0; + int rc = pcmk_rc_ok; child->timerid = 0; if (child->timeout) { @@ -1057,7 +1064,7 @@ child_timeout_callback(gpointer p) } rc = child_kill_helper(child); - if (rc == -ESRCH) { + if (rc == ESRCH) { /* Nothing left to do. pid doesn't exist */ return FALSE; } @@ -1171,7 +1178,8 @@ mainloop_child_kill(pid_t pid) mainloop_child_t *match = NULL; /* It is impossible to block SIGKILL, this allows us to * call waitpid without WNOHANG flag.*/ - int waitflags = 0, rc = 0; + int waitflags = 0; + int rc = pcmk_rc_ok; for (iter = child_list; iter != NULL && match == NULL; iter = iter->next) { child = iter->data; @@ -1185,7 +1193,7 @@ mainloop_child_kill(pid_t pid) } rc = child_kill_helper(match); - if(rc == -ESRCH) { + if (rc == ESRCH) { /* It's gone, but hasn't shown up in waitpid() yet. Wait until we get * SIGCHLD and let handler clean it up as normal (so we get the correct * return code/status). The blocking alternative would be to call @@ -1194,10 +1202,11 @@ mainloop_child_kill(pid_t pid) pcmk__trace("Waiting for signal that child process %lld completed", (long long) match->pid); return TRUE; + } - } else if(rc != 0) { - /* If KILL for some other reason set the WNOHANG flag since we - * can't be certain what happened. + if (rc != pcmk_rc_ok) { + /* If kill() failed for some other reason, set the WNOHANG flag, since + * we can't be certain what happened. */ waitflags = WNOHANG; } From 3cf479f46666d9102e895592768eb430e148aefe Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 9 Jan 2026 22:01:22 -0800 Subject: [PATCH 122/202] Refactor: libcrmcommon: Track mainloop children in a hash table We index it by stringified PID. pid_t is specified as a signed integer type, but there's no guarantee that it's a 32-bit integer. So GINT_TO_POINTER() is technically unsafe (although it's likely fine in practice). The semantics of using a hash table seem like a better fit for looking up a child record by PID. Removing an entry is also simpler, and we can set the destroy function when we create the table. Now that I've looked at most of the mainloop child code, I REALLY want to start using GSubprocess if possible. Note: I started working on this so that the CIB manager could free a mainloop_child_t that it created with mainloop_child_add(), without calling mainloop_child_kill() and killing it. I now realize this is not necessary, since mainloop_cleanup() frees any children that we haven't already freed. However, a hash table still seems like a better data structure for storing these children. Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 122 +++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 42 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 2d664e7f7e4..fd0f57b225f 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -40,7 +40,7 @@ struct mainloop_timer_s { void *userdata; }; -static GList *child_list = NULL; +static GHashTable *mainloop_children = NULL; static qb_array_t *gio_map = NULL; static void @@ -423,9 +423,7 @@ mainloop_destroy_signal(int sig) void mainloop_cleanup(void) { - g_list_free_full(child_list, (GDestroyNotify) child_free); - child_list = NULL; - + g_clear_pointer(&mainloop_children, g_hash_table_destroy); g_clear_pointer(&gio_map, qb_array_free); for (int sig = 0; sig < NSIG; ++sig) { @@ -1140,22 +1138,28 @@ child_waitpid(mainloop_child_t *child, int flags) return true; } +static gboolean +child_waitpid_no_hang(gpointer key, gpointer value, gpointer user_data) +{ + mainloop_child_t *child = value; + + if (!child_waitpid(child, WNOHANG)) { + return FALSE; + } + + pcmk__trace("Removing completed process %lld from child table", + (long long) child->pid); + return TRUE; +} + static void child_death_dispatch(int signal) { - for (GList *iter = child_list; iter; ) { - GList *saved = iter; - mainloop_child_t *child = iter->data; - - iter = iter->next; - if (child_waitpid(child, WNOHANG)) { - pcmk__trace("Removing completed process %lld from child list", - (long long) child->pid); - child_list = g_list_remove_link(child_list, saved); - g_list_free(saved); - child_free(child); - } + if (mainloop_children == NULL) { + return; } + + g_hash_table_foreach_remove(mainloop_children, child_waitpid_no_hang, NULL); } static gboolean @@ -1173,35 +1177,42 @@ child_signal_init(gpointer p) gboolean mainloop_child_kill(pid_t pid) { - GList *iter; - mainloop_child_t *child = NULL; - mainloop_child_t *match = NULL; - /* It is impossible to block SIGKILL, this allows us to - * call waitpid without WNOHANG flag.*/ + /* It is impossible to block SIGKILL. This allows us to call waitpid() + * without the WNOHANG flag. + */ int waitflags = 0; int rc = pcmk_rc_ok; + char *pid_s = NULL; + mainloop_child_t *child = NULL; + gboolean killed = FALSE; - for (iter = child_list; iter != NULL && match == NULL; iter = iter->next) { - child = iter->data; - if (pid == child->pid) { - match = child; - } + if (mainloop_children == NULL) { + // We're not tracking any children, so don't kill this PID + goto done; } - if (match == NULL) { - return FALSE; + pid_s = pcmk__assert_asprintf("%lld", (long long) pid); + child = g_hash_table_lookup(mainloop_children, pid_s); + + if (child == NULL) { + // We're not tracking a child with this PID, so don't kill it + goto done; } - rc = child_kill_helper(match); + rc = child_kill_helper(child); if (rc == ESRCH) { - /* It's gone, but hasn't shown up in waitpid() yet. Wait until we get - * SIGCHLD and let handler clean it up as normal (so we get the correct - * return code/status). The blocking alternative would be to call - * child_waitpid(match, 0). + /* kill() didn't find the PID. Assume this means the process is in some + * intermediate stage of exiting. Wait until we get SIGCHLD and let + * the handler clean it up as normal, so that we get the correct return + * code/status. The blocking alternative would be to call + * child_waitpid(child, 0). + * + * Treat this as success in the meantime. */ pcmk__trace("Waiting for signal that child process %lld completed", - (long long) match->pid); - return TRUE; + (long long) child->pid); + killed = TRUE; + goto done; } if (rc != pcmk_rc_ok) { @@ -1211,19 +1222,34 @@ mainloop_child_kill(pid_t pid) waitflags = WNOHANG; } - if (!child_waitpid(match, waitflags)) { - /* not much we can do if this occurs */ - return FALSE; + if (!child_waitpid(child, waitflags)) { + /* waitpid() didn't find the child. This shouldn't happen if kill() + * succeeded. In any case, there's no obviously good way to proceed. + * Treat this as a failure. + * + * @TODO Should we call the child's exit_fn with a failure here, and/or + * free the mainloop_child_t? We can hope that we successfully wait on + * it in response to a SIGCHLD that comes later. But we might just never + * do anything with this child again. + * + * @TODO Consider using GSubprocess for all subprocess tracking. + */ + goto done; } - child_list = g_list_remove(child_list, match); - child_free(match); - return TRUE; + g_hash_table_remove(mainloop_children, pid_s); + killed = TRUE; + +done: + free(pid_s); + return killed; } /* Create/Log a new tracked process * To track a process group, use -pid * + * Newly created child will be freed by mainloop_cleanup(), if not sooner. + * * @TODO Using a non-positive pid (i.e. any child, or process group) would * likely not be useful since we will free the child after the first * completed process. @@ -1235,6 +1261,7 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, pcmk__mainloop_child_exit_fn_t exit_fn) { static bool need_init = TRUE; + bool is_new = false; mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); child->pid = pid; @@ -1249,7 +1276,17 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, child->timerid = pcmk__create_timer(timeout, child_timeout_callback, child); } - child_list = g_list_append(child_list, child); + if (mainloop_children == NULL) { + mainloop_children = pcmk__strkey_table(free, + (GDestroyNotify) child_free); + } + + is_new = g_hash_table_insert(mainloop_children, + pcmk__assert_asprintf("%lld", (long long) pid), + child); + + // It should be impossible to have this PID in mainloop_children already + CRM_LOG_ASSERT(is_new); if(need_init) { need_init = FALSE; @@ -1261,6 +1298,7 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, } } +// Newly created child will be freed by mainloop_cleanup(), if not sooner void mainloop_child_add(pid_t pid, int timeout, const char *desc, void *privatedata, pcmk__mainloop_child_exit_fn_t exit_fn) From 720fabacb459f9a136ef208365eddb1cc5993072 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 16:48:30 -0800 Subject: [PATCH 123/202] Refactor: based: Add missing include to pacemaker-based.c Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 1 + 1 file changed, 1 insertion(+) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 5af9a464771..afe8522369e 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -23,6 +23,7 @@ #include // CRM_CONFIG_DIR, CRM_DAEMON_USER #include // pcmk__node_update, etc. +#include // PCMK__EXITC_ERROR, pcmk__err, etc. #include // crm_ipc_* #include // crm_log_* #include // mainloop_add_signal From 05b66a445829b699f3b913f3eeece440ead2cdb3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 18:25:23 -0800 Subject: [PATCH 124/202] Refactor: based: Clean up shutdown logic And rename based_terminate() to based_quit_main_loop(). The main loop processes events in batches. On each iteration, it checks to see what sources are ready (that is, pending dispatch), and then it dispatches them. It iterates over the list of ready sources and runs each source's callback function, provided the source has not been removed/destroyed. When we call g_main_loop_quit(), we call it from within a source's callback. g_main_loop_quit() ensures that no additional loop iterations will occur. However, any sources that are already in the pending list will still be dispatched. This means that if we quit the main loop, we need to be prepared for any pending source callbacks. This commit handles that by returning early from functions that shouldn't do anything during shutdown, if we're in the process of shutting down (that is, if we've called g_main_loop_quit()). This lets us quit based the same way regardless of the exit code and consolidate cleanup at the end of main(). It also hopefully makes behavior more reliable when we quit the main loop. Previously, if we were quitting with CRM_EX_OK (due to a SIGTERM), we would free based_cib, cib_root, and possibly other important data structures that we generally assume are non-NULL. If some function tried to dereference those when processing pending sources, we might seg fault. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 9 +++++++ daemons/based/based_corosync.c | 18 +++++++++---- daemons/based/based_io.c | 4 +++ daemons/based/based_remote.c | 5 ++++ daemons/based/pacemaker-based.c | 45 +++++++++++++-------------------- daemons/based/pacemaker-based.h | 2 +- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 92689ad6be9..bfab7e1b9a5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -69,6 +69,10 @@ digest_timer_cb(gpointer data) { xmlNode *ping = NULL; + if (based_shutting_down()) { + return G_SOURCE_REMOVE; + } + if (!based_get_local_node_dc()) { // Only the DC sends a ping return G_SOURCE_REMOVE; @@ -686,6 +690,11 @@ based_process_request(xmlNode *request, bool privileged, xmlNode *output = NULL; time_t start_time = 0; + if (based_shutting_down()) { + pcmk__info("Ignoring pending CIB request during shutdown"); + return ENOTCONN; + } + rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 67ff02e5394..d091fd1162f 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -9,6 +9,7 @@ #include +#include // PRIu32 #include #include // NULL, size_t #include // uint32_t @@ -60,11 +61,18 @@ based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *groupName, uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) { - xmlNode *xml = NULL; + char *data = NULL; const char *from = NULL; - char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); + xmlNode *xml = NULL; + + if (based_shutting_down()) { + pcmk__info("Ignoring CPG message from node %" PRIu32 " during shutdown", + nodeid); + return; + } - if(data == NULL) { + data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); + if (data == NULL) { return; } @@ -89,8 +97,8 @@ based_cpg_destroy(gpointer user_data) return; } - pcmk__crit("Exiting immediately after losing connection to cluster layer"); - based_terminate(CRM_EX_DISCONNECT); + pcmk__crit("Exiting after losing connection to cluster layer"); + based_quit_main_loop(CRM_EX_DISCONNECT); } #endif diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index abb811895fb..d85c94b39c2 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -93,6 +93,10 @@ write_cib_async(gpointer user_data) pid_t pid = 0; int blackbox_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); + if (based_shutting_down()) { + pcmk__info("Skipping CIB write during shutdown"); + } + /* Disable blackbox logging before the fork to avoid two processes writing * to the same shared memory. The disable should not be done in the child, * because this would close shared memory files in the parent. diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 3eb59495b3a..6f9c158424c 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -565,6 +565,11 @@ cib_remote_listen(gpointer user_data) .destroy = based_remote_client_destroy, }; + if (based_shutting_down()) { + pcmk__info("Ignoring new remote connection during shutdown"); + return 0; + } + /* accept the connection */ laddr = sizeof(addr); memset(&addr, 0, sizeof(addr)); diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index afe8522369e..bae9497b3a0 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -229,6 +229,7 @@ static void based_cleanup(void) { based_callbacks_cleanup(); + based_cluster_disconnect(); based_io_cleanup(); based_ipc_cleanup(); based_remote_cleanup(); @@ -239,48 +240,39 @@ based_cleanup(void) /*! * \internal - * \brief Clean up data structures and exit + * \brief Set an exit code and quit the main loop * - * \param[in] exit_status Exit code + * \param[in] ec Exit code */ void -based_terminate(crm_exit_t exit_status) +based_quit_main_loop(crm_exit_t ec) { - shutting_down = true; - based_cleanup(); - - if (exit_status != CRM_EX_OK) { - /* After calling g_main_loop_quit(), sources that have already been - * dispatched are still executed. On error, skip that and exit - * immediately after cleaning up data structures. - * - * @TODO Is this necessary? It would be nice to do the cleanup at the - * end of main(). If so, then one (complicated) option would be to keep - * track of all main loop sources and destroy them so that - * g_main_dispatch() ignores them. - */ - crm_exit(exit_status); + if (shutting_down) { + return; } - based_cluster_disconnect(); + shutting_down = true; + exit_code = ec; // There should be no way to get here without the main loop running CRM_CHECK((mainloop != NULL) && g_main_loop_is_running(mainloop), - crm_exit(exit_status)); + crm_exit(exit_code)); g_main_loop_quit(mainloop); } +/*! + * \internal + * \brief Quit the main loop and set the exit code to \c CRM_EX_OK + * + * \param[in] nsig Ignored + * + * \note This is a main loop signal handler function. + */ static void based_shutdown(int nsig) { - if (based_shutting_down()) { - // Already shutting down - return; - } - - shutting_down = true; - based_terminate(CRM_EX_OK); + based_quit_main_loop(CRM_EX_OK); } int @@ -418,7 +410,6 @@ main(int argc, char **argv) g_strfreev(processed_args); pcmk__free_arg_context(context); - based_cluster_disconnect(); based_cleanup(); pcmk__output_and_clear_error(&error, out); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 6a4a20238b7..f53314f9f85 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -39,6 +39,6 @@ void based_set_local_node_dc(bool value); bool based_shutting_down(void); bool based_stand_alone(void); -void based_terminate(crm_exit_t exit_status); +void based_quit_main_loop(crm_exit_t ec); #endif // PACEMAKER_BASED__H From 0fcd99b630d95efc1fe8113a857f679966c7b097 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 01:35:56 -0800 Subject: [PATCH 125/202] Refactor: based: based_cib should always be non-NULL after init now Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index bfab7e1b9a5..637090097e5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -609,14 +609,9 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, originator = pcmk__s(originator, "local"); client_name = pcmk__s(client_name, "client"); - /* @FIXME based_cib should always be non-NULL, but that's currently not the - * case during shutdown - */ - if (based_cib != NULL) { - pcmk__xe_get_int(based_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); - pcmk__xe_get_int(based_cib, PCMK_XA_EPOCH, &epoch); - pcmk__xe_get_int(based_cib, PCMK_XA_NUM_UPDATES, &num_updates); - } + pcmk__xe_get_int(based_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); + pcmk__xe_get_int(based_cib, PCMK_XA_EPOCH, &epoch); + pcmk__xe_get_int(based_cib, PCMK_XA_NUM_UPDATES, &num_updates); do_crm_log(level, "Completed %s operation for section %s: %s (rc=%d, " From 0c877053a51d728df8d3c55c149fd36bf6ec3c26 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 19:45:17 -0800 Subject: [PATCH 126/202] Refactor: various: Unref main loop consistently Signed-off-by: Reid Wahl --- daemons/attrd/attrd_utils.c | 2 +- daemons/based/pacemaker-based.c | 1 + daemons/controld/controld_control.c | 2 -- daemons/controld/pacemaker-controld.c | 2 ++ daemons/execd/cts-exec-helper.c | 1 + daemons/execd/pacemaker-execd.c | 1 + daemons/fenced/cts-fence-helper.c | 3 ++- daemons/fenced/pacemaker-fenced.c | 1 + daemons/pacemakerd/pacemakerd.c | 3 ++- daemons/schedulerd/pacemaker-schedulerd.c | 1 + lib/pacemaker/pcmk_fence.c | 3 ++- tools/crm_resource.c | 4 +--- 12 files changed, 15 insertions(+), 9 deletions(-) diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index 6be021fc4f0..fcb757c1093 100644 --- a/daemons/attrd/attrd_utils.c +++ b/daemons/attrd/attrd_utils.c @@ -77,7 +77,6 @@ attrd_shutdown(int nsig) crm_exit(CRM_EX_OK); } else { g_main_loop_quit(mloop); - g_main_loop_unref(mloop); } } @@ -99,6 +98,7 @@ void attrd_run_mainloop(void) { g_main_loop_run(mloop); + g_clear_pointer(&mloop, g_main_loop_unref); } /* strlen("value") */ diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index bae9497b3a0..005bd0b3c03 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -405,6 +405,7 @@ main(int argc, char **argv) pcmk__notice("Pacemaker CIB manager successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(processed_args); diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 9a858304d62..6054eec448d 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -279,8 +279,6 @@ crmd_exit(crm_exit_t exit_code) g_main_context_pending(ctx)); g_main_loop_quit(mloop); - /* Won't do anything yet, since we're inside it now */ - g_main_loop_unref(mloop); } else { mainloop_destroy_signal(SIGCHLD); } diff --git a/daemons/controld/pacemaker-controld.c b/daemons/controld/pacemaker-controld.c index 2e8564fb90f..e94ea6ecc64 100644 --- a/daemons/controld/pacemaker-controld.c +++ b/daemons/controld/pacemaker-controld.c @@ -199,6 +199,8 @@ main(int argc, char **argv) // Run mainloop controld_globals.mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(controld_globals.mainloop); + g_main_loop_unref(controld_globals.mainloop); + if (pcmk__is_set(controld_globals.fsa_input_register, R_STAYDOWN)) { pcmk__info("Inhibiting automated respawn"); exit_code = CRM_EX_FATAL; diff --git a/daemons/execd/cts-exec-helper.c b/daemons/execd/cts-exec-helper.c index 613d3face8f..0ed88c327ba 100644 --- a/daemons/execd/cts-exec-helper.c +++ b/daemons/execd/cts-exec-helper.c @@ -605,6 +605,7 @@ main(int argc, char **argv) pcmk__info("Starting"); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(processed_args); diff --git a/daemons/execd/pacemaker-execd.c b/daemons/execd/pacemaker-execd.c index 97f223f5afd..836a6f49749 100644 --- a/daemons/execd/pacemaker-execd.c +++ b/daemons/execd/pacemaker-execd.c @@ -439,6 +439,7 @@ main(int argc, char **argv) "accepting connections"); pcmk__notice("OCF resource agent search path is %s", PCMK__OCF_RA_PATH); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); /* should never get here */ exit_executor(); diff --git a/daemons/fenced/cts-fence-helper.c b/daemons/fenced/cts-fence-helper.c index 37b7759facc..1ad7e15b81e 100644 --- a/daemons/fenced/cts-fence-helper.c +++ b/daemons/fenced/cts-fence-helper.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2025 the Pacemaker project contributors + * Copyright 2009-2026 the Pacemaker project contributors * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. @@ -605,6 +605,7 @@ mainloop_tests(void) pcmk__info("Starting"); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); } static GOptionContext * diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c index ceb3da0fc83..ea6cd6bfefa 100644 --- a/daemons/fenced/pacemaker-fenced.c +++ b/daemons/fenced/pacemaker-fenced.c @@ -447,6 +447,7 @@ main(int argc, char **argv) pcmk__notice("Pacemaker fencer successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(processed_args); diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c index 98f38cc5fca..49084554438 100644 --- a/daemons/pacemakerd/pacemakerd.c +++ b/daemons/pacemakerd/pacemakerd.c @@ -474,10 +474,11 @@ main(int argc, char **argv) pcmk__notice("Pacemaker daemon successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); + pacemakerd_ipc_cleanup(); pacemakerd_unregister_handlers(); - g_main_loop_unref(mainloop); #if SUPPORT_COROSYNC cluster_disconnect_cfg(); #endif diff --git a/daemons/schedulerd/pacemaker-schedulerd.c b/daemons/schedulerd/pacemaker-schedulerd.c index 2534ea4050d..0088721aaf4 100644 --- a/daemons/schedulerd/pacemaker-schedulerd.c +++ b/daemons/schedulerd/pacemaker-schedulerd.c @@ -162,6 +162,7 @@ main(int argc, char **argv) pcmk__notice("Pacemaker scheduler successfully started and accepting " "connections"); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); done: g_strfreev(options.remainder); diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c index 186fe043d3e..8b06bf9b4a1 100644 --- a/lib/pacemaker/pcmk_fence.c +++ b/lib/pacemaker/pcmk_fence.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2025 the Pacemaker project contributors + * Copyright 2009-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -222,6 +222,7 @@ pcmk__request_fencing(stonith_t *st, const char *target, const char *action, mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); free(async_fence_data.name); diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 8e655d1f6e3..51f143fbdd2 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -2374,9 +2374,7 @@ main(int argc, char **argv) cib__clean_up_connection(&cib_conn); pcmk_free_ipc_api(controld_api); pcmk_free_scheduler(scheduler); - if (mainloop != NULL) { - g_main_loop_unref(mainloop); - } + g_clear_pointer(&mainloop, g_main_loop_unref); pcmk__output_and_clear_error(&error, out); From ca1fd734782710291a484ce70914186dfd396101 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 19:55:25 -0800 Subject: [PATCH 127/202] Refactor: attrd: Drop mainloop_destroy_signal() from attrd_shutdown() mainloop_add_signal() adds mainloop_signal_handler() as a "wrapper" handler for the given signal. If the signal is received, mainloop_signal_handler() sets a boolean trigger value to true and returns. This is a lightweight operation. The "wrapped" signal handler gets called in a later main loop iteration. However, this can't happen after we've called attrd_shutdown(). Either we exit immediately or we quit the main loop. In either case, there will be no additional main loop iterations. Note that the controller is a bit different. There, we "drain" the main loop, which may run more loop iterations. So it makes some sense to destroy signal handlers before doing so. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_utils.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index fcb757c1093..56277484a81 100644 --- a/daemons/attrd/attrd_utils.c +++ b/daemons/attrd/attrd_utils.c @@ -57,14 +57,6 @@ attrd_shutdown(int nsig) // Tell various functions not to do anthing shutting_down = true; - // Don't respond to signals while shutting down - mainloop_destroy_signal(SIGTERM); - mainloop_destroy_signal(SIGCHLD); - mainloop_destroy_signal(SIGPIPE); - mainloop_destroy_signal(SIGUSR1); - mainloop_destroy_signal(SIGUSR2); - mainloop_destroy_signal(SIGTRAP); - attrd_free_waitlist(); attrd_free_confirmations(); From 28e6d4186f02c03203546616c50a46dfe486746d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 20:02:29 -0800 Subject: [PATCH 128/202] Refactor: attrd: Assume main loop is running in attrd_shutdown() There are two call sites. * attrd_cpg_destroy: This is called only via the main loop. See pcmk_cluster_set_destroy_fn() and pcmk__cpg_connect(). * attrd_shutdown: We can reach this only through the main loop. It's set up as a main loop signal handler via mainloop_add_signal(). The true signal handler is mainloop_signal_handler(), which sets a main loop trigger to call attrd_shutdown(). That trigger can't do anything unless the main loop is running. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_utils.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index 56277484a81..00103cdef30 100644 --- a/daemons/attrd/attrd_utils.c +++ b/daemons/attrd/attrd_utils.c @@ -62,14 +62,11 @@ attrd_shutdown(int nsig) g_clear_pointer(&peer_protocol_vers, g_hash_table_destroy); - if ((mloop == NULL) || !g_main_loop_is_running(mloop)) { - /* If there's no main loop active, just exit. This should be possible - * only if we get SIGTERM in brief windows at start-up and shutdown. - */ - crm_exit(CRM_EX_OK); - } else { - g_main_loop_quit(mloop); - } + // There should be no way to get here without the main loop running + CRM_CHECK((mloop != NULL) && g_main_loop_is_running(mloop), + crm_exit(CRM_EX_OK)); + + g_main_loop_quit(mloop); } /*! From 38dadf0a44750604968c15b0d8ad86a6505ba92e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 22:40:34 -0800 Subject: [PATCH 129/202] Refactor: based: Don't init node caches explicitly And assume the cache is already initialized when we call based_peer_callback(). pcmk__corosync_connect() initializes the node caches by calling pcmk__get_node(). Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 18 ++---------------- daemons/based/pacemaker-based.c | 1 - 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index d091fd1162f..b8019845b91 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -30,29 +30,15 @@ static pcmk_cluster_t *cluster = NULL; static void -based_peer_callback(xmlNode *msg, void *private_data) +based_peer_callback(xmlNode *msg) { - const char *reason = NULL; const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); - if (pcmk__peer_cache == NULL) { - reason = "membership not established"; - goto bail; - } - if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); } based_process_request(msg, true, NULL); - return; - - bail: - if (reason) { - const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); - - pcmk__warn("Discarding %s message from %s: %s", op, originator, reason); - } } #if SUPPORT_COROSYNC @@ -83,7 +69,7 @@ based_cpg_dispatch(cpg_handle_t handle, return; } pcmk__xe_set(xml, PCMK__XA_SRC, from); - based_peer_callback(xml, NULL); + based_peer_callback(xml); pcmk__xml_free(xml); free(data); diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 005bd0b3c03..ff3682cfe6f 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -372,7 +372,6 @@ main(int argc, char **argv) goto done; } - pcmk__cluster_init_node_caches(); based_io_init(); /* Read initial CIB. based_read_cib() returns new, non-NULL XML, so this From dba981323ea8709e7e16ec20f0280b1814aeb049 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 22:44:09 -0800 Subject: [PATCH 130/202] Refactor: based: Drop based_peer_callback() Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 21 ++++++++------------- lib/cluster/corosync.c | 3 +++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index b8019845b91..9f925c31867 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -29,18 +29,6 @@ static pcmk_cluster_t *cluster = NULL; -static void -based_peer_callback(xmlNode *msg) -{ - const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); - - if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); - } - - based_process_request(msg, true, NULL); -} - #if SUPPORT_COROSYNC static void based_cpg_dispatch(cpg_handle_t handle, @@ -68,8 +56,15 @@ based_cpg_dispatch(cpg_handle_t handle, free(data); return; } + pcmk__xe_set(xml, PCMK__XA_SRC, from); - based_peer_callback(xml); + + if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(msg, PCMK__XA_SRC)); + } + + based_process_request(msg, true, NULL); pcmk__xml_free(xml); free(data); diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 2d0e52f556b..87596541b82 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -461,6 +461,9 @@ pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, * \param[in,out] cluster Initialized cluster object to connect * * \return Standard Pacemaker return code + * + * \note This initializes the node caches on success by calling + * \c pcmk__get_node(). */ int pcmk__corosync_connect(pcmk_cluster_t *cluster) From 1ecae199a41d8b0b0b06191ec13e74870d97b636 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 03:19:10 -0800 Subject: [PATCH 131/202] API: libcib: Deprecate cib_query and cib_connected_query This doesn't meaningfully change behavior except in the following ways. If you call the signon() method for a cib_native cib_t object, and you pass cib_query for the type: * The resulting cib_t:state value on success is cib_connected_command. Previously it was cib_connected_query. * The resulting connection can be used for read/write requests. Previously it could only be used for read-only requests. The cib_query (read-only) vs. cib_command (read/write) API was added when the CIB API was first created, in commit 58fdec76 (2004). No rationale was given. All cib_file and cib_remote clients use cib_command unconditionally. Their signon() methods ignore the type argument. Requests that the CIB manager receives via the cluster layer (Corosync) are also given read/write privileges. Read-only connections have been restricted to cib_native clients that sign on with type=cib_query. The asymmetry of cib_native clients vs. cib_remote clients doesn't seem to make sense, and cib_query doesn't seem especially useful. One can make an argument that it promotes safety/least-privilege by allowing a client to ensure that it doesn't modify the CIB. However, this can be achieved with some basic discipline, and it already wasn't available for cib_remote connections. Supporting read-only connections complicates our CIB manager implementation substantially. We will now treat connections created with cib_query the same as those created with cib_command, and this will simplify things by a lot. Signed-off-by: Reid Wahl --- daemons/execd/remoted_schemas.c | 4 ++-- include/crm/cib/cib_types.h | 7 ++++++- lib/cib/cib_native.c | 10 ++++------ python/pacemaker/_cts/patterns.py | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/daemons/execd/remoted_schemas.c b/daemons/execd/remoted_schemas.c index da4fece9267..191ed00b854 100644 --- a/daemons/execd/remoted_schemas.c +++ b/daemons/execd/remoted_schemas.c @@ -1,5 +1,5 @@ /* - * Copyright 2023-2025 the Pacemaker project contributors + * Copyright 2023-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -144,7 +144,7 @@ get_schema_files(void) _exit(CRM_EX_OSERR); } - rc = cib->cmds->signon(cib, crm_system_name, cib_query); + rc = cib->cmds->signon(cib, crm_system_name, cib_command); rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { pcmk__err("Could not connect to the CIB manager: %s", pcmk_rc_str(rc)); diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 7f91210ac5c..baca7292775 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -41,6 +41,7 @@ enum cib_state { cib_connected_command, // NOTE: sbd (as of at least 1.5.2) uses this value + //! \deprecated Look for \c cib_connected_command instead cib_connected_query, cib_disconnected @@ -50,6 +51,7 @@ enum cib_conn_type { cib_command, // NOTE: sbd (as of at least 1.5.2) uses this value + //! \deprecated Use \c cib_command instead cib_query, cib_no_connection, @@ -138,7 +140,10 @@ typedef struct cib_s cib_t; */ typedef struct cib_api_operations_s { // NOTE: sbd (as of at least 1.5.2) uses this - // @COMPAT At compatibility break, drop name (always use crm_system_name) + /* @COMPAT At a compatibility break, drop name (always use crm_system_name) + * and type (always use cib_command -- cib_file and cib_remote already do + * this). + */ int (*signon) (cib_t *cib, const char *name, enum cib_conn_type type); // NOTE: sbd (as of at least 1.5.2) uses this diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index a8bf366ca4f..f3ee5dbd283 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -301,16 +301,14 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) switch (type) { case cib_command: case cib_command_nonblocking: - // @COMPAT cib_command_nonblocking is deprecated since 3.0.2 + case cib_query: + /* @COMPAT cib_command_nonblocking and cib_query are deprecated + * since 3.0.2 + */ cib->state = cib_connected_command; channel = PCMK__SERVER_BASED_RW; break; - case cib_query: - cib->state = cib_connected_query; - channel = PCMK__SERVER_BASED_RO; - break; - default: return -ENOTCONN; } diff --git a/python/pacemaker/_cts/patterns.py b/python/pacemaker/_cts/patterns.py index 4b69cfe9a14..2cff9504969 100644 --- a/python/pacemaker/_cts/patterns.py +++ b/python/pacemaker/_cts/patterns.py @@ -177,7 +177,7 @@ def __init__(self): r"error.*: Operation 'reboot' .* using FencingFail returned ", r"getinfo response error: 1$", r"sbd.* error: inquisitor_child: DEBUG MODE IS ACTIVE", - r"sbd.* pcmk:\s*error:.*Connection to cib_ro.* (failed|closed)", + r"sbd.* pcmk:\s*error:.*Connection to cib_rw.* (failed|closed)", ] self._bad_news = [ From 72df8c275273d697a52d336c6b3b6df7c4126c10 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:19:48 -0800 Subject: [PATCH 132/202] Refactor: remoted: Drop cib_proxy_accept_ro() As of the previous commit, no remote client can connect to the PCMK__SERVER_BASED_RO channel. (cib_native clients now use PCMK__SERVER_BASED_RW unconditionally.) So replace the two separate cib_proxy_accept_*() functions with a single based_proxy_accept(). Keep the two ipcs objects (based_ipcs_ro and based_ipcs_rw) for now, since pcmk__serve_based_ipc() needs both. Their callbacks are the same, however. Signed-off-by: Reid Wahl --- daemons/execd/remoted_proxy.c | 55 +++++++++++++---------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c index 0f74d705401..358d70c69b9 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -28,10 +28,9 @@ #include "pacemaker-execd.h" // lrmd_server_send_notify -static qb_ipcs_service_t *cib_ro = NULL; -static qb_ipcs_service_t *cib_rw = NULL; - static qb_ipcs_service_t *attrd_ipcs = NULL; +static qb_ipcs_service_t *based_ipcs_ro = NULL; +static qb_ipcs_service_t *based_ipcs_rw = NULL; static qb_ipcs_service_t *crmd_ipcs = NULL; static qb_ipcs_service_t *fencer_ipcs = NULL; static qb_ipcs_service_t *pacemakerd_ipcs = NULL; @@ -134,27 +133,21 @@ attrd_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) } static int32_t -fencer_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +based_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - return ipc_proxy_accept(c, uid, gid, "stonith-ng"); -} - -static int32_t -pacemakerd_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) -{ - return -EREMOTEIO; + return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RW); } static int32_t -cib_proxy_accept_rw(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +fencer_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RW); + return ipc_proxy_accept(c, uid, gid, "stonith-ng"); } static int32_t -cib_proxy_accept_ro(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +pacemakerd_proxy_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { - return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RO); + return -EREMOTEIO; } int @@ -439,6 +432,14 @@ static struct qb_ipcs_service_handlers attrd_proxy_callbacks = { .connection_destroyed = ipc_proxy_destroy }; +static struct qb_ipcs_service_handlers based_proxy_callbacks = { + .connection_accept = based_proxy_accept, + .connection_created = NULL, + .msg_process = ipc_proxy_dispatch, + .connection_closed = ipc_proxy_closed, + .connection_destroyed = ipc_proxy_destroy +}; + static struct qb_ipcs_service_handlers fencer_proxy_callbacks = { .connection_accept = fencer_proxy_accept, .connection_created = NULL, @@ -455,22 +456,6 @@ static struct qb_ipcs_service_handlers pacemakerd_proxy_callbacks = { .connection_destroyed = NULL }; -static struct qb_ipcs_service_handlers cib_proxy_callbacks_ro = { - .connection_accept = cib_proxy_accept_ro, - .connection_created = NULL, - .msg_process = ipc_proxy_dispatch, - .connection_closed = ipc_proxy_closed, - .connection_destroyed = ipc_proxy_destroy -}; - -static struct qb_ipcs_service_handlers cib_proxy_callbacks_rw = { - .connection_accept = cib_proxy_accept_rw, - .connection_created = NULL, - .msg_process = ipc_proxy_dispatch, - .connection_closed = ipc_proxy_closed, - .connection_destroyed = ipc_proxy_destroy -}; - void ipc_proxy_add_provider(pcmk__client_t *ipc_proxy) { @@ -518,9 +503,9 @@ ipc_proxy_init(void) { ipc_clients = pcmk__strkey_table(NULL, NULL); - pcmk__serve_based_ipc(&cib_ro, &cib_rw, &cib_proxy_callbacks_ro, - &cib_proxy_callbacks_rw); pcmk__serve_attrd_ipc(&attrd_ipcs, &attrd_proxy_callbacks); + pcmk__serve_based_ipc(&based_ipcs_ro, &based_ipcs_rw, + &based_proxy_callbacks, &based_proxy_callbacks); pcmk__serve_fenced_ipc(&fencer_ipcs, &fencer_proxy_callbacks); pcmk__serve_pacemakerd_ipc(&pacemakerd_ipcs, &pacemakerd_proxy_callbacks); crmd_ipcs = pcmk__serve_controld_ipc(&crmd_proxy_callbacks); @@ -540,8 +525,8 @@ ipc_proxy_cleanup(void) g_clear_pointer(&ipc_clients, g_hash_table_destroy); g_clear_pointer(&attrd_ipcs, qb_ipcs_destroy); - g_clear_pointer(&cib_ro, qb_ipcs_destroy); - g_clear_pointer(&cib_rw, qb_ipcs_destroy); + g_clear_pointer(&based_ipcs_ro, qb_ipcs_destroy); + g_clear_pointer(&based_ipcs_rw, qb_ipcs_destroy); g_clear_pointer(&crmd_ipcs, qb_ipcs_destroy); g_clear_pointer(&fencer_ipcs, qb_ipcs_destroy); g_clear_pointer(&pacemakerd_ipcs, qb_ipcs_destroy); From cb69129e46fb015bc79bcc57beba522e2fe78f7d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:27:09 -0800 Subject: [PATCH 133/202] Refactor: based: Check for old PCMK__SERVER_BASED_RW instance If there was a PCMK__SERVER_BASED_RO instance, there also should have been a PCMK__SERVER_BASED_RW instance. We're moving toward using PCMK__SERVER_BASED_RW everywhere, so use it here. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index ff3682cfe6f..cc7e697f13c 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -330,7 +330,7 @@ main(int argc, char **argv) crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); pcmk__notice("Starting Pacemaker CIB manager"); - old_instance = crm_ipc_new(PCMK__SERVER_BASED_RO, 0); + old_instance = crm_ipc_new(PCMK__SERVER_BASED_RW, 0); if (old_instance == NULL) { /* crm_ipc_new() will have already logged an error message with * pcmk__err() From 2bcc43cbaa1e9aaa414fa71a27e4f2616fee327b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:01:28 -0800 Subject: [PATCH 134/202] Refactor: based: Make PCMK__SERVER_BASED_RO behave like _RW This affects only proxied connections from older Pacemaker Remote nodes. IPC clients on cluster nodes and on same-version Pacemaker Remote nodes use PCMK__SERVER_BASED_RW unconditionally (as of a recent commit that updated cib_native_signon()). This shouldn't cause any breakages or noticeable changes for existing clients. The capabilities of PCMK__SERVER_BASED_RW were already a proper superset of the capabilities of PCMK__SERVER_BASED_RO. That is, a client connected to the PCMK__SERVER_BASED_RW channel could do anything that a client connected to the PCMK__SERVER_BASED_RO channel can do, but not vice-versa. So nothing that previously worked should break. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 69 ++++++--------------------------------- 1 file changed, 10 insertions(+), 59 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 9e32dfdbd5f..0da2361a3d2 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -61,17 +61,16 @@ based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) * \internal * \brief Handle a message from an IPC connection * - * \param[in,out] c Established IPC connection - * \param[in] data The message data read from the connection - this - * can be a complete IPC message or just a part of - * one if it's very large - * \param[in] privileged If \c true, operations with - * \c cib__op_attr_privileged can be run + * \param[in,out] c Established IPC connection + * \param[in] data The message data read from the connection - this can be + * a complete IPC message or just a part of one if it's + * very large + * \param[in] size Unused * * \return 0 in all cases */ static int32_t -dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) +based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) { int rc = pcmk_rc_ok; uint32_t id = 0; @@ -88,9 +87,6 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) return 0; } - pcmk__trace("Dispatching %sprivileged request from client %s", - (privileged? "" : "un"), client->id); - rc = pcmk__ipc_msg_append(&client->buffer, data); if (rc == pcmk_rc_ipc_more) { @@ -202,49 +198,13 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) goto cleanup; } - based_process_request(msg, privileged, client); + based_process_request(msg, true, client); cleanup: pcmk__xml_free(msg); return 0; } -/*! - * \internal - * \brief Handle a message from a read-only IPC connection - * - * \param[in,out] c Established IPC connection - * \param[in] data The message data read from the connection - this can be - * a complete IPC message or just a part of one if it's - * very large - * \param[in] size Unused - * - * \return 0 in all cases - */ -static int32_t -based_ipc_dispatch_ro(qb_ipcs_connection_t *c, void *data, size_t size) -{ - return dispatch_common(c, data, false); -} - -/*! - * \internal - * \brief Handle a message from a read/write IPC connection - * - * \param[in,out] c Established IPC connection - * \param[in] data The message data read from the connection - this can be - * a complete IPC message or just a part of one if it's - * very large - * \param[in] size Unused - * - * \return 0 in all cases - */ -static int32_t -based_ipc_dispatch_rw(qb_ipcs_connection_t *c, void *data, size_t size) -{ - return dispatch_common(c, data, true); -} - /*! * \internal * \brief Destroy a client IPC connection @@ -281,18 +241,10 @@ based_ipc_destroy(qb_ipcs_connection_t *c) based_ipc_closed(c); } -static struct qb_ipcs_service_handlers ipc_ro_callbacks = { - .connection_accept = based_ipc_accept, - .connection_created = NULL, - .msg_process = based_ipc_dispatch_ro, - .connection_closed = based_ipc_closed, - .connection_destroyed = based_ipc_destroy, -}; - -static struct qb_ipcs_service_handlers ipc_rw_callbacks = { +static struct qb_ipcs_service_handlers ipc_callbacks = { .connection_accept = based_ipc_accept, .connection_created = NULL, - .msg_process = based_ipc_dispatch_rw, + .msg_process = based_ipc_dispatch, .connection_closed = based_ipc_closed, .connection_destroyed = based_ipc_destroy, }; @@ -304,8 +256,7 @@ static struct qb_ipcs_service_handlers ipc_rw_callbacks = { void based_ipc_init(void) { - pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipc_ro_callbacks, - &ipc_rw_callbacks); + pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipc_callbacks, &ipc_callbacks); } /*! From e2ae6bbec4c393813c8dcb35f58d2f349c1abc4f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:41:05 -0800 Subject: [PATCH 135/202] Refactor: based: Drop privileged argument of based_process_request() We always pass true. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 18 +++++------------- daemons/based/based_callbacks.h | 5 +---- daemons/based/based_corosync.c | 3 +-- daemons/based/based_ipc.c | 3 +-- daemons/based/based_remote.c | 2 +- daemons/based/based_transaction.c | 7 ++----- 6 files changed, 11 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 637090097e5..f7495761e24 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -648,17 +648,14 @@ send_peer_reply(xmlNode *msg, const char *originator) * \internal * \brief Handle an IPC or CPG message containing a request * - * \param[in,out] request Request XML - * \param[in] privileged If \c true, operations with - * \c cib__op_attr_privileged can be run - * \param[in] client IPC client that sent request (\c NULL if request - * came from CPG) + * \param[in,out] request Request XML + * \param[in] client IPC client that sent request (\c NULL if request came + * from CPG) * * \return Standard Pacemaker return code */ int -based_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client) +based_process_request(xmlNode *request, const pcmk__client_t *client) { // @TODO: Break into multiple smaller functions uint32_t call_options = cib_none; @@ -779,12 +776,7 @@ based_process_request(xmlNode *request, bool privileged, start_time = time(NULL); - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { - - rc = EACCES; - - } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { rc = cib__perform_op_ro(op_function, request, &based_cib, &output); } else { diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 7363e7ab2d6..93565ef1a93 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -10,8 +10,6 @@ #ifndef BASED_CALLBACKS__H #define BASED_CALLBACKS__H -#include - #include // xmlNode #include // pcmk__client_t @@ -19,7 +17,6 @@ void based_callbacks_init(void); void based_callbacks_cleanup(void); -int based_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client); +int based_process_request(xmlNode *request, const pcmk__client_t *client); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 9f925c31867..0aeed8817bf 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -10,7 +10,6 @@ #include #include // PRIu32 -#include #include // NULL, size_t #include // uint32_t #include // free @@ -64,7 +63,7 @@ based_cpg_dispatch(cpg_handle_t handle, pcmk__xe_get(msg, PCMK__XA_SRC)); } - based_process_request(msg, true, NULL); + based_process_request(msg, NULL); pcmk__xml_free(xml); free(data); diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 0da2361a3d2..a431b3cda76 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -10,7 +10,6 @@ #include #include // ECONNREFUSED, ENOMEM -#include #include // NULL, size_t #include // int32_t, uint32_t #include // gid_t, uid_t @@ -198,7 +197,7 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) goto cleanup; } - based_process_request(msg, true, client); + based_process_request(msg, client); cleanup: pcmk__xml_free(msg); diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 6f9c158424c..82a1c412281 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -393,7 +393,7 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) based_update_notify_flags(command, client); } - based_process_request(command, true, client); + based_process_request(command, client); } static int diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 61f84d4b331..38d445bafa6 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -10,7 +10,6 @@ #include #include // EOPNOTSUPP -#include #include // NULL #include // free @@ -80,11 +79,9 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, || (host != NULL)) { rc = EOPNOTSUPP; + } else { - /* Commit-transaction is a privileged operation. If we reached - * this point, the request came from a privileged connection. - */ - rc = based_process_request(request, true, client); + rc = based_process_request(request, client); } } From d2d807039edb39bef5155af34e0fe23854412fc4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:44:07 -0800 Subject: [PATCH 136/202] Refactor: libcib: Drop cib__op_attr_privileged Nothing uses it anymore. Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 3 --- lib/cib/cib_ops.c | 47 +++++++++++--------------------------- 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 1e0b0370357..e82ced195d2 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -51,9 +51,6 @@ enum cib__op_attr { //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), - //! Requires privileges - cib__op_attr_privileged = (UINT32_C(1) << 2), - //! Must only be processed locally cib__op_attr_local = (UINT32_C(1) << 3), diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 83393316e82..39575a1d9a1 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -35,56 +35,38 @@ static GHashTable *operation_table = NULL; static const cib__operation_t cib_ops[] = { { - PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete, - cib__op_attr_modifies|cib__op_attr_privileged + PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete, cib__op_attr_modifies }, { PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_BUMP, cib__op_bump, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_replaces - |cib__op_attr_writes_through + cib__op_attr_modifies|cib__op_attr_replaces|cib__op_attr_writes_through }, { PCMK__CIB_REQUEST_CREATE, cib__op_create, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_DELETE, cib__op_delete, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_ERASE, cib__op_erase, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_replaces - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_replaces|cib__op_attr_transaction }, { - PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary, - cib__op_attr_privileged + PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary, cib__op_attr_none }, { PCMK__CIB_REQUEST_MODIFY, cib__op_modify, - cib__op_attr_modifies - |cib__op_attr_privileged - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none @@ -95,7 +77,7 @@ static const cib__operation_t cib_ops[] = { { // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, - cib__op_attr_modifies|cib__op_attr_privileged|cib__op_attr_local + cib__op_attr_modifies|cib__op_attr_local }, { PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none @@ -103,7 +85,6 @@ static const cib__operation_t cib_ops[] = { { PCMK__CIB_REQUEST_REPLACE, cib__op_replace, cib__op_attr_modifies - |cib__op_attr_privileged |cib__op_attr_replaces |cib__op_attr_writes_through |cib__op_attr_transaction @@ -112,19 +93,17 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local }, { - PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, - cib__op_attr_privileged|cib__op_attr_local + PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, cib__op_attr_local }, { - PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_privileged + PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_none }, { - PCMK__CIB_REQUEST_SYNC, cib__op_sync, cib__op_attr_privileged + PCMK__CIB_REQUEST_SYNC, cib__op_sync, cib__op_attr_none }, { PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, cib__op_attr_modifies - |cib__op_attr_privileged |cib__op_attr_writes_through |cib__op_attr_transaction }, From 9e30b69485542b4481d277e836493f0b91db819c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:56:47 -0800 Subject: [PATCH 137/202] Refactor: liblrmd: Map proxied PCMK__SERVER_BASED_RO clients to _RW This happens only on the cluster node that is providing an IPC proxy for a Pacemaker Remote client. This way, we'll treat the client as being associated with PCMK__SERVER_BASED_RW in every way. We no longer have anything using PCMK__SERVER_BASED_RO except for this function that maps it to PCMK__SERVER_BASED_RW, and pcmk__parse_server() (which should no longer be receiving PCMK__SERVER_BASED_RO as an argument). Signed-off-by: Reid Wahl --- daemons/controld/controld_remote_proxy.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/daemons/controld/controld_remote_proxy.c b/daemons/controld/controld_remote_proxy.c index ea4ed0f3d09..2a36b912081 100644 --- a/daemons/controld/controld_remote_proxy.c +++ b/daemons/controld/controld_remote_proxy.c @@ -189,6 +189,16 @@ remote_proxy_new(lrmd_t *lrmd, const char *node_name, const char *session_id, return NULL; } + /* @COMPAT Proxied clients from Pacemaker Remote nodes older than version + * 3.0.2 can connect using PCMK__SERVER_BASED_RO. Since we use + * PCMK__SERVER_BASED_RW for everything now, and since no local or same- + * versioned proxied clients can connect to PCMK__SERVER_BASED_RO, just map + * it to PCMK__SERVER_BASED_RW here. + */ + if (pcmk__str_eq(channel, PCMK__SERVER_BASED_RO, pcmk__str_none)) { + channel = PCMK__SERVER_BASED_RW; + } + proxy = pcmk__assert_alloc(1, sizeof(remote_proxy_t)); proxy->node_name = pcmk__str_copy(node_name); From 752031903c2fa71d5eed23d043ffb5d5cad32b0d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 16:13:27 -0800 Subject: [PATCH 138/202] Refactor: libcrmcommon: pcmk__serve_based_ipc() starts only one server Now that there is no possibility of receiving an IPC client connection (either local or proxied, whether in a rolling upgrade or not) with server name set to PCMK__SERVER_BASED_RO, we don't need to serve anything with that name anymore. _RO already behaved the same as _RW. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 12 ++++------ daemons/execd/remoted_proxy.c | 9 +++----- include/crm/common/ipc_internal.h | 6 ++--- lib/common/ipc_server.c | 23 +++++++------------ lib/common/servers.c | 38 +++++++++++++++++-------------- 5 files changed, 38 insertions(+), 50 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index a431b3cda76..3b6d989c5ff 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -27,8 +27,7 @@ #include "pacemaker-based.h" -static qb_ipcs_service_t *ipcs_ro = NULL; -static qb_ipcs_service_t *ipcs_rw = NULL; +static qb_ipcs_service_t *ipcs = NULL; /*! * \internal @@ -255,7 +254,7 @@ static struct qb_ipcs_service_handlers ipc_callbacks = { void based_ipc_init(void) { - pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipc_callbacks, &ipc_callbacks); + pcmk__serve_based_ipc(&ipcs, &ipc_callbacks); } /*! @@ -265,11 +264,8 @@ based_ipc_init(void) void based_ipc_cleanup(void) { - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + pcmk__drop_all_clients(ipcs); + g_clear_pointer(&ipcs, qb_ipcs_destroy); /* Drop remote clients here because they're part of the IPC client table and * must be dropped before \c pcmk__client_cleanup() diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c index 358d70c69b9..47cd2d8c7c9 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -29,8 +29,7 @@ #include "pacemaker-execd.h" // lrmd_server_send_notify static qb_ipcs_service_t *attrd_ipcs = NULL; -static qb_ipcs_service_t *based_ipcs_ro = NULL; -static qb_ipcs_service_t *based_ipcs_rw = NULL; +static qb_ipcs_service_t *based_ipcs = NULL; static qb_ipcs_service_t *crmd_ipcs = NULL; static qb_ipcs_service_t *fencer_ipcs = NULL; static qb_ipcs_service_t *pacemakerd_ipcs = NULL; @@ -504,8 +503,7 @@ ipc_proxy_init(void) ipc_clients = pcmk__strkey_table(NULL, NULL); pcmk__serve_attrd_ipc(&attrd_ipcs, &attrd_proxy_callbacks); - pcmk__serve_based_ipc(&based_ipcs_ro, &based_ipcs_rw, - &based_proxy_callbacks, &based_proxy_callbacks); + pcmk__serve_based_ipc(&based_ipcs, &based_proxy_callbacks); pcmk__serve_fenced_ipc(&fencer_ipcs, &fencer_proxy_callbacks); pcmk__serve_pacemakerd_ipc(&pacemakerd_ipcs, &pacemakerd_proxy_callbacks); crmd_ipcs = pcmk__serve_controld_ipc(&crmd_proxy_callbacks); @@ -525,8 +523,7 @@ ipc_proxy_cleanup(void) g_clear_pointer(&ipc_clients, g_hash_table_destroy); g_clear_pointer(&attrd_ipcs, qb_ipcs_destroy); - g_clear_pointer(&based_ipcs_ro, qb_ipcs_destroy); - g_clear_pointer(&based_ipcs_rw, qb_ipcs_destroy); + g_clear_pointer(&based_ipcs, qb_ipcs_destroy); g_clear_pointer(&crmd_ipcs, qb_ipcs_destroy); g_clear_pointer(&fencer_ipcs, qb_ipcs_destroy); g_clear_pointer(&pacemakerd_ipcs, qb_ipcs_destroy); diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h index db1bec65fe5..46a8108c702 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -249,10 +249,8 @@ void pcmk__serve_schedulerd_ipc(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); qb_ipcs_service_t *pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb); -void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, - qb_ipcs_service_t **ipcs_rw, - struct qb_ipcs_service_handlers *ro_cb, - struct qb_ipcs_service_handlers *rw_cb); +void pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb); static inline const char * pcmk__ipc_sys_name(const char *ipc_name, const char *fallback) diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index b0d13eb85d9..1c2a8888f68 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1017,28 +1017,21 @@ pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c, * \internal * \brief Add an IPC server to the main loop for the CIB manager API * - * \param[out] ipcs_ro New IPC server for read-only CIB manager API - * \param[out] ipcs_rw New IPC server for read/write CIB manager API - * \param[in] ro_cb IPC callbacks for read-only API - * \param[in] rw_cb IPC callbacks for read/write and shared-memory APIs + * \param[out] ipcs Where to store newly created IPC server + * \param[in] cb IPC callbacks * * \note This function exits fatally on error. */ void -pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, - struct qb_ipcs_service_handlers *ro_cb, - struct qb_ipcs_service_handlers *rw_cb) +pcmk__serve_based_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb) { - pcmk__assert((ipcs_ro != NULL) && (*ipcs_ro == NULL) && (ro_cb != NULL) - && (ipcs_rw != NULL) && (*ipcs_rw == NULL) && (rw_cb != NULL)); - - *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO, QB_IPC_SHM, - ro_cb); + pcmk__assert((ipcs != NULL) && (*ipcs == NULL) && (cb != NULL)); - *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW, QB_IPC_SHM, - rw_cb); + *ipcs = mainloop_add_ipc_server(pcmk__server_ipc_name(pcmk_ipc_based), + QB_IPC_SHM, cb); - if ((*ipcs_ro == NULL) || (*ipcs_rw == NULL)) { + if (*ipcs == NULL) { pcmk__crit("Failed to create %s IPC server; shutting down", pcmk__server_log_name(pcmk_ipc_based)); pcmk__crit("Verify pacemaker and pacemaker_remote are not both " diff --git a/lib/common/servers.c b/lib/common/servers.c index 6a4fdc7405b..e5a6715db2f 100644 --- a/lib/common/servers.c +++ b/lib/common/servers.c @@ -29,66 +29,72 @@ * members, and libqb IPC server endpoints for both the old and new names, and * could drop the old names only after we no longer supported connections with * older nodes. + * + * @TODO It would be easy to use system_names[0] as a server's IPC name. + * Everything would automatically use the new names except for proxied + * connections from *older* Pacemaker Remote nodes. We would just have to map + * the old names to the new names in remote_proxy_new(), the same as we're + * currently mapping PCMK__SERVER_BASED_RO to PCMK__SERVER_BASED_RW there. */ static struct { const char *log_name; // Readable server name for use in logs const char *system_names[2]; // crm_system_name values (subdaemon names) - const char *ipc_names[2]; // libqb IPC names used to contact server + const char *ipc_name; // libqb IPC name used to contact server const char *message_types[3]; // IPC/cluster message types sent to server } server_info[] = { [pcmk_ipc_unknown] = { NULL, { NULL, NULL, }, - { NULL, NULL, }, + NULL, { NULL, NULL, NULL, }, }, [pcmk_ipc_attrd] = { "attribute manager", { PCMK__SERVER_ATTRD, NULL, }, - { PCMK__VALUE_ATTRD, NULL, }, + PCMK__VALUE_ATTRD, { PCMK__VALUE_ATTRD, NULL, NULL, }, }, [pcmk_ipc_based] = { "CIB manager", { PCMK__SERVER_BASED, NULL, }, - { PCMK__SERVER_BASED_RW, PCMK__SERVER_BASED_RO, }, + PCMK__SERVER_BASED_RW, { CRM_SYSTEM_CIB, NULL, NULL, }, }, [pcmk_ipc_controld] = { "controller", { PCMK__SERVER_CONTROLD, NULL, }, - { PCMK__VALUE_CRMD, NULL, }, + PCMK__VALUE_CRMD, { PCMK__VALUE_CRMD, CRM_SYSTEM_DC, CRM_SYSTEM_TENGINE, }, }, [pcmk_ipc_execd] = { "executor", { PCMK__SERVER_EXECD, PCMK__SERVER_REMOTED, }, - { PCMK__VALUE_LRMD, NULL, }, + PCMK__VALUE_LRMD, { PCMK__VALUE_LRMD, NULL, NULL, }, }, [pcmk_ipc_fenced] = { "fencer", { PCMK__SERVER_FENCED, NULL, }, - { PCMK__VALUE_STONITH_NG, NULL, }, + PCMK__VALUE_STONITH_NG, { PCMK__VALUE_STONITH_NG, NULL, NULL, }, }, [pcmk_ipc_pacemakerd] = { "launcher", { PCMK__SERVER_PACEMAKERD, NULL, }, - { CRM_SYSTEM_MCP, NULL, }, + CRM_SYSTEM_MCP, { CRM_SYSTEM_MCP, NULL, NULL, }, }, [pcmk_ipc_schedulerd] = { "scheduler", { PCMK__SERVER_SCHEDULERD, NULL, }, - { CRM_SYSTEM_PENGINE, NULL, }, + CRM_SYSTEM_PENGINE, { CRM_SYSTEM_PENGINE, NULL, NULL, }, }, }; @@ -131,7 +137,7 @@ pcmk__server_log_name(enum pcmk_ipc_server server) /*! * \internal - * \brief Return the (primary) IPC endpoint name for a server + * \brief Return the IPC endpoint name for a server * * \param[in] server Server to get IPC endpoint for * @@ -144,7 +150,7 @@ pcmk__server_ipc_name(enum pcmk_ipc_server server) { CRM_CHECK((server > 0) && (server < PCMK__NELEM(server_info)), return NULL); - return server_info[server].ipc_names[0]; + return server_info[server].ipc_name; } /*! @@ -191,13 +197,11 @@ pcmk__parse_server(const char *text) return server; } } - for (name = 0; - (name < 2) && (server_info[server].ipc_names[name] != NULL); - ++name) { - if (strcmp(text, server_info[server].ipc_names[name]) == 0) { - return server; - } + + if (pcmk__str_eq(text, server_info[server].ipc_name, pcmk__str_none)) { + return server; } + for (name = 0; (name < 3) && (server_info[server].message_types[name] != NULL); ++name) { From 8d6a97f9fb33053f3287a48be7ea3454aa6e350d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 17:49:04 -0800 Subject: [PATCH 139/202] Refactor: based: Standardize based_cpg_dispatch() Make it look like fenced_cpg_dispatch() and attrd_cpg_dispatch(), and have it call a based_peer_message() (structured similarly to fenced_peer_message() and attrd_peer_message()). Not everything is in accordance with my preferences here, but the goal is to make it look as similar as possible to the other daemons. Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 88 +++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 0aeed8817bf..82c94227a25 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -28,43 +28,87 @@ static pcmk_cluster_t *cluster = NULL; -#if SUPPORT_COROSYNC static void -based_cpg_dispatch(cpg_handle_t handle, - const struct cpg_name *groupName, - uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +based_peer_message(pcmk__node_status_t *peer, xmlNode *xml) { - char *data = NULL; - const char *from = NULL; - xmlNode *xml = NULL; + int rc = pcmk_rc_ok; if (based_shutting_down()) { - pcmk__info("Ignoring CPG message from node %" PRIu32 " during shutdown", - nodeid); + pcmk__info("Ignoring CPG message from %s[%" PRIu32 "] during shutdown", + peer->name, peer->cluster_layer_id); return; + + } else { + pcmk__request_t request = { + .ipc_client = NULL, + .ipc_id = 0, + .ipc_flags = 0, + .peer = peer->name, + .xml = xml, + .call_options = cib_none, + .result = PCMK__UNKNOWN_RESULT, + }; + + rc = pcmk__xe_get_flags(xml, PCMK__XA_CIB_CALLOPT, + (uint32_t *) &request.call_options, cib_none); + if (rc != pcmk_rc_ok) { + pcmk__warn("Couldn't parse options from request: %s", + pcmk_rc_str(rc)); + } + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + if (pcmk__xe_get(request.xml, PCMK__XA_CIB_CLIENTNAME) == NULL) { + pcmk__xe_set(request.xml, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(request.xml, PCMK__XA_SRC)); + } + + based_process_request(request.xml, request.ipc_client); + pcmk__reset_request(&request); } +} + +#if SUPPORT_COROSYNC +/*! + * \internal + * \brief Callback for when a peer message is received + * + * \param[in] handle Cluster connection + * \param[in] group_name Group that \p nodeid is a member of + * \param[in] nodeid Peer node that sent \p msg + * \param[in] pid Process that sent \p msg + * \param[in,out] msg Received message + * \param[in] msg_len Length of \p msg + */ +static void +based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *group_name, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +{ + xmlNode *xml = NULL; + const char *from = NULL; + char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); - data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); if (data == NULL) { return; } xml = pcmk__xml_parse(data); if (xml == NULL) { - pcmk__err("Invalid XML: '%.120s'", data); - free(data); - return; + pcmk__err("Bad message received from %s[%" PRIu32 "]: '%.120s'", from, + nodeid, data); + + } else { + pcmk__xe_set(xml, PCMK__XA_SRC, from); + based_peer_message(pcmk__get_node(nodeid, from, NULL, + pcmk__node_search_cluster_member), + xml); } - pcmk__xe_set(xml, PCMK__XA_SRC, from); - - if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, - pcmk__xe_get(msg, PCMK__XA_SRC)); - } - - based_process_request(msg, NULL); - pcmk__xml_free(xml); free(data); } From d340d06f61211098da70b9add41e5065d9fd5ef0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:11:38 -0800 Subject: [PATCH 140/202] Refactor: based: Standardize based_ipc_dispatch() Make it look like fenced_ipc_dispatch() and attrd_ipc_dispatch(). Not everything is in accordance with my preferences here, but the goal is to make it look as similar as possible to the other daemons. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 41 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 3b6d989c5ff..353c09d6832 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -70,13 +70,13 @@ based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) static int32_t based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) { - int rc = pcmk_rc_ok; uint32_t id = 0; uint32_t flags = 0; uint32_t call_options = cib_none; xmlNode *msg = NULL; pcmk__client_t *client = pcmk__find_client(c); const char *op = NULL; + int rc = pcmk_rc_ok; // Sanity-check, and parse XML from IPC data CRM_CHECK(client != NULL, return 0); @@ -122,18 +122,13 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) if (client->name == NULL) { const char *value = pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME); - if (value == NULL) { - client->name = pcmk__itoa(client->pid); - } else { - client->name = pcmk__str_copy(value); - } + client->name = pcmk__assert_asprintf("%s.%u", pcmk__s(value, "unknown"), + client->pid); } rc = pcmk__xe_get_flags(msg, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request from IPC client %s: %s", - client->name, pcmk_rc_str(rc)); - pcmk__log_xml_info(msg, "bad-call-opts"); + pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); } /* Requests with cib_transaction set should not be sent to based directly @@ -148,7 +143,7 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) } if (pcmk__is_set(call_options, cib_sync_call)) { - CRM_LOG_ASSERT(flags & crm_ipc_client_response); + pcmk__assert(pcmk__is_set(flags, crm_ipc_client_response)); // If false, the client has two synchronous events in flight CRM_LOG_ASSERT(client->request_id == 0); @@ -163,8 +158,6 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) CRM_LOG_ASSERT(client->user != NULL); pcmk__update_acl_user(msg, PCMK__XA_CIB_USER, client->user); - pcmk__log_xml_trace(msg, "ipc-request"); - op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { @@ -193,10 +186,28 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) } pcmk__ipc_send_ack(client, id, flags, NULL, status); - goto cleanup; - } - based_process_request(msg, client); + } else { + pcmk__request_t request = { + .ipc_client = client, + .ipc_id = id, + .ipc_flags = flags, + .peer = NULL, + .xml = msg, + .call_options = call_options, + .result = PCMK__UNKNOWN_RESULT, + }; + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return 0); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + based_process_request(request.xml, request.ipc_client); + pcmk__reset_request(&request); + } cleanup: pcmk__xml_free(msg); From 694022dda477ed653d0f5d45c15d20c0eaf5cfe7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:34:13 -0800 Subject: [PATCH 141/202] Refactor: remoted: Minor improvements to remoted__read_handshake_data() Before modeling a new function after it. Signed-off-by: Reid Wahl --- daemons/execd/remoted_tls.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c index c5a7f1e2678..5772830791a 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -39,11 +39,12 @@ static int ssock = -1; /*! * \internal - * \brief Read (more) TLS handshake data from client + * \brief Read (more) TLS handshake data from a client * - * \param[in,out] client IPC client doing handshake + * \param[in,out] client IPC client * - * \return 0 on success or more data needed, -1 on error + * \retval 0 on success or more data needed + * \retval -1 on error */ static int remoted__read_handshake_data(pcmk__client_t *client) @@ -51,24 +52,27 @@ remoted__read_handshake_data(pcmk__client_t *client) int rc = pcmk__read_handshake_data(client); if (rc == EAGAIN) { - /* No more data is available at the moment. Just return for now; - * we'll get invoked again once the client sends more. + /* No more data is available at the moment. Just return for now; we'll + * get invoked again once the client sends more. */ return 0; - } else if (rc != pcmk_rc_ok) { + } + + if (rc != pcmk_rc_ok) { return -1; } - if (client->remote->auth_timeout) { + if (client->remote->auth_timeout != 0) { g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; } - client->remote->auth_timeout = 0; pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete); - pcmk__notice("Remote client connection accepted"); + pcmk__notice("Connection from remote client %s accepted", + pcmk__client_name(client)); /* Now that the handshake is done, see if any client TLS certificate is - * close to its expiration date and log if so. If a TLS certificate is not + * close to its expiration date and log if so. If a TLS certificate is not * in use, this function will just return so we don't need to check for the * session type here. */ From 7829d4c95242446024680272dfbb556cfd35f7f6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:37:14 -0800 Subject: [PATCH 142/202] Refactor: based: New based_read_handshake_data() Modeled after remoted__read_handshake_data(). Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 79 ++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 82a1c412281..4d1b38e059d 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -85,6 +85,54 @@ remote_auth_timeout_cb(gpointer data) return G_SOURCE_REMOVE; } +/*! + * \internal + * \brief Read (more) TLS handshake data from a client + * + * \param[in,out] client IPC client + * + * \retval 0 on success or more data needed + * \retval -1 on error + */ +static int +based_read_handshake_data(pcmk__client_t *client) +{ + int rc = pcmk__read_handshake_data(client); + + if (rc == EAGAIN) { + /* No more data is available at the moment. Just return for now; we'll + * get invoked again once the client sends more. + */ + return 0; + } + + if (rc != pcmk_rc_ok) { + return -1; + } + + if (client->remote->auth_timeout != 0) { + g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; + } + + pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete); + pcmk__debug("Completed TLS handshake with remote client %s", + pcmk__client_name(client)); + + /* Now that the handshake is done, see if any client TLS certificate is + * close to its expiration date and log if so. If a TLS certificate is not + * in use, this function will just return so we don't need to check for the + * session type here. + */ + pcmk__tls_check_cert_expiration(client->remote->tls_session); + + // Require the client to authenticate within this time + client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, + remote_auth_timeout_cb, + client); + return 0; +} + /*! * \internal * \brief Check whether a given user is a member of \c CRM_DAEMON_GROUP @@ -410,36 +458,7 @@ cib_remote_msg(gpointer data) if ((PCMK__CLIENT_TYPE(client) == pcmk__client_tls) && !pcmk__is_set(client->flags, pcmk__client_tls_handshake_complete)) { - int rc = pcmk__read_handshake_data(client); - - if (rc == EAGAIN) { - /* No more data is available at the moment. Just return for now; - * we'll get invoked again once the client sends more. - */ - return 0; - } else if (rc != pcmk_rc_ok) { - return -1; - } - - pcmk__debug("Completed TLS handshake with remote client %s", - client_name); - pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete); - if (client->remote->auth_timeout) { - g_source_remove(client->remote->auth_timeout); - } - - /* Now that the handshake is done, see if any client TLS certificate is - * close to its expiration date and log if so. If a TLS certificate is not - * in use, this function will just return so we don't need to check for the - * session type here. - */ - pcmk__tls_check_cert_expiration(client->remote->tls_session); - - // Require the client to authenticate within this time - client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, - remote_auth_timeout_cb, - client); - return 0; + return based_read_handshake_data(client); } rc = pcmk__read_available_remote_data(client->remote); From 81f9f1da6e8de4af3646661d4e0651db7c45ee89 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:57:14 -0800 Subject: [PATCH 143/202] Refactor: based: pcmk__remote_ready() in cib_remote_msg() This makes cib_remote_msg() look more like lrmd_remote_client_msg(). It's not clear why cib_remote_msg() wasn't already calling pcmk__remote_ready() (or should lrmd_remote_client_msg() NOT be calling it?). It seems reasonable to call it, however. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 4d1b38e059d..bcc5ea88d86 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -461,17 +461,30 @@ cib_remote_msg(gpointer data) return based_read_handshake_data(client); } + rc = pcmk__remote_ready(client->remote, 0); + switch (rc) { + case pcmk_rc_ok: + break; + + case ETIME: + // No message available to read + return 0; + + default: + pcmk__trace("Error polling remote client: %s", pcmk_rc_str(rc)); + return -1; + } + rc = pcmk__read_available_remote_data(client->remote); switch (rc) { case pcmk_rc_ok: break; case EAGAIN: - /* We haven't read the whole message yet */ + // We haven't read the whole message yet return 0; default: - /* Error */ pcmk__trace("Error reading from remote client: %s", pcmk_rc_str(rc)); return -1; From c09a459bad5e279626646a24df5c2e2facfca3c5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 19:22:38 -0800 Subject: [PATCH 144/202] Refactor: based: Some cleanup/standardization of cib_remote_msg() Note that if cib_remote_auth() returns true, PCMK_XA_USER must be set. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 59 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index bcc5ea88d86..c1ff93b3341 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -445,11 +445,11 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) } static int -cib_remote_msg(gpointer data) +based_remote_client_dispatch(gpointer data) { - xmlNode *command = NULL; + int rc = pcmk_rc_ok; + xmlNode *msg = NULL; pcmk__client_t *client = data; - int rc; const char *client_name = pcmk__client_name(client); pcmk__trace("Remote %s message received for client %s", @@ -490,45 +490,44 @@ cib_remote_msg(gpointer data) return -1; } - /* must pass auth before we will process anything else */ + // Must pass auth before we will process anything else if (!pcmk__is_set(client->flags, pcmk__client_authenticated)) { - xmlNode *reg; - const char *user = NULL; + xmlNode *cib_result = NULL; - command = pcmk__remote_message_xml(client->remote); - if (!cib_remote_auth(command)) { - pcmk__xml_free(command); + msg = pcmk__remote_message_xml(client->remote); + if (!cib_remote_auth(msg)) { + pcmk__xml_free(msg); return -1; } + if (client->remote->auth_timeout != 0) { + g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; + } + pcmk__set_client_flags(client, pcmk__client_authenticated); - g_source_remove(client->remote->auth_timeout); - client->remote->auth_timeout = 0; - client->name = pcmk__xe_get_copy(command, PCMK_XA_NAME); - user = pcmk__xe_get(command, PCMK_XA_USER); - if (user) { - client->user = pcmk__str_copy(user); - } + client->name = pcmk__xe_get_copy(msg, PCMK_XA_NAME); + client->user = pcmk__xe_get_copy(msg, PCMK_XA_USER); pcmk__notice("Remote connection accepted for authenticated user %s " - QB_XS " client %s", - pcmk__s(user, ""), client_name); + QB_XS " client %s", client->user, + pcmk__client_name(client)); + + cib_result = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT); + pcmk__xe_set(cib_result, PCMK__XA_CIB_OP, CRM_OP_REGISTER); + pcmk__xe_set(cib_result, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__remote_send_xml(client->remote, cib_result); - /* send ACK */ - reg = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT); - pcmk__xe_set(reg, PCMK__XA_CIB_OP, CRM_OP_REGISTER); - pcmk__xe_set(reg, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__remote_send_xml(client->remote, reg); - pcmk__xml_free(reg); - pcmk__xml_free(command); + pcmk__xml_free(cib_result); + pcmk__xml_free(msg); } - command = pcmk__remote_message_xml(client->remote); - if (command != NULL) { + msg = pcmk__remote_message_xml(client->remote); + if (msg != NULL) { pcmk__trace("Remote message received from client %s", client_name); - cib_handle_remote_msg(client, command); - pcmk__xml_free(command); + cib_handle_remote_msg(client, msg); + pcmk__xml_free(msg); } return 0; @@ -593,7 +592,7 @@ cib_remote_listen(gpointer user_data) pcmk__client_t *new_client = NULL; static struct mainloop_fd_callbacks remote_client_fd_callbacks = { - .dispatch = cib_remote_msg, + .dispatch = based_remote_client_dispatch, .destroy = based_remote_client_destroy, }; From cf1a4fb7bf953536cac51fdf08b46f149e24d035 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 19:56:12 -0800 Subject: [PATCH 145/202] Refactor: libcrmcommon: Reorganize pcmk__is_user_in_group() unit test And move it to the correct directory. Signed-off-by: Reid Wahl --- lib/common/tests/acl/Makefile.am | 3 +- lib/common/tests/utils/Makefile.am | 1 + .../pcmk__is_user_in_group_test.c | 47 ++++++++++++------- 3 files changed, 33 insertions(+), 18 deletions(-) rename lib/common/tests/{acl => utils}/pcmk__is_user_in_group_test.c (58%) diff --git a/lib/common/tests/acl/Makefile.am b/lib/common/tests/acl/Makefile.am index e8887ff7851..36c59d89d81 100644 --- a/lib/common/tests/acl/Makefile.am +++ b/lib/common/tests/acl/Makefile.am @@ -13,8 +13,7 @@ include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. -check_PROGRAMS = pcmk__is_user_in_group_test -check_PROGRAMS += pcmk_acl_required_test +check_PROGRAMS = pcmk_acl_required_test check_PROGRAMS += xml_acl_denied_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/utils/Makefile.am b/lib/common/tests/utils/Makefile.am index fc99b307958..266593bfd9a 100644 --- a/lib/common/tests/utils/Makefile.am +++ b/lib/common/tests/utils/Makefile.am @@ -17,6 +17,7 @@ check_PROGRAMS += pcmk__daemon_user_test check_PROGRAMS += pcmk__fail_attr_name_test check_PROGRAMS += pcmk__failcount_name_test check_PROGRAMS += pcmk__getpid_s_test +check_PROGRAMS += pcmk__is_user_in_group_test check_PROGRAMS += pcmk__lastfailure_name_test check_PROGRAMS += pcmk__lookup_user_test check_PROGRAMS += pcmk__realloc_test diff --git a/lib/common/tests/acl/pcmk__is_user_in_group_test.c b/lib/common/tests/utils/pcmk__is_user_in_group_test.c similarity index 58% rename from lib/common/tests/acl/pcmk__is_user_in_group_test.c rename to lib/common/tests/utils/pcmk__is_user_in_group_test.c index b698b4cc244..64ba14ccaab 100644 --- a/lib/common/tests/acl/pcmk__is_user_in_group_test.c +++ b/lib/common/tests/utils/pcmk__is_user_in_group_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2025 the Pacemaker project contributors + * Copyright 2020-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,32 +9,47 @@ #include -#include - #include -#include #include "../../crmcommon_private.h" #include "mock_private.h" -static void -is_pcmk__is_user_in_group(void **state) +static int +setup(void **state) { pcmk__mock_grent = true; + return 0; +} - // null user +static int +teardown(void **state) +{ + pcmk__mock_grent = false; + return 0; +} + +static void +null_args(void **state) +{ + assert_false(pcmk__is_user_in_group(NULL, NULL)); assert_false(pcmk__is_user_in_group(NULL, "grp0")); - // null group assert_false(pcmk__is_user_in_group("user0", NULL)); - // nonexistent group - assert_false(pcmk__is_user_in_group("user0", "nonexistent_group")); - // user is in group +} + +static void +user_in_group(void **state) +{ assert_true(pcmk__is_user_in_group("user0", "grp0")); - // user is not in group - assert_false(pcmk__is_user_in_group("user2", "grp0")); +} - pcmk__mock_grent = false; +static void +user_not_in_group(void **state) +{ + assert_false(pcmk__is_user_in_group("user0", "nonexistent_group")); + assert_false(pcmk__is_user_in_group("user2", "grp0")); } -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(is_pcmk__is_user_in_group)) +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(null_args), + cmocka_unit_test(user_in_group), + cmocka_unit_test(user_not_in_group)) From dff94edcd0458e285de806ce9f4d11b984daf785 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 20:18:32 -0800 Subject: [PATCH 146/202] Refactor: libcrmcommon: Use getgrnam() in pcmk__is_user_in_group() This matches the way we do it in the CIB manager (is_daemon_group_member()) and will let us get rid of that function and use this instead. This required a mocking overhaul, which I'm not thrilled about, but it seems correct and is simpler than before. Signed-off-by: Reid Wahl --- lib/common/mock.c | 56 +++++----------- lib/common/mock_private.h | 14 ++-- .../tests/utils/pcmk__is_user_in_group_test.c | 44 ++++++++++--- lib/common/utils.c | 65 +++++++++++++------ mk/tap.mk | 6 +- 5 files changed, 105 insertions(+), 80 deletions(-) diff --git a/lib/common/mock.c b/lib/common/mock.c index 69d3dc4e4a2..9f14c9fe196 100644 --- a/lib/common/mock.c +++ b/lib/common/mock.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2025 the Pacemaker project contributors + * Copyright 2021-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -223,20 +223,16 @@ __wrap_getpid(void) } -/* setgrent(), getgrent() and endgrent() +/* getgrnam() * - * If pcmk__mock_grent is set to true, getgrent() will behave as if the only + * If pcmk__mock_getgrnam is set to true, getgrnam() will behave as if the only * groups on the system are: * * - grp0 (user0, user1) * - grp1 (user1) - * - grp2 (user2, user1) */ -bool pcmk__mock_grent = false; - -// Index of group that will be returned next from getgrent() -static int group_idx = 0; +bool pcmk__mock_getgrnam = false; // Data used for testing static const char* grp0_members[] = { @@ -247,10 +243,6 @@ static const char* grp1_members[] = { "user1", NULL }; -static const char* grp2_members[] = { - "user2", "user1", NULL -}; - /* An array of "groups" (a struct from grp.h) * * The members of the groups are initalized here to some testing data, casting @@ -260,43 +252,27 @@ static const char* grp2_members[] = { * string literal = const char* (cannot be changed b/c ? ) * vs. char* (it's getting casted to this) */ -static const int NUM_GROUPS = 3; static struct group groups[] = { {(char*)"grp0", (char*)"", 0, (char**)grp0_members}, {(char*)"grp1", (char*)"", 1, (char**)grp1_members}, - {(char*)"grp2", (char*)"", 2, (char**)grp2_members}, }; -// This function resets the group_idx to 0. -void -__wrap_setgrent(void) { - if (pcmk__mock_grent) { - group_idx = 0; - } else { - __real_setgrent(); - } -} - -/* This function returns the next group entry in the list of groups, or - * NULL if there aren't any left. - * group_idx is a global variable which keeps track of where you are in the list +/* This function returns the group entry whose name matches the argument, or + * NULL if no match is found. */ struct group * -__wrap_getgrent(void) { - if (pcmk__mock_grent) { - if (group_idx >= NUM_GROUPS) { - return NULL; +__wrap_getgrnam(const char *name) { + if (pcmk__mock_getgrnam) { + for (int i = 0; i < PCMK__NELEM(groups); i++) { + if (pcmk__str_eq(groups[i].gr_name, name, pcmk__str_none)) { + return &groups[i]; + } } - return &groups[group_idx++]; - } else { - return __real_getgrent(); - } -} -void -__wrap_endgrent(void) { - if (!pcmk__mock_grent) { - __real_endgrent(); + return NULL; + + } else { + return __real_getgrnam(name); } } diff --git a/lib/common/mock_private.h b/lib/common/mock_private.h index 5fa2d918fa6..896f526ae11 100644 --- a/lib/common/mock_private.h +++ b/lib/common/mock_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2021-2025 the Pacemaker project contributors + * Copyright 2021-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -66,18 +66,14 @@ extern bool pcmk__mock_getpid; pid_t __real_getpid(void); pid_t __wrap_getpid(void); -extern bool pcmk__mock_grent; -void __real_setgrent(void); -void __wrap_setgrent(void); -struct group * __wrap_getgrent(void); -struct group * __real_getgrent(void); -void __wrap_endgrent(void); -void __real_endgrent(void); - extern bool pcmk__mock_getpwnam; struct passwd *__real_getpwnam(const char *name); struct passwd *__wrap_getpwnam(const char *name); +extern bool pcmk__mock_getgrnam; +struct group *__real_getgrnam(const char *name); +struct group *__wrap_getgrnam(const char *name); + extern bool pcmk__mock_readlink; ssize_t __real_readlink(const char *restrict path, char *restrict buf, size_t bufsize); diff --git a/lib/common/tests/utils/pcmk__is_user_in_group_test.c b/lib/common/tests/utils/pcmk__is_user_in_group_test.c index 64ba14ccaab..64173ffdfaa 100644 --- a/lib/common/tests/utils/pcmk__is_user_in_group_test.c +++ b/lib/common/tests/utils/pcmk__is_user_in_group_test.c @@ -14,39 +14,67 @@ #include "../../crmcommon_private.h" #include "mock_private.h" +#define assert_user_in_group(user, group, expected) \ + do { \ + /* Primary group: grp1 */ \ + const struct passwd entry = { .pw_gid = 1 }; \ + \ + expect_string(__wrap_getpwnam, name, user); \ + will_return(__wrap_getpwnam, 0); \ + will_return(__wrap_getpwnam, &entry); \ + \ + if (expected) { \ + assert_true(pcmk__is_user_in_group(user, group)); \ + } else { \ + assert_false(pcmk__is_user_in_group(user, group)); \ + } \ + } while (0); + static int setup(void **state) { - pcmk__mock_grent = true; + pcmk__mock_getgrnam = true; + pcmk__mock_getpwnam = true; return 0; } static int teardown(void **state) { - pcmk__mock_grent = false; + pcmk__mock_getgrnam = false; + pcmk__mock_getpwnam = false; return 0; } static void null_args(void **state) { - assert_false(pcmk__is_user_in_group(NULL, NULL)); - assert_false(pcmk__is_user_in_group(NULL, "grp0")); - assert_false(pcmk__is_user_in_group("user0", NULL)); + pcmk__assert_asserts(pcmk__is_user_in_group(NULL, NULL)); + pcmk__assert_asserts(pcmk__is_user_in_group(NULL, "grp0")); + pcmk__assert_asserts(pcmk__is_user_in_group("user0", NULL)); } static void user_in_group(void **state) { - assert_true(pcmk__is_user_in_group("user0", "grp0")); + // user0 is not in grp1's member list + assert_user_in_group("user0", "grp1", true); + + // user1 has grp1 as primary group and is also in grp1's member list + assert_user_in_group("user1", "grp1", true); + + // user1 has grp1 as primary group but is in grp0's member list + assert_user_in_group("user1", "grp0", true); } static void user_not_in_group(void **state) { - assert_false(pcmk__is_user_in_group("user0", "nonexistent_group")); - assert_false(pcmk__is_user_in_group("user2", "grp0")); + // Group does not exist + assert_user_in_group("user0", "nonexistent_group", false); + + // Group exists but user is not a member + assert_user_in_group("user2", "grp0", false); } PCMK__UNIT_TEST(setup, teardown, diff --git a/lib/common/utils.c b/lib/common/utils.c index 883e6e9d0b2..c3550500028 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.c @@ -63,35 +63,62 @@ pcmk_common_cleanup(void) xmlCleanupParser(); } +/*! + * \internal + * \brief Check whether a given user is a member of a given group + * + * \param[in] user User name + * \param[in] group Group name + * + * \return \c true if \p user is a member of \p group, or \c false otherwise + */ bool pcmk__is_user_in_group(const char *user, const char *group) { - struct group *grent; - char **gr_mem; + int rc = pcmk_rc_ok; + gid_t gid = 0; + const struct group *group_entry = NULL; + + pcmk__assert((user != NULL) && (group != NULL)); + + /* group->gr_mem only contains those users that are listed in /etc/group. + * It won't list the user if the group is their primary (that is, it's in + * the GID field in /etc/passwd (or passwd->pw_gid as returned by getpwent). + * So, we first need to perform a primary group check. + */ + rc = pcmk__lookup_user(user, NULL, &gid); + if (rc != pcmk_rc_ok) { + pcmk__info("Could not find user '%s': %s", user, pcmk_rc_str(rc)); + return false; + } + + errno = 0; + group_entry = getgrnam(group); + if (errno != 0) { + pcmk__info("Could not find group '%s': %s", group, strerror(errno)); + return false; + } - if (user == NULL || group == NULL) { + if (group_entry == NULL) { + pcmk__info("Could not find group '%s'", group); return false; } - - setgrent(); - while ((grent = getgrent()) != NULL) { - if (grent->gr_mem == NULL) { - continue; - } - if(strcmp(group, grent->gr_name) != 0) { - continue; - } + if (group_entry->gr_gid == gid) { + return true; + } - gr_mem = grent->gr_mem; - while (*gr_mem != NULL) { - if (!strcmp(user, *gr_mem++)) { - endgrent(); - return true; - } + /* If the primary group didn't match, check if group is a secondary group + * for the user + */ + for (const char *const *member = (const char *const *) group_entry->gr_mem; + *member != NULL; member++) { + + if (pcmk__str_eq(user, *member, pcmk__str_none)) { + return true; } } - endgrent(); + return false; } diff --git a/mk/tap.mk b/mk/tap.mk index bac6d0e7e09..194fc61d34b 100644 --- a/mk/tap.mk +++ b/mk/tap.mk @@ -1,5 +1,5 @@ # -# Copyright 2021-2025 the Pacemaker project contributors +# Copyright 2021-2026 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -19,16 +19,14 @@ CLEANFILES = *.log *.trs WRAPPED = abort \ calloc \ - endgrent \ fopen \ getenv \ getpid \ - getgrent \ + getgrnam \ getpwnam \ readlink \ realloc \ setenv \ - setgrent \ strdup \ unsetenv From db73d53b5160707e2da6dd8cdc2ee63928367b43 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:17:47 -0700 Subject: [PATCH 147/202] Refactor: based: Drop is_daemon_group_member() Call pcmk__is_user_in_group() instead. We lose a bit of specificity at the notice log level, but the more specific logs from pcmk__is_user_in_group() are still available at info level. This also requires moving pcmk__is_user_in_group() to utils_internal.h. And we add a client_name argument to cib_remote_auth(), for better logging if pcmk__is_user_in_group() returns false. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 68 +++---------------- include/crm/common/utils_internal.h | 5 +- lib/common/crmcommon_private.h | 3 - .../tests/utils/pcmk__is_user_in_group_test.c | 1 - 4 files changed, 13 insertions(+), 64 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index c1ff93b3341..bf7a4b10293 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -11,7 +11,6 @@ #include // htons #include // errno, EAGAIN -#include // getgrgid, getgrnam, group #include // PRIx64 #include // sockaddr_in, INADDR_ANY #include @@ -133,61 +132,6 @@ based_read_handshake_data(pcmk__client_t *client) return 0; } -/*! - * \internal - * \brief Check whether a given user is a member of \c CRM_DAEMON_GROUP - * - * \param[in] user User name - * - * \return \c true if \p user is a member of \c CRM_DAEMON_GROUP, or \c false - * otherwise - */ -static bool -is_daemon_group_member(const char *user) -{ - int rc = pcmk_rc_ok; - gid_t gid = 0; - const struct group *group = NULL; - - /* group->gr_mem only contains those users that are listed in /etc/group. - * It won't list the user if the group is their primary (that is, it's in - * the GID field in /etc/passwd (or passwd->pw_gid as returned by getpwent). - * So, we first need to perform a primary group check. - */ - rc = pcmk__lookup_user(user, NULL, &gid); - if (rc != pcmk_rc_ok) { - pcmk__notice("Rejecting remote client: could not find user '%s': %s", - user, pcmk_rc_str(rc)); - return false; - } - - group = getgrnam(CRM_DAEMON_GROUP); - if (group == NULL) { - pcmk__err("Rejecting remote client: " CRM_DAEMON_GROUP " is not a " - "valid group"); - return false; - } - - if (group->gr_gid == gid) { - return true; - } - - /* If that didn't work, check if CRM_DAEMON_GROUP is a secondary group for - * the user. - */ - for (const char *const *member = (const char *const *) group->gr_mem; - *member != NULL; member++) { - - if (pcmk__str_eq(user, *member, pcmk__str_none)) { - return true; - } - } - - pcmk__notice("Rejecting remote client: User %s is not a member of group %s", - user, CRM_DAEMON_GROUP); - return false; -} - #ifdef HAVE_PAM /*! * \internal @@ -341,7 +285,7 @@ authenticate_user(const char *user, const char *passwd) } static bool -cib_remote_auth(xmlNode * login) +cib_remote_auth(xmlNode *login, const char *client_name) { const char *user = NULL; const char *pass = NULL; @@ -379,7 +323,13 @@ cib_remote_auth(xmlNode * login) pcmk__log_xml_debug(login, "auth"); - return is_daemon_group_member(user) && authenticate_user(user, pass); + if (!pcmk__is_user_in_group(user, CRM_DAEMON_GROUP)) { + pcmk__notice("Rejecting remote client %s: User %s is not a member of " + "group %s", client_name, user, CRM_DAEMON_GROUP); + return false; + } + + return authenticate_user(user, pass); } static void @@ -495,7 +445,7 @@ based_remote_client_dispatch(gpointer data) xmlNode *cib_result = NULL; msg = pcmk__remote_message_xml(client->remote); - if (!cib_remote_auth(msg)) { + if (!cib_remote_auth(msg, pcmk__client_name(client))) { pcmk__xml_free(msg); return -1; } diff --git a/include/crm/common/utils_internal.h b/include/crm/common/utils_internal.h index 08f49a0d430..aa545c0f659 100644 --- a/include/crm/common/utils_internal.h +++ b/include/crm/common/utils_internal.h @@ -25,9 +25,12 @@ extern "C" { #define PCMK__NELEM(a) ((int) (sizeof(a)/sizeof(a[0])) ) int pcmk__compare_versions(const char *version1, const char *version2); + int pcmk__daemon_user(uid_t *uid, gid_t *gid); -char *pcmk__generate_uuid(void); +bool pcmk__is_user_in_group(const char *user, const char *group); int pcmk__lookup_user(const char *name, uid_t *uid, gid_t *gid); + +char *pcmk__generate_uuid(void); void pcmk__panic(const char *reason); pid_t pcmk__locate_sbd(void); void pcmk__sleep_ms(unsigned int ms); diff --git a/lib/common/crmcommon_private.h b/lib/common/crmcommon_private.h index 58add461801..fdd9f5c3e77 100644 --- a/lib/common/crmcommon_private.h +++ b/lib/common/crmcommon_private.h @@ -147,9 +147,6 @@ G_GNUC_INTERNAL void pcmk__unpack_acls(xmlDoc *source, xml_doc_private_t *target, const char *user); -G_GNUC_INTERNAL -bool pcmk__is_user_in_group(const char *user, const char *group); - G_GNUC_INTERNAL void pcmk__apply_acls(xmlDoc *doc); diff --git a/lib/common/tests/utils/pcmk__is_user_in_group_test.c b/lib/common/tests/utils/pcmk__is_user_in_group_test.c index 64173ffdfaa..cc862ed7835 100644 --- a/lib/common/tests/utils/pcmk__is_user_in_group_test.c +++ b/lib/common/tests/utils/pcmk__is_user_in_group_test.c @@ -11,7 +11,6 @@ #include -#include "../../crmcommon_private.h" #include "mock_private.h" #define assert_user_in_group(user, group, expected) \ From cfc4a7dd4d6048cde6623c1a1e881ce37e3d8977 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:30:09 -0700 Subject: [PATCH 148/202] Refactor: based: authenticate_user() takes client_name argument For better logging. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 44 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index bf7a4b10293..1581e27b6d0 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -193,14 +193,15 @@ construct_pam_passwd(int num_msg, const struct pam_message **msg, * \internal * \brief Verify the username and password passed for a remote CIB connection * - * \param[in] user Username passed for remote CIB connection - * \param[in] passwd Password passed for remote CIB connection + * \param[in] user Username passed for remote CIB connection + * \param[in] passwd Password passed for remote CIB connection + * \param[in] client_name Remote client name (for logging only) * * \return \c true if the username and password are accepted, otherwise \c false * \note This function rejects all credentials when built without PAM support. */ static bool -authenticate_user(const char *user, const char *passwd) +authenticate_user(const char *user, const char *passwd, const char *client_name) { #ifdef HAVE_PAM int rc = 0; @@ -223,16 +224,17 @@ authenticate_user(const char *user, const char *passwd) rc = pam_start(pam_name, user, &p_conv, &pam_h); if (rc != PAM_SUCCESS) { - pcmk__warn("Rejecting remote client for user %s because PAM " - "initialization failed: %s", - user, pam_strerror(pam_h, rc)); + pcmk__warn("Rejecting remote client %s because PAM initialization " + "failed for user %s: %s", client_name, user, + pam_strerror(pam_h, rc)); goto bail; } // Check user credentials rc = pam_authenticate(pam_h, PAM_SILENT); if (rc != PAM_SUCCESS) { - pcmk__notice("Access for remote user %s denied: %s", user, + pcmk__notice("Rejecting remote client %s because PAM authentication " + "failed for user %s: %s", client_name, user, pam_strerror(pam_h, rc)); goto bail; } @@ -243,33 +245,34 @@ authenticate_user(const char *user, const char *passwd) */ rc = pam_get_item(pam_h, PAM_USER, &p_user); if (rc != PAM_SUCCESS) { - pcmk__warn("Rejecting remote client for user %s because PAM failed to " - "return final user name: %s", + pcmk__warn("Rejecting remote client %s because PAM failed to return " + "the authenticated user name for user %s: %s", client_name, user, pam_strerror(pam_h, rc)); goto bail; } + if (p_user == NULL) { - pcmk__warn("Rejecting remote client for user %s because PAM returned " - "no final user name", - user); + pcmk__warn("Rejecting remote client %s because PAM returned no " + "authenticated user name for user %s", client_name, user); goto bail; } // @TODO Why do we require these to match? if (!pcmk__str_eq(p_user, user, pcmk__str_none)) { - pcmk__warn("Rejecting remote client for user %s because PAM returned " - "different final user name %s", - user, p_user); + pcmk__warn("Rejecting remote client %s because PAM returned " + "non-matching authenticated user name %s for user %s", + client_name, user, p_user); goto bail; } // Check user account restrictions (expiration, etc.) rc = pam_acct_mgmt(pam_h, PAM_SILENT); if (rc != PAM_SUCCESS) { - pcmk__notice("Access for remote user %s denied: %s", user, - pam_strerror(pam_h, rc)); + pcmk__notice("Rejecting remote client %s because PAM denied access to " + "user %s", client_name, user, pam_strerror(pam_h, rc)); goto bail; } + pass = true; bail: @@ -277,9 +280,8 @@ authenticate_user(const char *user, const char *passwd) return pass; #else // @TODO Implement for non-PAM environments - pcmk__warn("Rejecting remote user %s because this build does not have PAM " - "support", - user); + pcmk__warn("Rejecting remote client %s (user %s) because this build does " + "not have PAM support", client_name, user); return false; #endif } @@ -329,7 +331,7 @@ cib_remote_auth(xmlNode *login, const char *client_name) return false; } - return authenticate_user(user, pass); + return authenticate_user(user, pass, client_name); } static void From 10a67149428dc57b23de3768bdc0c6570dfa6036 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:39:16 -0700 Subject: [PATCH 149/202] Refactor: based: Rename login argument to msg For consistency and to simplify an upcoming change. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 1581e27b6d0..af3383a3c49 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -287,43 +287,42 @@ authenticate_user(const char *user, const char *passwd, const char *client_name) } static bool -cib_remote_auth(xmlNode *login, const char *client_name) +cib_remote_auth(xmlNode *msg, const char *client_name) { const char *user = NULL; const char *pass = NULL; const char *tmp = NULL; - if (login == NULL) { + if (msg == NULL) { return false; } - if (!pcmk__xe_is(login, PCMK__XE_CIB_COMMAND)) { + if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { pcmk__warn("Rejecting remote client: Unrecognizable message (element " - "'%s' not '" PCMK__XE_CIB_COMMAND "')", - login->name); - pcmk__log_xml_debug(login, "bad"); + "'%s' not '" PCMK__XE_CIB_COMMAND "')", msg->name); + pcmk__log_xml_debug(msg, "bad"); return false; } - tmp = pcmk__xe_get(login, PCMK_XA_OP); + tmp = pcmk__xe_get(msg, PCMK_XA_OP); if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) { pcmk__warn("Rejecting remote client: Unrecognizable message (operation " "'%s' not 'authenticate')", tmp); - pcmk__log_xml_debug(login, "bad"); + pcmk__log_xml_debug(msg, "bad"); return false; } - user = pcmk__xe_get(login, PCMK_XA_USER); - pass = pcmk__xe_get(login, PCMK__XA_PASSWORD); + user = pcmk__xe_get(msg, PCMK_XA_USER); + pass = pcmk__xe_get(msg, PCMK__XA_PASSWORD); if (!user || !pass) { pcmk__warn("Rejecting remote client: No %s given", ((user == NULL)? "username" : "password")); - pcmk__log_xml_debug(login, "bad"); + pcmk__log_xml_debug(msg, "bad"); return false; } - pcmk__log_xml_debug(login, "auth"); + pcmk__log_xml_debug(msg, "auth"); if (!pcmk__is_user_in_group(user, CRM_DAEMON_GROUP)) { pcmk__notice("Rejecting remote client %s: User %s is not a member of " From 397a80e3d001270e191a52f085dd7915a0c51f4d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:40:59 -0700 Subject: [PATCH 150/202] Refactor: based: Rename cib_remote_auth() variables For clarity. Also match op case-sensitively. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index af3383a3c49..6cd46a3df66 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -289,9 +289,9 @@ authenticate_user(const char *user, const char *passwd, const char *client_name) static bool cib_remote_auth(xmlNode *msg, const char *client_name) { + const char *op = NULL; const char *user = NULL; - const char *pass = NULL; - const char *tmp = NULL; + const char *password = NULL; if (msg == NULL) { return false; @@ -304,18 +304,17 @@ cib_remote_auth(xmlNode *msg, const char *client_name) return false; } - tmp = pcmk__xe_get(msg, PCMK_XA_OP); - if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) { + op = pcmk__xe_get(msg, PCMK_XA_OP); + if (!pcmk__str_eq(op, "authenticate", pcmk__str_none)) { pcmk__warn("Rejecting remote client: Unrecognizable message (operation " - "'%s' not 'authenticate')", - tmp); + "'%s' not 'authenticate')", op); pcmk__log_xml_debug(msg, "bad"); return false; } user = pcmk__xe_get(msg, PCMK_XA_USER); - pass = pcmk__xe_get(msg, PCMK__XA_PASSWORD); - if (!user || !pass) { + password = pcmk__xe_get(msg, PCMK__XA_PASSWORD); + if ((user == NULL) || (password == NULL)) { pcmk__warn("Rejecting remote client: No %s given", ((user == NULL)? "username" : "password")); pcmk__log_xml_debug(msg, "bad"); @@ -330,7 +329,7 @@ cib_remote_auth(xmlNode *msg, const char *client_name) return false; } - return authenticate_user(user, pass, client_name); + return authenticate_user(user, password, client_name); } static void From 2c62dd15cd0380c4f503380c4353e9199afa7b83 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:45:52 -0700 Subject: [PATCH 151/202] Log: based: Improve cib_remote_auth() log messages Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 6cd46a3df66..9d1a6f392a6 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -294,35 +294,34 @@ cib_remote_auth(xmlNode *msg, const char *client_name) const char *password = NULL; if (msg == NULL) { + pcmk__warn("Rejecting remote client %s: Unrecognizable message", + client_name); return false; } if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { - pcmk__warn("Rejecting remote client: Unrecognizable message (element " - "'%s' not '" PCMK__XE_CIB_COMMAND "')", msg->name); - pcmk__log_xml_debug(msg, "bad"); + pcmk__warn("Rejecting remote client %s: Expected element " + "'" PCMK__XE_CIB_COMMAND "', got '%s'", client_name, + msg->name); return false; } op = pcmk__xe_get(msg, PCMK_XA_OP); if (!pcmk__str_eq(op, "authenticate", pcmk__str_none)) { - pcmk__warn("Rejecting remote client: Unrecognizable message (operation " - "'%s' not 'authenticate')", op); - pcmk__log_xml_debug(msg, "bad"); + pcmk__warn("Rejecting remote client %s: Expected " + PCMK_XA_OP "='authenticate', got " PCMK_XA_OP "='%s'", + client_name, op); return false; } user = pcmk__xe_get(msg, PCMK_XA_USER); password = pcmk__xe_get(msg, PCMK__XA_PASSWORD); if ((user == NULL) || (password == NULL)) { - pcmk__warn("Rejecting remote client: No %s given", + pcmk__warn("Rejecting remote client %s: No %s given", client_name, ((user == NULL)? "username" : "password")); - pcmk__log_xml_debug(msg, "bad"); return false; } - pcmk__log_xml_debug(msg, "auth"); - if (!pcmk__is_user_in_group(user, CRM_DAEMON_GROUP)) { pcmk__notice("Rejecting remote client %s: User %s is not a member of " "group %s", client_name, user, CRM_DAEMON_GROUP); @@ -446,6 +445,7 @@ based_remote_client_dispatch(gpointer data) msg = pcmk__remote_message_xml(client->remote); if (!cib_remote_auth(msg, pcmk__client_name(client))) { + pcmk__log_xml_debug(msg, "rejected"); pcmk__xml_free(msg); return -1; } From b337acd76b8cdf28df6830aab71f6c5999812cc9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 20:34:42 -0800 Subject: [PATCH 152/202] Refactor: based: New based_remote_client_auth() This subsumes cib_remote_auth() and includes a bit more of based_remote_client_dispatch(), so that that function can be more straightforward. The new function is a bit long for my taste, so we could break it up into two or three parts. But this is okay for now. I don't see where PCMK_XA_NAME has ever been set, even though we try to fetch it and set the client name based on it. This should be testable by connecting with a remote client and checking whether its name in the logs is a UUID or something friendlier. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 107 ++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 9d1a6f392a6..23da14cf628 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -286,24 +286,45 @@ authenticate_user(const char *user, const char *passwd, const char *client_name) #endif } +/*! + * \internal + * \brief Try to authenticate a remote client based on the message in its buffer + * + * Read the first message from the client's buffer. Validate that it's a well- + * formed remote client authentication request. Parse the username and password. + * Ensure that the user is a member of \c CRM_DAEMON_GROUP and use the + * credentials to authenticate the user via PAM (if available). Finally, on + * success, set the \c pcmk__client_authenticated flag, and send a reply + * informing the client of its success and its client ID. + * + * \param[in,out] client Remote CIB manager client + * + * \return \c true if \p client authenticated successfully, or \c false + * otherwise + */ static bool -cib_remote_auth(xmlNode *msg, const char *client_name) +based_remote_client_auth(pcmk__client_t *client) { + // @TODO If we want to debug/trace-log an auth message, strip password first const char *op = NULL; const char *user = NULL; const char *password = NULL; + const char *client_name = pcmk__client_name(client); + xmlNode *msg = NULL; + xmlNode *cib_result = NULL; + msg = pcmk__remote_message_xml(client->remote); if (msg == NULL) { pcmk__warn("Rejecting remote client %s: Unrecognizable message", client_name); - return false; + goto done; } if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { pcmk__warn("Rejecting remote client %s: Expected element " "'" PCMK__XE_CIB_COMMAND "', got '%s'", client_name, msg->name); - return false; + goto done; } op = pcmk__xe_get(msg, PCMK_XA_OP); @@ -311,7 +332,7 @@ cib_remote_auth(xmlNode *msg, const char *client_name) pcmk__warn("Rejecting remote client %s: Expected " PCMK_XA_OP "='authenticate', got " PCMK_XA_OP "='%s'", client_name, op); - return false; + goto done; } user = pcmk__xe_get(msg, PCMK_XA_USER); @@ -319,16 +340,53 @@ cib_remote_auth(xmlNode *msg, const char *client_name) if ((user == NULL) || (password == NULL)) { pcmk__warn("Rejecting remote client %s: No %s given", client_name, ((user == NULL)? "username" : "password")); - return false; + goto done; } if (!pcmk__is_user_in_group(user, CRM_DAEMON_GROUP)) { pcmk__notice("Rejecting remote client %s: User %s is not a member of " "group %s", client_name, user, CRM_DAEMON_GROUP); - return false; + goto done; } - return authenticate_user(user, password, client_name); + if (!authenticate_user(user, password, client_name)) { + // Error already logged + goto done; + } + + // @FIXME Should this be done regardless of whether auth succeeds? + if (client->remote->auth_timeout != 0) { + g_source_remove(client->remote->auth_timeout); + client->remote->auth_timeout = 0; + } + + pcmk__set_client_flags(client, pcmk__client_authenticated); + + // @TODO What sets PCMK_XA_NAME? Added by commit 22832641. + client->name = pcmk__xe_get_copy(msg, PCMK_XA_NAME); + client->user = pcmk__str_copy(user); + + // Setting client->name may have changed the return value + client_name = pcmk__client_name(client); + + pcmk__notice("Remote connection accepted for authenticated user %s " + QB_XS " client %s", client->user, client_name); + + // Notify client of success and of its ID + cib_result = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT); + pcmk__xe_set(cib_result, PCMK__XA_CIB_OP, CRM_OP_REGISTER); + pcmk__xe_set(cib_result, PCMK__XA_CIB_CLIENTID, client->id); + + pcmk__remote_send_xml(client->remote, cib_result); + +done: + if (!pcmk__is_set(client->flags, pcmk__client_authenticated)) { + pcmk__log_xml_debug(msg, "rejected"); + } + + pcmk__xml_free(msg); + pcmk__xml_free(cib_result); + return pcmk__is_set(client->flags, pcmk__client_authenticated); } static void @@ -439,38 +497,11 @@ based_remote_client_dispatch(gpointer data) return -1; } - // Must pass auth before we will process anything else - if (!pcmk__is_set(client->flags, pcmk__client_authenticated)) { - xmlNode *cib_result = NULL; - - msg = pcmk__remote_message_xml(client->remote); - if (!cib_remote_auth(msg, pcmk__client_name(client))) { - pcmk__log_xml_debug(msg, "rejected"); - pcmk__xml_free(msg); - return -1; - } - - if (client->remote->auth_timeout != 0) { - g_source_remove(client->remote->auth_timeout); - client->remote->auth_timeout = 0; - } - - pcmk__set_client_flags(client, pcmk__client_authenticated); - - client->name = pcmk__xe_get_copy(msg, PCMK_XA_NAME); - client->user = pcmk__xe_get_copy(msg, PCMK_XA_USER); + // Client must authenticate before we will process anything else + if (!pcmk__is_set(client->flags, pcmk__client_authenticated) + && !based_remote_client_auth(client)) { - pcmk__notice("Remote connection accepted for authenticated user %s " - QB_XS " client %s", client->user, - pcmk__client_name(client)); - - cib_result = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT); - pcmk__xe_set(cib_result, PCMK__XA_CIB_OP, CRM_OP_REGISTER); - pcmk__xe_set(cib_result, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__remote_send_xml(client->remote, cib_result); - - pcmk__xml_free(cib_result); - pcmk__xml_free(msg); + return -1; } msg = pcmk__remote_message_xml(client->remote); From ee461f01cf7c3b5777f9e605f114adeec51fc989 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 22:57:52 -0800 Subject: [PATCH 153/202] Refactor: based: Functionize validating/parsing remote auth message Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 88 +++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 23da14cf628..ce9efd97f19 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -132,6 +132,65 @@ based_read_handshake_data(pcmk__client_t *client) return 0; } +/*! + * \internal + * \brief Parse a remote client auth message + * + * This first validates that the message is a well-formed remote client + * authentication request and then extracts the username and password + * attributes. + * + * \param[in] msg Message from remote client + * \param[out] user Where to store username + * \param[out] password Where to store password + * \param[in] client_name Remote client name (for logging only) + * + * \return \c true if \p msg is a well-formed authentication request, or + * \c false otherwise. + * + * \note \p *user and \p *password are set to \c NULL on error. + */ +static bool +parse_auth_message(const xmlNode *msg, const char **user, const char **password, + const char *client_name) +{ + const char *op = NULL; + + if (msg == NULL) { + pcmk__warn("Rejecting remote client %s: Unrecognizable message", + client_name); + return false; + } + + if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { + pcmk__warn("Rejecting remote client %s: Expected element " + "'" PCMK__XE_CIB_COMMAND "', got '%s'", client_name, + msg->name); + return false; + } + + op = pcmk__xe_get(msg, PCMK_XA_OP); + if (!pcmk__str_eq(op, "authenticate", pcmk__str_none)) { + pcmk__warn("Rejecting remote client %s: Expected " + PCMK_XA_OP "='authenticate', got " PCMK_XA_OP "='%s'", + client_name, op); + return false; + } + + *user = pcmk__xe_get(msg, PCMK_XA_USER); + *password = pcmk__xe_get(msg, PCMK__XA_PASSWORD); + + if ((*user == NULL) || (*password == NULL)) { + pcmk__warn("Rejecting remote client %s: No %s given", client_name, + ((*user == NULL)? "username" : "password")); + *user = NULL; + *password = NULL; + return false; + } + + return true; +} + #ifdef HAVE_PAM /*! * \internal @@ -306,7 +365,6 @@ static bool based_remote_client_auth(pcmk__client_t *client) { // @TODO If we want to debug/trace-log an auth message, strip password first - const char *op = NULL; const char *user = NULL; const char *password = NULL; const char *client_name = pcmk__client_name(client); @@ -314,32 +372,8 @@ based_remote_client_auth(pcmk__client_t *client) xmlNode *cib_result = NULL; msg = pcmk__remote_message_xml(client->remote); - if (msg == NULL) { - pcmk__warn("Rejecting remote client %s: Unrecognizable message", - client_name); - goto done; - } - - if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { - pcmk__warn("Rejecting remote client %s: Expected element " - "'" PCMK__XE_CIB_COMMAND "', got '%s'", client_name, - msg->name); - goto done; - } - - op = pcmk__xe_get(msg, PCMK_XA_OP); - if (!pcmk__str_eq(op, "authenticate", pcmk__str_none)) { - pcmk__warn("Rejecting remote client %s: Expected " - PCMK_XA_OP "='authenticate', got " PCMK_XA_OP "='%s'", - client_name, op); - goto done; - } - - user = pcmk__xe_get(msg, PCMK_XA_USER); - password = pcmk__xe_get(msg, PCMK__XA_PASSWORD); - if ((user == NULL) || (password == NULL)) { - pcmk__warn("Rejecting remote client %s: No %s given", client_name, - ((user == NULL)? "username" : "password")); + if (!parse_auth_message(msg, &user, &password, client_name)) { + // Error already logged goto done; } From 318c4fea15f7f3faac72e4ecf2fdf45e942b64c0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 00:19:49 -0800 Subject: [PATCH 154/202] Doc: based: Document confusion about "dangerous" options Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index ce9efd97f19..829d6115434 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -439,7 +439,32 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) client->name = pcmk__str_copy(client->id); } - /* unset dangerous options */ + /* Unset dangerous options. + * + * @TODO These were commented as "dangerous" with no explanation when this + * code was added by commit 8e08a242 (2007). We usually process whatever + * message we receive, taking a "submit a malformed request at your own + * risk" view. Our client API and CLI tools should not be able to submit a + * malformed request. A malicious user would have to send it directly, + * without our tools. If they have that level of access and are able to + * authenticate their request, then they can cause havoc regardless of + * whether we remove these "dangerous" attributes that shouldn't be present + * in a remote client's request. + * + * This seems overly paranoid, and seems like an arbitrary place to be + * paranoid. + * + * Best guesses about how they might be dangerous (or not): + * * PCMK_XA_SRC: This is mostly used for logging. Perhaps the CIB could get + * synced to the wrong host, or local client notifications could get sent + * on the wrong host? + * * PCMK__XA_CIB_HOST: It seems as if this should be allowed. Our client + * code actually sets this, apparently as a destination node. + * cib_remote_perform_op() takes a host argument and passes it to + * cib__create_op(), which sets it as PCMK__XA_CIB_HOST. + * * PCMK__XA_CIB_UPDATE: This can prevent CIB versions from being updated, + * because the update is treated as a sync. + */ pcmk__xe_remove_attr(command, PCMK__XA_SRC); pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST); pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE); From a99dd22e99d8f66ceead4eb2b199c51b432579a6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 02:17:45 -0800 Subject: [PATCH 155/202] Refactor: based: Reorganize cib_handle_remote_msg() Have it create a pcmk__request_t and look more like lrmd_remote_client_msg() and based_peer_callback(). Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 1 - daemons/based/based_remote.c | 104 ++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 353c09d6832..fa47e1b290c 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -138,7 +138,6 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) pcmk__warn("Ignoring CIB request from IPC client %s with " "cib_transaction flag set outside of any transaction", client->name); - pcmk__log_xml_info(msg, "no-transaction"); goto cleanup; } diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 829d6115434..3019676ed3b 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -398,6 +398,10 @@ based_remote_client_auth(pcmk__client_t *client) // @TODO What sets PCMK_XA_NAME? Added by commit 22832641. client->name = pcmk__xe_get_copy(msg, PCMK_XA_NAME); + if (client->name == NULL) { + client->name = pcmk__str_copy(client->id); + } + client->user = pcmk__str_copy(user); // Setting client->name may have changed the return value @@ -424,19 +428,31 @@ based_remote_client_auth(pcmk__client_t *client) } static void -cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) +based_remote_client_message(pcmk__client_t *client, xmlNode *msg) { int rc = pcmk_rc_ok; uint32_t call_options = cib_none; - const char *op = pcmk__xe_get(command, PCMK__XA_CIB_OP); + const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); - if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) { - pcmk__log_xml_trace(command, "bad"); + if (!pcmk__xe_is(msg, PCMK__XE_CIB_COMMAND)) { + pcmk__debug("Unrecognizable remote data from client %s", + pcmk__client_name(client)); return; } - if (client->name == NULL) { - client->name = pcmk__str_copy(client->id); + rc = pcmk__xe_get_flags(msg, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); + if (rc != pcmk_rc_ok) { + pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); + } + + /* Requests with cib_transaction set should not be sent to based directly + * (that is, outside of a commit-transaction request) + */ + if (pcmk__is_set(call_options, cib_transaction)) { + pcmk__warn("Ignoring CIB request from remote client %s with " + "cib_transaction flag set outside of any transaction", + pcmk__client_name(client)); + return; } /* Unset dangerous options. @@ -465,49 +481,52 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) * * PCMK__XA_CIB_UPDATE: This can prevent CIB versions from being updated, * because the update is treated as a sync. */ - pcmk__xe_remove_attr(command, PCMK__XA_SRC); - pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST); - pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE); + pcmk__xe_remove_attr(msg, PCMK__XA_SRC); + pcmk__xe_remove_attr(msg, PCMK__XA_CIB_HOST); + pcmk__xe_remove_attr(msg, PCMK__XA_CIB_UPDATE); - pcmk__xe_set(command, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(command, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__xe_set(command, PCMK__XA_CIB_CLIENTNAME, client->name); - pcmk__xe_set(command, PCMK__XA_CIB_USER, client->user); - - if (pcmk__xe_get(command, PCMK__XA_CIB_CALLID) == NULL) { + // Similarly impossible via our API/tools. cib__create_op() sets this. + if (pcmk__xe_get(msg, PCMK__XA_CIB_CALLID) == NULL) { char *call_uuid = pcmk__generate_uuid(); - /* fix the command */ - pcmk__xe_set(command, PCMK__XA_CIB_CALLID, call_uuid); + pcmk__xe_set(msg, PCMK__XA_CIB_CALLID, call_uuid); free(call_uuid); } - rc = pcmk__xe_get_flags(command, PCMK__XA_CIB_CALLOPT, &call_options, - cib_none); - if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request from remote client %s: " - "%s", client->name, pcmk_rc_str(rc)); - pcmk__log_xml_info(command, "bad-call-opts"); - } - - /* Requests with cib_transaction set should not be sent to based directly - * (that is, outside of a commit-transaction request) - */ - if (pcmk__is_set(call_options, cib_transaction)) { - pcmk__warn("Ignoring CIB request from remote client %s with " - "cib_transaction flag set outside of any transaction", - client->name); - pcmk__log_xml_info(command, "no-transaction"); - return; - } + pcmk__xe_set(msg, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); + pcmk__xe_set(msg, PCMK__XA_CIB_USER, client->user); - pcmk__log_xml_trace(command, "remote-request"); + pcmk__log_xml_trace(msg, "remote-request"); if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { - based_update_notify_flags(command, client); - } + based_update_notify_flags(msg, client); - based_process_request(command, client); + } else { + /* @TODO Should ipc_id be set to a nonzero value? client->request_id + * needs to match it if so, since pcmk__request_sync is set. + */ + pcmk__request_t request = { + .ipc_client = client, + .ipc_id = 0, + .ipc_flags = crm_ipc_flags_none, + .peer = NULL, + .xml = msg, + .call_options = call_options, + .result = PCMK__UNKNOWN_RESULT, + }; + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + based_process_request(request.xml, request.ipc_client); + pcmk__reset_request(&request); + } } static int @@ -564,12 +583,9 @@ based_remote_client_dispatch(gpointer data) } msg = pcmk__remote_message_xml(client->remote); - if (msg != NULL) { - pcmk__trace("Remote message received from client %s", client_name); - cib_handle_remote_msg(client, msg); - pcmk__xml_free(msg); - } + based_remote_client_message(client, msg); + pcmk__xml_free(msg); return 0; } From 3caaf8396793690b38b45ff83b2e50316dec4dd3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 02:39:32 -0800 Subject: [PATCH 156/202] Refactor: based: Use pcmk__request_t in process_transaction_requests() Signed-off-by: Reid Wahl --- daemons/based/based_transaction.c | 61 +++++++++++++++++++++++-------- daemons/based/based_transaction.h | 4 +- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 38d445bafa6..682e06bd5a6 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -60,20 +60,27 @@ based_transaction_source_str(const pcmk__client_t *client, const char *origin) * \return Standard Pacemaker return code */ static int -process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, +process_transaction_requests(xmlNode *transaction, pcmk__client_t *client, const char *source) { - for (xmlNode *request = pcmk__xe_first_child(transaction, - PCMK__XE_CIB_COMMAND, NULL, - NULL); - request != NULL; - request = pcmk__xe_next(request, PCMK__XE_CIB_COMMAND)) { - - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); + for (xmlNode *xml = pcmk__xe_first_child(transaction, PCMK__XE_CIB_COMMAND, + NULL, NULL); + xml != NULL; xml = pcmk__xe_next(xml, PCMK__XE_CIB_COMMAND)) { + + int rc = pcmk_rc_ok; + uint32_t call_options = cib_none; + const char *op = pcmk__xe_get(xml, PCMK__XA_CIB_OP); + const char *host = pcmk__xe_get(xml, PCMK__XA_CIB_HOST); const cib__operation_t *operation = NULL; - int rc = cib__get_operation(op, &operation); + rc = pcmk__xe_get_flags(xml, PCMK__XA_CIB_CALLOPT, &call_options, + cib_none); + if (rc != pcmk_rc_ok) { + pcmk__warn("Couldn't parse options from request: %s", + pcmk_rc_str(rc)); + } + + rc = cib__get_operation(op, &operation); if (rc == pcmk_rc_ok) { if (!pcmk__is_set(operation->flags, cib__op_attr_transaction) || (host != NULL)) { @@ -81,21 +88,43 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, rc = EOPNOTSUPP; } else { - rc = based_process_request(request, client); + /* @FIXME It would be better for this function to accept a + * pcmk__request_t argument and reuse it. In particular, the + * values below for ipc_id and ipc_flags are intended as sane + * placeholders. + */ + pcmk__request_t request = { + .ipc_client = client, + .ipc_id = client->request_id, + .ipc_flags = crm_ipc_flags_none, + .peer = NULL, + .xml = xml, + .call_options = call_options, + .result = PCMK__UNKNOWN_RESULT, + }; + + request.op = pcmk__xe_get_copy(request.xml, PCMK__XA_CIB_OP); + CRM_CHECK(request.op != NULL, return 0); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + rc = based_process_request(request.xml, request.ipc_client); + pcmk__reset_request(&request); } } if (rc != pcmk_rc_ok) { pcmk__err("Aborting CIB transaction for %s due to failed %s " - "request: %s", - source, op, pcmk_rc_str(rc)); - pcmk__log_xml_info(request, "Failed request"); + "request: %s", source, op, pcmk_rc_str(rc)); + pcmk__log_xml_info(xml, "failed"); return rc; } pcmk__trace("Applied %s request to transaction working CIB for %s", op, source); - pcmk__log_xml_trace(request, "Successful request"); + pcmk__log_xml_trace(xml, "successful"); } return pcmk_rc_ok; @@ -120,7 +149,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, * success, and for freeing it on failure. */ int -based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, +based_commit_transaction(xmlNode *transaction, pcmk__client_t *client, const char *origin, xmlNode **result_cib) { xmlNode *saved_cib = based_cib; diff --git a/daemons/based/based_transaction.h b/daemons/based/based_transaction.h index 19dc01ba529..7a062d26d05 100644 --- a/daemons/based/based_transaction.h +++ b/daemons/based/based_transaction.h @@ -1,5 +1,5 @@ /* - * Copyright 2023-2025 the Pacemaker project contributors + * Copyright 2023-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ char *based_transaction_source_str(const pcmk__client_t *client, const char *origin); -int based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, +int based_commit_transaction(xmlNode *transaction, pcmk__client_t *client, const char *origin, xmlNode **result_cib); #endif // BASED_TRANSACTION__H From 31e9935ece4fedf2c1d2de7842c2480436dd3f61 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 02:49:55 -0800 Subject: [PATCH 157/202] Refactor: based: based_process_request() takes pcmk__request_t argument And rename it to based_handle_request(), to align with other daemons. This commit aims to make basically the minimal changes necessary for this function to take a pcmk__request_t. There are more improvements to be made. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 94 ++++++++++++++++--------------- daemons/based/based_callbacks.h | 2 +- daemons/based/based_corosync.c | 3 +- daemons/based/based_ipc.c | 3 +- daemons/based/based_remote.c | 3 +- daemons/based/based_transaction.c | 3 +- 6 files changed, 53 insertions(+), 55 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f7495761e24..9938e54370f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -646,20 +646,16 @@ send_peer_reply(xmlNode *msg, const char *originator) /*! * \internal - * \brief Handle an IPC or CPG message containing a request + * \brief Handle a request from a CIB manager client or peer * - * \param[in,out] request Request XML - * \param[in] client IPC client that sent request (\c NULL if request came - * from CPG) + * \param[in,out] request CIB manager request * * \return Standard Pacemaker return code */ int -based_process_request(xmlNode *request, const pcmk__client_t *client) +based_handle_request(pcmk__request_t *request) { // @TODO: Break into multiple smaller functions - uint32_t call_options = cib_none; - bool process = true; // Whether to process request locally now bool needs_reply = true; // Whether to build a reply bool local_notify = false; // Whether to notify (local) requester @@ -667,14 +663,19 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) xmlNode *reply = NULL; int rc = pcmk_rc_ok; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); - const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); - const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); - const char *client_name = pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME), + const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); + const char *call_id = pcmk__xe_get(request->xml, PCMK__XA_CIB_CALLID); + const char *reply_to = pcmk__xe_get(request->xml, PCMK__XA_CIB_ISREPLYTO); + + /* These are the client ID and name on the originator node, so we need to + * get these from the request XML. request->ipc_client is NULL if this + * request came from the cluster. + */ + const char *client_id = pcmk__xe_get(request->xml, PCMK__XA_CIB_CLIENTID); + const char *client_name = pcmk__s(pcmk__xe_get(request->xml, + PCMK__XA_CIB_CLIENTNAME), "client"); - const char *reply_to = pcmk__xe_get(request, PCMK__XA_CIB_ISREPLYTO); const cib__operation_t *operation = NULL; cib__op_fn_t op_function = NULL; @@ -684,60 +685,58 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) if (based_shutting_down()) { pcmk__info("Ignoring pending CIB request during shutdown"); + pcmk__reset_request(request); return ENOTCONN; } - rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, - cib_none); - if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); - } - if (pcmk__str_empty(host)) { host = NULL; } - if (client == NULL) { + if (request->ipc_client == NULL) { pcmk__trace("Processing peer %s operation from %s/%s on %s intended " - "for %s (reply=%s)", op, client_name, call_id, originator, - pcmk__s(host, "all"), reply_to); + "for %s (reply=%s)", request->op, client_name, call_id, + originator, pcmk__s(host, "all"), reply_to); } else { - pcmk__xe_set(request, PCMK__XA_SRC, OUR_NODENAME); + pcmk__xe_set(request->xml, PCMK__XA_SRC, OUR_NODENAME); pcmk__trace("Processing local %s operation from %s/%s intended for %s", - op, client_name, call_id, pcmk__s(host, "all")); + request->op, client_name, call_id, pcmk__s(host, "all")); } - rc = cib__get_operation(op, &operation); + rc = cib__get_operation(request->op, &operation); if (rc != pcmk_rc_ok) { /* TODO: construct error reply? */ pcmk__err("Pre-processing of command failed: %s", pcmk_rc_str(rc)); + pcmk__reset_request(request); return rc; } op_function = based_get_op_function(operation); if (op_function == NULL) { - pcmk__err("Operation %s not supported by CIB manager", op); + pcmk__err("Operation %s not supported by CIB manager", request->op); + pcmk__reset_request(request); return EOPNOTSUPP; } - if (pcmk__is_set(call_options, cib_transaction)) { + if (pcmk__is_set(request->call_options, cib_transaction)) { /* All requests in a transaction are processed locally against a working * CIB copy, and we don't notify for individual requests because the * entire transaction is atomic. */ needs_reply = false; pcmk__trace("Processing %s op from %s/%s on %s locally because it's " - "part of a transaction", op, client_name, call_id, - pcmk__xe_get(request, PCMK__XA_SRC)); + "part of a transaction", request->op, client_name, call_id, + pcmk__xe_get(request->xml, PCMK__XA_SRC)); - } else if (client != NULL) { + } else if (request->ipc_client != NULL) { // Forward modifying and non-local requests via cluster if (!pcmk__is_set(operation->flags, cib__op_attr_local) && (pcmk__is_set(operation->flags, cib__op_attr_modifies) || !pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches))) { - forward_request(request); + forward_request(request->xml); + pcmk__reset_request(request); return pcmk_rc_ok; } @@ -745,14 +744,15 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) needs_reply = false; local_notify = true; - log_local_options(client, operation, host, op); + log_local_options(request->ipc_client, operation, host, request->op); - } else if (!parse_peer_options(operation, request, &local_notify, + } else if (!parse_peer_options(operation, request->xml, &local_notify, &needs_reply, &process)) { + pcmk__reset_request(request); return pcmk_rc_ok; } - if (pcmk__is_set(call_options, cib_discard_reply)) { + if (pcmk__is_set(request->call_options, cib_discard_reply)) { needs_reply = false; local_notify = false; pcmk__trace("Client is not interested in the reply"); @@ -763,8 +763,8 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, based_cib); + if (!pcmk__is_set(request->call_options, cib_discard_reply)) { + reply = create_cib_reply(request->xml, rc, based_cib); } goto done; @@ -777,16 +777,16 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) start_time = time(NULL); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &based_cib, &output); + rc = cib__perform_op_ro(op_function, request->xml, &based_cib, &output); } else { - rc = based_perform_op_rw(request, operation, op_function, &output); + rc = based_perform_op_rw(request->xml, operation, op_function, &output); } - log_op_result(request, operation, rc, difftime(time(NULL), start_time)); + log_op_result(request->xml, operation, rc, difftime(time(NULL), start_time)); - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, output); + if (!pcmk__is_set(request->call_options, cib_discard_reply)) { + reply = create_cib_reply(request->xml, rc, output); } if ((output != NULL) && (output->doc != based_cib->doc)) { @@ -795,17 +795,19 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) done: if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) - && needs_reply && !based_stand_alone() && (client == NULL)) { + && needs_reply && !based_stand_alone() + && (request->ipc_client == NULL)) { send_peer_reply(reply, originator); } if (local_notify && (client_id != NULL)) { - do_local_notify((process? reply : request), client_id, - pcmk__is_set(call_options, cib_sync_call), - (client == NULL)); + do_local_notify((process? reply : request->xml), client_id, + pcmk__is_set(request->call_options, cib_sync_call), + (request->ipc_client == NULL)); } pcmk__xml_free(reply); + pcmk__reset_request(request); return rc; } diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 93565ef1a93..29613b2c4d7 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -17,6 +17,6 @@ void based_callbacks_init(void); void based_callbacks_cleanup(void); -int based_process_request(xmlNode *request, const pcmk__client_t *client); +int based_handle_request(pcmk__request_t *request); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 82c94227a25..5e9b972e248 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -68,8 +68,7 @@ based_peer_message(pcmk__node_status_t *peer, xmlNode *xml) pcmk__xe_get(request.xml, PCMK__XA_SRC)); } - based_process_request(request.xml, request.ipc_client); - pcmk__reset_request(&request); + based_handle_request(&request); } } diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index fa47e1b290c..f27db1a0aef 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -204,8 +204,7 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) pcmk__set_request_flags(&request, pcmk__request_sync); } - based_process_request(request.xml, request.ipc_client); - pcmk__reset_request(&request); + based_handle_request(&request); } cleanup: diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 3019676ed3b..59cba3a98c9 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -524,8 +524,7 @@ based_remote_client_message(pcmk__client_t *client, xmlNode *msg) pcmk__set_request_flags(&request, pcmk__request_sync); } - based_process_request(request.xml, request.ipc_client); - pcmk__reset_request(&request); + based_handle_request(&request); } } diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 682e06bd5a6..537e90fdbc7 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -110,8 +110,7 @@ process_transaction_requests(xmlNode *transaction, pcmk__client_t *client, pcmk__set_request_flags(&request, pcmk__request_sync); } - rc = based_process_request(request.xml, request.ipc_client); - pcmk__reset_request(&request); + rc = based_handle_request(&request); } } From 31cb4277a7b43c967bf60ffe1ab201867b0053c6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:02:42 -0800 Subject: [PATCH 158/202] Refactor: based: Drop redundant client == NULL check If needs_reply is true, then client == NULL. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 9938e54370f..8539f28448c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -795,8 +795,7 @@ based_handle_request(pcmk__request_t *request) done: if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) - && needs_reply && !based_stand_alone() - && (request->ipc_client == NULL)) { + && needs_reply && !based_stand_alone()) { send_peer_reply(reply, originator); } From de5a05341279df53eb9e76f529c26515bd7b85bc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:21:23 -0800 Subject: [PATCH 159/202] Refactor: based: forward_request() takes pcmk__request_t Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8539f28448c..564f64901c8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -465,11 +465,14 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, * \brief Forward a CIB request to the appropriate target host(s) * * \param[in] request CIB request to forward + * + * \note \p request is modified within the function, but its initial state is + * restored before returning. This probably doesn't matter, however. */ static void -forward_request(xmlNode *request) +forward_request(pcmk__request_t *request) { - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); pcmk__node_status_t *peer = NULL; if (host != NULL) { @@ -477,9 +480,9 @@ forward_request(xmlNode *request) } // Set PCMK__XA_CIB_DELEGATED_FROM only temporarily - pcmk__xe_set(request, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); - pcmk__cluster_send_message(peer, pcmk_ipc_based, request); - pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM); + pcmk__xe_set(request->xml, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); + pcmk__cluster_send_message(peer, pcmk_ipc_based, request->xml); + pcmk__xe_remove_attr(request->xml, PCMK__XA_CIB_DELEGATED_FROM); } static int @@ -735,7 +738,7 @@ based_handle_request(pcmk__request_t *request) || !pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches))) { - forward_request(request->xml); + forward_request(request); pcmk__reset_request(request); return pcmk_rc_ok; } From 14ee04515f525ebd56de047b9ab946b0d5942889 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:27:43 -0800 Subject: [PATCH 160/202] Refactor: based: parse_peer_options() takes pcmk__request_t Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 60 ++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 564f64901c8..f3d5c1b32de 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -349,14 +349,14 @@ log_local_options(const pcmk__client_t *client, } static bool -parse_peer_options(const cib__operation_t *operation, xmlNode *request, +parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, bool *local_notify, bool *needs_reply, bool *process) { - const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); - const char *delegated = pcmk__xe_get(request, PCMK__XA_CIB_DELEGATED_FROM); - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - const char *reply_to = pcmk__xe_get(request, PCMK__XA_CIB_ISREPLYTO); + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); + const char *delegated = pcmk__xe_get(request->xml, + PCMK__XA_CIB_DELEGATED_FROM); + const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); + const char *reply_to = pcmk__xe_get(request->xml, PCMK__XA_CIB_ISREPLYTO); bool is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei); @@ -364,12 +364,12 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, originator = "peer"; } - if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - process_ping_reply(request); + if (is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { + process_ping_reply(request->xml); return false; } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { + if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { /* @COMPAT We stopped sending shutdown requests as of 3.0.2. During a * rolling upgrade, the requesting node expects a reply. We might as * well continue sending one until we no longer support rolling upgrades @@ -379,9 +379,11 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - if (is_reply && pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); + if (is_reply + && pcmk__str_eq(request->op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { + + pcmk__trace("Will notify local clients for %s reply from %s", + request->op, originator); *process = false; *needs_reply = false; *local_notify = true; @@ -389,12 +391,14 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if ((reply_to != NULL) - && pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { + && pcmk__str_eq(request->op, PCMK__CIB_REQUEST_REPLACE, + pcmk__str_none)) { // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO delegated = reply_to; - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { + } else if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_UPGRADE, + pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. * @@ -405,8 +409,9 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, * Except this time PCMK__XA_CIB_SCHEMA_MAX will be set which puts a * limit on how far newer nodes will go */ - const char *max = pcmk__xe_get(request, PCMK__XA_CIB_SCHEMA_MAX); - const char *upgrade_rc = pcmk__xe_get(request, PCMK__XA_CIB_UPGRADE_RC); + const char *max = pcmk__xe_get(request->xml, PCMK__XA_CIB_SCHEMA_MAX); + const char *upgrade_rc = pcmk__xe_get(request->xml, + PCMK__XA_CIB_UPGRADE_RC); pcmk__trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s", (is_reply? "reply" : "request"), @@ -416,10 +421,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (upgrade_rc != NULL) { // Our upgrade request was rejected by DC, notify clients of result pcmk__assert(is_reply); - pcmk__xe_set(request, PCMK__XA_CIB_RC, upgrade_rc); + pcmk__xe_set(request->xml, PCMK__XA_CIB_RC, upgrade_rc); - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); + pcmk__trace("Will notify local clients for %s reply from %s", + request->op, originator); *process = false; *needs_reply = false; *local_notify = true; @@ -435,26 +440,27 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { - pcmk__trace("Processing %s request sent to us from %s", op, originator); + pcmk__trace("Processing %s request sent to us from %s", request->op, + originator); return true; } if (host != NULL) { - pcmk__trace("Ignoring %s request intended for CIB manager on %s", op, - host); + pcmk__trace("Ignoring %s request intended for CIB manager on %s", + request->op, host); return false; } - if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { + if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { return true; } *needs_reply = false; pcmk__trace("Processing %s request broadcast by %s call %s on %s " - "(local clients will%s be notified)", op, - pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME), + "(local clients will%s be notified)", request->op, + pcmk__s(pcmk__xe_get(request->xml, PCMK__XA_CIB_CLIENTNAME), "client"), - pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CALLID), + pcmk__s(pcmk__xe_get(request->xml, PCMK__XA_CIB_CALLID), "without ID"), originator, (*local_notify? "" : "not")); return true; @@ -749,7 +755,7 @@ based_handle_request(pcmk__request_t *request) log_local_options(request->ipc_client, operation, host, request->op); - } else if (!parse_peer_options(operation, request->xml, &local_notify, + } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { pcmk__reset_request(request); return pcmk_rc_ok; From 5558090914163440f0fba113eca1af9f70042429 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:30:25 -0800 Subject: [PATCH 161/202] Refactor: based: based_perform_op_rw() takes pcmk__request_t Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f3d5c1b32de..aa8db6fa53d 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -492,7 +492,7 @@ forward_request(pcmk__request_t *request) } static int -based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, +based_perform_op_rw(pcmk__request_t *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { const char *feature_set = pcmk__xe_get(based_cib, @@ -500,21 +500,16 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, xmlNode *result_cib = based_cib; xmlNode *cib_diff = NULL; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - uint32_t call_options = cib_none; - + const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); bool config_changed = false; int rc = pcmk_rc_ok; - pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - /* result_cib must not be modified after cib__perform_op_rw() returns. * * It's not important whether the client variant is cib_native or * cib_remote. */ - rc = cib__perform_op_rw(cib_undefined, op_function, request, + rc = cib__perform_op_rw(cib_undefined, op_function, request->xml, &config_changed, &result_cib, &cib_diff, output); /* On validation error, include the schema-violating result CIB in any reply @@ -527,7 +522,9 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } // Discard result for failure or dry run - if ((rc != pcmk_rc_ok) || pcmk__any_flags_set(call_options, cib_dryrun)) { + if ((rc != pcmk_rc_ok) + || pcmk__any_flags_set(request->call_options, cib_dryrun)) { + if (result_cib != based_cib) { pcmk__xml_free(result_cib); } @@ -542,12 +539,13 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, * An exception is a request within a transaction. Since a transaction * is atomic, intermediate results must not be written to disk. */ - const bool to_disk = !pcmk__is_set(call_options, cib_transaction) + const bool to_disk = !pcmk__is_set(request->call_options, + cib_transaction) && (config_changed || pcmk__is_set(operation->flags, cib__op_attr_writes_through)); - rc = based_activate_cib(result_cib, to_disk, op); + rc = based_activate_cib(result_cib, to_disk, request->op); } /* @COMPAT Nodes older than feature set 3.19.0 don't support transactions. @@ -562,7 +560,7 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { - sync_our_cib(request, true); + sync_our_cib(request->xml, true); } if (cib_diff != NULL) { @@ -572,10 +570,10 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, mainloop_timer_start(digest_timer); done: - if (!pcmk__any_flags_set(call_options, + if (!pcmk__any_flags_set(request->call_options, cib_dryrun|cib_inhibit_notify|cib_transaction)) { - based_diff_notify(request, rc, cib_diff); + based_diff_notify(request->xml, rc, cib_diff); } pcmk__xml_free(cib_diff); @@ -789,7 +787,7 @@ based_handle_request(pcmk__request_t *request) rc = cib__perform_op_ro(op_function, request->xml, &based_cib, &output); } else { - rc = based_perform_op_rw(request->xml, operation, op_function, &output); + rc = based_perform_op_rw(request, operation, op_function, &output); } log_op_result(request->xml, operation, rc, difftime(time(NULL), start_time)); From 537de7c78ffc75db579df60f9ce04d0b8078648b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 17:56:14 -0800 Subject: [PATCH 162/202] Doc: based: Drop TODO comment This TODO has been present since commit 83919a1d (2004). So the code is probably (though not necesssarily) okay as-is. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index aa8db6fa53d..49e8357c86b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -712,7 +712,6 @@ based_handle_request(pcmk__request_t *request) rc = cib__get_operation(request->op, &operation); if (rc != pcmk_rc_ok) { - /* TODO: construct error reply? */ pcmk__err("Pre-processing of command failed: %s", pcmk_rc_str(rc)); pcmk__reset_request(request); return rc; From e13a82969ffda4bffd205e6b8bc9f2d476ac63dc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 22:22:45 -0800 Subject: [PATCH 163/202] Refactor: based: Free output in the done section This makes no difference, just consolidates the freeing. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 49e8357c86b..dfca55a8f38 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -795,10 +795,6 @@ based_handle_request(pcmk__request_t *request) reply = create_cib_reply(request->xml, rc, output); } - if ((output != NULL) && (output->doc != based_cib->doc)) { - pcmk__xml_free(output); - } - done: if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) && needs_reply && !based_stand_alone()) { @@ -812,6 +808,10 @@ based_handle_request(pcmk__request_t *request) (request->ipc_client == NULL)); } + if ((output != NULL) && (output->doc != based_cib->doc)) { + pcmk__xml_free(output); + } + pcmk__xml_free(reply); pcmk__reset_request(request); return rc; From e3aca737d10a9e6317a990d2208fee1dd57aad00 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 23:08:44 -0800 Subject: [PATCH 164/202] Low: based: Don't forward requests via cluster if in stand-alone mode I'm not sure whether or how this ever worked. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index dfca55a8f38..eccf1135d6e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -736,7 +736,8 @@ based_handle_request(pcmk__request_t *request) } else if (request->ipc_client != NULL) { // Forward modifying and non-local requests via cluster - if (!pcmk__is_set(operation->flags, cib__op_attr_local) + if (!based_stand_alone() + && !pcmk__is_set(operation->flags, cib__op_attr_local) && (pcmk__is_set(operation->flags, cib__op_attr_modifies) || !pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches))) { From 7aff27e901b711e3735bf666147c5164c556de1f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 23:13:49 -0800 Subject: [PATCH 165/202] Refactor: libcib: Drop unneeded modifies flag for primary request We dropped support for legacy mode in 3.0.0. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 39575a1d9a1..3545f753ff5 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -75,9 +75,7 @@ static const cib__operation_t cib_ops[] = { CRM_OP_PING, cib__op_ping, cib__op_attr_none }, { - // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support - PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, - cib__op_attr_modifies|cib__op_attr_local + PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, cib__op_attr_local }, { PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none From b379436145a7de7a2ba8bebb198aeb0842ca2818 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 23:26:07 -0800 Subject: [PATCH 166/202] Feature: based: Drop support for cib_ismaster requests Prior to Pacemaker 3.0.0, it was possible to send these requests via the cib_api_operations_t:is_primary() method. However, as far as I can tell, nothing internal ever used that method or sent a cib_ismaster request directly. I also didn't find any instance of sbd or dlm using it. Since the is_primary() method was public API, technically this breaks compatibility if some external program is calling it on Pacemaker Remote nodes running versions older than 3.0.0. That's why I'm adding this commit to the change log. However, this seems very unlikely to affect anyone in practice. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 7 ------- daemons/based/based_messages.h | 1 - daemons/based/based_operation.c | 1 - include/crm/cib/internal.h | 2 -- lib/cib/cib_ops.c | 3 --- 5 files changed, 14 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index a77344d92d1..3b027feac72 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -75,13 +75,6 @@ based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) return rc; } -int -based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) -{ - // @COMPAT Pacemaker Remote clients <3.0.0 may send this - return (based_get_local_node_dc()? pcmk_rc_ok : EPERM); -} - // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index f06873920a5..9e321fe7475 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -18,7 +18,6 @@ int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer); -int based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 04d1c5417e8..2e12ea94139 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -24,7 +24,6 @@ static const cib__op_fn_t op_functions[] = { [cib__op_create] = cib__process_create, [cib__op_delete] = cib__process_delete, [cib__op_erase] = cib__process_erase, - [cib__op_is_primary] = based_process_is_primary, [cib__op_modify] = cib__process_modify, [cib__op_noop] = based_process_noop, [cib__op_ping] = based_process_ping, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index e82ced195d2..944b2df62be 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -24,7 +24,6 @@ extern "C" { #define PCMK__CIB_REQUEST_SECONDARY "cib_slave" #define PCMK__CIB_REQUEST_PRIMARY "cib_master" #define PCMK__CIB_REQUEST_SYNC "cib_sync" -#define PCMK__CIB_REQUEST_IS_PRIMARY "cib_ismaster" #define PCMK__CIB_REQUEST_BUMP "cib_bump" #define PCMK__CIB_REQUEST_QUERY "cib_query" #define PCMK__CIB_REQUEST_CREATE "cib_create" @@ -76,7 +75,6 @@ enum cib__op_type { cib__op_create, cib__op_delete, cib__op_erase, - cib__op_is_primary, cib__op_modify, cib__op_noop, cib__op_ping, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 3545f753ff5..8b142834354 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -61,9 +61,6 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_ERASE, cib__op_erase, cib__op_attr_modifies|cib__op_attr_replaces|cib__op_attr_transaction }, - { - PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary, cib__op_attr_none - }, { PCMK__CIB_REQUEST_MODIFY, cib__op_modify, cib__op_attr_modifies|cib__op_attr_transaction From 32345e2b8efafaaee71c370106742b01d0972377 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 00:08:25 -0800 Subject: [PATCH 167/202] Refactor: libcib, based: Drop cib__op_attr_local Three CIB operations had the cib__op_attr_local flag. All of them are supported only by the CIB manager (cib_native and cib_remote); they're unsupported for cib_file clients. There's no straightforward way to misuse these. The API methods (fetch_schemas(), set_primary(), set_secondary()) don't take a host argument. They all three call cib__internal_op() with a NULL host argument, so that PCMK__XA_CIB_HOST is unset in the resulting request. As far as I can tell, sending one of these requests with a PCMK__XA_CIB_HOST attribute (whether set to the local node name or to something else) is impossible without some pretty devious and irresponsible hacking, like sending an XML payload directly without using the libcib functions to do so. This commit adds a CRM_CHECK() call to each processor function, just in case this ever happens due to malice or to future programmer error. I don't see any reason to wait for a compatibility break for this change. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 24 ++---------------------- daemons/based/based_messages.c | 9 +++++++++ include/crm/cib/internal.h | 3 --- lib/cib/cib_ops.c | 6 +++--- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index eccf1135d6e..10b7a707ca3 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -315,28 +315,9 @@ process_ping_reply(const xmlNode *reply) } static void -log_local_options(const pcmk__client_t *client, - const cib__operation_t *operation, const char *host, +log_local_options(const pcmk__client_t *client, const char *host, const char *op) { - if (pcmk__is_set(operation->flags, cib__op_attr_local)) { - /* @COMPAT Currently host is ignored. At a compatibility break, throw an - * error (from based_process_request() or earlier) if host is not NULL - * or OUR_NODENAME. - */ - pcmk__trace("Processing always-local %s op from client %s", op, - pcmk__client_name(client)); - - if (pcmk__str_eq(host, OUR_NODENAME, - pcmk__str_casei|pcmk__str_null_matches)) { - return; - } - - pcmk__warn("Operation '%s' is always local but its target host is set " - "to '%s'", op, host); - return; - } - if (based_stand_alone()) { pcmk__trace("Processing %s op from client %s (stand-alone)", op, pcmk__client_name(client)); @@ -737,7 +718,6 @@ based_handle_request(pcmk__request_t *request) } else if (request->ipc_client != NULL) { // Forward modifying and non-local requests via cluster if (!based_stand_alone() - && !pcmk__is_set(operation->flags, cib__op_attr_local) && (pcmk__is_set(operation->flags, cib__op_attr_modifies) || !pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches))) { @@ -751,7 +731,7 @@ based_handle_request(pcmk__request_t *request) needs_reply = false; local_notify = true; - log_local_options(request->ipc_client, operation, host, request->op); + log_local_options(request->ipc_client, host, request->op); } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 3b027feac72..e0e0db8b1ba 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -123,6 +123,9 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { + // This should always be processed locally and never addressed to any host + CRM_CHECK(pcmk__xe_get(req, PCMK__XA_CIB_HOST) == NULL, return EOPNOTSUPP); + if (!based_get_local_node_dc()) { pcmk__info("We are now in R/W mode"); based_set_local_node_dc(true); @@ -143,6 +146,9 @@ based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) GList *schemas = NULL; GList *already_included = NULL; + // This should always be processed locally and never addressed to any host + CRM_CHECK(pcmk__xe_get(req, PCMK__XA_CIB_HOST) == NULL, return EOPNOTSUPP); + *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); data = cib__get_calldata(req); @@ -179,6 +185,9 @@ based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) { + // This should always be processed locally and never addressed to any host + CRM_CHECK(pcmk__xe_get(req, PCMK__XA_CIB_HOST) == NULL, return EOPNOTSUPP); + if (based_get_local_node_dc()) { pcmk__info("We are now in R/O mode"); based_set_local_node_dc(false); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 944b2df62be..0297a4e9ae1 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -50,9 +50,6 @@ enum cib__op_attr { //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), - //! Must only be processed locally - cib__op_attr_local = (UINT32_C(1) << 3), - //! Replaces CIB cib__op_attr_replaces = (UINT32_C(1) << 4), diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 8b142834354..efc77c54a23 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -72,7 +72,7 @@ static const cib__operation_t cib_ops[] = { CRM_OP_PING, cib__op_ping, cib__op_attr_none }, { - PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, cib__op_attr_local + PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, cib__op_attr_none }, { PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none @@ -85,10 +85,10 @@ static const cib__operation_t cib_ops[] = { |cib__op_attr_transaction }, { - PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local + PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_none }, { - PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, cib__op_attr_local + PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, cib__op_attr_none }, { PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_none From 01fa7efdc186b61503f07d402d6784204e3c4136 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 00:19:44 -0800 Subject: [PATCH 168/202] Feature: based: Drop support for cib_delete_alt requests The behavior change here is pretty minor. See commit 3f65618 -- this request type apparently never worked since it was added in 2008. It always returned EINVAL. Now, the CIB manager will still return EINVAL for this request, as that will be the return value from cib__get_operation(). The only difference I see is that we'll no longer send the local client a notification. So I see no reason to wait for a formal backward compatibility break to make this change. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 21 --------------------- daemons/based/based_messages.h | 1 - daemons/based/based_operation.c | 1 - include/crm/cib/internal.h | 2 -- lib/cib/cib_ops.c | 3 --- 5 files changed, 28 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index e0e0db8b1ba..f34fcbd6bf5 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -29,27 +29,6 @@ #include "pacemaker-based.h" -/*! - * \internal - * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE - * - * \param[in] req Ignored - * \param[in] cib Ignored - * \param[in] answer Ignored - * - * \return \c EINVAL - * - * \note This is unimplemented and simply returns an error. - */ -int -based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) -{ - /* @COMPAT Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed. Note that - * external clients with Pacemaker versions < 3.0.0 can send it. - */ - return EINVAL; -} - int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 9e321fe7475..e1167850186 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -14,7 +14,6 @@ #include // xmlNode * -int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 2e12ea94139..1e0699c775c 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -17,7 +17,6 @@ #include "pacemaker-based.h" static const cib__op_fn_t op_functions[] = { - [cib__op_abs_delete] = based_process_abs_delete, [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = based_process_commit_transact, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 0297a4e9ae1..a12ed053c59 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -33,7 +33,6 @@ extern "C" { #define PCMK__CIB_REQUEST_REPLACE "cib_replace" #define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff" #define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade" -#define PCMK__CIB_REQUEST_ABS_DELETE "cib_delete_alt" #define PCMK__CIB_REQUEST_NOOP "noop" #define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req" #define PCMK__CIB_REQUEST_COMMIT_TRANSACT "cib_commit_transact" @@ -65,7 +64,6 @@ enum cib__op_attr { * \brief Types of CIB operations */ enum cib__op_type { - cib__op_abs_delete, cib__op_apply_patch, cib__op_bump, cib__op_commit_transact, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index efc77c54a23..c44b6ebc00d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -34,9 +34,6 @@ static GHashTable *operation_table = NULL; static const cib__operation_t cib_ops[] = { - { - PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete, cib__op_attr_modifies - }, { PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, cib__op_attr_modifies|cib__op_attr_transaction From 56d63438a6bb900a23c0d0dc23c44530a3e8480b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 01:32:29 -0800 Subject: [PATCH 169/202] Refactor: libcib: Drop cib__op_attr_replaces Nothing has used it since c13a9d9a. Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 3 --- lib/cib/cib_ops.c | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index a12ed053c59..2d54bdae41b 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -49,9 +49,6 @@ enum cib__op_attr { //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), - //! Replaces CIB - cib__op_attr_replaces = (UINT32_C(1) << 4), - //! Writes to disk on success cib__op_attr_writes_through = (UINT32_C(1) << 5), diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index c44b6ebc00d..b10a769f667 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -44,7 +44,7 @@ static const cib__operation_t cib_ops[] = { }, { PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, - cib__op_attr_modifies|cib__op_attr_replaces|cib__op_attr_writes_through + cib__op_attr_modifies|cib__op_attr_writes_through }, { PCMK__CIB_REQUEST_CREATE, cib__op_create, @@ -56,7 +56,7 @@ static const cib__operation_t cib_ops[] = { }, { PCMK__CIB_REQUEST_ERASE, cib__op_erase, - cib__op_attr_modifies|cib__op_attr_replaces|cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_MODIFY, cib__op_modify, @@ -77,7 +77,6 @@ static const cib__operation_t cib_ops[] = { { PCMK__CIB_REQUEST_REPLACE, cib__op_replace, cib__op_attr_modifies - |cib__op_attr_replaces |cib__op_attr_writes_through |cib__op_attr_transaction }, From 1c4d79fa9c4e425fb9a0047b389b9f7fd0b3bfca Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 00:34:09 -0800 Subject: [PATCH 170/202] Low: based: Replace/commit ops don't automatically write to disk Write to disk if the configuration section changed. Otherwise, there seems to be no reason to treat the replace and commit-transaction operations specially. If the PCMK_XE_CONFIGURATION element or any of its children was created, modified, moved, or deleted, then it will have the pcmk__xf_dirty flag set and/or be in the doc's deleted objects list. See is_config_change() in patchset.c. The "always write to disk for replace ops" logic began with commit 8d83bd9b (2010). We were firmly in the "legacy mode" era (pre-1.1.12) at that time. The change detection logic appeared to be quite different. Now we examine the v2 patchset to see if the config changed. That should find the ordering changes that we were concerned about. The writes_through flag was set for the commit-transaction operation when that operation was added (commit c252b7b1). It seems that my reasoning at the time was "a commit-transaction operation is quite similar to a replace operation, since we replace the pre-transaction CIB with the post-transaction CIB on success. So we should set this flag for it too." Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- lib/cib/cib_ops.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 10b7a707ca3..7a9c17b3c58 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -515,7 +515,7 @@ based_perform_op_rw(pcmk__request_t *request, const cib__operation_t *operation, if (result_cib != based_cib) { /* Always write to disk for successful ops with the writes-through flag - * set. This also avoids the need to detect ordering changes. + * set. * * An exception is a request within a transaction. Since a transaction * is atomic, intermediate results must not be written to disk. diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index b10a769f667..9eb8d38eec9 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -44,7 +44,7 @@ static const cib__operation_t cib_ops[] = { }, { PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, - cib__op_attr_modifies|cib__op_attr_writes_through + cib__op_attr_modifies }, { PCMK__CIB_REQUEST_CREATE, cib__op_create, @@ -76,9 +76,7 @@ static const cib__operation_t cib_ops[] = { }, { PCMK__CIB_REQUEST_REPLACE, cib__op_replace, - cib__op_attr_modifies - |cib__op_attr_writes_through - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, { PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_none From 07f8b0f57379bc8674593c6266f9dda497ea7fd8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 01:48:45 -0800 Subject: [PATCH 171/202] Refactor: libcrmcommon: Drop pcmk__update_schema transform argument It's always true since 2c4737bc. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 2 +- include/crm/common/schemas_internal.h | 2 +- lib/cib/cib_ops.c | 2 +- lib/common/schemas.c | 14 ++++++-------- tools/cibadmin.c | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index f34fcbd6bf5..a517b726da4 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -226,7 +226,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) return pcmk_rc_cib_corrupt; } - rc = pcmk__update_schema(&scratch, NULL, true, true); + rc = pcmk__update_schema(&scratch, NULL, true); new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { diff --git a/include/crm/common/schemas_internal.h b/include/crm/common/schemas_internal.h index fa4c1039099..1365bb609e5 100644 --- a/include/crm/common/schemas_internal.h +++ b/include/crm/common/schemas_internal.h @@ -40,7 +40,7 @@ bool pcmk__validate_xml(xmlNode *xml, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context); bool pcmk__configured_schema_validates(xmlNode *xml); int pcmk__update_schema(xmlNode **xml, const char *max_schema_name, - bool transform, bool to_logs); + bool to_logs); void pcmk__warn_if_schema_deprecated(const char *schema); int pcmk__update_configured_schema(xmlNode **xml, bool to_logs); diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 9eb8d38eec9..2af605f1e3f 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -978,7 +978,7 @@ cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) // pcmk__update_schema() may free the original validate-with string original_schema = pcmk__xe_get_copy(*cib, PCMK_XA_VALIDATE_WITH); - rc = pcmk__update_schema(&updated, max_schema, true, + rc = pcmk__update_schema(&updated, max_schema, !pcmk__is_set(options, cib_verbose)); *cib = pcmk__xml_replace_with_copy(*cib, updated); pcmk__xml_free(updated); diff --git a/lib/common/schemas.c b/lib/common/schemas.c index 27c1485d36e..99e72cd034e 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -1108,16 +1108,13 @@ get_configured_schema(const xmlNode *xml) * after being transformed) * \param[in] max_schema_name If not NULL, do not update \p xml to any * schema later than this one - * \param[in] transform If false, do not update \p xml to any schema - * that requires an XSL transform * \param[in] to_logs If false, certain validation errors will be * sent to stderr rather than logged * * \return Standard Pacemaker return code */ int -pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, - bool to_logs) +pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool to_logs) { int max_stable_schemas = xml_latest_schema_index(); int max_schema_index = 0; @@ -1183,7 +1180,7 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, } // coverity[null_field] The index check ensures entry->next is not NULL - if (!transform || (current_schema->transforms == NULL) + if ((current_schema->transforms == NULL) || validate_with_silent((*xml)->doc, entry->next->data)) { /* The next schema either doesn't require a transform or validates * successfully even without the transform. Skip the transform and @@ -1210,8 +1207,9 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, if ((best_schema != NULL) && (best_schema->schema_index > original_schema->schema_index)) { - pcmk__info("%s the configuration schema to %s", - (transform? "Transformed" : "Upgraded"), best_schema->name); + + pcmk__info("Transformed the configuration schema to %s", + best_schema->name); pcmk__xe_set(*xml, PCMK_XA_VALIDATE_WITH, best_schema->name); } return rc; @@ -1257,7 +1255,7 @@ pcmk__update_configured_schema(xmlNode **xml, bool to_logs) entry = NULL; converted = pcmk__xml_copy(NULL, *xml); - if (pcmk__update_schema(&converted, NULL, true, to_logs) == pcmk_rc_ok) { + if (pcmk__update_schema(&converted, NULL, to_logs) == pcmk_rc_ok) { new_schema_name = pcmk__xe_get(converted, PCMK_XA_VALIDATE_WITH); entry = pcmk__get_schema(new_schema_name); } diff --git a/tools/cibadmin.c b/tools/cibadmin.c index 0c769aa91ed..701b27f8bd7 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -330,7 +330,7 @@ cibadmin_post_upgrade(pcmk__output_t *out, cib_t *cib_conn, int call_options, if (cib_conn->cmds->query(cib_conn, NULL, &obj, call_options) == pcmk_ok) { - pcmk__update_schema(&obj, NULL, true, false); + pcmk__update_schema(&obj, NULL, false); } pcmk__xml_free(obj); } From 59255d680a1078789bbdfd2f2a4ebe961835bb21 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 02:08:10 -0800 Subject: [PATCH 172/202] Refactor: libcib: Drop cib__op_attr_writes_through Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 ++++------ include/crm/cib/internal.h | 3 --- lib/cib/cib_ops.c | 4 +--- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 7a9c17b3c58..666c5318535 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -514,17 +514,15 @@ based_perform_op_rw(pcmk__request_t *request, const cib__operation_t *operation, } if (result_cib != based_cib) { - /* Always write to disk for successful ops with the writes-through flag - * set. - * - * An exception is a request within a transaction. Since a transaction + /* Write to disk on config change or successful upgrade (which may + * update PCMK_XA_VALIDATE_WITH without changing the configuration), + * unless the request is part of a transaction. Since a transaction * is atomic, intermediate results must not be written to disk. */ const bool to_disk = !pcmk__is_set(request->call_options, cib_transaction) && (config_changed - || pcmk__is_set(operation->flags, - cib__op_attr_writes_through)); + || (operation->type == cib__op_upgrade)); rc = based_activate_cib(result_cib, to_disk, request->op); } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 2d54bdae41b..3136aea2e44 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -49,9 +49,6 @@ enum cib__op_attr { //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), - //! Writes to disk on success - cib__op_attr_writes_through = (UINT32_C(1) << 5), - //! Supported in a transaction cib__op_attr_transaction = (UINT32_C(1) << 6), }; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 2af605f1e3f..a3c17347b23 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -92,9 +92,7 @@ static const cib__operation_t cib_ops[] = { }, { PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, - cib__op_attr_modifies - |cib__op_attr_writes_through - |cib__op_attr_transaction + cib__op_attr_modifies|cib__op_attr_transaction }, }; From d3923c8c885204d86784bf311b03ad72117189a1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 02:14:42 -0800 Subject: [PATCH 173/202] Refactor: libcib: Drop cib__op_attr_transaction PCMK__CIB_REQUEST_COMMIT_TRANSACT/cib__op_commit_transact is now the only request type that has cib__op_attr_modifies but does not have cib__op_attr_transaction. There are no plans to change that, so we can drop cib__op_attr_transaction and check for that operation type directly. Signed-off-by: Reid Wahl --- daemons/based/based_transaction.c | 2 +- include/crm/cib/internal.h | 3 --- lib/cib/cib_ops.c | 23 ++++++++--------------- lib/cib/cib_utils.c | 9 +++++---- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 537e90fdbc7..baa6c72d1dd 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -82,7 +82,7 @@ process_transaction_requests(xmlNode *transaction, pcmk__client_t *client, rc = cib__get_operation(op, &operation); if (rc == pcmk_rc_ok) { - if (!pcmk__is_set(operation->flags, cib__op_attr_transaction) + if ((operation->type == cib__op_commit_transact) || (host != NULL)) { rc = EOPNOTSUPP; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 3136aea2e44..e7167e934ad 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -48,9 +48,6 @@ enum cib__op_attr { //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), - - //! Supported in a transaction - cib__op_attr_transaction = (UINT32_C(1) << 6), }; /*! diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index a3c17347b23..d9a5f1e0497 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -36,31 +36,26 @@ static GHashTable *operation_table = NULL; static const cib__operation_t cib_ops[] = { { PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, - cib__op_attr_modifies|cib__op_attr_transaction + cib__op_attr_modifies }, { - PCMK__CIB_REQUEST_BUMP, cib__op_bump, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_BUMP, cib__op_bump, cib__op_attr_modifies }, { PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, cib__op_attr_modifies }, { - PCMK__CIB_REQUEST_CREATE, cib__op_create, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_CREATE, cib__op_create, cib__op_attr_modifies }, { - PCMK__CIB_REQUEST_DELETE, cib__op_delete, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_DELETE, cib__op_delete, cib__op_attr_modifies }, { - PCMK__CIB_REQUEST_ERASE, cib__op_erase, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_ERASE, cib__op_erase, cib__op_attr_modifies }, { - PCMK__CIB_REQUEST_MODIFY, cib__op_modify, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_MODIFY, cib__op_modify, cib__op_attr_modifies }, { PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none @@ -75,8 +70,7 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none }, { - PCMK__CIB_REQUEST_REPLACE, cib__op_replace, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_REPLACE, cib__op_replace, cib__op_attr_modifies }, { PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_none @@ -91,8 +85,7 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_SYNC, cib__op_sync, cib__op_attr_none }, { - PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, - cib__op_attr_modifies|cib__op_attr_transaction + PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, cib__op_attr_modifies }, }; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index ea9f3e169a6..67a67f6ea58 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -713,24 +713,25 @@ validate_transaction_request(const xmlNode *request) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); const cib__operation_t *operation = NULL; - int rc = cib__get_operation(op, &operation); + int rc = pcmk_rc_ok; + rc = cib__get_operation(op, &operation); if (rc != pcmk_rc_ok) { // cib__get_operation() logs error return rc; } - if (!pcmk__is_set(operation->flags, cib__op_attr_transaction)) { + if (operation->type == cib__op_commit_transact) { pcmk__err("Operation %s is not supported in CIB transactions", op); return EOPNOTSUPP; } if (host != NULL) { pcmk__err("Operation targeting a specific node (%s) is not supported " - "in a CIB transaction", - host); + "in a CIB transaction", host); return EOPNOTSUPP; } + return pcmk_rc_ok; } From 7fb2e6b13166f553114507269180783ac9f9d40f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 02:25:57 -0800 Subject: [PATCH 174/202] Refactor: libcib: Drop enum cib__op_attr Replace it with a boolean "modifies_cib". Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 ++++----- include/crm/cib/internal.h | 14 +------------ lib/cib/cib_file.c | 7 ++----- lib/cib/cib_ops.c | 36 ++++++++++++++++----------------- 4 files changed, 24 insertions(+), 43 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 666c5318535..ddb447ddaa8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -584,7 +584,7 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, int epoch = 0; int num_updates = 0; - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + if (!operation->modifies_cib) { level = LOG_TRACE; } else if (rc != pcmk_rc_ok) { @@ -716,7 +716,7 @@ based_handle_request(pcmk__request_t *request) } else if (request->ipc_client != NULL) { // Forward modifying and non-local requests via cluster if (!based_stand_alone() - && (pcmk__is_set(operation->flags, cib__op_attr_modifies) + && (operation->modifies_cib || !pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches))) { @@ -761,7 +761,7 @@ based_handle_request(pcmk__request_t *request) start_time = time(NULL); - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + if (!operation->modifies_cib) { rc = cib__perform_op_ro(op_function, request->xml, &based_cib, &output); } else { @@ -775,9 +775,7 @@ based_handle_request(pcmk__request_t *request) } done: - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) - && needs_reply && !based_stand_alone()) { - + if (!operation->modifies_cib && needs_reply && !based_stand_alone()) { send_peer_reply(reply, originator); } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index e7167e934ad..3c411b03448 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -38,18 +38,6 @@ extern "C" { #define PCMK__CIB_REQUEST_COMMIT_TRANSACT "cib_commit_transact" #define PCMK__CIB_REQUEST_SCHEMAS "cib_schemas" -/*! - * \internal - * \brief Flags for CIB operation attributes - */ -enum cib__op_attr { - //! No special attributes - cib__op_attr_none = 0, - - //! May modify state (of the CIB itself or of the CIB manager) - cib__op_attr_modifies = (UINT32_C(1) << 1), -}; - /*! * \internal * \brief Types of CIB operations @@ -89,7 +77,7 @@ typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode **cib, xmlNode **output); typedef struct { const char *name; enum cib__op_type type; - uint32_t flags; //!< Group of enum cib__op_attr flags + bool modifies_cib; } cib__operation_t; typedef struct { diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 52219f2315f..3dec663bc8d 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -145,7 +145,6 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); bool changed = false; - bool read_only = false; xmlNode *result_cib = NULL; xmlNode *cib_diff = NULL; xmlNode *local_output = NULL; @@ -166,9 +165,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); } - read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); - - if (read_only) { + if (!operation->modifies_cib) { rc = cib__perform_op_ro(op_function, request, &private->cib_xml, &local_output); } else { @@ -188,7 +185,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) // Show validation errors to stderr pcmk__validate_xml(result_cib, NULL, NULL); - } else if ((rc == pcmk_rc_ok) && !read_only) { + } else if ((rc == pcmk_rc_ok) && operation->modifies_cib) { if (result_cib != private->cib_xml) { pcmk__xml_free(private->cib_xml); private->cib_xml = result_cib; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index d9a5f1e0497..f59b08e55b7 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -35,57 +35,55 @@ static GHashTable *operation_table = NULL; static const cib__operation_t cib_ops[] = { { - PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, - cib__op_attr_modifies + PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch, true }, { - PCMK__CIB_REQUEST_BUMP, cib__op_bump, cib__op_attr_modifies + PCMK__CIB_REQUEST_BUMP, cib__op_bump, true }, { - PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, - cib__op_attr_modifies + PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact, true }, { - PCMK__CIB_REQUEST_CREATE, cib__op_create, cib__op_attr_modifies + PCMK__CIB_REQUEST_CREATE, cib__op_create, true }, { - PCMK__CIB_REQUEST_DELETE, cib__op_delete, cib__op_attr_modifies + PCMK__CIB_REQUEST_DELETE, cib__op_delete, true }, { - PCMK__CIB_REQUEST_ERASE, cib__op_erase, cib__op_attr_modifies + PCMK__CIB_REQUEST_ERASE, cib__op_erase, true }, { - PCMK__CIB_REQUEST_MODIFY, cib__op_modify, cib__op_attr_modifies + PCMK__CIB_REQUEST_MODIFY, cib__op_modify, true }, { - PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none + PCMK__CIB_REQUEST_NOOP, cib__op_noop, false }, { - CRM_OP_PING, cib__op_ping, cib__op_attr_none + CRM_OP_PING, cib__op_ping, false }, { - PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, cib__op_attr_none + PCMK__CIB_REQUEST_PRIMARY, cib__op_primary, false }, { - PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none + PCMK__CIB_REQUEST_QUERY, cib__op_query, false }, { - PCMK__CIB_REQUEST_REPLACE, cib__op_replace, cib__op_attr_modifies + PCMK__CIB_REQUEST_REPLACE, cib__op_replace, true }, { - PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_none + PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, false }, { - PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, cib__op_attr_none + PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary, false }, { - PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_none + PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, false }, { - PCMK__CIB_REQUEST_SYNC, cib__op_sync, cib__op_attr_none + PCMK__CIB_REQUEST_SYNC, cib__op_sync, false }, { - PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, cib__op_attr_modifies + PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, true }, }; From d88aa64ebbf40845d0e569f7072a9ae96bc13b47 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 3 May 2026 00:00:48 -0700 Subject: [PATCH 175/202] Refactor: libcib: Clean up includes Using include-what-you-use. Signed-off-by: Reid Wahl --- include/crm/cib.h | 20 +++++++++---------- include/crm/cib/cib_types.h | 3 --- include/crm/cib/util.h | 7 ++++--- include/crm/cib_compat.h | 4 ++-- lib/cib/cib_attrs.c | 33 ++++++++++++++++--------------- lib/cib/cib_client.c | 35 +++++++++++++++++++-------------- lib/cib/cib_file.c | 39 +++++++++++++++++++++---------------- lib/cib/cib_native.c | 31 +++++++++++++++-------------- lib/cib/cib_ops.c | 37 +++++++++++++++++------------------ lib/cib/cib_remote.c | 23 +++++++++++----------- lib/cib/cib_utils.c | 37 +++++++++++++++++++++++------------ 11 files changed, 145 insertions(+), 124 deletions(-) diff --git a/include/crm/cib.h b/include/crm/cib.h index 2c180e14754..6c25b98a96f 100644 --- a/include/crm/cib.h +++ b/include/crm/cib.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,13 +8,13 @@ */ #ifndef PCMK__CRM_CIB__H -# define PCMK__CRM_CIB__H +#define PCMK__CRM_CIB__H -# include // gboolean -# include -# include -# include -# include +#include // gboolean + +// cib.h is a wrapper for the following headers +#include +#include #ifdef __cplusplus extern "C" { @@ -27,7 +27,7 @@ extern "C" { */ // Use pcmk__compare_versions() for doing comparisons -# define CIB_FEATURE_SET "2.0" +#define CIB_FEATURE_SET "2.0" /* Core functions */ @@ -46,7 +46,7 @@ void cib_free_notify(cib_t *cib); void cib_free_callbacks(cib_t *cib); // NOTE: sbd (as of at least 1.5.2) uses this -void cib_delete(cib_t * cib); +void cib_delete(cib_t *cib); void cib_dump_pending_callbacks(void); int num_cib_op_callbacks(void); @@ -62,4 +62,4 @@ void remove_cib_op_callback(int call_id, gboolean all_callbacks); #include #endif -#endif +#endif // PCMK__CRM_CIB__H diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index baca7292775..6771ebb9434 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -16,9 +16,6 @@ #include // gboolean, GList #include // xmlNode -#include -#include - #ifdef __cplusplus extern "C" { #endif diff --git a/include/crm/cib/util.h b/include/crm/cib/util.h index 8da9b4d0074..e98f0a0d423 100644 --- a/include/crm/cib/util.h +++ b/include/crm/cib/util.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2024 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,10 +8,11 @@ */ #ifndef PCMK__CRM_CIB_UTIL__H -# define PCMK__CRM_CIB_UTIL__H +#define PCMK__CRM_CIB_UTIL__H #include // gboolean #include // xmlNode + #include // cib_t #ifdef __cplusplus @@ -58,4 +59,4 @@ int cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, } #endif -#endif +#endif // PCMK__CRM_CIB_UTIL__H diff --git a/include/crm/cib_compat.h b/include/crm/cib_compat.h index dd0d6dbfc2d..8aab578e85f 100644 --- a/include/crm/cib_compat.h +++ b/include/crm/cib_compat.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -8,7 +8,7 @@ */ #ifndef PCMK__CRM_CIB_COMPAT__H -# define PCMK__CRM_CIB_COMPAT__H +#define PCMK__CRM_CIB_COMPAT__H #include // cib_t diff --git a/lib/cib/cib_attrs.c b/lib/cib/cib_attrs.c index 1a2de1c5c17..a8655e6ca16 100644 --- a/lib/cib/cib_attrs.c +++ b/lib/cib/cib_attrs.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2025 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,22 +9,23 @@ #include -#include - -#include - +#include // EINVAL, ENOMSG, ENOTUNIQ, ENXIO #include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include +#include // NULL +#include // free +#include // strdup + +#include // g_*, gboolean, GString, TRUE, FALSE, etc. +#include // xmlNode + +#include // pcmk__str_*, etc. +#include // CRM_CHECK +#include // crm_create_nvpair_xml +#include // PCMK_META_*, PCMK_VALUE_* +#include // pcmk_rc_*, pcmk_ok, CRM_EX_OK, etc. +#include // PCMK_XA_*, PCMK_XE_* +#include // cib_*, *_delegate, query_node_uuid +#include // cib__*, PCMK___CIB_* static pcmk__output_t * new_output_object(const char *ty) diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 89843aacecf..3403ba68db0 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -8,22 +8,27 @@ */ #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include +#include // errno, EEXIST, EINVAL, ETIME, etc. +#include // getpwuid, struct passwd +#include +#include // NULL +#include // calloc, free, getenv, setenv, unsetenv +#include // strcmp, strerror +#include // mkdir +#include // geteuid + +#include // gboolean, g_*, G_SOURCE_CONTINUE, etc. +#include // xmlNode + +#include // cib_* +#include // cib__*, PCMK__CIB_*, etc. +#include // pcmk__str_*, pcmk__trace, etc. +#include // CRM_CHECK +#include // pcmk_rc_*, pcmk_strerror, pcmk_ok, etc. +#include // PCMK_XA_VERSION +#include // CRM_CONFIG_DIR, CRM_DAEMON_USER +#include // CRM_OP_PING static GHashTable *cib_op_callback_table = NULL; diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 3dec663bc8d..a90f1ff5ce1 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -9,24 +9,29 @@ */ #include -#include -#include + +#include // errno, EINVAL, ENOENT, ENXIO, etc. #include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include +#include // NULL +#include // uint32_t, UINT32_C +#include // fprintf, rename, stderr +#include // calloc, free, getenv, mkstemp +#include // strcmp, strdup, strerror, strrchr +#include // fchmod, stat, umask, S_* +#include // gid_t, uid_t +#include // chown, close, fchown, link, unlink, etc. + +#include // gpointer, g_* +#include // xmlNode +#include // LOG_TRACE + +#include // cib_* +#include // cib__* +#include // pcmk__err, pcmk__xml_*, etc. +#include // CRM_CHECK +#include // pcmk_rc_*, pcmk_err_*, etc. +#include // PCMK_XA_*, PCMK_XE_* +#include // CRM_CONFIG_DIR, CRM_DAEMON_USER #define CIB_SERIES "cib" #define CIB_SERIES_MAX 100 diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index f3ee5dbd283..421aed1a152 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -10,22 +10,23 @@ #include -#include -#include -#include +#include // ECOMM, EINVAL, ENOMSG, ENOTCONN, etc. #include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include +#include // NULL +#include // calloc, free +#include // ssize_t + +#include // gpointer, g_*, G_*, FALSE, TRUE +#include // xmlNode + +#include // cib_*, remove_cib_op_callback +#include // cib__*, PCMK__CIB_REQUEST_QUERY +#include // pcmk__err, pcmk__xml_*, etc. +#include // crm_ipc_* +#include // CRM_CHECK, crm_log_xml_explicit +#include // mainloop_* +#include // pcmk_rc_ok, pcmk_ok, pcmk_strerror, etc. +#include // CRM_OP_REGISTER, crm_system_name typedef struct { char *token; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index f59b08e55b7..0151189699b 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -9,26 +9,25 @@ #include +#include // EEXIST, EINVAL, ENXIO #include -#include // uint32_t -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include // xmlXPathObject, etc. - -#include -#include - -#include +#include // NULL +#include // uint32_t +#include // free + +#include // g_*, GHashTable, gpointer +#include // xmlGetNodePath, xmlNode, XML_ELEMENT_NODE +#include // xmlChar +#include // xmlXPathObject, xmlXPathFreeObject + +#include // cib_*, createEmptyCib +#include // cib__*, PCMK__CIB_* +#include // pcmk_cib_*, pcmk_find_cib_element +#include // pcmk__err, pcmk__xml_*, etc. +#include // CRM_CHECK +#include // pcmk_rc_*, pcmk_legacy2rc +#include // xml_apply_patchset, PCMK_XA_, PCMK_XE_* +#include // CRM_OP_PING // @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon static GHashTable *operation_table = NULL; diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index a4e240f2dbb..4b0b5a3f25d 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -9,25 +9,26 @@ #include -#include // ENOTCONN, EPROTO, EAGAIN -#include // false, bool, true -#include // NULL, free, calloc +#include // ENOTCONN, EPROTO, EAGAIN, etc. +#include // bool, true, false +#include // NULL +#include // calloc, free, getenv #include // strdup #include // shutdown, SHUT_RDWR #include // time, time_t #include // close -#include // gpointer, gboolean, g_list_foreach +#include // gpointer, g_*, etc. #include // gnutls_deinit, gnutls_bye #include // xmlNode #include // QB_XS -#include // cib_t, cib_remote_new -#include // cib__create_op, cib__extend_transaction -#include -#include // mainloop_fd_callbacks -#include // pcmk_rc_str, pcmk_rc_* -#include // PCMK_XA_*, +#include // cib_* +#include // cib__* +#include // pcmk__xml_*, pcmk__err, etc. +#include // mainloop_* +#include // pcmk_rc_*, pcmk_ok, etc. +#include // PCMK_XA_* #include // CRM_OP_REGISTER, crm_system_name // GnuTLS handshake timeout in seconds @@ -35,8 +36,6 @@ static pcmk__tls_t *tls = NULL; -#include - typedef struct { int port; char *server; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 67a67f6ea58..b213635d1e4 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -7,20 +7,33 @@ * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include -#include -#include +#include // errno, EACCES, EAGAIN, EALREADY, etc. +#include +#include // NULL +#include // uint32_t +#include // free, getenv +#include // LOG_CRIT, LOG_INFO + +#include // gboolean, GHashTable, g_*, etc. +#include // xmlNode +#include // QB_XS + +#include // cib_*, createEmptyCib, etc. +#include // cib__*, PCMK__CIB_* +#include // pcmk_acl_*, xml_acl_* +#include // pcmk_find_cib_element +#include // pcmk__err, pcmk__xml_*, etc. +#include // crm_time_* +#include // CRM_CHECK +#include // pcmk_unpack_nvpair_blocks +#include // PCMK_OPT_*, PCMK_VALUE_* +#include // pcmk_rc_*, pcmk_err_*, pcmk_ok, etc. +#include // pcmk_rule_input_t +#include // xml_*_patchset, PCMK_XA_*, PCMK_XE_* +#include // CRM_FEATURE_SET, crm_system_name gboolean cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates) From 083ab42b083147a2f1e9c92d39443e7a23110214 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 13:23:14 -0800 Subject: [PATCH 176/202] Refactor: libcib: Drop redundant cib_discard_reply check in cib_remote.c We return early if cib_discard_reply is set. Signed-off-by: Reid Wahl --- lib/cib/cib_remote.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 4b0b5a3f25d..1fa5af97a0b 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -112,11 +112,12 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, } pcmk__xml_free(op_msg); - if ((call_options & cib_discard_reply)) { + if (pcmk__is_set(call_options, cib_discard_reply)) { pcmk__trace("Discarding reply"); return pcmk_ok; + } - } else if (!(call_options & cib_sync_call)) { + if (!pcmk__is_set(call_options, cib_sync_call)) { return cib->call_id; } @@ -196,10 +197,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, pcmk__log_xml_warn(op_reply, "failed"); } - if (output_data == NULL) { - /* do nothing more */ - - } else if (!(call_options & cib_discard_reply)) { + if (output_data != NULL) { xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { From 581a29a443e1ed0fdbc261472a7b7b358207b211 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 17:35:14 -0800 Subject: [PATCH 177/202] API: libcib: Deprecate cib_api_operations_t:query_from() method Use cib_api_operations_t:query() instead. A CIB query should always be processed locally. Except for brief intervals when changes are being processed, the CIB contents should be the same on all nodes within a cluster partition. As of this commit, a query is in fact always processed locally. The host argument of query_from is ignored. Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 1 + lib/cib/cib_client.c | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 6771ebb9434..22e84fb7489 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -171,6 +171,7 @@ typedef struct cib_api_operations_s { int (*query) (cib_t *cib, const char *section, xmlNode **output_data, int call_options); + //! \deprecated This method will be removed and should not be used int (*query_from) (cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options); diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 3403ba68db0..b53023df88b 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -230,6 +230,7 @@ cib_client_register_callback(cib_t *cib, int call_id, int timeout, callback_name, callback, NULL); } +// @COMPAT Deprecated static int cib_client_noop(cib_t * cib, int call_options) { @@ -247,15 +248,16 @@ cib_client_ping(cib_t * cib, xmlNode ** output_data, int call_options) static int cib_client_query(cib_t * cib, const char *section, xmlNode ** output_data, int call_options) { - return cib->cmds->query_from(cib, NULL, section, output_data, call_options); + return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, NULL, section, NULL, + output_data, call_options, cib->user); } +// @COMPAT Deprecated static int cib_client_query_from(cib_t * cib, const char *host, const char *section, xmlNode ** output_data, int call_options) { - return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, host, section, NULL, - output_data, call_options, cib->user); + return cib->cmds->query(cib, section, output_data, call_options); } static int @@ -286,7 +288,7 @@ cib_client_upgrade(cib_t * cib, int call_options) NULL, call_options, cib->user); } -// @COMPAT cib_api_operations_t:sync is deprecated since 3.0.2 +// @COMPAT Deprecated static int cib_client_sync(cib_t * cib, const char *section, int call_options) { @@ -641,12 +643,8 @@ cib_new_variant(void) new_cib->cmds->register_callback = cib_client_register_callback; new_cib->cmds->register_callback_full = cib_client_register_callback_full; - new_cib->cmds->noop = cib_client_noop; // Deprecated method new_cib->cmds->ping = cib_client_ping; new_cib->cmds->query = cib_client_query; - new_cib->cmds->sync = cib_client_sync; - - new_cib->cmds->query_from = cib_client_query_from; new_cib->cmds->sync_from = cib_client_sync_from; new_cib->cmds->set_primary = set_primary; @@ -668,6 +666,11 @@ cib_new_variant(void) new_cib->cmds->fetch_schemas = cib_client_fetch_schemas; + // @COMPAT Deprecated methods + new_cib->cmds->noop = cib_client_noop; + new_cib->cmds->sync = cib_client_sync; + new_cib->cmds->query_from = cib_client_query_from; + return new_cib; } From fe9b4b57c4dbfe8638065b48474476c309e79c55 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 23:25:18 -0800 Subject: [PATCH 178/202] Refactor: based: Drop redundant based_stand_alone() check parse_peer_options() is the only place where needs_reply can get set to true. If we're in stand-alone mode, we can't receive a message from a cluster peer. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ddb447ddaa8..d49fe9ec840 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -775,7 +775,7 @@ based_handle_request(pcmk__request_t *request) } done: - if (!operation->modifies_cib && needs_reply && !based_stand_alone()) { + if (!operation->modifies_cib && needs_reply) { send_peer_reply(reply, originator); } From dc948ef2a4d55296dbcba0be710beeca5afece36 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 16 Jan 2026 00:09:49 -0800 Subject: [PATCH 179/202] Refactor: based: Call process_ping_reply() in process section ...meaning after the "if (!process)" guard. This is an incremental change to make other changes easier. This changes behavior in a small way -- if cib_status is not OK, we will return an error instead of calling process_ping_reply(), which would sync our CIB. However, returning an error seems more correct. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index d49fe9ec840..85814f917f8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -346,8 +346,8 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, } if (is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { - process_ping_reply(request->xml); - return false; + *needs_reply = false; + return true; } if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { @@ -649,6 +649,7 @@ based_handle_request(pcmk__request_t *request) xmlNode *reply = NULL; int rc = pcmk_rc_ok; + const char *op = pcmk__xe_get(request->xml, PCMK__XA_CIB_OP); const char *originator = pcmk__xe_get(request->xml, PCMK__XA_SRC); const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); const char *call_id = pcmk__xe_get(request->xml, PCMK__XA_CIB_CALLID); @@ -761,7 +762,12 @@ based_handle_request(pcmk__request_t *request) start_time = time(NULL); - if (!operation->modifies_cib) { + if (pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none) + && pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei)) { + + process_ping_reply(request->xml); + + } else if (!operation->modifies_cib) { rc = cib__perform_op_ro(op_function, request->xml, &based_cib, &output); } else { From 43f50a63e38be017d2c02fe892816fbc3d42d15b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 16 Jan 2026 00:28:48 -0800 Subject: [PATCH 180/202] Refactor: based: Check cib_status after checking process This doesn't change behavior, but it facilitates an upcoming change. There are only two situations in which parse_peer_options() returns false. In both cases, we're supposed to ignore the request. An upcoming commit will make parse_peer_options() return void, and we'll make decisions about how to proceed based on the values of process, needs_reply, and local_notify. At that point, we don't want to check cib_status for the two cases that we're supposed to ignore. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 85814f917f8..77001266cad 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -744,6 +744,10 @@ based_handle_request(pcmk__request_t *request) pcmk__trace("Client is not interested in the reply"); } + if (!process) { + goto done; + } + if (cib_status != pcmk_rc_ok) { rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " @@ -756,10 +760,6 @@ based_handle_request(pcmk__request_t *request) goto done; } - if (!process) { - goto done; - } - start_time = time(NULL); if (pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none) From 80facec74855dab7f7ae6358431304096b1ddadd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 15 Jan 2026 23:53:57 -0800 Subject: [PATCH 181/202] Refactor: based: Return void from parse_peer_options() The meaning of the return code was never documented, and I've found it confusing. It meant "if false, don't do anything else -- don't check the CIB status, don't call a processor function, don't send a reply, and don't notify a local client." IMO it's simpler to let the existing logic in based_handle_request() handle these cases. We'll end up returning without doing anything if appropriate. The "return true" cases don't need any modification other than changing to "return". The "return false" cases need to negate the default values by setting process and needs_reply to false. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 77001266cad..ffa897a2ba1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -329,7 +329,7 @@ log_local_options(const pcmk__client_t *client, const char *host, } } -static bool +static void parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, bool *local_notify, bool *needs_reply, bool *process) { @@ -347,7 +347,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { *needs_reply = false; - return true; + return; } if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { @@ -357,7 +357,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, * from versions earlier than 3.0.2. (If the requesting node doesn't * receive a reply, it simply exits with CRM_EX_ERROR after 10 seconds.) */ - return true; + return; } if (is_reply @@ -368,7 +368,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, *process = false; *needs_reply = false; *local_notify = true; - return true; + return; } if ((reply_to != NULL) @@ -409,12 +409,14 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, *process = false; *needs_reply = false; *local_notify = true; - return true; + return; } if ((max == NULL) && !based_get_local_node_dc()) { // Ignore broadcast client requests when we're not the DC - return false; + *process = false; + *needs_reply = false; + return; } } @@ -423,17 +425,19 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", request->op, originator); - return true; + return; } if (host != NULL) { pcmk__trace("Ignoring %s request intended for CIB manager on %s", request->op, host); - return false; + *process = false; + *needs_reply = false; + return; } if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { - return true; + return; } *needs_reply = false; @@ -444,7 +448,6 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, pcmk__s(pcmk__xe_get(request->xml, PCMK__XA_CIB_CALLID), "without ID"), originator, (*local_notify? "" : "not")); - return true; } /*! @@ -732,10 +735,9 @@ based_handle_request(pcmk__request_t *request) log_local_options(request->ipc_client, host, request->op); - } else if (!parse_peer_options(operation, request, &local_notify, - &needs_reply, &process)) { - pcmk__reset_request(request); - return pcmk_rc_ok; + } else { + parse_peer_options(operation, request, &local_notify, &needs_reply, + &process); } if (pcmk__is_set(request->call_options, cib_discard_reply)) { From 67eadad50fb63501d2bf1eb3200624d7b8c96ecc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 23:15:45 -0800 Subject: [PATCH 182/202] Refactor: based: Default needs_reply to false This is intended to be equivalent to the existing code. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ffa897a2ba1..4c480df41a6 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -346,7 +346,6 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, } if (is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { - *needs_reply = false; return; } @@ -357,6 +356,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, * from versions earlier than 3.0.2. (If the requesting node doesn't * receive a reply, it simply exits with CRM_EX_ERROR after 10 seconds.) */ + *needs_reply = true; return; } @@ -366,7 +366,6 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, pcmk__trace("Will notify local clients for %s reply from %s", request->op, originator); *process = false; - *needs_reply = false; *local_notify = true; return; } @@ -407,7 +406,6 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, pcmk__trace("Will notify local clients for %s reply from %s", request->op, originator); *process = false; - *needs_reply = false; *local_notify = true; return; } @@ -415,7 +413,6 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if ((max == NULL) && !based_get_local_node_dc()) { // Ignore broadcast client requests when we're not the DC *process = false; - *needs_reply = false; return; } } @@ -425,6 +422,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", request->op, originator); + *needs_reply = true; return; } @@ -432,15 +430,14 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, pcmk__trace("Ignoring %s request intended for CIB manager on %s", request->op, host); *process = false; - *needs_reply = false; return; } if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { + *needs_reply = true; return; } - *needs_reply = false; pcmk__trace("Processing %s request broadcast by %s call %s on %s " "(local clients will%s be notified)", request->op, pcmk__s(pcmk__xe_get(request->xml, PCMK__XA_CIB_CLIENTNAME), @@ -646,7 +643,7 @@ based_handle_request(pcmk__request_t *request) { // @TODO: Break into multiple smaller functions bool process = true; // Whether to process request locally now - bool needs_reply = true; // Whether to build a reply + bool needs_reply = false; // Whether to build a reply bool local_notify = false; // Whether to notify (local) requester xmlNode *reply = NULL; @@ -712,7 +709,6 @@ based_handle_request(pcmk__request_t *request) * CIB copy, and we don't notify for individual requests because the * entire transaction is atomic. */ - needs_reply = false; pcmk__trace("Processing %s op from %s/%s on %s locally because it's " "part of a transaction", request->op, client_name, call_id, pcmk__xe_get(request->xml, PCMK__XA_SRC)); @@ -730,7 +726,6 @@ based_handle_request(pcmk__request_t *request) } // Process locally and notify local client; no peer to reply to - needs_reply = false; local_notify = true; log_local_options(request->ipc_client, host, request->op); From 89657aa90494adf9fdcb1186ff4f6fa56f104b34 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 16 Jan 2026 20:44:08 -0800 Subject: [PATCH 183/202] Refactor: based: Construct reply only if needs_reply is true Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4c480df41a6..a3dd3be3902 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -750,7 +750,7 @@ based_handle_request(pcmk__request_t *request) pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - if (!pcmk__is_set(request->call_options, cib_discard_reply)) { + if (needs_reply) { reply = create_cib_reply(request->xml, rc, based_cib); } @@ -773,7 +773,7 @@ based_handle_request(pcmk__request_t *request) log_op_result(request->xml, operation, rc, difftime(time(NULL), start_time)); - if (!pcmk__is_set(request->call_options, cib_discard_reply)) { + if (needs_reply) { reply = create_cib_reply(request->xml, rc, output); } From 71ff00426832e592af381301da719a76f25b20a2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 17 Jan 2026 23:31:12 -0800 Subject: [PATCH 184/202] Low: based: Fix memory leak on upgrade when validate-with is not set The leak was introduced by 2c4737bc. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index a517b726da4..810c51c942d 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -217,8 +217,6 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) return cib__process_upgrade(req, cib, answer); } - scratch = pcmk__xml_copy(NULL, *cib); - original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); if (original_schema == NULL) { pcmk__info("Rejecting upgrade request from %s: No " @@ -226,6 +224,8 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) return pcmk_rc_cib_corrupt; } + scratch = pcmk__xml_copy(NULL, *cib); + rc = pcmk__update_schema(&scratch, NULL, true); new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); From 05067c01f1f05f951c2ad5d5c20c10f4d4d4bcc2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 18 Jan 2026 15:34:15 -0800 Subject: [PATCH 185/202] Refactor: libcrmcluster: pcmk__cpg_send_xml() returns void It always returned true. Signed-off-by: Reid Wahl --- lib/cluster/cluster.c | 6 +++--- lib/cluster/cpg.c | 14 +++----------- lib/cluster/crmcluster_private.h | 4 ++-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index ae51f183c3a..3912480ccf1 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -215,13 +215,13 @@ pcmk__cluster_send_message(const pcmk__node_status_t *node, switch (pcmk_get_cluster_layer()) { #if SUPPORT_COROSYNC case pcmk_cluster_layer_corosync: - return pcmk__cpg_send_xml(data, node, service); + pcmk__cpg_send_xml(data, node, service); + return true; #endif // SUPPORT_COROSYNC default: - break; + return false; } - return false; } /*! diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index e7b95402d8d..2798a934b1a 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -908,10 +908,8 @@ pcmk__cpg_disconnect(pcmk_cluster_t *cluster) * \param[in] data Data to send * \param[in] node Cluster node to send message to * \param[in] dest Type of message to send - * - * \return \c true on success, or \c false otherwise */ -static bool +static void send_cpg_text(const char *data, const pcmk__node_status_t *node, enum pcmk_ipc_server dest) { @@ -1020,8 +1018,6 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, cs_message_queue = g_list_append(cs_message_queue, iov); crm_cs_flush(&pcmk_cpg_handle); - - return true; } /*! @@ -1031,19 +1027,15 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, * \param[in] msg XML message to send * \param[in] node Cluster node to send message to * \param[in] dest Type of message to send - * - * \return TRUE on success, otherwise FALSE */ -bool +void pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, enum pcmk_ipc_server dest) { - bool rc = true; GString *data = g_string_sized_new(1024); pcmk__xml_string(msg, 0, data, 0); - rc = send_cpg_text(data->str, node, dest); + send_cpg_text(data->str, node, dest); g_string_free(data, TRUE); - return rc; } diff --git a/lib/cluster/crmcluster_private.h b/lib/cluster/crmcluster_private.h index 44a9d33798d..0db9eca79ec 100644 --- a/lib/cluster/crmcluster_private.h +++ b/lib/cluster/crmcluster_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the Pacemaker project contributors + * Copyright 2020-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -71,7 +71,7 @@ G_GNUC_INTERNAL uint32_t pcmk__cpg_local_nodeid(cpg_handle_t handle); G_GNUC_INTERNAL -bool pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, +void pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, enum pcmk_ipc_server dest); #endif // SUPPORT_COROSYNC From 69c89163dc773c54dd47b9063bcfeb5675362209 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:21:35 -0700 Subject: [PATCH 186/202] Refactor: libcrmcluster: Pass GString to send_cpg_text(0 Drop the NULL check since we have only a single caller that guarantees non-NULL. Use data->len rather than calling strlen(). Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 2798a934b1a..0f89ba5658d 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -910,7 +910,7 @@ pcmk__cpg_disconnect(pcmk_cluster_t *cluster) * \param[in] dest Type of message to send */ static void -send_cpg_text(const char *data, const pcmk__node_status_t *node, +send_cpg_text(const GString *data, const pcmk__node_status_t *node, enum pcmk_ipc_server dest) { static int msg_id = 0; @@ -929,10 +929,6 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, local_name_len = strlen(local_name); } - if (data == NULL) { - data = ""; - } - if (local_pid == 0) { local_pid = getpid(); } @@ -971,18 +967,18 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, memcpy(msg->sender.uname, local_name, msg->sender.size); } - msg->size = 1 + strlen(data); + msg->size = data->len + 1; msg->header.size = sizeof(pcmk__cpg_msg_t) + msg->size; if (msg->size < PCMK__BZ2_THRESHOLD) { msg = pcmk__realloc(msg, msg->header.size); - memcpy(msg->data, data, msg->size); + memcpy(msg->data, data->str, msg->size); } else { char *compressed = NULL; unsigned int new_size = 0; - if (pcmk__compress(data, (unsigned int) msg->size, 0, &compressed, + if (pcmk__compress(data->str, (unsigned int) msg->size, 0, &compressed, &new_size) == pcmk_rc_ok) { msg->header.size = sizeof(pcmk__cpg_msg_t) + new_size; @@ -994,7 +990,7 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, } else { msg = pcmk__realloc(msg, msg->header.size); - memcpy(msg->data, data, msg->size); + memcpy(msg->data, data->str, msg->size); } free(compressed); @@ -1007,11 +1003,12 @@ send_cpg_text(const char *data, const pcmk__node_status_t *node, if (msg->compressed_size > 0) { pcmk__trace("Queueing CPG message %" PRIu32 " to %s " "(%zu bytes, %" PRIu32 " bytes compressed payload): %.200s", - msg->id, target, iov->iov_len, msg->compressed_size, data); + msg->id, target, iov->iov_len, msg->compressed_size, + data->str); } else { pcmk__trace("Queueing CPG message %" PRIu32 " to %s " "(%zu bytes, %" PRIu32 " bytes payload): %.200s", - msg->id, target, iov->iov_len, msg->size, data); + msg->id, target, iov->iov_len, msg->size, data->str); } free(target); @@ -1036,6 +1033,6 @@ pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, pcmk__xml_string(msg, 0, data, 0); - send_cpg_text(data->str, node, dest); + send_cpg_text(data, node, dest); g_string_free(data, TRUE); } From 446df1bfab7788f72dad4ee14092cc9107a565a1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:35:03 -0700 Subject: [PATCH 187/202] Refactor: libcrmcluster: Drop two memset() calls The struct was created using calloc() (via pcmk__assert_alloc()), so we can assume these arrays are already zeroed. I've kept the line that sets msg->sender.id = 0, just so that its value is explicitly documented. Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 0f89ba5658d..282c7b68e25 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -945,7 +945,6 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, if (node->name != NULL) { target = pcmk__str_copy(node->name); msg->host.size = strlen(node->name); - memset(msg->host.uname, 0, MAX_NAME); memcpy(msg->host.uname, node->name, msg->host.size); } else { @@ -961,7 +960,6 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, msg->sender.type = pcmk__parse_server(crm_system_name); msg->sender.pid = local_pid; msg->sender.size = local_name_len; - memset(msg->sender.uname, 0, MAX_NAME); if ((local_name != NULL) && (msg->sender.size != 0)) { memcpy(msg->sender.uname, local_name, msg->sender.size); From 1b79374da95eec697547b510e1ac2e0fb2675ade Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:40:02 -0700 Subject: [PATCH 188/202] Refactor: libcrmcluster: Consolidate some init in send_cpg_text() Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 282c7b68e25..5c8e28e546f 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -913,32 +913,29 @@ static void send_cpg_text(const GString *data, const pcmk__node_status_t *node, enum pcmk_ipc_server dest) { - static int msg_id = 0; - static int local_pid = 0; - static int local_name_len = 0; static const char *local_name = NULL; + static int local_name_len = 0; + static int local_pid = 0; + static int msg_id = 0; char *target = NULL; struct iovec *iov; pcmk__cpg_msg_t *msg = NULL; + // @TODO Refactor to take a cluster object and use its node name? if (local_name == NULL) { local_name = pcmk__cluster_local_node_name(); - } - if ((local_name_len == 0) && (local_name != NULL)) { - local_name_len = strlen(local_name); - } - if (local_pid == 0) { + if (local_name != NULL) { + local_name_len = strlen(local_name); + } + local_pid = getpid(); } msg = pcmk__assert_alloc(1, sizeof(pcmk__cpg_msg_t)); - - msg_id++; - msg->id = msg_id; + msg->id = ++msg_id; msg->header.error = CS_OK; - msg->host.type = dest; if (node != NULL) { From caed892c926384fa4a0da3e5e28d5dac3440a2c4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:47:23 -0700 Subject: [PATCH 189/202] Refactor: libcrmcluster: Use size_t for local_name_len Also, it should be functionally impossible for local_name_len to overflow a uint32_t, but add an assertion due to type differences. The memcpy() call is safe if local_name_len is 0; it will do nothing. Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 5c8e28e546f..129c0bd54a2 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -914,7 +914,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, enum pcmk_ipc_server dest) { static const char *local_name = NULL; - static int local_name_len = 0; + static size_t local_name_len = 0; static int local_pid = 0; static int msg_id = 0; @@ -928,6 +928,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, if (local_name != NULL) { local_name_len = strlen(local_name); + pcmk__assert(local_name_len <= UINT32_MAX); } local_pid = getpid(); @@ -956,11 +957,8 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, msg->sender.id = 0; msg->sender.type = pcmk__parse_server(crm_system_name); msg->sender.pid = local_pid; - msg->sender.size = local_name_len; - - if ((local_name != NULL) && (msg->sender.size != 0)) { - memcpy(msg->sender.uname, local_name, msg->sender.size); - } + msg->sender.size = (uint32_t) local_name_len; + memcpy(msg->sender.uname, local_name, local_name_len); msg->size = data->len + 1; msg->header.size = sizeof(pcmk__cpg_msg_t) + msg->size; From 3cf89290b9293c9af8942b2e37d24d235d6dde7d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:52:20 -0700 Subject: [PATCH 190/202] Refactor: libcrmcluster: Use pid_t for local_pid Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 129c0bd54a2..57a9172dd25 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -16,7 +16,7 @@ #include #include // uint32_t #include -#include // size_t +#include // pid_t, size_t #include #include @@ -52,7 +52,7 @@ static int cs_message_timer = 0; struct pcmk__cpg_host_s { uint32_t id; - uint32_t pid; + uint32_t pid; // For logging only gboolean local; // Unused but needed for compatibility enum pcmk_ipc_server type; // For logging only uint32_t size; @@ -915,7 +915,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, { static const char *local_name = NULL; static size_t local_name_len = 0; - static int local_pid = 0; + static pid_t local_pid = 0; static int msg_id = 0; char *target = NULL; @@ -932,6 +932,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, } local_pid = getpid(); + CRM_LOG_ASSERT((local_pid > 0) && (local_pid <= UINT32_MAX)); } msg = pcmk__assert_alloc(1, sizeof(pcmk__cpg_msg_t)); @@ -956,7 +957,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, msg->sender.id = 0; msg->sender.type = pcmk__parse_server(crm_system_name); - msg->sender.pid = local_pid; + msg->sender.pid = (uint32_t) local_pid; msg->sender.size = (uint32_t) local_name_len; memcpy(msg->sender.uname, local_name, local_name_len); From a6fc7f3cadf49e93ea2d455b31af06442a443744 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 13:10:52 -0700 Subject: [PATCH 191/202] Refactor: libcrmcommon: Drop max argument from pcmk__compress() The only caller outside of a unit test passes 0. Signed-off-by: Reid Wahl --- include/crm/common/strings_internal.h | 4 ++-- lib/cluster/cpg.c | 2 +- lib/common/strings.c | 13 +++++-------- lib/common/tests/strings/pcmk__compress_test.c | 17 ++++------------- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/include/crm/common/strings_internal.h b/include/crm/common/strings_internal.h index 33586adf214..cbb65c4a087 100644 --- a/include/crm/common/strings_internal.h +++ b/include/crm/common/strings_internal.h @@ -47,8 +47,8 @@ int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result); void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator); -int pcmk__compress(const char *data, unsigned int length, unsigned int max, - char **result, unsigned int *result_len); +int pcmk__compress(const char *data, unsigned int length, char **result, + unsigned int *result_len); int pcmk__scan_ll(const char *text, long long *result, long long default_value); int pcmk__scan_min_int(const char *text, int *result, int minimum); diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 57a9172dd25..887c5a95ecc 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -972,7 +972,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, char *compressed = NULL; unsigned int new_size = 0; - if (pcmk__compress(data->str, (unsigned int) msg->size, 0, &compressed, + if (pcmk__compress(data->str, (unsigned int) msg->size, &compressed, &new_size) == pcmk_rc_ok) { msg->header.size = sizeof(pcmk__cpg_msg_t) + new_size; diff --git a/lib/common/strings.c b/lib/common/strings.c index a920a899e3c..348cb3331be 100644 --- a/lib/common/strings.c +++ b/lib/common/strings.c @@ -585,15 +585,14 @@ pcmk__add_separated_word(GString **list, size_t init_size, const char *word, * * \param[in] data Data to compress * \param[in] length Number of characters of data to compress - * \param[in] max Maximum size of compressed data (or 0 to estimate) * \param[out] result Where to store newly allocated compressed result * \param[out] result_len Where to store actual compressed length of result * * \return Standard Pacemaker return code */ int -pcmk__compress(const char *data, unsigned int length, unsigned int max, - char **result, unsigned int *result_len) +pcmk__compress(const char *data, unsigned int length, char **result, + unsigned int *result_len) { int rc; char *compressed = NULL; @@ -603,17 +602,15 @@ pcmk__compress(const char *data, unsigned int length, unsigned int max, struct timespec before_t; #endif - if (max == 0) { - max = (length * 1.01) + 601; // Size guaranteed to hold result - } + // Size guaranteed to hold result. (@TODO Why 601? See commit 4e20d332.) + *result_len = (length * 1.01) + 601; #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_MONOTONIC, &before_t); #endif - compressed = pcmk__assert_alloc((size_t) max, sizeof(char)); + compressed = pcmk__assert_alloc((size_t) *result_len, sizeof(char)); - *result_len = max; rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, PCMK__BZ2_BLOCKS, 0, PCMK__BZ2_WORK); rc = pcmk__bzlib2rc(rc); diff --git a/lib/common/tests/strings/pcmk__compress_test.c b/lib/common/tests/strings/pcmk__compress_test.c index 85994b7a498..40906a6b37e 100644 --- a/lib/common/tests/strings/pcmk__compress_test.c +++ b/lib/common/tests/strings/pcmk__compress_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2025 the Pacemaker project contributors + * Copyright 2022-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -25,19 +25,11 @@ simple_compress(void **state) char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; - assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len), pcmk_rc_ok); + assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, &result, &len), + pcmk_rc_ok); assert_memory_equal(result, SIMPLE_COMPRESSED, 13); } -static void -max_too_small(void **state) -{ - char *result = pcmk__assert_alloc(1024, sizeof(char)); - unsigned int len; - - assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), EFBIG); -} - static void calloc_fails(void **state) { char *result = pcmk__assert_alloc(1024, sizeof(char)); @@ -50,7 +42,7 @@ calloc_fails(void **state) { expect_uint_value(__wrap_calloc, nmemb, (size_t) ((40 * 1.01) + 601)); expect_uint_value(__wrap_calloc, size, sizeof(char)); - pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len); + pcmk__compress(SIMPLE_DATA, 40, &result, &len); pcmk__mock_calloc = false; // Use the real calloc() } ); @@ -58,5 +50,4 @@ calloc_fails(void **state) { PCMK__UNIT_TEST(NULL, NULL, cmocka_unit_test(simple_compress), - cmocka_unit_test(max_too_small), cmocka_unit_test(calloc_fails)) From 773e3c343afdab27d447feb749c85fb4303d5184 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 13:52:41 -0700 Subject: [PATCH 192/202] Doc: libcrmcluster: Document a type conversion mess Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 887c5a95ecc..40cec6bd568 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -964,6 +964,11 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, msg->size = data->len + 1; msg->header.size = sizeof(pcmk__cpg_msg_t) + msg->size; + /* @FIXME This is a mess of conversions among size_t, unsigned int, and + * uint32_t. It might be worth sending multipart messages, as we do in our + * IPC library and as Netlink does. This is also the only caller of + * pcmk__compress(), so we could get rid of it if this no longer needed it. + */ if (msg->size < PCMK__BZ2_THRESHOLD) { msg = pcmk__realloc(msg, msg->header.size); memcpy(msg->data, data->str, msg->size); From fd9db3bbe20a0142a447113a3685c9bc358d5f34 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 13:57:17 -0700 Subject: [PATCH 193/202] Refactor: libcrmcluster: Reduce log call duplication Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index 40cec6bd568..e724f379438 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -80,7 +80,7 @@ typedef struct pcmk__cpg_msg_s pcmk__cpg_msg_t; static void crm_cs_flush(gpointer data); -#define msg_data_len(msg) (msg->is_compressed?msg->compressed_size:msg->size) +#define msg_data_len(msg) (msg->is_compressed? msg->compressed_size : msg->size) #define cs_repeat(rc, counter, max, code) do { \ rc = code; \ @@ -999,21 +999,15 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, iov->iov_base = msg; iov->iov_len = msg->header.size; - if (msg->compressed_size > 0) { - pcmk__trace("Queueing CPG message %" PRIu32 " to %s " - "(%zu bytes, %" PRIu32 " bytes compressed payload): %.200s", - msg->id, target, iov->iov_len, msg->compressed_size, - data->str); - } else { - pcmk__trace("Queueing CPG message %" PRIu32 " to %s " - "(%zu bytes, %" PRIu32 " bytes payload): %.200s", - msg->id, target, iov->iov_len, msg->size, data->str); - } - - free(target); + pcmk__trace("Queueing CPG message %" PRIu32 " to %s (%zu bytes, " + "%" PRIu32 " bytes %spayload): %.200s", + msg->id, target, iov->iov_len, msg_data_len(msg), + (msg->is_compressed? "compressed " : ""), data->str); cs_message_queue = g_list_append(cs_message_queue, iov); crm_cs_flush(&pcmk_cpg_handle); + + free(target); } /*! From 415516c934e4a3c292665114cee8ab7b04bc4054 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 14:01:08 -0700 Subject: [PATCH 194/202] Refactor: libcrmcluster: Drop send_cpg_text() Pull it into pcmk__cpg_send_xml(). Signed-off-by: Reid Wahl --- lib/cluster/cpg.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index e724f379438..0cb5d594db8 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -903,15 +903,15 @@ pcmk__cpg_disconnect(pcmk_cluster_t *cluster) /*! * \internal - * \brief Send string data via Corosync CPG + * \brief Send an XML message via Corosync CPG * - * \param[in] data Data to send - * \param[in] node Cluster node to send message to - * \param[in] dest Type of message to send + * \param[in] msg XML message to send + * \param[in] node Cluster node to send message to + * \param[in] dest Type of message to send */ -static void -send_cpg_text(const GString *data, const pcmk__node_status_t *node, - enum pcmk_ipc_server dest) +void +pcmk__cpg_send_xml(const xmlNode *xml, const pcmk__node_status_t *node, + enum pcmk_ipc_server dest) { static const char *local_name = NULL; static size_t local_name_len = 0; @@ -921,6 +921,7 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, char *target = NULL; struct iovec *iov; pcmk__cpg_msg_t *msg = NULL; + GString *data = NULL; // @TODO Refactor to take a cluster object and use its node name? if (local_name == NULL) { @@ -961,6 +962,9 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, msg->sender.size = (uint32_t) local_name_len; memcpy(msg->sender.uname, local_name, local_name_len); + data = g_string_sized_new(1024); + pcmk__xml_string(xml, 0, data, 0); + msg->size = data->len + 1; msg->header.size = sizeof(pcmk__cpg_msg_t) + msg->size; @@ -1008,24 +1012,5 @@ send_cpg_text(const GString *data, const pcmk__node_status_t *node, crm_cs_flush(&pcmk_cpg_handle); free(target); -} - -/*! - * \internal - * \brief Send an XML message via Corosync CPG - * - * \param[in] msg XML message to send - * \param[in] node Cluster node to send message to - * \param[in] dest Type of message to send - */ -void -pcmk__cpg_send_xml(const xmlNode *msg, const pcmk__node_status_t *node, - enum pcmk_ipc_server dest) -{ - GString *data = g_string_sized_new(1024); - - pcmk__xml_string(msg, 0, data, 0); - - send_cpg_text(data, node, dest); g_string_free(data, TRUE); } From 88d07faa27b7122a1b89e7f568a8ae8ff1d95afd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 15:23:00 -0700 Subject: [PATCH 195/202] Refactor: attrd: Return void from attrd_send_message() There are nine calls and none of them check the return value. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_messages.c | 4 ++-- daemons/attrd/pacemaker-attrd.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/daemons/attrd/attrd_messages.c b/daemons/attrd/attrd_messages.c index 1a680bc6e69..8b0915abb10 100644 --- a/daemons/attrd/attrd_messages.c +++ b/daemons/attrd/attrd_messages.c @@ -348,7 +348,7 @@ attrd_send_protocol(const pcmk__node_status_t *peer) pcmk__xml_free(attrd_op); } -gboolean +void attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, bool confirm) { const char *op = pcmk__xe_get(data, PCMK_XA_TASK); @@ -365,5 +365,5 @@ attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, bool confirm) } attrd_xml_add_writer(data); - return pcmk__cluster_send_message(node, pcmk_ipc_attrd, data); + pcmk__cluster_send_message(node, pcmk_ipc_attrd, data); } diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h index 8ca0c194f2a..229706d796f 100644 --- a/daemons/attrd/pacemaker-attrd.h +++ b/daemons/attrd/pacemaker-attrd.h @@ -8,7 +8,7 @@ */ #ifndef PACEMAKER_ATTRD__H -# define PACEMAKER_ATTRD__H +#define PACEMAKER_ATTRD__H #include #include @@ -210,8 +210,8 @@ void attrd_client_clear_failure(pcmk__request_t *request); void attrd_client_update(pcmk__request_t *request); void attrd_client_refresh(pcmk__request_t *request); xmlNode *attrd_client_query(pcmk__request_t *request); -gboolean attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, - bool confirm); +void attrd_send_message(const pcmk__node_status_t *node, xmlNode *data, + bool confirm); xmlNode *attrd_add_value_xml(xmlNode *parent, const attribute_t *a, const attribute_value_t *v, bool force_write); @@ -267,4 +267,4 @@ void attrd_set_node_xml_id(const char *node_name, const char *node_xml_id); void attrd_forget_node_xml_id(const char *node_name); void attrd_cleanup_xml_ids(void); -#endif /* PACEMAKER_ATTRD__H */ +#endif // PACEMAKER_ATTRD__H From 1ba500141716674ee8aff9c879bb5f5ede92f6c5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 15:29:26 -0700 Subject: [PATCH 196/202] Refactor: libcrmcluster: Return void from pcmk__cluster_send_message() Of the 36 calls, only 6 checked the return value. We could aim to check the return value in more places, but currently it's impossible for it to return false unless Pacemaker was built without Corosync support (that is, without support for any cluster layer), which in turn is currently not allowed. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 11 +++++------ daemons/controld/controld_control.c | 4 +--- daemons/controld/controld_messages.c | 14 +++++--------- daemons/controld/controld_te_actions.c | 22 ++++++---------------- include/crm/cluster/internal.h | 2 +- lib/cluster/cluster.c | 9 +++------ 6 files changed, 21 insertions(+), 41 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 810c51c942d..afb2cda4c79 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -272,9 +272,8 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); - if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { - pcmk__warn("Could not send CIB upgrade result to %s", host); - } + + pcmk__cluster_send_message(origin, pcmk_ipc_based, up); pcmk__xml_free(up); } @@ -355,9 +354,9 @@ sync_our_cib(const xmlNode *request, bool all) if (!all) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); } - if (!pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request)) { - rc = ENOTCONN; - } + + pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request); + pcmk__xml_free(replace_request); free(digest); return rc; diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 6054eec448d..6dbf00fc161 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -117,9 +117,7 @@ do_shutdown_req(long long action, enum crmd_fsa_cause cause, msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL, CRM_SYSTEM_CRMD, CRM_OP_SHUTDOWN_REQ, NULL); - if (!pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg)) { - register_fsa_error(I_ERROR, msg_data); - } + pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg); pcmk__xml_free(msg); } diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c index 490b09b6b01..271434d93ae 100644 --- a/daemons/controld/controld_messages.c +++ b/daemons/controld/controld_messages.c @@ -1130,18 +1130,14 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause) name = pcmk__xe_get(stored_msg, PCMK_XA_UNAME); if(cause == C_IPC_MESSAGE) { + pcmk__notice("Instructing peers to remove references to node %s/%d", + name, id); + msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL, CRM_SYSTEM_CRMD, CRM_OP_RM_NODE_CACHE, NULL); - if (!pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg)) { - pcmk__err("Could not instruct peers to remove references to " - "node %s/%u", - name, id); - } else { - pcmk__notice("Instructing peers to remove references to node " - "%s/%u", - name, id); - } + + pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg); pcmk__xml_free(msg); } else { diff --git a/daemons/controld/controld_te_actions.c b/daemons/controld/controld_te_actions.c index 7ef00df0aa2..b6ba8f0cc1d 100644 --- a/daemons/controld/controld_te_actions.c +++ b/daemons/controld/controld_te_actions.c @@ -110,7 +110,6 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) const char *on_node = NULL; const char *router_node = NULL; - gboolean rc = TRUE; gboolean no_wait = FALSE; const pcmk__node_status_t *node = NULL; @@ -176,15 +175,9 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) node = pcmk__get_node(0, router_node, NULL, pcmk__node_search_cluster_member); - rc = pcmk__cluster_send_message(node, pcmk_ipc_controld, cmd); - free(counter); - pcmk__xml_free(cmd); - - if (rc == FALSE) { - pcmk__err("Action %d failed: send", action->id); - return ECOMM; + pcmk__cluster_send_message(node, pcmk_ipc_controld, cmd); - } else if (no_wait) { + if (no_wait) { te_action_confirmed(action, graph); } else { @@ -198,6 +191,8 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) te_start_action_timer(graph, action); } + free(counter); + pcmk__xml_free(cmd); return pcmk_rc_ok; } @@ -359,7 +354,6 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) xmlNode *cmd = NULL; xmlNode *rsc_op = NULL; - gboolean rc = TRUE; gboolean no_wait = FALSE; gboolean is_local = FALSE; @@ -430,7 +424,7 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) QB_XS " transition %s action %d", router_node, task_uuid, on_node, (no_wait? " without waiting" : ""), counter, action->id); - rc = pcmk__cluster_send_message(node, pcmk_ipc_execd, cmd); + pcmk__cluster_send_message(node, pcmk_ipc_execd, cmd); } free(counter); @@ -438,11 +432,7 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action) pcmk__set_graph_action_flags(action, pcmk__graph_action_executed); - if (rc == FALSE) { - pcmk__err("Action %d failed: send", action->id); - return ECOMM; - - } else if (no_wait) { + if (no_wait) { /* Just mark confirmed. Don't bump the job count only to immediately * decrement it. */ diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h index 11f3aa0536d..f79c64abbdf 100644 --- a/include/crm/cluster/internal.h +++ b/include/crm/cluster/internal.h @@ -243,7 +243,7 @@ void pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, gboolean), void (*destroy) (gpointer)); -bool pcmk__cluster_send_message(const pcmk__node_status_t *node, +void pcmk__cluster_send_message(const pcmk__node_status_t *node, enum pcmk_ipc_server service, const xmlNode *data); diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index 3912480ccf1..b91ee0e6537 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -204,23 +204,20 @@ pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(gpointer)) * \param[in] node Cluster node to send message to * \param[in] service Message type to use in message host info * \param[in] data XML message to send - * - * \return \c true on success, or \c false otherwise */ -bool +void pcmk__cluster_send_message(const pcmk__node_status_t *node, enum pcmk_ipc_server service, const xmlNode *data) { - // @TODO Return standard Pacemaker return code switch (pcmk_get_cluster_layer()) { #if SUPPORT_COROSYNC case pcmk_cluster_layer_corosync: pcmk__cpg_send_xml(data, node, service); - return true; + return; #endif // SUPPORT_COROSYNC default: - return false; + return; } } From c7c2eac851604fb532a0fb704438167dfd7ebad3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 15:32:50 -0700 Subject: [PATCH 197/202] Refactor: based: Reuse req in based_process_upgrade() We modify and reuse request XML elsewhere. We might as well do it here, as it makes the function much shorter. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 36 +++++++--------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index afb2cda4c79..2f8ce08c3ce 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -201,9 +201,6 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) xmlNode *scratch = NULL; const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); - const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); - const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); const char *original_schema = NULL; const char *new_schema = NULL; pcmk__node_status_t *origin = NULL; @@ -230,22 +227,12 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - rc = pcmk_rc_ok; pcmk__notice("Upgrade request from %s verified", host); - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); - - pcmk__xml_free(up); + pcmk__xe_set(req, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(req, PCMK__XA_CIB_SCHEMA_MAX, new_schema); + pcmk__cluster_send_message(NULL, pcmk_ipc_based, req); goto done; } @@ -262,19 +249,10 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) ((origin != NULL)? origin->name : "lost")); if (origin != NULL) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); - - pcmk__cluster_send_message(origin, pcmk_ipc_based, up); - pcmk__xml_free(up); + pcmk__xe_set(req, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(req, PCMK__XA_CIB_ISREPLYTO, host); + pcmk__xe_set_int(req, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + pcmk__cluster_send_message(origin, pcmk_ipc_based, req); } done: From 86e0b31f9a6bef79f44a67f693f7721fe79ffe9a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 20 Jan 2026 13:56:01 -0700 Subject: [PATCH 198/202] Log: based: Drop some basically redundant log messages The messages at the beginning of based_handle_request() give us basically the same info as the messages we're dropping from log_local_options() and parse_peer_options(). Those seem more useful anyway, because they provide context for any other messages we log in based_handle_request(). Also add a brief message for forwarding a request, since we return immediately after forwarding. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a3dd3be3902..3e481097ce4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -314,21 +314,6 @@ process_ping_reply(const xmlNode *reply) sync_our_cib(reply, false); } -static void -log_local_options(const pcmk__client_t *client, const char *host, - const char *op) -{ - if (based_stand_alone()) { - pcmk__trace("Processing %s op from client %s (stand-alone)", op, - pcmk__client_name(client)); - - } else { - pcmk__trace("Processing %saddressed %s op from client %s", - ((host != NULL)? "locally " : "un"), op, - pcmk__client_name(client)); - } -} - static void parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, bool *local_notify, bool *needs_reply, bool *process) @@ -435,16 +420,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { *needs_reply = true; - return; } - - pcmk__trace("Processing %s request broadcast by %s call %s on %s " - "(local clients will%s be notified)", request->op, - pcmk__s(pcmk__xe_get(request->xml, PCMK__XA_CIB_CLIENTNAME), - "client"), - pcmk__s(pcmk__xe_get(request->xml, PCMK__XA_CIB_CALLID), - "without ID"), - originator, (*local_notify? "" : "not")); } /*! @@ -720,6 +696,8 @@ based_handle_request(pcmk__request_t *request) || !pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches))) { + pcmk__trace("Forwarding %s op from %s/%s", request->op, client_name, + call_id); forward_request(request); pcmk__reset_request(request); return pcmk_rc_ok; @@ -728,8 +706,6 @@ based_handle_request(pcmk__request_t *request) // Process locally and notify local client; no peer to reply to local_notify = true; - log_local_options(request->ipc_client, host, request->op); - } else { parse_peer_options(operation, request, &local_notify, &needs_reply, &process); From d018e94e0bd9fe6982db636dfb8bcc8b5f5b98ee Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 20 Jan 2026 14:03:31 -0700 Subject: [PATCH 199/202] Refactor: based: Create reply only if we will send it Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3e481097ce4..d569c9497be 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -717,6 +717,11 @@ based_handle_request(pcmk__request_t *request) pcmk__trace("Client is not interested in the reply"); } + if (operation->modifies_cib) { + // A modifying request gets processed by each node, so no need to reply + needs_reply = false; + } + if (!process) { goto done; } @@ -726,10 +731,7 @@ based_handle_request(pcmk__request_t *request) pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - if (needs_reply) { - reply = create_cib_reply(request->xml, rc, based_cib); - } - + output = based_cib; goto done; } @@ -749,12 +751,9 @@ based_handle_request(pcmk__request_t *request) log_op_result(request->xml, operation, rc, difftime(time(NULL), start_time)); +done: if (needs_reply) { reply = create_cib_reply(request->xml, rc, output); - } - -done: - if (!operation->modifies_cib && needs_reply) { send_peer_reply(reply, originator); } From f448d09035128d93b1182a0085af70477421594f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 20 Jan 2026 15:12:10 -0700 Subject: [PATCH 200/202] Refactor: libcib: Reorder CIB request constants alphabetically Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 3c411b03448..46347972c8f 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -21,22 +21,22 @@ extern "C" { #endif // Request types for CIB manager IPC/CPG -#define PCMK__CIB_REQUEST_SECONDARY "cib_slave" -#define PCMK__CIB_REQUEST_PRIMARY "cib_master" -#define PCMK__CIB_REQUEST_SYNC "cib_sync" -#define PCMK__CIB_REQUEST_BUMP "cib_bump" -#define PCMK__CIB_REQUEST_QUERY "cib_query" -#define PCMK__CIB_REQUEST_CREATE "cib_create" -#define PCMK__CIB_REQUEST_MODIFY "cib_modify" -#define PCMK__CIB_REQUEST_DELETE "cib_delete" -#define PCMK__CIB_REQUEST_ERASE "cib_erase" -#define PCMK__CIB_REQUEST_REPLACE "cib_replace" -#define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff" -#define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade" -#define PCMK__CIB_REQUEST_NOOP "noop" -#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req" +#define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff" +#define PCMK__CIB_REQUEST_BUMP "cib_bump" #define PCMK__CIB_REQUEST_COMMIT_TRANSACT "cib_commit_transact" -#define PCMK__CIB_REQUEST_SCHEMAS "cib_schemas" +#define PCMK__CIB_REQUEST_CREATE "cib_create" +#define PCMK__CIB_REQUEST_DELETE "cib_delete" +#define PCMK__CIB_REQUEST_ERASE "cib_erase" +#define PCMK__CIB_REQUEST_MODIFY "cib_modify" +#define PCMK__CIB_REQUEST_NOOP "noop" +#define PCMK__CIB_REQUEST_PRIMARY "cib_master" +#define PCMK__CIB_REQUEST_QUERY "cib_query" +#define PCMK__CIB_REQUEST_REPLACE "cib_replace" +#define PCMK__CIB_REQUEST_SCHEMAS "cib_schemas" +#define PCMK__CIB_REQUEST_SECONDARY "cib_slave" +#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req" +#define PCMK__CIB_REQUEST_SYNC "cib_sync" +#define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade" /*! * \internal From 7e6859b1d75f38d13f60cf95fde966cbc9928754 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 03:06:49 -0800 Subject: [PATCH 201/202] wip Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 18 +++++++++++++++--- daemons/based/based_messages.c | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index d569c9497be..2c408271347 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -318,6 +318,14 @@ static void parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, bool *local_notify, bool *needs_reply, bool *process) { + /* Don't send replies for modifying ops because they already get forwarded + * to all nodes. Also don't send a reply if the client specifically told us + * not to. + */ + const bool can_reply = !operation->modifies_cib + && !pcmk__is_set(request->call_options, + cib_discard_reply); + const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); const char *delegated = pcmk__xe_get(request->xml, PCMK__XA_CIB_DELEGATED_FROM); @@ -362,6 +370,11 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO delegated = reply_to; + /* @FIXME can_reply is always false here because cib__op_replace has + * modifies_cib=true. Should we be sending a reply here? + */ + *needs_reply = can_reply; + } else if (pcmk__str_eq(request->op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process @@ -407,7 +420,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", request->op, originator); - *needs_reply = true; + *needs_reply = can_reply; return; } @@ -419,7 +432,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, } if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { - *needs_reply = true; + *needs_reply = can_reply; } } @@ -712,7 +725,6 @@ based_handle_request(pcmk__request_t *request) } if (pcmk__is_set(request->call_options, cib_discard_reply)) { - needs_reply = false; local_notify = false; pcmk__trace("Client is not interested in the reply"); } diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 2f8ce08c3ce..10700276a4e 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -263,6 +263,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) static xmlNode * cib_msg_copy(const xmlNode *msg) { + // @FIXME Copying CIB_CALLOPT seems problematic if it has cib_discard_reply static const char *field_list[] = { PCMK__XA_T, PCMK__XA_CIB_CLIENTID, From 0ff7e6f9939888cfd491e7647d4721febd7483bd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 01:52:16 -0800 Subject: [PATCH 202/202] Refactor: based: Ignore cib_discard_reply Send a reply even if the cib_discard_reply flag is set. The client will still discard the reply. (TODO: The client discards the reply for sync requests but still needs to discard it for async.) Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 2c408271347..fb416f1bf29 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -322,9 +322,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, * to all nodes. Also don't send a reply if the client specifically told us * not to. */ - const bool can_reply = !operation->modifies_cib - && !pcmk__is_set(request->call_options, - cib_discard_reply); + const bool can_reply = !operation->modifies_cib; const char *host = pcmk__xe_get(request->xml, PCMK__XA_CIB_HOST); const char *delegated = pcmk__xe_get(request->xml, @@ -724,16 +722,6 @@ based_handle_request(pcmk__request_t *request) &process); } - if (pcmk__is_set(request->call_options, cib_discard_reply)) { - local_notify = false; - pcmk__trace("Client is not interested in the reply"); - } - - if (operation->modifies_cib) { - // A modifying request gets processed by each node, so no need to reply - needs_reply = false; - } - if (!process) { goto done; }