From 8867d486f850703035739570027c3a356948ee19 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 22 Jul 2026 17:11:10 -0700 Subject: [PATCH 01/98] Refactor: various: Drop cluster/internal.h includes from .c files Our .c files include crm_internal.h, which includes crm/cluster/internal.h. The explicit include makes sense for header files, since we don't include crm_internal.h there, but not for .c files. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_alerts.c | 1 - daemons/attrd/attrd_cib.c | 1 - daemons/attrd/attrd_corosync.c | 1 - daemons/attrd/attrd_ipc.c | 1 - daemons/attrd/attrd_messages.c | 1 - daemons/attrd/pacemaker-attrd.c | 1 - daemons/based/based_callbacks.c | 1 - daemons/based/based_corosync.c | 1 - daemons/based/based_messages.c | 1 - daemons/based/pacemaker-based.c | 1 - daemons/controld/controld_control.c | 1 - daemons/controld/controld_corosync.c | 1 - daemons/controld/controld_election.c | 1 - daemons/controld/controld_membership.c | 1 - daemons/controld/controld_messages.c | 1 - daemons/fenced/cts-fence-helper.c | 1 - daemons/fenced/fenced_cib.c | 2 -- daemons/fenced/fenced_commands.c | 1 - daemons/fenced/fenced_history.c | 1 - daemons/fenced/fenced_remote.c | 1 - lib/cluster/cluster.c | 1 - lib/cluster/corosync.c | 1 - lib/cluster/cpg.c | 1 - lib/cluster/election.c | 1 - lib/cluster/membership.c | 1 - tools/stonith_admin.c | 1 - 26 files changed, 27 deletions(-) diff --git a/daemons/attrd/attrd_alerts.c b/daemons/attrd/attrd_alerts.c index 9bcbaf4f8f8..a07a0a6e50a 100644 --- a/daemons/attrd/attrd_alerts.c +++ b/daemons/attrd/attrd_alerts.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c index e33d525fd41..72877d83b86 100644 --- a/daemons/attrd/attrd_cib.c +++ b/daemons/attrd/attrd_cib.c @@ -18,7 +18,6 @@ #include #include #include -#include // pcmk__get_node() #include "pacemaker-attrd.h" diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c index 20519e0911d..b6b9e2e8f23 100644 --- a/daemons/attrd/attrd_corosync.c +++ b/daemons/attrd/attrd_corosync.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/daemons/attrd/attrd_ipc.c b/daemons/attrd/attrd_ipc.c index 3b0113ccac1..b4ae24b4811 100644 --- a/daemons/attrd/attrd_ipc.c +++ b/daemons/attrd/attrd_ipc.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/daemons/attrd/attrd_messages.c b/daemons/attrd/attrd_messages.c index ed90865fb18..fe9f9927964 100644 --- a/daemons/attrd/attrd_messages.c +++ b/daemons/attrd/attrd_messages.c @@ -14,7 +14,6 @@ #include -#include // pcmk__get_node() #include #include "pacemaker-attrd.h" diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c index 1682ba8ba56..b04d9f88e59 100644 --- a/daemons/attrd/pacemaker-attrd.c +++ b/daemons/attrd/pacemaker-attrd.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "pacemaker-attrd.h" diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 71679d43275..de1881ff3bd 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_send_message #include // pcmk__s, pcmk__str_eq #include // crm_ipc_*, pcmk_ipc_* #include // CRM_LOG_ASSERT, CRM_CHECK diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index cf8fc547d6c..688be5f3a63 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -20,7 +20,6 @@ #include // SUPPORT_COROSYNC #include // pcmk_cluster_* -#include // pcmk__cluster_*, etc. #include // pcmk__err, pcmk__xml_free, etc. #include // CRM_EX_DISCONNECT, pcmk_rc_ok diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index a77344d92d1..678840d7ae9 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -19,7 +19,6 @@ #include // QB_XS #include // PCMK__CIB_REQUEST_UPGRADE -#include // pcmk__cluster_send_message #include // pcmk__info, pcmk__xml_free, etc. #include // pcmk_ipc_server #include // CRM_CHECK diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 46206af7ffc..218929093b2 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__node_update, etc. #include // PCMK__EXITC_ERROR, pcmk__err, etc. #include // crm_ipc_* #include // crm_log_* diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 0f916edf63b..7a2337ae858 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c index e065fcc9ede..f5f9c5b0da1 100644 --- a/daemons/controld/controld_corosync.c +++ b/daemons/controld/controld_corosync.c @@ -18,7 +18,6 @@ #include #include -#include #include #include diff --git a/daemons/controld/controld_election.c b/daemons/controld/controld_election.c index 43d5f0f7e5f..f833da82898 100644 --- a/daemons/controld/controld_election.c +++ b/daemons/controld/controld_election.c @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/daemons/controld/controld_membership.c b/daemons/controld/controld_membership.c index fd33d0827e4..7f32d8df012 100644 --- a/daemons/controld/controld_membership.c +++ b/daemons/controld/controld_membership.c @@ -16,7 +16,6 @@ #include #include -#include #include diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c index 319bd468d45..523f35b40ff 100644 --- a/daemons/controld/controld_messages.c +++ b/daemons/controld/controld_messages.c @@ -18,7 +18,6 @@ #include #include -#include #include #include diff --git a/daemons/fenced/cts-fence-helper.c b/daemons/fenced/cts-fence-helper.c index 2de750c3e1a..f4386ad16b4 100644 --- a/daemons/fenced/cts-fence-helper.c +++ b/daemons/fenced/cts-fence-helper.c @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/daemons/fenced/fenced_cib.c b/daemons/fenced/fenced_cib.c index 803cad11e33..939518e5700 100644 --- a/daemons/fenced/fenced_cib.c +++ b/daemons/fenced/fenced_cib.c @@ -17,8 +17,6 @@ #include #include -#include - #include #include diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c index 9c8f0667b36..9be94a49231 100644 --- a/daemons/fenced/fenced_commands.c +++ b/daemons/fenced/fenced_commands.c @@ -28,7 +28,6 @@ #include #include -#include #include #include diff --git a/daemons/fenced/fenced_history.c b/daemons/fenced/fenced_history.c index 23864f7e2cf..75bac9d36f4 100644 --- a/daemons/fenced/fenced_history.c +++ b/daemons/fenced/fenced_history.c @@ -18,7 +18,6 @@ #include #include -#include #include #include diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c index 9d59d054e81..394dd4859e3 100644 --- a/daemons/fenced/fenced_remote.c +++ b/daemons/fenced/fenced_remote.c @@ -28,7 +28,6 @@ #include #include -#include #include #include diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index ec2d54a1ae2..692df516dc1 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -27,7 +27,6 @@ #include #include -#include #include "crmcluster_private.h" /*! diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 5b1c9136745..192b9889a98 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -28,7 +28,6 @@ #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 diff --git a/lib/cluster/cpg.c b/lib/cluster/cpg.c index c121a2f9d07..c0fed8a2a19 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -28,7 +28,6 @@ #include #include -#include #include #include #include diff --git a/lib/cluster/election.c b/lib/cluster/election.c index db44bfdfccd..40e0f8c5341 100644 --- a/lib/cluster/election.c +++ b/lib/cluster/election.c @@ -17,7 +17,6 @@ #include #include -#include #include #include "crmcluster_private.h" diff --git a/lib/cluster/membership.c b/lib/cluster/membership.c index 14b33ec9fbe..d48d36013a0 100644 --- a/lib/cluster/membership.c +++ b/lib/cluster/membership.c @@ -20,7 +20,6 @@ #include #include -#include #include #include "crmcluster_private.h" diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c index ef8face6f3d..4780ce663c6 100644 --- a/tools/stonith_admin.c +++ b/tools/stonith_admin.c @@ -26,7 +26,6 @@ #include #include -#include #include #include // stonith__register_messages() From 6b63bca76cd90c31f010f8cb248ec6f78ad0cd27 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 22 Jul 2026 17:18:49 -0700 Subject: [PATCH 02/98] Refactor: various: Don't include cluster/election_internal.h directly Include cluster/internal.h instead. Note that cluster/internal.h is included by crm_internal.h. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_alerts.c | 3 ++- daemons/attrd/attrd_elections.c | 1 - daemons/attrd/pacemaker-attrd.h | 2 +- daemons/controld/controld_control.c | 1 - daemons/controld/controld_election.c | 1 - daemons/controld/controld_fsa.c | 1 - daemons/controld/controld_fsa.h | 3 +-- include/crm/cluster/election_internal.h | 5 +++++ include/crm/cluster/internal.h | 5 +++++ lib/cluster/election.c | 1 - tests/test-headers.sh | 1 + 11 files changed, 15 insertions(+), 9 deletions(-) diff --git a/daemons/attrd/attrd_alerts.c b/daemons/attrd/attrd_alerts.c index a07a0a6e50a..e58629e5dab 100644 --- a/daemons/attrd/attrd_alerts.c +++ b/daemons/attrd/attrd_alerts.c @@ -8,11 +8,12 @@ */ #include + #include #include -#include #include #include + #include "pacemaker-attrd.h" static GList *attrd_alert_list = NULL; diff --git a/daemons/attrd/attrd_elections.c b/daemons/attrd/attrd_elections.c index c48c808fda7..11c2841a94e 100644 --- a/daemons/attrd/attrd_elections.c +++ b/daemons/attrd/attrd_elections.c @@ -12,7 +12,6 @@ #include #include -#include #include #include "pacemaker-attrd.h" diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h index 8d9b50ad408..b2e94052f39 100644 --- a/daemons/attrd/pacemaker-attrd.h +++ b/daemons/attrd/pacemaker-attrd.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 7a2337ae858..5ca16b37986 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -16,7 +16,6 @@ #include #include -#include #include diff --git a/daemons/controld/controld_election.c b/daemons/controld/controld_election.c index f833da82898..12f33e41b3a 100644 --- a/daemons/controld/controld_election.c +++ b/daemons/controld/controld_election.c @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/daemons/controld/controld_fsa.c b/daemons/controld/controld_fsa.c index b44ca99c55d..786446cc6cd 100644 --- a/daemons/controld/controld_fsa.c +++ b/daemons/controld/controld_fsa.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/daemons/controld/controld_fsa.h b/daemons/controld/controld_fsa.h index 04eeb8156cf..b1ef802f1cf 100644 --- a/daemons/controld/controld_fsa.h +++ b/daemons/controld/controld_fsa.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. * @@ -16,7 +16,6 @@ # include # include # include -# include # include /*! States the controller can be in */ diff --git a/include/crm/cluster/election_internal.h b/include/crm/cluster/election_internal.h index 4825f8e103e..93cecf943e3 100644 --- a/include/crm/cluster/election_internal.h +++ b/include/crm/cluster/election_internal.h @@ -7,6 +7,11 @@ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ +#ifndef PCMK__INCLUDED_CRM_CLUSTER_INTERNAL_H +#error "Include instead of " \ + "directly" +#endif + #ifndef PCMK__CRM_CLUSTER_ELECTION_INTERNAL__H #define PCMK__CRM_CLUSTER_ELECTION_INTERNAL__H diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h index aed702eb9a1..85512e27fae 100644 --- a/include/crm/cluster/internal.h +++ b/include/crm/cluster/internal.h @@ -10,6 +10,8 @@ #ifndef PCMK__CRM_CLUSTER_INTERNAL__H #define PCMK__CRM_CLUSTER_INTERNAL__H +#define PCMK__INCLUDED_CRM_CLUSTER_INTERNAL_H + #include #include // uint32_t, uint64_t @@ -18,11 +20,14 @@ #include // enum crm_ipc_server #include +#include #if SUPPORT_COROSYNC #include // cpg_name, cpg_handle_t #endif +#undef PCMK__INCLUDED_CRM_CLUSTER_INTERNAL_H + #ifdef __cplusplus extern "C" { #endif diff --git a/lib/cluster/election.c b/lib/cluster/election.c index 40e0f8c5341..27655d55982 100644 --- a/lib/cluster/election.c +++ b/lib/cluster/election.c @@ -17,7 +17,6 @@ #include #include -#include #include "crmcluster_private.h" #define STORM_INTERVAL 2 /* in seconds */ diff --git a/tests/test-headers.sh b/tests/test-headers.sh index 764431dfca9..d92bf90a8b7 100644 --- a/tests/test-headers.sh +++ b/tests/test-headers.sh @@ -34,6 +34,7 @@ do cat >"$TESTFILE" < #ifndef $PROTECT From a16fb66176b87cdf74a5d096058f78c9ebabec4e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 22 Jul 2026 17:23:55 -0700 Subject: [PATCH 03/98] Refactor: various: Drop common/internal.h includes from .c files Our .c files include crm_internal.h, which includes crm/common/internal.h. The explicit include makes sense for header files, since we don't include crm_internal.h there, but not for .c files. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 - daemons/based/based_corosync.c | 1 - daemons/based/based_io.c | 1 - daemons/based/based_ipc.c | 1 - daemons/based/based_messages.c | 1 - daemons/based/based_notify.c | 1 - daemons/based/based_operation.c | 1 - daemons/based/based_remote.c | 1 - daemons/based/based_transaction.c | 1 - daemons/based/pacemaker-based.c | 1 - daemons/controld/controld_remote_proxy.c | 1 - daemons/execd/execd_ipc.c | 1 - daemons/execd/execd_messages.c | 1 - daemons/execd/remoted_proxy.c | 1 - daemons/execd/remoted_tls.c | 1 - lib/cib/cib_remote.c | 1 - lib/cluster/corosync.c | 1 - lib/common/fuzzers/iso8601_fuzzer.c | 3 ++- lib/common/fuzzers/scores_fuzzer.c | 3 ++- lib/common/logging.c | 1 - lib/common/tls.c | 1 - lib/lrmd/lrmd_client.c | 1 - lib/pacemaker/pcmk_acl.c | 1 - tools/cibsecret.c | 1 - tools/crm_resource_runtime.c | 1 - 25 files changed, 4 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index de1881ff3bd..cef3d4bcdc3 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__s, pcmk__str_eq #include // crm_ipc_*, pcmk_ipc_* #include // CRM_LOG_ASSERT, CRM_CHECK #include // mainloop_* diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 688be5f3a63..134d44a4889 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -20,7 +20,6 @@ #include // SUPPORT_COROSYNC #include // pcmk_cluster_* -#include // pcmk__err, pcmk__xml_free, etc. #include // CRM_EX_DISCONNECT, pcmk_rc_ok #include "pacemaker-based.h" diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 5e6a12153ab..f4d5c5004ff 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -29,7 +29,6 @@ #include // cib_file_* #include // createEmptyCib -#include // pcmk__assert_asprintf, PCMK__XE_*, etc. #include // CRM_CHECK #include // mainloop_* #include // pcmk_legacy2rc, pcmk_rc_* diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 9e32dfdbd5f..42d8998c958 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -20,7 +20,6 @@ #include // qb_ipcs_* #include // cib_none, cib_sync_call -#include // pcmk__client_*, pcmk__trace, etc. #include // crm_ipc_client_response #include // CRM_CHECK(), CRM_LOG_ASSERT() #include // CRM_EX_PROTOCOL, pcmk_rc_* diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 678840d7ae9..8dcbf7f5d7f 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -19,7 +19,6 @@ #include // QB_XS #include // PCMK__CIB_REQUEST_UPGRADE -#include // pcmk__info, pcmk__xml_free, etc. #include // pcmk_ipc_server #include // CRM_CHECK #include // pcmk_err, pcmk_ok, pcmk_rc* diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index d9dcab67950..47ced2c53d4 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -20,7 +20,6 @@ #include // xmlNode #include // QB_XS -#include // pcmk__client_t, etc. #include // pcmk_free_ipc_event #include // CRM_LOG_ASSERT #include // pcmk_rc_* diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 04d1c5417e8..1608018250f 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -12,7 +12,6 @@ #include // NULL #include // cib__* -#include // pcmk__assert, PCMK__NELEM #include "pacemaker-based.h" diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 7695ea8947b..ad3ac88219f 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -27,7 +27,6 @@ #include // QB_XS #include // CRM_DAEMON_GROUP -#include // pcmk__client_t, etc. #include // CRM_CHECK #include // mainloop_* #include // pcmk_rc_* diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 61f84d4b331..22fa18d9ea1 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -17,7 +17,6 @@ #include // xmlNode #include // cib__* -#include // pcmk__client_t, pcmk__s, pcmk__xe_*, etc. #include // CRM_CHECK #include // pcmk_rc_* diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 218929093b2..6f114ccfc82 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__EXITC_ERROR, pcmk__err, etc. #include // crm_ipc_* #include // crm_log_* #include // mainloop_add_signal diff --git a/daemons/controld/controld_remote_proxy.c b/daemons/controld/controld_remote_proxy.c index 261e5ac68db..8da874e02b6 100644 --- a/daemons/controld/controld_remote_proxy.c +++ b/daemons/controld/controld_remote_proxy.c @@ -21,7 +21,6 @@ #include // xmlNode #include // cib_* -#include // pcmk__xe_*, pcmk__xml_*, etc. #include // crm_ipc_* #include // crm_time_* #include // CRM_CHECK, crm_log_xml_explicit diff --git a/daemons/execd/execd_ipc.c b/daemons/execd/execd_ipc.c index c8821f51c9e..8d2f489de68 100644 --- a/daemons/execd/execd_ipc.c +++ b/daemons/execd/execd_ipc.c @@ -18,7 +18,6 @@ #include // xmlNode #include // qb_ipcs_connection_t -#include // pcmk__client_t, pcmk__find_client #include // crm_ipc_client_response #include // CRM_CHECK #include // pcmk_rc_*, pcmk_rc_str diff --git a/daemons/execd/execd_messages.c b/daemons/execd/execd_messages.c index 52384cf79be..f67d190e4b3 100644 --- a/daemons/execd/execd_messages.c +++ b/daemons/execd/execd_messages.c @@ -19,7 +19,6 @@ #include // QB_XS #include // CRM_OP_*, CRM_SYSTEM_LRMD -#include // pcmk__process_request, pcmk__xml_free #include // pcmk_exec_status, pcmk_rc_*, pcmk_rc_str #include // LRMD_OP_* diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c index bc3c9a98838..10213ff180d 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -19,7 +19,6 @@ #include // qb_ipcs_connection_t #include // QB_XS -#include #include // crm_ipc_flags #include // CRM_CHECK, CRM_LOG_ASSERT #include // pcmk_rc_*, pcmk_rc_str diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c index af28efc657c..02dc4984a02 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -23,7 +23,6 @@ #include // xmlNode #include // QB_XS -#include #include // CRM_CHECK #include // mainloop_* #include // pcmk_rc_str, pcmk_rc_* diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index eab7e5841bc..72624f2d6e1 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -24,7 +24,6 @@ #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_*, diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 192b9889a98..305c9a2ac45 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -28,7 +28,6 @@ #include // QB_XS #include // pcmk_cluster_*, etc. -#include // pcmk__corosync2rc, pcmk__err, etc. #include // crm_ipc_is_authentic_process #include // CRM_LOG_ASSERT #include // mainloop_* diff --git a/lib/common/fuzzers/iso8601_fuzzer.c b/lib/common/fuzzers/iso8601_fuzzer.c index e7c0ecb5b41..51e5fcf1c94 100644 --- a/lib/common/fuzzers/iso8601_fuzzer.c +++ b/lib/common/fuzzers/iso8601_fuzzer.c @@ -7,6 +7,8 @@ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ +#include + #include #include #include @@ -15,7 +17,6 @@ #include // qb_util_timespec_from_epoch_get() #include -#include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) diff --git a/lib/common/fuzzers/scores_fuzzer.c b/lib/common/fuzzers/scores_fuzzer.c index 3e06cf5fc13..375bba5e0c7 100644 --- a/lib/common/fuzzers/scores_fuzzer.c +++ b/lib/common/fuzzers/scores_fuzzer.c @@ -7,12 +7,13 @@ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ +#include + #include #include #include #include -#include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) diff --git a/lib/common/logging.c b/lib/common/logging.c index 96e3b62bae2..c49fda6b360 100644 --- a/lib/common/logging.c +++ b/lib/common/logging.c @@ -31,7 +31,6 @@ #include // LOG_TRACE, qb_log_* #include // CRM_DAEMON_USER, CRM_*_DIR -#include // pcmk__env_*, pcmk__output_*, etc. #include // do_crm_log, CRM_CHECK, etc. #include // crm_signal_handler, mainloop_add_signal #include // PCMK_VALUE_NONE diff --git a/lib/common/tls.c b/lib/common/tls.c index 1d834a59812..b88703d8eb8 100644 --- a/lib/common/tls.c +++ b/lib/common/tls.c @@ -26,7 +26,6 @@ #include // gnutls_x509_* #include // QB_XS -#include #include // crm_time_* #include // CRM_CHECK #include // pcmk_rc_* diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c index 65b23df8edd..8af18bf14d6 100644 --- a/lib/lrmd/lrmd_client.c +++ b/lib/lrmd/lrmd_client.c @@ -26,7 +26,6 @@ #include // PCMK_DEFAULT_ACTION_TIMEOUT_MS #include // PCMK_RESOURCE_CLASS_STONITH -#include #include // crm_ipc_* #include // CRM_CHECK, CRM_LOG_ASSERT #include // mainloop_set_trigger diff --git a/lib/pacemaker/pcmk_acl.c b/lib/pacemaker/pcmk_acl.c index 5f47e76e2ee..31fa1130f0d 100644 --- a/lib/pacemaker/pcmk_acl.c +++ b/lib/pacemaker/pcmk_acl.c @@ -25,7 +25,6 @@ #include #include -#include #include diff --git a/tools/cibsecret.c b/tools/cibsecret.c index bbf1d318a52..4e520ed0707 100644 --- a/tools/cibsecret.c +++ b/tools/cibsecret.c @@ -25,7 +25,6 @@ #include // xmlChar #include // cib__clean_up_connection, cib__signon_query -#include #include #include // crm_element_value, PCMK_XA_* diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c index 217e3b203e9..6e81367a184 100644 --- a/tools/crm_resource_runtime.c +++ b/tools/crm_resource_runtime.c @@ -27,7 +27,6 @@ #include // PCMK_ACTION_MONITOR #include // pcmk_get_ra_caps, pcmk_ra_cap_* #include // pcmk_cib_xpath_for -#include #include // pcmk_ipc_api_t #include // pcmk_controld_api_* #include // crm_time_new From 7d016f5a3c5ba51eddef62f4c465bdc504ff9089 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 18:25:23 -0800 Subject: [PATCH 04/98] 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 | 16 +++++++++--- 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, 49 insertions(+), 32 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cef3d4bcdc3..ddcaa005176 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -67,6 +67,10 @@ digest_timer_cb(void *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; @@ -694,6 +698,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 134d44a4889..6825c9ac64a 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 @@ -57,10 +58,17 @@ 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; + 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; + } + data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); if (data == NULL) { return; } @@ -86,8 +94,8 @@ based_cpg_destroy(void *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 f4d5c5004ff..0a17ea24866 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -92,6 +92,10 @@ write_cib_async(void *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 ad3ac88219f..7574cbe9cd5 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -564,6 +564,11 @@ cib_remote_listen(void *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 6f114ccfc82..c080bc5be13 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -227,6 +227,7 @@ static void based_cleanup(void) { based_callbacks_cleanup(); + based_cluster_disconnect(); based_io_cleanup(); based_ipc_cleanup(); based_remote_cleanup(); @@ -237,48 +238,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 @@ -417,7 +409,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 b4c53ded680..7e80a7287ef 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -36,6 +36,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 b0aa2d9c9c0b0e03a902f058b26fa02e18372622 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 01:35:56 -0800 Subject: [PATCH 05/98] 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 ddcaa005176..9f62c48f6d6 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -617,14 +617,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 56f7ded697e271a0fee1b218b0523b4b50740548 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 11:05:00 -0700 Subject: [PATCH 06/98] Refactor: controller: Don't set mainloop to NULL in crmd_exit() The comment above the assignment says "Don't re-enter this block." However, that's already impossible thanks to the in_progress static variable. If it's true, we exit at the beginning of the function. If it's false, we set it to true after checking it. That in_progress logic was not present as of commit 0f9b4c1, when the "Don't re-enter" comment was added. Interestingly though, the in_progress logic was added one hour after the "Don't re-enter" and NULL assignment, but the latter were never removed. See commit 716d9d7. Signed-off-by: Reid Wahl --- daemons/controld/controld_control.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index 5ca16b37986..9e0c00b7dac 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -239,9 +239,6 @@ crmd_exit(crm_exit_t exit_code) if (mloop != NULL) { GMainContext *ctx = g_main_loop_get_context(controld_globals.mainloop); - // Don't re-enter this block - controld_globals.mainloop = NULL; - // Try to drain the main loop before closing it for (int i = 0; (i < 10) && g_main_context_pending(ctx); i++) { g_main_context_dispatch(ctx); From 5a224966d694dbd1a31dbf544793c6929bd4ce71 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 19:45:17 -0800 Subject: [PATCH 07/98] 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 | 1 - 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 | 1 + daemons/fenced/pacemaker-fenced.c | 1 + daemons/pacemakerd/pacemakerd.c | 3 ++- daemons/schedulerd/pacemaker-schedulerd.c | 1 + lib/pacemaker/pcmk_fence.c | 1 + tools/crm_resource.c | 4 +--- 12 files changed, 13 insertions(+), 6 deletions(-) diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c index 3025f197e76..e68550f5774 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 c080bc5be13..c8976401b9b 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -404,6 +404,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 9e0c00b7dac..d51be7a1018 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -246,7 +246,6 @@ crmd_exit(crm_exit_t exit_code) // Exit the main loop and free it when we return from this dispatch g_main_loop_quit(mloop); - g_main_loop_unref(mloop); } throttle_fini(); diff --git a/daemons/controld/pacemaker-controld.c b/daemons/controld/pacemaker-controld.c index 58f17e004b1..f202981010c 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 8508da6ef51..d13f045b10d 100644 --- a/daemons/execd/cts-exec-helper.c +++ b/daemons/execd/cts-exec-helper.c @@ -610,6 +610,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 26b780e8e9a..5b38e4fa934 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 f4386ad16b4..21cbb7174b8 100644 --- a/daemons/fenced/cts-fence-helper.c +++ b/daemons/fenced/cts-fence-helper.c @@ -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 531d0738c10..be249badf9e 100644 --- a/daemons/fenced/pacemaker-fenced.c +++ b/daemons/fenced/pacemaker-fenced.c @@ -446,6 +446,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 286ff374ae4..48e9b87ecbc 100644 --- a/daemons/pacemakerd/pacemakerd.c +++ b/daemons/pacemakerd/pacemakerd.c @@ -477,10 +477,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 e3ce0ca7c4a..67d6bbf8702 100644 --- a/daemons/schedulerd/pacemaker-schedulerd.c +++ b/daemons/schedulerd/pacemaker-schedulerd.c @@ -164,6 +164,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 b43bb2fd799..bc28e1a1ea9 100644 --- a/lib/pacemaker/pcmk_fence.c +++ b/lib/pacemaker/pcmk_fence.c @@ -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 750c3d7d2cf..4150824c24a 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -2379,9 +2379,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 05166f47ce14aec7e90977b3dda2198917e06254 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 19:55:25 -0800 Subject: [PATCH 08/98] 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 e68550f5774..c4648fa6139 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 536e4b918db192cdc75bbd51496494fd5923a4a0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 20:02:29 -0800 Subject: [PATCH 09/98] 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 c4648fa6139..2428104f058 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 cc1ab8a6874791ca4bc0bf8a866235df17c008ff Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 22:40:34 -0800 Subject: [PATCH 10/98] 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 6825c9ac64a..81c88b49659 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -28,29 +28,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 @@ -80,7 +66,7 @@ based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *group_name, 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 c8976401b9b..f26cc41fe37 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -370,7 +370,6 @@ main(int argc, char **argv) goto done; } - pcmk__cluster_init_node_caches(); based_callbacks_init(); based_io_init(); From 19a6288d9833413ef774356dc8c3ac2266429e44 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 10 Jan 2026 22:44:09 -0800 Subject: [PATCH 11/98] 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 81c88b49659..a14c99a915f 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -27,18 +27,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, const struct cpg_name *group_name, @@ -65,8 +53,15 @@ based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *group_name, free(data); return; } + pcmk__xe_set(xml, PCMK__XA_SRC, from); - based_peer_callback(xml); + + if (pcmk__xe_get(xml, PCMK__XA_CIB_CLIENTNAME) == NULL) { + pcmk__xe_set(xml, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(xml, PCMK__XA_SRC)); + } + + based_process_request(xml, true, NULL); pcmk__xml_free(xml); free(data); diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index 305c9a2ac45..612ff5d3fe3 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -459,6 +459,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 6d0e08c98475b2151f34f763ab1576312295a200 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 03:19:10 -0800 Subject: [PATCH 12/98] 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 41fd6094b87..93ece539aa5 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 39524166492..df36af0fa88 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -300,16 +300,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 dc7089bd8b99d61d00477ca984cda73fbdd8713c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:19:48 -0800 Subject: [PATCH 13/98] 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 10213ff180d..87c20d2d286 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -27,10 +27,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 *controld_ipcs = NULL; static qb_ipcs_service_t *fencer_ipcs = NULL; static qb_ipcs_service_t *pacemakerd_ipcs = NULL; @@ -133,27 +132,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 @@ -438,6 +431,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, @@ -454,22 +455,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_controld_ipc(&controld_ipcs, &crmd_proxy_callbacks); if (controld_ipcs == NULL) { @@ -539,8 +524,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(&controld_ipcs, qb_ipcs_destroy); g_clear_pointer(&fencer_ipcs, qb_ipcs_destroy); g_clear_pointer(&pacemakerd_ipcs, qb_ipcs_destroy); From 7be7e4fb1018935788234545133d7f62e9c0aaa7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:27:09 -0800 Subject: [PATCH 14/98] 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 f26cc41fe37..9667423b124 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -328,7 +328,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 c17d49f589ac20db7e54536b40083030033ed0ca Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:01:28 -0800 Subject: [PATCH 15/98] 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 ++++++--------------------------------- lib/cib/cib_native.c | 11 ++++--- lib/cib/cib_remote.c | 8 ++--- 3 files changed, 20 insertions(+), 68 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 42d8998c958..50bdbd4091c 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -60,17 +60,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; @@ -87,9 +86,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) { @@ -201,49 +197,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 @@ -280,18 +240,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, }; @@ -303,8 +255,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); } /*! diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index df36af0fa88..1944f046bd5 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -104,9 +104,9 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, goto done; } - /* The only reason we can receive an ACK here is if dispatch_common -> + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + * based_ipc_dispatch does not return ACK, unlike other daemons. */ if (pcmk__xe_is(op_reply, PCMK__XE_ACK) && ack_is_failure(op_reply)) { rc = -EPROTO; @@ -339,9 +339,10 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) goto done; } - /* The only reason we can receive an ACK here is if dispatch_common -> - * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + /* The only reason we can receive an ACK here is if + * based_ipc_dispatch -> pcmk__client_data2xml processed something + * that's not valid XML. based_ipc_dispatch does not return ACK, unlike + * other daemons. */ if (pcmk__xe_is(reply, PCMK__XE_ACK) && ack_is_failure(reply)) { rc = -EPROTO; diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 72624f2d6e1..9a4a8605e29 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -172,9 +172,9 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, return -ENOMSG; } - /* The only reason we can receive an ACK here is if dispatch_common -> + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + * based_ipc_dispatch does not return ACK, unlike other daemons. */ if (pcmk__xe_is(op_reply, PCMK__XE_ACK) && ack_is_failure(op_reply)) { pcmk__xml_free(op_reply); @@ -526,9 +526,9 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) goto done; } - /* The only reason we can receive an ACK here is if dispatch_common -> + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> * pcmk__client_data2xml processed something that's not valid XML. - * dispatch_common does not return ACK, unlike other daemons. + * based_ipc_dispatch does not return ACK, unlike other daemons. */ if (pcmk__xe_is(answer, PCMK__XE_ACK) && ack_is_failure(answer)) { rc = -EPROTO; From 60391d9b65cf2d1756afb41a57a707f518bbb617 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:41:05 -0800 Subject: [PATCH 16/98] 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 9f62c48f6d6..f90ac2e66c8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -656,17 +656,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; @@ -795,12 +792,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 a14c99a915f..2efb71c5706 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 @@ -61,7 +60,7 @@ based_cpg_dispatch(cpg_handle_t handle, const struct cpg_name *group_name, pcmk__xe_get(xml, PCMK__XA_SRC)); } - based_process_request(xml, true, NULL); + based_process_request(xml, NULL); pcmk__xml_free(xml); free(data); diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 50bdbd4091c..3aad068ce89 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 @@ -197,7 +196,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 7574cbe9cd5..e91ef7214ef 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -392,7 +392,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 22fa18d9ea1..afe9bc3fbdc 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 @@ -79,11 +78,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 f773a3c5e62ca68dd57c71ec47930b0380d3e6da Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:44:07 -0800 Subject: [PATCH 17/98] 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 98a20b1e49f..99ce4bd359f 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 41ead4081b2..3cef5ff3025 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 d04c2394b461961e7f9a03a0c4579a3eb6786268 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 15:56:47 -0800 Subject: [PATCH 18/98] 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 8da874e02b6..16ae3db4f53 100644 --- a/daemons/controld/controld_remote_proxy.c +++ b/daemons/controld/controld_remote_proxy.c @@ -188,6 +188,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 143926567aeb02031493dfac1419a2666a95700f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 16:13:27 -0800 Subject: [PATCH 19/98] 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 3aad068ce89..850a638c525 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -26,8 +26,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 @@ -254,7 +253,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); } /*! @@ -264,11 +263,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 87c20d2d286..36f71c0b844 100644 --- a/daemons/execd/remoted_proxy.c +++ b/daemons/execd/remoted_proxy.c @@ -28,8 +28,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 *controld_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_controld_ipc(&controld_ipcs, &crmd_proxy_callbacks); if (controld_ipcs == NULL) { @@ -524,8 +522,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(&controld_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 b48a702993a..0b49ab6d517 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -250,10 +250,8 @@ void pcmk__serve_pacemakerd_ipc(qb_ipcs_service_t **ipcs, void pcmk__serve_schedulerd_ipc(qb_ipcs_service_t **ipcs, 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 ec92392918f..ba566d11157 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1061,28 +1061,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 635374ef81cd5529fedaaa7cfb3ef5ecf4524b9d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 17:49:04 -0800 Subject: [PATCH 20/98] 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 | 87 ++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 2efb71c5706..2a7bcbe0071 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -26,42 +26,87 @@ static pcmk_cluster_t *cluster = NULL; -#if SUPPORT_COROSYNC 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) +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__xe_set(xml, PCMK__XA_SRC, from); - - if (pcmk__xe_get(xml, PCMK__XA_CIB_CLIENTNAME) == NULL) { - pcmk__xe_set(xml, PCMK__XA_CIB_CLIENTNAME, - pcmk__xe_get(xml, PCMK__XA_SRC)); + 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); } - based_process_request(xml, NULL); - pcmk__xml_free(xml); free(data); } From 2652d38176a6ef000c3f53eefa6b247a864c71d4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:08:13 -0700 Subject: [PATCH 21/98] Refactor: libcib: Drop unnecessary rc check in cib_native_signon() It's initialized to pcmk_ok, and nothing can change it before we reach this point. (That was not the case when this line was added.) Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 1944f046bd5..d25af9e722d 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -288,7 +288,7 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) struct ipc_client_callbacks cib_callbacks = { .dispatch = cib_native_dispatch_internal, - .destroy = cib_native_destroy + .destroy = cib_native_destroy, }; if (name == NULL) { @@ -318,7 +318,7 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) &cib_callbacks); native->ipc = mainloop_get_ipc_client(native->source); - if (rc != pcmk_ok || native->ipc == NULL || !crm_ipc_connected(native->ipc)) { + if ((native->ipc == NULL) || !crm_ipc_connected(native->ipc)) { pcmk__info("Could not connect to CIB manager for %s", name); rc = -ENOTCONN; } From 95611f763d39d8adb2710821bd516f4a91231ce6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:09:22 -0700 Subject: [PATCH 22/98] Refactor: libcib: Unindent some of cib_native_signon() Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index d25af9e722d..3850f008509 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -321,13 +321,12 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) if ((native->ipc == NULL) || !crm_ipc_connected(native->ipc)) { pcmk__info("Could not connect to CIB manager for %s", name); rc = -ENOTCONN; + goto done; } - if (rc == pcmk_ok) { - rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL, - cib_sync_call, NULL, name, &hello); - rc = pcmk_rc2legacy(rc); - } + rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL, cib_sync_call, + NULL, name, &hello); + rc = pcmk_rc2legacy(rc); if (rc == pcmk_ok) { xmlNode *reply = NULL; From 6279154703a36d23b97eb27ab1066dd6e9878e2a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:12:51 -0700 Subject: [PATCH 23/98] Refactor: libcib: Unindent more of cib_native_signon() Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 60 +++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 3850f008509..03685bf7bd3 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -285,6 +285,8 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) const char *channel = NULL; cib_native_opaque_t *native = cib->variant_opaque; xmlNode *hello = NULL; + xmlNode *reply = NULL; + const char *msg_type = NULL; struct ipc_client_callbacks cib_callbacks = { .dispatch = cib_native_dispatch_internal, @@ -327,50 +329,44 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL, cib_sync_call, NULL, name, &hello); rc = pcmk_rc2legacy(rc); + if (rc != pcmk_ok) { + goto done; + } - if (rc == pcmk_ok) { - xmlNode *reply = NULL; - const char *msg_type = NULL; + if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, + &reply) <= 0) { + rc = -ECOMM; + goto done; + } - if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, - &reply) <= 0) { - rc = -ECOMM; - goto done; - } + /* The only reason we can receive an ACK here is if based_ipc_dispatch -> + * pcmk__client_data2xml processed something that's not valid XML. + * based_ipc_dispatch does not return ACK, unlike other daemons. + */ + if (pcmk__xe_is(reply, PCMK__XE_ACK) && ack_is_failure(reply)) { + rc = -EPROTO; + goto done; + } - /* The only reason we can receive an ACK here is if - * based_ipc_dispatch -> pcmk__client_data2xml processed something - * that's not valid XML. based_ipc_dispatch does not return ACK, unlike - * other daemons. - */ - if (pcmk__xe_is(reply, PCMK__XE_ACK) && ack_is_failure(reply)) { - rc = -EPROTO; - pcmk__xml_free(reply); - goto done; - } + msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); - msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); + pcmk__log_xml_trace(reply, "reg-reply"); - pcmk__log_xml_trace(reply, "reg-reply"); + if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { + pcmk__info("Reply to CIB registration message has unknown type '%s'", + msg_type); + rc = -EPROTO; - if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { - pcmk__info("Reply to CIB registration message has unknown type " - "'%s'", - msg_type); + } else { + native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); + if (native->token == NULL) { rc = -EPROTO; - - } else { - native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); - if (native->token == NULL) { - rc = -EPROTO; - } } - - pcmk__xml_free(reply); } done: pcmk__xml_free(hello); + pcmk__xml_free(reply); if (rc == pcmk_ok) { pcmk__info("Successfully connected to CIB manager for %s", name); From 1b431b7e8d627407bc94b7fe2ad2dc08fd1f68af Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:13:54 -0700 Subject: [PATCH 24/98] Refactor: libcib: Unindent one more block of cib_native_signon() Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 03685bf7bd3..73a34971a78 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -348,20 +348,19 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) goto done; } - msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); - pcmk__log_xml_trace(reply, "reg-reply"); + msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { pcmk__info("Reply to CIB registration message has unknown type '%s'", msg_type); rc = -EPROTO; + goto done; + } - } else { - native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); - if (native->token == NULL) { - rc = -EPROTO; - } + native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); + if (native->token == NULL) { + rc = -EPROTO; } done: From 88c713b0851327cf0e3f15cb0cedca782db61dae Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:16:16 -0700 Subject: [PATCH 25/98] Refactor: libcib: Compare CRM_OP_REGISTER case-sensitively Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 2 +- lib/cib/cib_remote.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 73a34971a78..1355976f6a7 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -351,7 +351,7 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) pcmk__log_xml_trace(reply, "reg-reply"); msg_type = pcmk__xe_get(reply, PCMK__XA_CIB_OP); - if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { + if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) { pcmk__info("Reply to CIB registration message has unknown type '%s'", msg_type); rc = -EPROTO; diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 9a4a8605e29..9750e8b69d1 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -541,7 +541,7 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) msg_type = pcmk__xe_get(answer, PCMK__XA_CIB_OP); tmp_ticket = pcmk__xe_get(answer, PCMK__XA_CIB_CLIENTID); - if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) { + if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) { pcmk__err("Invalid registration message: %s", msg_type); rc = -EPROTO; From e8ceff8807ec9b69d5c0ff0b39810adf12e36eb1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:45:07 -0700 Subject: [PATCH 26/98] Feature: libcib: Ignore name argument of cib_api_operations_t:signon() Instead, always use crm_system_name if set, or fall back to "client" otherwise. All internal callers pass crm_system_name. Further, crm_system_name should always be non-NULL if crm_log_preinit() has been called -- which is always true for internal callers. Up to now, callers have been allowed to pass an arbitrary client name. However, this does not seem to be useful for anything except logging, and certain names may cause Pacemaker daemons to treat requests from the client specially. It seems safer to use crm_system_name unconditionally (falling back to "client" if NULL). Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 7 +++++++ lib/cib/cib_file.c | 15 ++++++++++++--- lib/cib/cib_native.c | 12 +++++++++--- lib/cib/cib_remote.c | 12 +++++++++--- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 93ece539aa5..deb9523abe6 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -144,6 +144,13 @@ typedef struct cib_api_operations_s { * and type (always use cib_command -- cib_file and cib_remote already do * this). */ + /*! + * \brief Sign on a client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Type of CIB connection + */ 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_file.c b/lib/cib/cib_file.c index da7e6c1b1b8..94a509bd6f8 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -521,12 +521,22 @@ load_file_cib(const char *filename, xmlNode **output) return pcmk_ok; } +/*! + * \internal + * \brief Sign on a native client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Ignored + */ static int file_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; file_opaque_t *private = cib->variant_opaque; + name = pcmk__s(crm_system_name, "client"); + if (private->filename == NULL) { rc = -EINVAL; } else { @@ -535,15 +545,14 @@ file_signon(cib_t *cib, const char *name, enum cib_conn_type type) if (rc == pcmk_ok) { pcmk__debug("Opened connection to local file '%s' for %s", - private->filename, pcmk__s(name, "client")); + private->filename, name); cib->state = cib_connected_command; cib->type = cib_command; register_client(cib); } else { pcmk__info("Connection to local file '%s' for %s (client %s) failed: " - "%s", - private->filename, pcmk__s(name, "client"), private->id, + "%s", private->filename, name, private->id, pcmk_strerror(rc)); } return rc; diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 1355976f6a7..85c2d290229 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -278,6 +278,14 @@ cib_native_signoff(cib_t *cib) return pcmk_ok; } +/*! + * \internal + * \brief Sign on a native client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Type of CIB connection + */ static int cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) { @@ -293,9 +301,7 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) .destroy = cib_native_destroy, }; - if (name == NULL) { - name = pcmk__s(crm_system_name, "client"); - } + name = pcmk__s(crm_system_name, "client"); cib->call_timeout = PCMK__IPC_TIMEOUT; diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 9750e8b69d1..039f98872b1 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -568,15 +568,21 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) return rc; } +/*! + * \internal + * \brief Sign on a native client to the CIB API + * + * \param[in,out] cib CIB connection (client) + * \param[in] name Ignored + * \param[in] type Ignored + */ static int cib_remote_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; cib_remote_opaque_t *private = cib->variant_opaque; - if (name == NULL) { - name = pcmk__s(crm_system_name, "client"); - } + name = pcmk__s(crm_system_name, "client"); if (private->passwd == NULL) { if (private->out == NULL) { From b63210d13ea2ab56779b77ec50b2cb0e063b0377 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 15:57:53 -0700 Subject: [PATCH 27/98] Feature: libcib: Don't set cib_t:type Nothing checks it internally, and there's no apparent use case for anything external to check it. Further, cib_native clients already don't set it at all during signon. cib_file and cib_remote clients set it during signon; all clients set it back to cib_no_connection at signoff. Signed-off-by: Reid Wahl --- lib/cib/cib_client.c | 1 - lib/cib/cib_file.c | 2 -- lib/cib/cib_native.c | 1 - lib/cib/cib_remote.c | 2 -- 4 files changed, 6 deletions(-) diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 81658f69b6f..9d992975ef2 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -618,7 +618,6 @@ cib_new_variant(void) new_cib->call_id = 1; new_cib->variant = cib_undefined; - new_cib->type = cib_no_connection; new_cib->state = cib_disconnected; new_cib->variant_opaque = NULL; new_cib->notify_list = NULL; diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 94a509bd6f8..182d7a6e1d4 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -547,7 +547,6 @@ file_signon(cib_t *cib, const char *name, enum cib_conn_type type) pcmk__debug("Opened connection to local file '%s' for %s", private->filename, name); cib->state = cib_connected_command; - cib->type = cib_command; register_client(cib); } else { @@ -657,7 +656,6 @@ file_signoff(cib_t *cib) pcmk__debug("Disconnecting from the CIB manager"); cib->state = cib_disconnected; - cib->type = cib_no_connection; unregister_client(cib); cib->cmds->end_transaction(cib, false, cib_none); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 85c2d290229..e3c139c23da 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -273,7 +273,6 @@ cib_native_signoff(cib_t *cib) cib->cmds->end_transaction(cib, false, cib_none); cib->state = cib_disconnected; - cib->type = cib_no_connection; return pcmk_ok; } diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 039f98872b1..3813767e918 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -612,7 +612,6 @@ cib_remote_signon(cib_t *cib, const char *name, enum cib_conn_type type) pcmk__info("Opened connection to %s:%d for %s", private->server, private->port, name); cib->state = cib_connected_command; - cib->type = cib_command; } else { pcmk__info("Connection to %s:%d for %s failed: %s\n", private->server, @@ -632,7 +631,6 @@ cib_remote_signoff(cib_t *cib) cib->cmds->end_transaction(cib, false, cib_none); cib->state = cib_disconnected; - cib->type = cib_no_connection; return rc; } From cd3ed6eb360cb1bb7a3394b4dc72c385f796c267 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 16:00:56 -0700 Subject: [PATCH 28/98] API: libcib: Deprecate cib_no_connection Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index deb9523abe6..c239fae878b 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -54,6 +54,7 @@ enum cib_conn_type { //! \deprecated Use \c cib_command instead cib_query, + //! \deprecated Do not use cib_no_connection, //! \deprecated Use \c cib_command instead From f4c78cc80c665274f54f44debeac58b28aaa4793 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 16:05:58 -0700 Subject: [PATCH 29/98] Feature: libcib: Ignore type argument of signon method for native client cib_file and cib_remote clients already ignored the type argument. cib_native clients simply returned an ENOTCONN error if the type was invalid; otherwise, they ignored it. Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 2 +- lib/cib/cib_native.c | 24 ++++-------------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index c239fae878b..85f788c8596 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -150,7 +150,7 @@ typedef struct cib_api_operations_s { * * \param[in,out] cib CIB connection (client) * \param[in] name Ignored - * \param[in] type Type of CIB connection + * \param[in] type Ignored */ int (*signon) (cib_t *cib, const char *name, enum cib_conn_type type); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index e3c139c23da..aad64ec31f5 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -283,13 +283,12 @@ cib_native_signoff(cib_t *cib) * * \param[in,out] cib CIB connection (client) * \param[in] name Ignored - * \param[in] type Type of CIB connection + * \param[in] type Ignored */ static int cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; - const char *channel = NULL; cib_native_opaque_t *native = cib->variant_opaque; xmlNode *hello = NULL; xmlNode *reply = NULL; @@ -303,25 +302,10 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) name = pcmk__s(crm_system_name, "client"); cib->call_timeout = PCMK__IPC_TIMEOUT; + cib->state = cib_connected_command; - switch (type) { - case cib_command: - case cib_command_nonblocking: - 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; - - default: - return -ENOTCONN; - } - - pcmk__trace("Connecting %s channel", channel); - - native->source = mainloop_add_ipc_client(channel, G_PRIORITY_HIGH, 0, cib, + native->source = mainloop_add_ipc_client(PCMK__SERVER_BASED_RW, + G_PRIORITY_HIGH, 0, cib, &cib_callbacks); native->ipc = mainloop_get_ipc_client(native->source); From b39196718a57b56e2090f0140fa53bf17c5626d9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 16:03:11 -0700 Subject: [PATCH 30/98] API: libcib: Deprecate enum cib_conn_type cib_command is the only type we ever use, so this enum is now meaningless. Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 85f788c8596..86112f1c7e4 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -47,17 +47,19 @@ enum cib_state { cib_disconnected }; +/*! + * \deprecated Do not use + * + * \note Pass \c cib_command to cib_api_operations_t:signon as long as + * that function and argument exist. + */ 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, - //! \deprecated Do not use cib_no_connection, - - //! \deprecated Use \c cib_command instead cib_command_nonblocking, }; From 97fb8b34fbf2e58a4f9657258b91178ba5f607d5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 16:10:27 -0700 Subject: [PATCH 31/98] Refactor: libcib: Clean up the end of cib_native_signon() Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index aad64ec31f5..5d15893ed52 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -302,7 +302,6 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) name = pcmk__s(crm_system_name, "client"); cib->call_timeout = PCMK__IPC_TIMEOUT; - cib->state = cib_connected_command; native->source = mainloop_add_ipc_client(PCMK__SERVER_BASED_RW, G_PRIORITY_HIGH, 0, cib, @@ -350,20 +349,22 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) native->token = pcmk__xe_get_copy(reply, PCMK__XA_CIB_CLIENTID); if (native->token == NULL) { rc = -EPROTO; + goto done; } + pcmk__info("Successfully connected to CIB manager for %s", name); + cib->state = cib_connected_command; + done: pcmk__xml_free(hello); pcmk__xml_free(reply); - if (rc == pcmk_ok) { - pcmk__info("Successfully connected to CIB manager for %s", name); - return pcmk_ok; + if (rc != pcmk_ok) { + pcmk__info("Connection to CIB manager for %s failed: %s", name, + pcmk_strerror(rc)); + cib_native_signoff(cib); } - pcmk__info("Connection to CIB manager for %s failed: %s", name, - pcmk_strerror(rc)); - cib_native_signoff(cib); return rc; } From adbf9fdc8968ea7ec634b89c994e087d6a729628 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 22:10:05 -0700 Subject: [PATCH 32/98] Refactor: based: goto cleanup consistently in based_ipc_dispatch() Just for future-proofing and consistency. This doesn't fix a bug. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 850a638c525..6aad0f64083 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -78,17 +78,18 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) const char *op = NULL; // Sanity-check, and parse XML from IPC data - CRM_CHECK(client != NULL, return 0); + CRM_CHECK(client != NULL, goto cleanup); + if (data == NULL) { pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); - return 0; + goto cleanup; } rc = pcmk__ipc_msg_append(&client->buffer, data); if (rc == pcmk_rc_ipc_more) { /* We haven't read the complete message yet, so just return. */ - return 0; + goto cleanup; } else if (rc == pcmk_rc_ok) { /* We've read the complete message and there's already a header on @@ -109,13 +110,13 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) client->buffer = NULL; } - return 0; + goto cleanup; } if (msg == NULL) { pcmk__debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c)); pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_PROTOCOL); - return 0; + goto cleanup; } if (client->name == NULL) { From 2147d3f8db62158752828beef463ba0d539fe595 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 23 Jul 2026 22:30:06 -0700 Subject: [PATCH 33/98] Refactor: various: Use pcmk__is_set() and friends in more places Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 2 +- daemons/controld/controld_remote_proxy.c | 2 +- daemons/fenced/fenced_history.c | 7 +++--- daemons/fenced/fenced_remote.c | 20 ++++++++++++----- lib/cib/cib_native.c | 9 +++++--- lib/cib/cib_remote.c | 9 ++++---- lib/cluster/membership.c | 2 +- lib/common/io.c | 8 +++++-- lib/common/mainloop.c | 8 ++++--- lib/common/tls.c | 2 +- lib/fencing/st_client.c | 14 +++++++----- lib/services/dbus.c | 28 ++++++++++++++---------- lib/services/services_linux.c | 7 +++--- tools/crm_mon.c | 6 ++--- 14 files changed, 75 insertions(+), 49 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 6aad0f64083..3f9eaf9c0e2 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -148,7 +148,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); + CRM_LOG_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); diff --git a/daemons/controld/controld_remote_proxy.c b/daemons/controld/controld_remote_proxy.c index 16ae3db4f53..5f6cf009a6b 100644 --- a/daemons/controld/controld_remote_proxy.c +++ b/daemons/controld/controld_remote_proxy.c @@ -128,7 +128,7 @@ remote_proxy_dispatch(const char *buffer, ssize_t length, void *userdata) } flags = crm_ipc_buffer_flags(proxy->ipc); - if (flags & crm_ipc_proxied_relay_response) { + if (pcmk__is_set(flags, crm_ipc_proxied_relay_response)) { pcmk__trace("Passing response back to %.8s on %s: %.200s - request id: " "%d", proxy->session_id, proxy->node_name, buffer, proxy->last_request_id); diff --git a/daemons/fenced/fenced_history.c b/daemons/fenced/fenced_history.c index 75bac9d36f4..968de2a5b21 100644 --- a/daemons/fenced/fenced_history.c +++ b/daemons/fenced/fenced_history.c @@ -482,7 +482,8 @@ stonith_fence_history(xmlNode *msg, xmlNode **output, if (dev) { target = pcmk__xe_get(dev, PCMK__XA_ST_TARGET); - if (target && (options & st_opt_cs_nodeid)) { + + if ((target != NULL) && pcmk__is_set(options, st_opt_cs_nodeid)) { int nodeid; pcmk__node_status_t *node = NULL; @@ -496,14 +497,14 @@ stonith_fence_history(xmlNode *msg, xmlNode **output, } } - if (options & st_opt_cleanup) { + if (pcmk__is_set(options, st_opt_cleanup)) { const char *call_id = pcmk__xe_get(msg, PCMK__XA_ST_CALLID); pcmk__trace("Cleaning up operations on %s in %p", target, stonith_remote_op_list); stonith_fence_history_cleanup(target, (call_id != NULL)); - } else if (options & st_opt_broadcast) { + } else if (pcmk__is_set(options, st_opt_broadcast)) { /* there is no clear sign atm for when a history sync is done so send a notification for anything that smells like history-sync diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c index 394dd4859e3..496a8ede76a 100644 --- a/daemons/fenced/fenced_remote.c +++ b/daemons/fenced/fenced_remote.c @@ -1245,7 +1245,7 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer) op->replies_expected, pcmk__plural_alt(op->replies_expected, "reply", "replies")); - if (op->call_options & st_opt_cs_nodeid) { + if (pcmk__is_set(op->call_options, st_opt_cs_nodeid)) { int nodeid; pcmk__node_status_t *node = NULL; @@ -1396,7 +1396,7 @@ static peer_device_info_t * find_best_peer(const char *device, remote_fencing_op_t * op, enum find_best_peer_options options) { GList *iter = NULL; - gboolean verified_devices_only = (options & FIND_PEER_VERIFIED_ONLY) ? TRUE : FALSE; + bool verified_devices_only = pcmk__is_set(options, FIND_PEER_VERIFIED_ONLY); if ((device == NULL) && pcmk__is_set(op->call_options, st_opt_topology)) { return NULL; @@ -1409,10 +1409,16 @@ find_best_peer(const char *device, remote_fencing_op_t * op, enum find_best_peer "%x", peer->host, op->target, peer->ndevices, pcmk__plural_s(peer->ndevices), peer->tried, options); - if ((options & FIND_PEER_SKIP_TARGET) && pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + + if (pcmk__is_set(options, FIND_PEER_SKIP_TARGET) + && pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + continue; } - if ((options & FIND_PEER_TARGET_ONLY) && !pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + + if (pcmk__is_set(options, FIND_PEER_TARGET_ONLY) + && !pcmk__str_eq(peer->host, op->target, pcmk__str_casei)) { + continue; } @@ -1722,13 +1728,15 @@ report_timeout_period(remote_fencing_op_t * op, int op_timeout) const char *client_id = NULL; const char *call_id = NULL; - if (op->call_options & st_opt_sync_call) { + if (pcmk__is_set(op->call_options, st_opt_sync_call)) { /* There is no reason to report the timeout for a synchronous call. It * is impossible to use the reported timeout to do anything when the client * is blocking for the response. This update is only important for * async calls that require a callback to report the results in. */ return; - } else if (!op->request) { + } + + if (op->request == NULL) { return; } diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 5d15893ed52..6fa2d73bf62 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -76,7 +76,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, return -EINVAL; } - if (call_options & cib_sync_call) { + if (pcmk__is_set(call_options, cib_sync_call)) { pcmk__set_ipc_flags(ipc_flags, "client", crm_ipc_client_response); } @@ -115,7 +115,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, pcmk__log_xml_trace(op_reply, "Reply"); - if (!(call_options & cib_sync_call)) { + if (!pcmk__is_set(call_options, cib_sync_call)) { pcmk__trace("Async call, returning %d", cib->call_id); CRM_CHECK(cib->call_id != 0, rc = -ENOMSG; goto done); @@ -133,8 +133,11 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = -EPROTO; } - if (output_data == NULL || (call_options & cib_discard_reply)) { + if ((output_data == NULL) + || pcmk__is_set(call_options, cib_discard_reply)) { + pcmk__trace("Discarding reply"); + } else { *output_data = pcmk__xml_copy(NULL, tmp); } diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 3813767e918..79a8ee30e38 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -105,18 +105,19 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, } pcmk__trace("Sending %s message to the CIB manager", op); - if (!(call_options & cib_sync_call)) { + if (!pcmk__is_set(call_options, cib_sync_call)) { pcmk__remote_send_xml(&private->callback, op_msg); } else { pcmk__remote_send_xml(&private->command, op_msg); } 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; } @@ -199,7 +200,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, if (output_data == NULL) { /* do nothing more */ - } else if (!(call_options & cib_discard_reply)) { + } else if (!pcmk__is_set(call_options, cib_discard_reply)) { xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { diff --git a/lib/cluster/membership.c b/lib/cluster/membership.c index d48d36013a0..81ce288005e 100644 --- a/lib/cluster/membership.c +++ b/lib/cluster/membership.c @@ -1128,7 +1128,7 @@ crm_update_peer_proc(const char *source, pcmk__node_status_t *node, changed = TRUE; } - } else if (node->processes & flag) { + } else if (pcmk__is_set(node->processes, flag)) { node->processes = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, "Peer process", node->name, node->processes, diff --git a/lib/common/io.c b/lib/common/io.c index 15347e93c50..b8c07970896 100644 --- a/lib/common/io.c +++ b/lib/common/io.c @@ -252,6 +252,7 @@ pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat) pcmk_rc_str(rc)); return false; } + if (target_stat->st_uid != daemon_uid) { pcmk__notice("%s is not owned by user " CRM_DAEMON_USER " " QB_XS " uid %lld != %lld", @@ -259,13 +260,15 @@ pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat) (long long) target_stat->st_uid); return false; } - if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) { + + if (!pcmk__any_flags_set(target_stat->st_mode, S_IRUSR|S_IWUSR)) { pcmk__notice("%s is not readable and writable by user %s " QB_XS " st_mode=0%lo", target_name, CRM_DAEMON_USER, (unsigned long) target_stat->st_mode); return false; } + return true; } @@ -289,13 +292,14 @@ pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat) return false; } - if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) { + if (!pcmk__any_flags_set(target_stat->st_mode, S_IRGRP|S_IWGRP)) { pcmk__notice("%s is not readable and writable by group %s " QB_XS " st_mode=0%lo", target_name, CRM_DAEMON_GROUP, (unsigned long) target_stat->st_mode); return false; } + return true; } diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index b3457ff026b..7aee7ae85a7 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -687,7 +687,7 @@ mainloop_gio_callback(GIOChannel *gio, GIOCondition condition, void *data) pcmk__assert(client->fd == g_io_channel_unix_get_fd(gio)); - if (condition & G_IO_IN) { + if (pcmk__is_set(condition, G_IO_IN)) { if (client->ipc) { long read_rc = 0L; int max = 10; @@ -738,12 +738,14 @@ mainloop_gio_callback(GIOChannel *gio, GIOCondition condition, void *data) client->name, client, condition); rc = G_SOURCE_REMOVE; - } else if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) { + } else if (pcmk__any_flags_set(condition, + (G_IO_HUP | G_IO_NVAL | G_IO_ERR))) { + pcmk__trace("The connection %s[%p] has been closed (I/O condition=%d)", client->name, client, condition); rc = G_SOURCE_REMOVE; - } else if ((condition & G_IO_IN) == 0) { + } else if (!pcmk__is_set(condition, G_IO_IN)) { /* #define GLIB_SYSDEF_POLLIN =1 #define GLIB_SYSDEF_POLLPRI =2 diff --git a/lib/common/tls.c b/lib/common/tls.c index b88703d8eb8..9d0d5a4474e 100644 --- a/lib/common/tls.c +++ b/lib/common/tls.c @@ -642,7 +642,7 @@ pcmk__cred_file_useable(const char *location, bool *file_exists) return false; } - if ((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + if (pcmk__any_flags_set(sb.st_mode, S_IRWXG|S_IRWXO)) { pcmk__err("Refusing to use PSK credentials file %s because it has " "group and/or other permissions set", location); return false; diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index 33e41d7ff8f..3e2ff23439f 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.c @@ -1388,7 +1388,7 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti private->op_callback = callback; } else if (call_id < 0) { // Call failed immediately, so call callback now - if (!(options & st_opt_report_only_success)) { + if (!pcmk__is_set(options, st_opt_report_only_success)) { pcmk__action_result_t result = PCMK__UNKNOWN_RESULT; pcmk__trace("Call failed, calling %s: %s", callback_name, @@ -1405,10 +1405,10 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti blob = pcmk__assert_alloc(1, sizeof(stonith_callback_client_t)); blob->id = callback_name; - blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE; + blob->only_success = pcmk__is_set(options, st_opt_report_only_success); blob->user_data = user_data; blob->callback = callback; - blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE; + blob->allow_timeout_updates = pcmk__is_set(options, st_opt_timeout_updates); if (timeout > 0) { set_callback_timeout(blob, stonith, call_id, timeout); @@ -1637,7 +1637,7 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod { enum crm_ipc_flags ipc_flags = crm_ipc_flags_none; - if (call_options & st_opt_sync_call) { + if (pcmk__is_set(call_options, st_opt_sync_call)) { pcmk__set_ipc_flags(ipc_flags, "fencing command", crm_ipc_client_response); } @@ -1660,7 +1660,7 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod pcmk__log_xml_trace(op_reply, "Reply"); - if (!(call_options & st_opt_sync_call)) { + if (!pcmk__is_set(call_options, st_opt_sync_call)) { pcmk__trace("Async call %d, returning", stonith->call_id); pcmk__xml_free(op_reply); return stonith->call_id; @@ -1677,7 +1677,9 @@ stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNod rc = pcmk_rc2legacy(stonith__result2rc(&result)); pcmk__reset_result(&result); - if ((call_options & st_opt_discard_reply) || output_data == NULL) { + if (pcmk__is_set(call_options, st_opt_discard_reply) + || (output_data == NULL)) { + pcmk__trace("Discarding reply"); } else { diff --git a/lib/services/dbus.c b/lib/services/dbus.c index a21da13e599..bd32ab141dd 100644 --- a/lib/services/dbus.c +++ b/lib/services/dbus.c @@ -82,18 +82,19 @@ dispatch_messages(void) static const char* dbus_watch_flags_to_string(int flags) { - const char *watch_type; - - if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) { - watch_type = "read/write"; - } else if (flags & DBUS_WATCH_READABLE) { - watch_type = "read"; - } else if (flags & DBUS_WATCH_WRITABLE) { - watch_type = "write"; - } else { - watch_type = "neither read nor write"; + if (pcmk__all_flags_set(flags, DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE)) { + return "read/write"; + } + + if (pcmk__is_set(flags, DBUS_WATCH_READABLE)) { + return "read"; } - return watch_type; + + if (pcmk__is_set(flags, DBUS_WATCH_WRITABLE)) { + return "write"; + } + + return "neither read nor write"; } /*! @@ -120,7 +121,10 @@ dispatch_fd_data(void *userdata) dbus_watch_get_unix_fd(watch), flags, dbus_watch_flags_to_string(flags)); - if (enabled && (flags & (DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE))) { + if (enabled + && pcmk__any_flags_set(flags, + DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE)) { + oom = !dbus_watch_handle(watch, flags); } else if (enabled) { diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index 6ae1605ebbc..74a02d9dd2c 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -1092,16 +1092,17 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) wait_reason = NULL; if (poll_rc > 0) { - if (fds[0].revents & POLLIN) { + if (pcmk__is_set(fds[0].revents, POLLIN)) { svc_read_output(op->opaque->stdout_fd, op, FALSE); } - if (fds[1].revents & POLLIN) { + if (pcmk__is_set(fds[1].revents, POLLIN)) { svc_read_output(op->opaque->stderr_fd, op, TRUE); } - if ((fds[2].revents & POLLIN) + if (pcmk__is_set(fds[2].revents, POLLIN) && sigchld_received(fds[2].fd, op->pid, data)) { + wait_rc = waitpid(op->pid, &status, WNOHANG); if ((wait_rc > 0) || ((wait_rc < 0) && (errno == ECHILD))) { diff --git a/tools/crm_mon.c b/tools/crm_mon.c index c45a4dfe2a4..2c615249943 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1090,7 +1090,7 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, void *user_data) * Exit with an error, otherwise the process would persist in the * background and significantly raise the CPU usage. */ - if ((condition & G_IO_ERR) && (condition & G_IO_HUP)) { + if (pcmk__all_flags_set(condition, G_IO_ERR|G_IO_HUP)) { rc = G_SOURCE_REMOVE; clean_up(CRM_EX_IOERR); } @@ -1098,11 +1098,11 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, void *user_data) /* The connection/fd has been closed. Refresh the screen and remove this * event source hence ignore stdin. */ - if (condition & (G_IO_HUP | G_IO_NVAL)) { + if (pcmk__any_flags_set(condition, G_IO_HUP|G_IO_NVAL)) { rc = G_SOURCE_REMOVE; } - if ((condition & G_IO_IN) == 0) { + if (!pcmk__is_set(condition, G_IO_IN)) { return rc; } From e8c609bf832f185279a25e066d60409b9a7c5a02 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 24 Jul 2026 00:49:51 -0700 Subject: [PATCH 34/98] Refactor: based: Add extra XML attrs only before based_process_request() We don't use these attrs when processing CRM_OP_REGISTER and PCMK__VALUE_CIB_NOTIFY. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 3f9eaf9c0e2..759ad3a21cd 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -157,12 +157,6 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) client->request_id = id; } - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); - - 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); @@ -196,6 +190,12 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) goto cleanup; } + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); + + CRM_LOG_ASSERT(client->user != NULL); + pcmk__update_acl_user(msg, PCMK__XA_CIB_USER, client->user); + based_process_request(msg, client); cleanup: From 5a4a4eff3abd01e37103ddd5bc6270079815904c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 24 Jul 2026 01:03:41 -0700 Subject: [PATCH 35/98] Low: based: Ignore requests from unregistered clients And set client->name only when dispatching a register request. In practice, this means that a client must call the signon() method before any other methods. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 759ad3a21cd..4c680023817 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -119,16 +119,6 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) goto cleanup; } - 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); - } - } - 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", @@ -172,9 +162,31 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) pcmk__xe_set(reply, PCMK__XA_CIB_OP, CRM_OP_REGISTER); pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client->id); pcmk__ipc_send_xml(client, id, reply, flags); + pcmk__xml_free(reply); + + if (client->name != NULL) { + /* client->name is set if and only if we've processed a register + * request from the client + */ + pcmk__warn("Received register request from IPC client %s that is " + "already registered", pcmk__client_name(client)); + goto cleanup; + } client->request_id = 0; - pcmk__xml_free(reply); + + client->name = pcmk__xe_get_copy(msg, PCMK__XA_CIB_CLIENTNAME); + if (client->name == NULL) { + // Fall back to PID for logging purposes + client->name = pcmk__itoa(client->pid); + } + + goto cleanup; + } + + if (client->name == NULL) { + pcmk__warn("Ignoring CIB request from unregistered client %s", + pcmk__client_name(client)); goto cleanup; } From fc7061d93c922b34e9b93dbd8449621b8d27c5cb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:11:38 -0800 Subject: [PATCH 36/98] Refactor: based: Standardize based_ipc_dispatch() Make it look more 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 | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 4c680023817..8c885cbfda0 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -121,9 +121,7 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) 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 @@ -147,8 +145,6 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) client->request_id = id; } - 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)) { @@ -199,16 +195,34 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) } pcmk__ipc_send_ack(client, id, flags, NULL, status); - goto cleanup; - } - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); + } 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, goto cleanup); + + if (pcmk__is_set(request.call_options, cib_sync_call)) { + pcmk__set_request_flags(&request, pcmk__request_sync); + } + + pcmk__xe_set(request.xml, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__xe_set(request.xml, PCMK__XA_CIB_CLIENTNAME, client->name); - CRM_LOG_ASSERT(client->user != NULL); - pcmk__update_acl_user(msg, PCMK__XA_CIB_USER, client->user); + CRM_LOG_ASSERT(client->user != NULL); + pcmk__update_acl_user(request.xml, PCMK__XA_CIB_USER, client->user); - based_process_request(msg, client); + based_process_request(request.xml, request.ipc_client); + pcmk__reset_request(&request); + } cleanup: pcmk__xml_free(msg); From e985f33e76baa6828794cabedd118a00b697548b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 24 Jul 2026 09:52:40 -0700 Subject: [PATCH 37/98] Refactor: libcrmcommon: Assert non-NULL args in pcmk__serve_execd_ipc() Should have been done as part of a6bf06d. Signed-off-by: Reid Wahl --- lib/common/ipc_server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c index ba566d11157..453831ee75a 100644 --- a/lib/common/ipc_server.c +++ b/lib/common/ipc_server.c @@ -1152,6 +1152,8 @@ void pcmk__serve_execd_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_execd), QB_IPC_SHM, cb); From fd3e120cd8ba7d960ebf71eae4207afb219680bd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:34:13 -0800 Subject: [PATCH 38/98] 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 02dc4984a02..d36547d76c0 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -38,11 +38,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) @@ -50,24 +51,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 4b207b716a58353204222159f5c5b91d8bb376ac Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:37:14 -0800 Subject: [PATCH 39/98] 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 e91ef7214ef..206915f75af 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -84,6 +84,54 @@ remote_auth_timeout_cb(void *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 @@ -409,36 +457,7 @@ cib_remote_msg(void *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 4782a20a153f0b8d5a8016dd5ebcb229d2ec1f56 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 18:57:14 -0800 Subject: [PATCH 40/98] 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 206915f75af..1c48000f95c 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -460,17 +460,30 @@ cib_remote_msg(void *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 3967d9b33b2a918bb7f61c1c416d9c90066b22b6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 19:22:38 -0800 Subject: [PATCH 41/98] 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 1c48000f95c..80d7efdcfa9 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -444,11 +444,11 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) } static int -cib_remote_msg(void *data) +based_remote_client_dispatch(void *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", @@ -489,45 +489,44 @@ cib_remote_msg(void *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; @@ -592,7 +591,7 @@ cib_remote_listen(void *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 82f39704d2e389209d947ef87a14cc7ac1424dec Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 19:56:12 -0800 Subject: [PATCH 42/98] 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 280cdaff8893acfab7af3f545f8a3a42ce7e4a79 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 20:18:32 -0800 Subject: [PATCH 43/98] 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 ad8e94ba67a..77413ae1218 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 5f1cb61c2cd92eabdcf308a413d2ba092535cdc0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:17:47 -0700 Subject: [PATCH 44/98] 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 80d7efdcfa9..e6fec966a16 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 @@ -132,61 +131,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 @@ -340,7 +284,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; @@ -378,7 +322,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 @@ -494,7 +444,7 @@ based_remote_client_dispatch(void *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 82f0a5acb7a..09bb505c86a 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 22ff3814a07..33088baf3af 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 04955d0a767b61565d9521f41a8d6cb15160b4b8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:30:09 -0700 Subject: [PATCH 45/98] 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 e6fec966a16..befe6fdc82c 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -192,14 +192,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; @@ -222,16 +223,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; } @@ -242,33 +244,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: @@ -276,9 +279,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 } @@ -328,7 +330,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 fa1f4ad9213e2410e84026c53aaf42f083c03a2a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:39:16 -0700 Subject: [PATCH 46/98] 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 befe6fdc82c..2c8faf184e0 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -286,43 +286,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 58cde449348d3206ac517da205803e10b024cd31 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:40:59 -0700 Subject: [PATCH 47/98] 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 2c8faf184e0..20bd07324cf 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -288,9 +288,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; @@ -303,18 +303,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"); @@ -329,7 +328,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 464584fa4c93e16d565a82aecaf559c83cd9493c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 2 May 2026 22:45:52 -0700 Subject: [PATCH 48/98] 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 20bd07324cf..e514d5f5085 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -293,35 +293,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); @@ -445,6 +444,7 @@ based_remote_client_dispatch(void *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 d2144c06911fd80723a3645c0386d78c0db30e1e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 20:34:42 -0800 Subject: [PATCH 49/98] 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 e514d5f5085..145b2e709dd 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -285,24 +285,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); @@ -310,7 +331,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); @@ -318,16 +339,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 @@ -438,38 +496,11 @@ based_remote_client_dispatch(void *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 df139523a83424a3062c5e28a492fcfdfacc2907 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 11 Jan 2026 22:57:52 -0800 Subject: [PATCH 50/98] 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 145b2e709dd..3e02e4eed44 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -131,6 +131,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 @@ -305,7 +364,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); @@ -313,32 +371,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 bfe6476a7a8f36faa1e6317dad625458ff066dc5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 00:19:49 -0800 Subject: [PATCH 51/98] 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 3e02e4eed44..852b1202d1a 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -438,7 +438,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 4101f9f4c04753be43cbbd01da42cb4b47198bed Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 02:17:45 -0800 Subject: [PATCH 52/98] 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 8c885cbfda0..7c769c14463 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -131,7 +131,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 852b1202d1a..e72542a55bb 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -397,6 +397,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 @@ -423,19 +427,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. @@ -464,49 +480,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 @@ -563,12 +582,9 @@ based_remote_client_dispatch(void *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 0b35edec01056e0afd58f1a1165c523ed3d2ef95 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 02:39:32 -0800 Subject: [PATCH 53/98] 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 afe9bc3fbdc..b96dd9a18b8 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -59,20 +59,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)) { @@ -80,21 +87,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; @@ -119,7 +148,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 23669c6e4358c37e0374e70490aa0857021bf2be Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 02:49:55 -0800 Subject: [PATCH 54/98] 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 f90ac2e66c8..13015c81c1f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -654,20 +654,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 @@ -675,14 +671,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; @@ -692,23 +693,18 @@ 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 { const char *src = based_cluster_node_name(); @@ -716,25 +712,27 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) src = "localhost"; } - pcmk__xe_set(request, PCMK__XA_SRC, src); + pcmk__xe_set(request->xml, PCMK__XA_SRC, src); 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. @@ -742,10 +740,10 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) 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 (!based_stand_alone() && !pcmk__is_set(operation->flags, cib__op_attr_local) @@ -753,7 +751,8 @@ based_process_request(xmlNode *request, const pcmk__client_t *client) || !pcmk__str_eq(host, based_cluster_node_name(), pcmk__str_casei|pcmk__str_null_matches))) { - forward_request(request); + forward_request(request->xml); + pcmk__reset_request(request); return pcmk_rc_ok; } @@ -761,14 +760,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"); @@ -779,8 +779,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; @@ -793,16 +793,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)) { @@ -811,17 +811,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 2a7bcbe0071..b822baa63d0 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -66,8 +66,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 7c769c14463..e961c7b46ea 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -219,8 +219,7 @@ based_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) CRM_LOG_ASSERT(client->user != NULL); pcmk__update_acl_user(request.xml, PCMK__XA_CIB_USER, client->user); - 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 e72542a55bb..3b11ede1dcb 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -523,8 +523,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 b96dd9a18b8..b6b532899fd 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -109,8 +109,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 cbfaa10adc823405fb4a287394ec437ff32f81f4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:02:42 -0800 Subject: [PATCH 55/98] 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 13015c81c1f..c329a0a1af7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -811,8 +811,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 381fa061cdd3ca2c7f202bc0b18ab1a5bcd3bc44 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:21:23 -0800 Subject: [PATCH 56/98] Refactor: based: forward_request() takes pcmk__request_t Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c329a0a1af7..178eb3f5189 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -466,11 +466,13 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, * \param[in] request CIB request to forward * * \note This does nothing if running in stand-alone mode. + * \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 (based_stand_alone()) { @@ -482,10 +484,10 @@ forward_request(xmlNode *request) } // Set PCMK__XA_CIB_DELEGATED_FROM only temporarily - pcmk__xe_set(request, PCMK__XA_CIB_DELEGATED_FROM, + pcmk__xe_set(request->xml, PCMK__XA_CIB_DELEGATED_FROM, based_cluster_node_name()); - pcmk__cluster_send_message(peer, pcmk_ipc_based, request); - pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM); + pcmk__cluster_send_message(peer, pcmk_ipc_based, request->xml); + pcmk__xe_remove_attr(request->xml, PCMK__XA_CIB_DELEGATED_FROM); } static int @@ -751,7 +753,7 @@ based_handle_request(pcmk__request_t *request) || !pcmk__str_eq(host, based_cluster_node_name(), pcmk__str_casei|pcmk__str_null_matches))) { - forward_request(request->xml); + forward_request(request); pcmk__reset_request(request); return pcmk_rc_ok; } From 365c1f74f12ce82918d5f600ba97e2e1ef6520b2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:27:43 -0800 Subject: [PATCH 57/98] 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 178eb3f5189..4269cecf13b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -347,15 +347,15 @@ 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 *local_node_name = based_cluster_node_name(); - 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, local_node_name, pcmk__str_casei); @@ -363,12 +363,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 @@ -378,9 +378,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; @@ -388,12 +390,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. * @@ -404,8 +408,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"), @@ -415,10 +420,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; @@ -434,26 +439,27 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, *local_notify = pcmk__str_eq(delegated, local_node_name, pcmk__str_casei); if (pcmk__str_eq(host, local_node_name, 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); *needs_reply = false; 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)) { *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; @@ -764,7 +770,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 00380ac4eee605bba3a49a4cec4b24ef9de3bb86 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 03:30:25 -0800 Subject: [PATCH 58/98] Refactor: based: based_perform_op_rw() takes pcmk__request_t Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4269cecf13b..9b6d2897e98 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -497,28 +497,24 @@ 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) { xmlNode *result_cib = based_cib; xmlNode *cib_diff = NULL; const char *feature_set = 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 @@ -531,7 +527,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); } @@ -546,12 +544,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); } feature_set = pcmk__xe_get(based_cib, PCMK_XA_CRM_FEATURE_SET); @@ -569,7 +568,7 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, && pcmk__str_eq(originator, based_cluster_node_name(), 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) { @@ -579,10 +578,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); @@ -804,7 +803,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 30379e502490f8cae5c054eec6834309185a6765 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 17:56:14 -0800 Subject: [PATCH 59/98] 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 9b6d2897e98..5b18b2daaac 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -726,7 +726,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 c09ee81698e85f390fddc7be4d55e22785535e5e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 22:22:45 -0800 Subject: [PATCH 60/98] 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 5b18b2daaac..743687c21ba 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -811,10 +811,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()) { @@ -828,6 +824,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 5041ad45278cdd3cef6bb6cf2ab9202712a319e4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 23:13:49 -0800 Subject: [PATCH 61/98] 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 3cef5ff3025..de5625a94a8 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 de8b9dc14d63366415c726496ed581d576dc968e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 12 Jan 2026 23:26:07 -0800 Subject: [PATCH 62/98] 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 8dcbf7f5d7f..70513576ab5 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -73,13 +73,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 1608018250f..799e6c8881b 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -23,7 +23,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 99ce4bd359f..750fa4c85ec 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 de5625a94a8..ddf2dcd7e7f 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 ca11c7f81dc80b70e55034551b2b736cba153e5b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 00:08:25 -0800 Subject: [PATCH 63/98] 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 743687c21ba..63328e583c0 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -313,28 +313,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)) { - pcmk__trace("Processing always-local %s op from client %s", op, - pcmk__client_name(client)); - - /* @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 node name. - */ - if (!pcmk__str_eq(host, based_cluster_node_name(), - pcmk__str_casei|pcmk__str_null_matches)) { - - 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)); @@ -752,7 +733,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, based_cluster_node_name(), pcmk__str_casei|pcmk__str_null_matches))) { @@ -766,7 +746,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 70513576ab5..b95f79e52ce 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -121,6 +121,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); @@ -141,6 +144,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); @@ -177,6 +183,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 750fa4c85ec..7993527d0d1 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 ddf2dcd7e7f..1b4d45ef827 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 89d16892403e98dab36b3bc66259b41fc37093ad Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 00:19:44 -0800 Subject: [PATCH 64/98] 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 b95f79e52ce..ec881fd6f69 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -27,27 +27,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 799e6c8881b..0c7d524e49e 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -16,7 +16,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 7993527d0d1..c51333cb726 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 1b4d45ef827..488dbbfb41e 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 a94158f6ac792fe72659f0804a30326daf19ce59 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 01:32:29 -0800 Subject: [PATCH 65/98] 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 c51333cb726..f64c1728481 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 488dbbfb41e..6c5cd1c7402 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 764ddeee5c6be0b617a74337a6ef025c3332028f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 00:34:09 -0800 Subject: [PATCH 66/98] 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 63328e583c0..019e70e549c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -520,7 +520,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 6c5cd1c7402..bd202824c2c 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 9542079b03fa1e9c1e3702c04017663e4eef2a6c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 01:48:45 -0800 Subject: [PATCH 67/98] 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 ec881fd6f69..e6076872bcf 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -224,7 +224,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 bd202824c2c..e6d6f39751a 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 5c2dc92c569..6ed78f217ac 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -1110,16 +1110,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; @@ -1185,7 +1182,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 @@ -1212,8 +1209,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; @@ -1259,7 +1257,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 935e087b387..9b05acc0758 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -331,7 +331,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 8f41495dd9be7c2de54f027e46366d5b6dae9621 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 02:08:10 -0800 Subject: [PATCH 68/98] 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 019e70e549c..31f3f1670c5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -519,17 +519,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 f64c1728481..a4363e5c710 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 e6d6f39751a..3717f8a497b 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 b4682942b3bb27124e58db79b4db0425ae5a2947 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 02:14:42 -0800 Subject: [PATCH 69/98] 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 b6b532899fd..881772a342a 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -81,7 +81,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 a4363e5c710..41d62764a16 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 3717f8a497b..8b7fa827ebe 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 4416e454773..18ff0cd70a9 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 7ecfffe555614d57102090e91c222c2c39eecb9f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 02:25:57 -0800 Subject: [PATCH 70/98] 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 31f3f1670c5..850f6db3a71 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -592,7 +592,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) { @@ -731,7 +731,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, based_cluster_node_name(), pcmk__str_casei|pcmk__str_null_matches))) { @@ -776,7 +776,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 { @@ -790,9 +790,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 41d62764a16..324064f761a 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 182d7a6e1d4..a203869a314 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 8b7fa827ebe..8cf53978753 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 e22003c980dc768ace3f02da1498b296dded15cf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 3 May 2026 00:00:48 -0700 Subject: [PATCH 71/98] 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 | 18 ++++++++--------- lib/cib/cib_utils.c | 37 +++++++++++++++++++++++------------ 11 files changed, 142 insertions(+), 122 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 86112f1c7e4..b112e43e9b4 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 9d992975ef2..ae1b3885b1e 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 a203869a314..0c8ecc3ce8b 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 6fa2d73bf62..7e47529992b 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 8cf53978753..fd00d86e965 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 79a8ee30e38..33b69291a23 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -9,9 +9,10 @@ #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 @@ -22,11 +23,10 @@ #include // xmlNode #include // QB_XS -#include // cib_t, cib_remote_new -#include // cib__create_op, cib__extend_transaction -#include // mainloop_fd_callbacks -#include // pcmk_rc_str, pcmk_rc_* -#include // PCMK_XA_*, +#include // cib_* +#include // cib__* +#include // mainloop_* +#include // pcmk_rc_*, pcmk_ok, etc. #include // CRM_OP_REGISTER, crm_system_name // GnuTLS handshake timeout in seconds @@ -34,8 +34,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 18ff0cd70a9..4894fd614fb 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 686fe3ce2e9065bead15c9246c0a6ec9716134c0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 13:23:14 -0800 Subject: [PATCH 72/98] 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 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 33b69291a23..6f8c9c55a43 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -195,10 +195,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 (!pcmk__is_set(call_options, cib_discard_reply)) { + if (output_data != NULL) { xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { From 19307436a7486f87b3386f77e256a52444a8b72e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 17:35:14 -0800 Subject: [PATCH 73/98] 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 b112e43e9b4..2cbc9e77d50 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -181,6 +181,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 ae1b3885b1e..cfe7d0948fa 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.1.0 +// @COMPAT Deprecated static int cib_client_sync(cib_t * cib, const char *section, int call_options) { @@ -640,12 +642,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; @@ -667,6 +665,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 68d851af49e7f460de6da46166454eaa35e4749e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 23:25:18 -0800 Subject: [PATCH 74/98] 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 850f6db3a71..7a61af84111 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -790,7 +790,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 102f3400326fd331726666fbcfd9ad18560c80ff Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 16 Jan 2026 00:09:49 -0800 Subject: [PATCH 75/98] 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 7a61af84111..e1366535f8e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -345,8 +345,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)) { @@ -657,6 +657,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); @@ -776,7 +777,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, based_cluster_node_name(), 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 703df506f39edb832fe7275b2c9483d7f299d37d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 16 Jan 2026 00:28:48 -0800 Subject: [PATCH 76/98] 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 e1366535f8e..c719db338cb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -759,6 +759,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 " @@ -771,10 +775,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 202035495f99ee4768a92edb21ac310c03b6a55e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 15 Jan 2026 23:53:57 -0800 Subject: [PATCH 77/98] 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 | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c719db338cb..e0e8390a7ed 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -327,7 +327,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) { @@ -346,7 +346,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)) { @@ -356,7 +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.) */ - return true; + return; } if (is_reply @@ -367,7 +367,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) @@ -408,12 +408,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; } } @@ -422,14 +424,15 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (pcmk__str_eq(host, local_node_name, 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); + *process = false; *needs_reply = false; - return false; + return; } if (is_reply || !pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { @@ -443,7 +446,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; } /*! @@ -747,10 +749,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 3b1b10cc629b1909714d478e04fdecb09d65d878 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 23:15:45 -0800 Subject: [PATCH 78/98] 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 | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index e0e8390a7ed..701b494ff47 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -345,7 +345,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; } @@ -356,6 +355,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; } @@ -365,7 +365,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; } @@ -406,7 +405,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; } @@ -414,7 +412,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; } } @@ -424,6 +421,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (pcmk__str_eq(host, local_node_name, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", request->op, originator); + *needs_reply = true; return; } @@ -431,12 +429,11 @@ 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 = false; + if (!is_reply && pcmk__str_eq(request->op, CRM_OP_PING, pcmk__str_none)) { + *needs_reply = true; } pcmk__trace("Processing %s request broadcast by %s call %s on %s " @@ -653,7 +650,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; @@ -725,8 +722,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)); @@ -744,7 +739,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 22bc1ba6d7c13b36bc24ec6735d12d96944af33f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 16 Jan 2026 20:44:08 -0800 Subject: [PATCH 79/98] 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 701b494ff47..3ea7ae9fa5d 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -763,7 +763,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); } @@ -786,7 +786,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 250d353744f463258f490eae30e226c87995ae48 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 17 Jan 2026 23:31:12 -0800 Subject: [PATCH 80/98] 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 e6076872bcf..660c02c1b7a 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -215,8 +215,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 " @@ -224,6 +222,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 0b2651a8a654c8251e70f54585afc5bf27ddd8ad Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 18 Jan 2026 15:34:15 -0800 Subject: [PATCH 81/98] 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 692df516dc1..61a8804190d 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -214,13 +214,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 c0fed8a2a19..f7c4a5dd0cb 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -907,10 +907,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) { @@ -1019,8 +1017,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; } /*! @@ -1030,19 +1026,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 a7738980c215fcfe3910bb26c9330055cc4b3174 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:21:35 -0700 Subject: [PATCH 82/98] Refactor: libcrmcluster: Pass GString to send_cpg_text() 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 f7c4a5dd0cb..950bb542bf0 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -909,7 +909,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; @@ -928,10 +928,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(); } @@ -970,18 +966,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; @@ -993,7 +989,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); @@ -1006,11 +1002,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); @@ -1035,6 +1032,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 04b8c91d1274af958c14f03c8f6dbd547dea24ad Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:35:03 -0700 Subject: [PATCH 83/98] 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 950bb542bf0..2af3f87d57d 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -944,7 +944,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 { @@ -960,7 +959,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 ed543fac3a327f3658927cf2c6fcdede4cde5d8f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:40:02 -0700 Subject: [PATCH 84/98] 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 2af3f87d57d..2129a45feb3 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -912,32 +912,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 150e55b57b5ca8d8d53b1cf09a239273921016f5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:47:23 -0700 Subject: [PATCH 85/98] 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 2129a45feb3..1a0ae0a3fba 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -913,7 +913,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; @@ -927,6 +927,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(); @@ -955,11 +956,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 4716671b49386ea7739e572953fa2ec499e50c84 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 12:52:20 -0700 Subject: [PATCH 86/98] 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 1a0ae0a3fba..e25eff63251 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 @@ -51,7 +51,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; @@ -914,7 +914,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; @@ -931,6 +931,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)); @@ -955,7 +956,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 4ef9c5a77ca0c01b4f8b6cc280d0514b4b8b1c76 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 13:10:52 -0700 Subject: [PATCH 87/98] 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 94a93d7d118..36efdbf1044 100644 --- a/include/crm/common/strings_internal.h +++ b/include/crm/common/strings_internal.h @@ -47,8 +47,8 @@ int pcmk__uint_from_hash(GHashTable *table, const char *key, unsigned int default_val, unsigned int *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 e25eff63251..f97cf7d04f6 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -971,7 +971,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 0a6feff7b19..432269a1a37 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 dff3891559136ea516980de65bf1674519aec959 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 13:52:41 -0700 Subject: [PATCH 88/98] 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 f97cf7d04f6..40acc1939e7 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -963,6 +963,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 ddc26e8e9e1d10d5045de303131a4e83badae78a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 13:57:17 -0700 Subject: [PATCH 89/98] 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 40acc1939e7..6c1e80c310d 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -79,7 +79,7 @@ typedef struct pcmk__cpg_msg_s pcmk__cpg_msg_t; static void crm_cs_flush(void *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; \ @@ -998,21 +998,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 e931b585058e6ceeb9c3fee970589f82dabc314d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 14:01:08 -0700 Subject: [PATCH 90/98] 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 6c1e80c310d..f18773902b5 100644 --- a/lib/cluster/cpg.c +++ b/lib/cluster/cpg.c @@ -902,15 +902,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; @@ -920,6 +920,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) { @@ -960,6 +961,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; @@ -1007,24 +1011,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 4b3f68d6dcbeed703fe81008cecf801dbce837ca Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 15:23:00 -0700 Subject: [PATCH 91/98] 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 fe9f9927964..21c8d108a6b 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 b2e94052f39..a9bbfe20d9d 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 @@ -211,8 +211,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); @@ -266,4 +266,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 71e2d153e11168f40719dde315f3161ec9044d28 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 15:29:26 -0700 Subject: [PATCH 92/98] 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 660c02c1b7a..c4a3679de24 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -270,9 +270,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); } @@ -353,9 +352,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 d51be7a1018..f6e2740e3bf 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -115,9 +115,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 523f35b40ff..4690d31f4d6 100644 --- a/daemons/controld/controld_messages.c +++ b/daemons/controld/controld_messages.c @@ -1129,18 +1129,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 befce6d4ff1..4d07fe5e57c 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 85512e27fae..9333f7a65bb 100644 --- a/include/crm/cluster/internal.h +++ b/include/crm/cluster/internal.h @@ -248,7 +248,7 @@ void pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, gboolean), void (*destroy)(void *)); -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 61a8804190d..ef1a71c0bcb 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -203,23 +203,20 @@ pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(void *)) * \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 8d90e64e1660f9b139aa579ef0278f736d61c434 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 19 Jan 2026 15:32:50 -0700 Subject: [PATCH 93/98] 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 c4a3679de24..0aafacfa4a1 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -199,9 +199,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; @@ -228,22 +225,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; } @@ -260,19 +247,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 147902f1b9d3f601f4015164a72d75bb4b5d9c5d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 20 Jan 2026 13:56:01 -0700 Subject: [PATCH 94/98] 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 | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3ea7ae9fa5d..2d7f4d9861e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -312,21 +312,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,14 +420,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 = true; } - - 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")); } /*! @@ -733,6 +710,8 @@ based_handle_request(pcmk__request_t *request) || !pcmk__str_eq(host, based_cluster_node_name(), 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; @@ -741,8 +720,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 add47eb43f9c195ed91b980d3594ed77cdccf105 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 20 Jan 2026 14:03:31 -0700 Subject: [PATCH 95/98] 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 2d7f4d9861e..0121f27c9a9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -731,6 +731,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; } @@ -740,10 +745,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; } @@ -763,12 +765,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 53529067941594b7d1d803b4e14524ead9ed76e4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 20 Jan 2026 15:12:10 -0700 Subject: [PATCH 96/98] 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 324064f761a..d2138a667cf 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 6a8fe92e42f63436b13dc2e96e5cbe42124c8e14 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 13 Jan 2026 03:06:49 -0800 Subject: [PATCH 97/98] wip Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 19 ++++++++++++++++--- daemons/based/based_messages.c | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0121f27c9a9..3f76daec3c8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -317,6 +317,15 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, bool *local_notify, bool *needs_reply, bool *process) { const char *local_node_name = based_cluster_node_name(); + + /* 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); @@ -361,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 @@ -406,7 +420,7 @@ parse_peer_options(const cib__operation_t *operation, pcmk__request_t *request, if (pcmk__str_eq(host, local_node_name, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", request->op, originator); - *needs_reply = true; + *needs_reply = can_reply; return; } @@ -418,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; } } @@ -726,7 +740,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 0aafacfa4a1..2d4caa07b92 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -261,6 +261,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 eee26a6444d41fac112431a3baa32981be38484f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 14 Jan 2026 01:52:16 -0800 Subject: [PATCH 98/98] 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 3f76daec3c8..b9e77a5ba0e 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, @@ -739,16 +737,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; }