From 12b1f47a8a09a701fe3aca86b4f32dd44d109ef3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 11:32:48 -0800 Subject: [PATCH 001/350] Refactor: libcrmcommon: Include action_relation_internal.h at right spot Signed-off-by: Reid Wahl --- include/crm/common/internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h index 874cd162a89..25f9731870a 100644 --- a/include/crm/common/internal.h +++ b/include/crm/common/internal.h @@ -12,8 +12,8 @@ #define PCMK__INCLUDED_CRM_COMMON_INTERNAL_H -#include #include +#include #include #include #include From cc531b467d5eed23d4a30994b697aaefe7bb6044 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 14:13:05 -0800 Subject: [PATCH 002/350] Refactor: based: Drop cib.pam file It was added in 2007 by commit f3ec7ec. No motivation was given, the file doesn't get installed, and it appears that nothing has ever used it. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 4 +--- daemons/based/cib.pam | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 daemons/based/cib.pam diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 60d19eeb315..0cc82088180 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2004-2024 the Pacemaker project contributors +# Copyright 2004-2025 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -10,8 +10,6 @@ include $(top_srcdir)/mk/common.mk include $(top_srcdir)/mk/man.mk -EXTRA_DIST = cib.pam - halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based diff --git a/daemons/based/cib.pam b/daemons/based/cib.pam deleted file mode 100644 index 5d0f6553acc..00000000000 --- a/daemons/based/cib.pam +++ /dev/null @@ -1,6 +0,0 @@ -# login: auth account password session -# may require permission to read /etc/shadow -auth include common-auth -account include common-account -password include common-password -session include common-session From 606b4fba5923706155443427e22afd2ffce49460 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 14:44:37 -0800 Subject: [PATCH 003/350] Refactor: based: New based_io_init() and make cib_writer static Also rename cib_writer to write_trigger and create a new based_io.h. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 5 +++-- daemons/based/based_io.c | 22 ++++++++++++++++++---- daemons/based/based_io.h | 15 +++++++++++++++ daemons/based/pacemaker-based.c | 4 ++-- daemons/based/pacemaker-based.h | 4 ++-- 5 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 daemons/based/based_io.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 0cc82088180..4fd4a64fe97 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -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. # @@ -14,7 +14,8 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based -noinst_HEADERS = based_transaction.h \ +noinst_HEADERS = based_io.h \ + based_transaction.h \ pacemaker-based.h pacemaker_based_CFLAGS = $(CFLAGS_HARDENED_EXE) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 981db4ffd7b..c4fd5ad0a2e 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.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. * @@ -36,10 +36,24 @@ #include -crm_trigger_t *cib_writer = NULL; +static crm_trigger_t *write_trigger = NULL; int write_cib_contents(gpointer p); +/*! + * \internal + * \brief Initialize data structures for \c pacemaker-based I/O + * + * Currently there is only one, but this may be expanded later, and the name + * clarifies its purpose in the caller. + */ +void +based_io_init(void) +{ + write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, + NULL); +} + static void cib_rename(const char *old) { @@ -306,7 +320,7 @@ activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) pcmk__xml_free(saved_cib); if (cib_writes_enabled && cib_status == pcmk_rc_ok && to_disk) { pcmk__debug("Triggering CIB write for %s op", op); - mainloop_set_trigger(cib_writer); + mainloop_set_trigger(write_trigger); } return pcmk_ok; } @@ -343,7 +357,7 @@ cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int ((core != 0)? " and dumped core" : "")); } - mainloop_trigger_complete(cib_writer); + mainloop_trigger_complete(write_trigger); } int diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h new file mode 100644 index 00000000000..c4cd524980c --- /dev/null +++ b/daemons/based/based_io.h @@ -0,0 +1,15 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_IO__H +#define BASED_IO__H + +void based_io_init(void); + +#endif // BASED_IO__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 41712b43af7..317456d1ea4 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.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. * @@ -208,7 +208,7 @@ main(int argc, char **argv) mainloop_add_signal(SIGTERM, cib_shutdown); mainloop_add_signal(SIGPIPE, cib_enable_writes); - cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, NULL); + based_io_init(); if ((g_strv_length(processed_args) >= 2) && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) { diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 8ef9396f123..5f2ad5b86ee 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.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. * @@ -30,6 +30,7 @@ #include #include +#include "based_io.h" #include "based_transaction.h" #include @@ -48,7 +49,6 @@ enum cib_client_flags { extern bool based_is_primary; extern GHashTable *config_hash; extern xmlNode *the_cib; -extern crm_trigger_t *cib_writer; extern gboolean cib_writes_enabled; extern GMainLoop *mainloop; From 2342283bee4d1372950642cabbcc9e4d484512f7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 14:47:47 -0800 Subject: [PATCH 004/350] Refactor: based: Make write_cib_contents() static No code changes aside from moving two function definitions to a position above based_io_init() and dropping an extern declaration. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 212 ++++++++++++++++---------------- daemons/based/pacemaker-based.c | 1 - 2 files changed, 105 insertions(+), 108 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index c4fd5ad0a2e..7a5a14bdf32 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -38,7 +38,111 @@ static crm_trigger_t *write_trigger = NULL; -int write_cib_contents(gpointer p); +static void +cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode) +{ + const char *errmsg = "Could not write CIB to disk"; + + if ((exitcode != 0) && cib_writes_enabled) { + cib_writes_enabled = FALSE; + errmsg = "Disabling CIB disk writes after failure"; + } + + if ((signo == 0) && (exitcode == 0)) { + pcmk__trace("Disk write [%d] succeeded", (int) pid); + + } else if (signo == 0) { + pcmk__err("%s: process %d exited %d", errmsg, (int) pid, exitcode); + + } else { + pcmk__err("%s: process %d terminated with signal %d (%s)%s", + errmsg, (int) pid, signo, strsignal(signo), + ((core != 0)? " and dumped core" : "")); + } + + mainloop_trigger_complete(write_trigger); +} + +static int +write_cib_contents(gpointer p) +{ + int exit_rc = pcmk_ok; + xmlNode *cib_local = NULL; + + /* Make a copy of the CIB to write (possibly in a forked child) */ + if (p) { + /* Synchronous write out */ + cib_local = pcmk__xml_copy(NULL, p); + + } else { + int pid = 0; + int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); + + /* Turn it off before the fork() to avoid: + * - 2 processes writing to the same shared mem + * - the child needing to disable it + * (which would close it from underneath the parent) + * This way, the shared mem files are already closed + */ + qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); + + pid = fork(); + if (pid < 0) { + pcmk__err("Disabling disk writes after fork failure: %s", + pcmk_rc_str(errno)); + cib_writes_enabled = FALSE; + return FALSE; + } + + if (pid) { + /* Parent */ + mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); + if (bb_state == QB_LOG_STATE_ENABLED) { + /* Re-enable now that it it safe */ + qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); + } + + return -1; /* -1 means 'still work to do' */ + } + + /* Asynchronous write-out after a fork() */ + + /* In theory, we can scribble on the_cib here and not affect the parent, + * but let's be safe anyway. + */ + cib_local = pcmk__xml_copy(NULL, the_cib); + } + + /* Write the CIB */ + exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml"); + + /* A nonzero exit code will cause further writes to be disabled */ + pcmk__xml_free(cib_local); + if (p == NULL) { + crm_exit_t exit_code = CRM_EX_OK; + + switch (exit_rc) { + case pcmk_ok: + exit_code = CRM_EX_OK; + break; + case pcmk_err_cib_modified: + exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest + break; + case pcmk_err_cib_backup: // Existing CIB couldn't be backed up + case pcmk_err_cib_save: // New CIB couldn't be saved + exit_code = CRM_EX_CANTCREAT; + break; + default: + exit_code = CRM_EX_ERROR; + break; + } + + /* Use _exit() because exit() could affect the parent adversely */ + pcmk_common_cleanup(); + _exit(exit_code); + } + return exit_rc; +} /*! * \internal @@ -334,109 +438,3 @@ activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) } return -ENODATA; } - -static void -cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode) -{ - const char *errmsg = "Could not write CIB to disk"; - - if ((exitcode != 0) && cib_writes_enabled) { - cib_writes_enabled = FALSE; - errmsg = "Disabling CIB disk writes after failure"; - } - - if ((signo == 0) && (exitcode == 0)) { - pcmk__trace("Disk write [%d] succeeded", (int) pid); - - } else if (signo == 0) { - pcmk__err("%s: process %d exited %d", errmsg, (int) pid, exitcode); - - } else { - pcmk__err("%s: process %d terminated with signal %d (%s)%s", - errmsg, (int) pid, signo, strsignal(signo), - ((core != 0)? " and dumped core" : "")); - } - - mainloop_trigger_complete(write_trigger); -} - -int -write_cib_contents(gpointer p) -{ - int exit_rc = pcmk_ok; - xmlNode *cib_local = NULL; - - /* Make a copy of the CIB to write (possibly in a forked child) */ - if (p) { - /* Synchronous write out */ - cib_local = pcmk__xml_copy(NULL, p); - - } else { - int pid = 0; - int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); - - /* Turn it off before the fork() to avoid: - * - 2 processes writing to the same shared mem - * - the child needing to disable it - * (which would close it from underneath the parent) - * This way, the shared mem files are already closed - */ - qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); - - pid = fork(); - if (pid < 0) { - pcmk__err("Disabling disk writes after fork failure: %s", - pcmk_rc_str(errno)); - cib_writes_enabled = FALSE; - return FALSE; - } - - if (pid) { - /* Parent */ - mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); - if (bb_state == QB_LOG_STATE_ENABLED) { - /* Re-enable now that it it safe */ - qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); - } - - return -1; /* -1 means 'still work to do' */ - } - - /* Asynchronous write-out after a fork() */ - - /* In theory, we can scribble on the_cib here and not affect the parent, - * but let's be safe anyway. - */ - cib_local = pcmk__xml_copy(NULL, the_cib); - } - - /* Write the CIB */ - exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml"); - - /* A nonzero exit code will cause further writes to be disabled */ - pcmk__xml_free(cib_local); - if (p == NULL) { - crm_exit_t exit_code = CRM_EX_OK; - - switch (exit_rc) { - case pcmk_ok: - exit_code = CRM_EX_OK; - break; - case pcmk_err_cib_modified: - exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest - break; - case pcmk_err_cib_backup: // Existing CIB couldn't be backed up - case pcmk_err_cib_save: // New CIB couldn't be saved - exit_code = CRM_EX_CANTCREAT; - break; - default: - exit_code = CRM_EX_ERROR; - break; - } - - /* Use _exit() because exit() could affect the parent adversely */ - pcmk_common_cleanup(); - _exit(exit_code); - } - return exit_rc; -} diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 317456d1ea4..b614ecfe1d8 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -50,7 +50,6 @@ GHashTable *config_hash = NULL; static void cib_init(void); void cib_shutdown(int nsig); static bool startCib(const char *filename); -extern int write_cib_contents(gpointer p); static crm_exit_t exit_code = CRM_EX_OK; From 20c3da1aea5c45ec4b0a3e49c0563c9656765dad Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 15:19:01 -0800 Subject: [PATCH 005/350] Refactor: based: Unindent code in write_cib_contents() No other code changes, except moving a variable declaration. The p argument is always NULL, though that was not the case when this function was created. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 109 ++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 7a5a14bdf32..2f0284d20f4 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -68,79 +68,72 @@ write_cib_contents(gpointer p) { int exit_rc = pcmk_ok; xmlNode *cib_local = NULL; + crm_exit_t exit_code = CRM_EX_OK; /* Make a copy of the CIB to write (possibly in a forked child) */ - if (p) { - /* Synchronous write out */ - cib_local = pcmk__xml_copy(NULL, p); + int pid = 0; + int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); + + /* Turn it off before the fork() to avoid: + * - 2 processes writing to the same shared mem + * - the child needing to disable it + * (which would close it from underneath the parent) + * This way, the shared mem files are already closed + */ + qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); + + pid = fork(); + if (pid < 0) { + pcmk__err("Disabling disk writes after fork failure: %s", + pcmk_rc_str(errno)); + cib_writes_enabled = FALSE; + return FALSE; + } - } else { - int pid = 0; - int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); - - /* Turn it off before the fork() to avoid: - * - 2 processes writing to the same shared mem - * - the child needing to disable it - * (which would close it from underneath the parent) - * This way, the shared mem files are already closed - */ - qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); - - pid = fork(); - if (pid < 0) { - pcmk__err("Disabling disk writes after fork failure: %s", - pcmk_rc_str(errno)); - cib_writes_enabled = FALSE; - return FALSE; + if (pid) { + /* Parent */ + mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); + if (bb_state == QB_LOG_STATE_ENABLED) { + /* Re-enable now that it it safe */ + qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); } - if (pid) { - /* Parent */ - mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); - if (bb_state == QB_LOG_STATE_ENABLED) { - /* Re-enable now that it it safe */ - qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); - } - - return -1; /* -1 means 'still work to do' */ - } + return -1; /* -1 means 'still work to do' */ + } - /* Asynchronous write-out after a fork() */ + /* Asynchronous write-out after a fork() */ - /* In theory, we can scribble on the_cib here and not affect the parent, - * but let's be safe anyway. - */ - cib_local = pcmk__xml_copy(NULL, the_cib); - } + /* In theory, we can scribble on the_cib here and not affect the parent, + * but let's be safe anyway. + */ + cib_local = pcmk__xml_copy(NULL, the_cib); /* Write the CIB */ exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml"); /* A nonzero exit code will cause further writes to be disabled */ pcmk__xml_free(cib_local); - if (p == NULL) { - crm_exit_t exit_code = CRM_EX_OK; - - switch (exit_rc) { - case pcmk_ok: - exit_code = CRM_EX_OK; - break; - case pcmk_err_cib_modified: - exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest - break; - case pcmk_err_cib_backup: // Existing CIB couldn't be backed up - case pcmk_err_cib_save: // New CIB couldn't be saved - exit_code = CRM_EX_CANTCREAT; - break; - default: - exit_code = CRM_EX_ERROR; - break; - } - /* Use _exit() because exit() could affect the parent adversely */ - pcmk_common_cleanup(); - _exit(exit_code); + switch (exit_rc) { + case pcmk_ok: + exit_code = CRM_EX_OK; + break; + case pcmk_err_cib_modified: + exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest + break; + case pcmk_err_cib_backup: // Existing CIB couldn't be backed up + case pcmk_err_cib_save: // New CIB couldn't be saved + exit_code = CRM_EX_CANTCREAT; + break; + default: + exit_code = CRM_EX_ERROR; + break; } + + /* Use _exit() because exit() could affect the parent adversely */ + pcmk_common_cleanup(); + _exit(exit_code); + return exit_rc; } From 4d24fda28d1af5a1be4563b8fcd62d88456811fa Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 15:25:00 -0800 Subject: [PATCH 006/350] Refactor: based: Use G_SOURCE_{CONTINUE,REMOVE} in write_cib_contents() Also, drop the return statement after calling _exit(), and rename exit_rc to rc. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 2f0284d20f4..bd553195df4 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -66,7 +66,7 @@ cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int static int write_cib_contents(gpointer p) { - int exit_rc = pcmk_ok; + int rc = pcmk_ok; xmlNode *cib_local = NULL; crm_exit_t exit_code = CRM_EX_OK; @@ -87,7 +87,7 @@ write_cib_contents(gpointer p) pcmk__err("Disabling disk writes after fork failure: %s", pcmk_rc_str(errno)); cib_writes_enabled = FALSE; - return FALSE; + return G_SOURCE_REMOVE; } if (pid) { @@ -98,7 +98,7 @@ write_cib_contents(gpointer p) qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); } - return -1; /* -1 means 'still work to do' */ + return G_SOURCE_CONTINUE; } /* Asynchronous write-out after a fork() */ @@ -109,12 +109,12 @@ write_cib_contents(gpointer p) cib_local = pcmk__xml_copy(NULL, the_cib); /* Write the CIB */ - exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml"); + rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml"); /* A nonzero exit code will cause further writes to be disabled */ pcmk__xml_free(cib_local); - switch (exit_rc) { + switch (rc) { case pcmk_ok: exit_code = CRM_EX_OK; break; @@ -133,8 +133,6 @@ write_cib_contents(gpointer p) /* Use _exit() because exit() could affect the parent adversely */ pcmk_common_cleanup(); _exit(exit_code); - - return exit_rc; } /*! From e06bdc73eaaff4e408384d8c747cb2d5d89f2c8f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 15:34:31 -0800 Subject: [PATCH 007/350] Refactor: based: Drop exit_code variable in write_cib_contents() And drop comments that seem redundant. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index bd553195df4..c17722102e6 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -68,7 +68,6 @@ write_cib_contents(gpointer p) { int rc = pcmk_ok; xmlNode *cib_local = NULL; - crm_exit_t exit_code = CRM_EX_OK; /* Make a copy of the CIB to write (possibly in a forked child) */ int pid = 0; @@ -114,25 +113,23 @@ write_cib_contents(gpointer p) /* A nonzero exit code will cause further writes to be disabled */ pcmk__xml_free(cib_local); + pcmk_common_cleanup(); + + /* Use _exit() because exit() could affect the parent adversely */ switch (rc) { case pcmk_ok: - exit_code = CRM_EX_OK; - break; + _exit(CRM_EX_OK); + case pcmk_err_cib_modified: - exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest - break; - case pcmk_err_cib_backup: // Existing CIB couldn't be backed up - case pcmk_err_cib_save: // New CIB couldn't be saved - exit_code = CRM_EX_CANTCREAT; - break; + _exit(CRM_EX_DIGEST); + + case pcmk_err_cib_backup: + case pcmk_err_cib_save: + _exit(CRM_EX_CANTCREAT); + default: - exit_code = CRM_EX_ERROR; - break; + _exit(CRM_EX_ERROR); } - - /* Use _exit() because exit() could affect the parent adversely */ - pcmk_common_cleanup(); - _exit(exit_code); } /*! From dc554e48d5169532ba03b7188509eae792f474d6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 15:41:08 -0800 Subject: [PATCH 008/350] Refactor: based: Don't copy the CIB in write_cib_contents() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index c17722102e6..111770c7ce3 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -67,9 +67,6 @@ static int write_cib_contents(gpointer p) { int rc = pcmk_ok; - xmlNode *cib_local = NULL; - - /* Make a copy of the CIB to write (possibly in a forked child) */ int pid = 0; int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); @@ -100,22 +97,25 @@ write_cib_contents(gpointer p) return G_SOURCE_CONTINUE; } - /* Asynchronous write-out after a fork() */ - - /* In theory, we can scribble on the_cib here and not affect the parent, - * but let's be safe anyway. + /* Write the CIB. Note that this modifies the_cib, but this child is about + * to exit. The parent's copy of the_cib won't be affected. */ - cib_local = pcmk__xml_copy(NULL, the_cib); - - /* Write the CIB */ - rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml"); - - /* A nonzero exit code will cause further writes to be disabled */ - pcmk__xml_free(cib_local); + rc = cib_file_write_with_digest(the_cib, cib_root, "cib.xml"); pcmk_common_cleanup(); - /* Use _exit() because exit() could affect the parent adversely */ + /* A nonzero exit code will cause further writes to be disabled. Use _exit() + * because exit() could affect the parent adversely. + * + * @TODO Investigate whether _exit() instead of exit() is really necessary. + * This goes back to commit 58cb43dc, which states that exit() may close + * things it shoudn't close. There is no explanation of what these things + * might be. The exit(2) man page states that exit() calls atexit/on_exit + * handlers and flushes open stdio streams. The exit(3) man page states that + * file created with tmpfile() are removed. But neither Pacemaker nor libqb + * uses atexit or on_exit, and it's not clear why we'd be worried about + * stdio streams. + */ switch (rc) { case pcmk_ok: _exit(CRM_EX_OK); From 972d7f72cbe301a91c65a5df4d9e480d6fa59bed Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 15:42:33 -0800 Subject: [PATCH 009/350] Refactor: based: Use pid_t in write_cib_contents() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 111770c7ce3..c2808f26db7 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -67,7 +67,7 @@ static int write_cib_contents(gpointer p) { int rc = pcmk_ok; - int pid = 0; + pid_t pid = 0; int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); /* Turn it off before the fork() to avoid: @@ -86,7 +86,7 @@ write_cib_contents(gpointer p) return G_SOURCE_REMOVE; } - if (pid) { + if (pid > 0) { /* Parent */ mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); if (bb_state == QB_LOG_STATE_ENABLED) { From 4aa30752927706b4b36d35c74a28d8bdeca245cb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 16:20:07 -0800 Subject: [PATCH 010/350] Refactor: based: Clarify blackbox state save-and-restore somewhat But not much. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index c2808f26db7..b6cac69964b 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -68,13 +68,13 @@ write_cib_contents(gpointer p) { int rc = pcmk_ok; pid_t pid = 0; - int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); + int blackbox_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); - /* Turn it off before the fork() to avoid: - * - 2 processes writing to the same shared mem - * - the child needing to disable it - * (which would close it from underneath the parent) - * This way, the shared mem files are already closed + /* 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. + * + * @TODO How? What is meant by this last sentence? */ qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); @@ -87,10 +87,10 @@ write_cib_contents(gpointer p) } if (pid > 0) { - /* Parent */ + // Parent mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); - if (bb_state == QB_LOG_STATE_ENABLED) { - /* Re-enable now that it it safe */ + + if (blackbox_state == QB_LOG_STATE_ENABLED) { qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); } From ea3876d96122271d8f5c9976e51dd2bf6a3557e6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 16:23:07 -0800 Subject: [PATCH 011/350] Refactor: based: Convert to standard return code in write_cib_contents() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index b6cac69964b..1b37e519f12 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -66,7 +66,7 @@ cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int static int write_cib_contents(gpointer p) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; pid_t pid = 0; int blackbox_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0); @@ -101,6 +101,7 @@ write_cib_contents(gpointer p) * to exit. The parent's copy of the_cib won't be affected. */ rc = cib_file_write_with_digest(the_cib, cib_root, "cib.xml"); + rc = pcmk_legacy2rc(rc); pcmk_common_cleanup(); @@ -117,14 +118,14 @@ write_cib_contents(gpointer p) * stdio streams. */ switch (rc) { - case pcmk_ok: + case pcmk_rc_ok: _exit(CRM_EX_OK); - case pcmk_err_cib_modified: + case pcmk_rc_cib_modified: _exit(CRM_EX_DIGEST); - case pcmk_err_cib_backup: - case pcmk_err_cib_save: + case pcmk_rc_cib_backup: + case pcmk_rc_cib_save: _exit(CRM_EX_CANTCREAT); default: From 6fc8d8da7511736231188b6c6214bff752c5a79f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 16:36:18 -0800 Subject: [PATCH 012/350] Refactor: based: Rename write_cib_contents() to write_cib_async() And add Doxygen, and call strerror(errno) since we know it's a system errno and we don't have to worry about a Pacemaker return code. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 1b37e519f12..8c677b005ee 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -63,8 +63,18 @@ cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int mainloop_trigger_complete(write_trigger); } +/*! + * \internal + * \brief Write the CIB to disk in a forked child + * + * This avoids blocking in the parent. The child writes synchronously. The + * parent tracks the child via the mainloop and runs a callback when the child + * exits. + * + * \param[in] user_data Ignored + */ static int -write_cib_contents(gpointer p) +write_cib_async(gpointer user_data) { int rc = pcmk_rc_ok; pid_t pid = 0; @@ -81,7 +91,7 @@ write_cib_contents(gpointer p) pid = fork(); if (pid < 0) { pcmk__err("Disabling disk writes after fork failure: %s", - pcmk_rc_str(errno)); + strerror(errno)); cib_writes_enabled = FALSE; return G_SOURCE_REMOVE; } @@ -143,8 +153,7 @@ write_cib_contents(gpointer p) void based_io_init(void) { - write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, - NULL); + write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL); } static void From 768fd51e5e2a8a939b78c050b3dac1b7b058f3b4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 16:59:34 -0800 Subject: [PATCH 013/350] Refactor: libcrmcommon: Don't pass pid in mainloop child callback It's accessible via the mainloop_child_t object, now that we're exposing its definition internally. We could use the existing mainloop_child_pid() function. However, nothing is using that yet, so I would rather deprecate it in the future. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 8 ++++---- daemons/execd/remoted_schemas.c | 7 ++++--- daemons/pacemakerd/pcmkd_subdaemons.c | 19 ++++++++++--------- include/crm/common/mainloop.h | 6 ++++-- include/crm/common/mainloop_internal.h | 19 +++++++++++++++++-- lib/common/mainloop.c | 21 +++++---------------- lib/services/services_linux.c | 6 ++---- 7 files changed, 46 insertions(+), 40 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 8c677b005ee..04fe79c25e8 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -39,7 +39,7 @@ static crm_trigger_t *write_trigger = NULL; static void -cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode) +cib_diskwrite_complete(mainloop_child_t *p, int core, int signo, int exitcode) { const char *errmsg = "Could not write CIB to disk"; @@ -49,14 +49,14 @@ cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int } if ((signo == 0) && (exitcode == 0)) { - pcmk__trace("Disk write [%d] succeeded", (int) pid); + pcmk__trace("Disk write [%d] succeeded", (int) p->pid); } else if (signo == 0) { - pcmk__err("%s: process %d exited %d", errmsg, (int) pid, exitcode); + pcmk__err("%s: process %d exited %d", errmsg, (int) p->pid, exitcode); } else { pcmk__err("%s: process %d terminated with signal %d (%s)%s", - errmsg, (int) pid, signo, strsignal(signo), + errmsg, (int) p->pid, signo, strsignal(signo), ((core != 0)? " and dumped core" : "")); } diff --git a/daemons/execd/remoted_schemas.c b/daemons/execd/remoted_schemas.c index 40f4fa284b3..da4fece9267 100644 --- a/daemons/execd/remoted_schemas.c +++ b/daemons/execd/remoted_schemas.c @@ -201,7 +201,8 @@ get_schema_files(void) * saving them to disk. */ static void -get_schema_files_complete(mainloop_child_t *p, pid_t pid, int core, int signo, int exitcode) +get_schema_files_complete(mainloop_child_t *p, int core, int signo, + int exitcode) { const char *errmsg = "Could not load additional schema files"; @@ -217,12 +218,12 @@ get_schema_files_complete(mainloop_child_t *p, pid_t pid, int core, int signo, i } else { if (signo == 0) { - pcmk__err("%s: process %lld exited %d", errmsg, (long long) pid, + pcmk__err("%s: process %lld exited %d", errmsg, (long long) p->pid, exitcode); } else { pcmk__err("%s: process %lld terminated with signal %d (%s)%s", - errmsg, (long long) pid, signo, strsignal(signo), + errmsg, (long long) p->pid, signo, strsignal(signo), ((core != 0)? " and dumped core" : "")); } diff --git a/daemons/pacemakerd/pcmkd_subdaemons.c b/daemons/pacemakerd/pcmkd_subdaemons.c index 7e785159cfc..954b922faf4 100644 --- a/daemons/pacemakerd/pcmkd_subdaemons.c +++ b/daemons/pacemakerd/pcmkd_subdaemons.c @@ -104,7 +104,8 @@ static bool fatal_error = false; static int child_liveness(pcmkd_child_t *child); static gboolean escalate_shutdown(gpointer data); static int start_child(pcmkd_child_t *child); -static void pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode); +static void pcmk_child_exit(mainloop_child_t *p, int core, int signo, + int exitcode); static void pcmk_process_exit(pcmkd_child_t *child); static gboolean pcmk_shutdown_worker(gpointer user_data); static void stop_child(pcmkd_child_t *child, int signal); @@ -253,7 +254,7 @@ escalate_shutdown(gpointer data) } static void -pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode) +pcmk_child_exit(mainloop_child_t *p, int core, int signo, int exitcode) { pcmkd_child_t *child = mainloop_child_userdata(p); const char *name = mainloop_child_name(p); @@ -262,7 +263,7 @@ pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitco // cts-lab looks for this message do_crm_log(((signo == SIGKILL)? LOG_WARNING : LOG_ERR), "%s[%d] terminated with signal %d (%s)%s", - name, pid, signo, strsignal(signo), + name, p->pid, signo, strsignal(signo), (core? " and dumped core" : "")); pcmk_process_exit(child); return; @@ -270,13 +271,13 @@ pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitco switch(exitcode) { case CRM_EX_OK: - pcmk__info("%s[%d] exited with status %d (%s)", name, pid, exitcode, - crm_exit_str(exitcode)); + pcmk__info("%s[%d] exited with status %d (%s)", name, p->pid, + exitcode, crm_exit_str(exitcode)); break; case CRM_EX_FATAL: pcmk__warn("Shutting cluster down because %s[%d] had fatal failure", - name, pid); + name, p->pid); child->flags &= ~child_respawn; fatal_error = true; pcmk_shutdown(SIGTERM); @@ -289,7 +290,7 @@ pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitco child->flags &= ~child_respawn; fatal_error = true; msg = pcmk__assert_asprintf("Subdaemon %s[%d] requested panic", - name, pid); + name, p->pid); pcmk__panic(msg); // Should never get here @@ -300,8 +301,8 @@ pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitco default: // cts-lab looks for this message - pcmk__err("%s[%d] exited with status %d (%s)", name, pid, exitcode, - crm_exit_str(exitcode)); + pcmk__err("%s[%d] exited with status %d (%s)", name, p->pid, + exitcode, crm_exit_str(exitcode)); break; } diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h index 30bde33111a..a680c44bd04 100644 --- a/include/crm/common/mainloop.h +++ b/include/crm/common/mainloop.h @@ -171,14 +171,16 @@ void mainloop_child_add(pid_t pid, int timeout, const char *desc, void *userdata, - void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)); + void (*callback)(mainloop_child_t *p, int core, + int signo, int exitcode)); void mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *userdata, enum mainloop_child_flags, - void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)); + void (*callback)(mainloop_child_t *p, int core, + int signo, int exitcode)); void *mainloop_child_userdata(mainloop_child_t * child); int mainloop_child_timeout(mainloop_child_t * child); diff --git a/include/crm/common/mainloop_internal.h b/include/crm/common/mainloop_internal.h index 3b5d07ec1d7..05c3f45672e 100644 --- a/include/crm/common/mainloop_internal.h +++ b/include/crm/common/mainloop_internal.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2026 the Pacemaker project contributors + * Copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,7 +14,9 @@ #ifndef PCMK__CRM_COMMON_MAINLOOP_INTERNAL__H #define PCMK__CRM_COMMON_MAINLOOP_INTERNAL__H -#include // guint +#include // pid_t + +#include // gboolean, guint #include // crm_ipc_t #include // ipc_client_callbacks, mainloop_* @@ -23,6 +25,19 @@ extern "C" { #endif +struct mainloop_child_s { + pid_t pid; + char *desc; + unsigned timerid; + gboolean timeout; + void *privatedata; + + enum mainloop_child_flags flags; + + /* Called when a process dies */ + void (*callback)(mainloop_child_t *p, int core, int signo, int exitcode); +}; + int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, const struct ipc_client_callbacks *callbacks, mainloop_io_t **source); diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index b38ed21dfb7..5652fe89bb7 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -23,19 +23,6 @@ #include -struct mainloop_child_s { - pid_t pid; - char *desc; - unsigned timerid; - gboolean timeout; - void *privatedata; - - enum mainloop_child_flags flags; - - /* Called when a process dies */ - void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode); -}; - struct trigger_s { GSource source; gboolean running; @@ -1172,7 +1159,7 @@ child_waitpid(mainloop_child_t *child, int flags) } if (callback_needed && child->callback) { - child->callback(child, child->pid, core, signo, exitcode); + child->callback(child, core, signo, exitcode); } return callback_needed; } @@ -1265,7 +1252,8 @@ mainloop_child_kill(pid_t pid) */ void mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *privatedata, enum mainloop_child_flags flags, - void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)) + void (*callback)(mainloop_child_t *p, int core, + int signo, int exitcode)) { static bool need_init = TRUE; mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); @@ -1296,7 +1284,8 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *pr void mainloop_child_add(pid_t pid, int timeout, const char *desc, void *privatedata, - void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)) + void (*callback)(mainloop_child_t *p, int core, int signo, + int exitcode)) { mainloop_child_add_with_flags(pid, timeout, desc, privatedata, 0, callback); } diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index 2c1218a96d1..28057b002e3 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -705,19 +705,17 @@ parse_exit_reason_from_stderr(svc_action_t *op) * \brief Process the completion of an asynchronous child process * * \param[in,out] p Child process that completed - * \param[in] pid Process ID of child * \param[in] core (Unused) * \param[in] signo Signal that interrupted child, if any * \param[in] exitcode Exit status of child process */ static void -async_action_complete(mainloop_child_t *p, pid_t pid, int core, int signo, - int exitcode) +async_action_complete(mainloop_child_t *p, int core, int signo, int exitcode) { svc_action_t *op = mainloop_child_userdata(p); mainloop_clear_child_userdata(p); - CRM_CHECK(op->pid == pid, + CRM_CHECK(op->pid == p->pid, services__set_result(op, services__generic_error(op), PCMK_EXEC_ERROR, "Bug in mainloop handling"); return); From 05991c4569d9a8edbebf678fc6d15d415f569fd6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 17:13:52 -0800 Subject: [PATCH 014/350] Refactor: based: Best practices in cib_diskwrite_complete() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 04fe79c25e8..5086c1f921b 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -38,25 +38,35 @@ static crm_trigger_t *write_trigger = NULL; +/*! + * \internal + * \brief Process the exit status of a child forked from \c write_cib_async() + * + * \param[in] child Mainloop child data + * \param[in] core If set to 1, the child process dumped core + * \param[in] signo Signal that the child process exited with + * \param[in] exit_code Child process's exit code + */ static void -cib_diskwrite_complete(mainloop_child_t *p, int core, int signo, int exitcode) +write_cib_cb(mainloop_child_t *child, int core, int signo, int exit_code) { - const char *errmsg = "Could not write CIB to disk"; + const char *error = "Could not write CIB to disk"; - if ((exitcode != 0) && cib_writes_enabled) { + if ((exit_code != 0) && cib_writes_enabled) { cib_writes_enabled = FALSE; - errmsg = "Disabling CIB disk writes after failure"; + error = "Disabling CIB disk writes after failure"; } - if ((signo == 0) && (exitcode == 0)) { - pcmk__trace("Disk write [%d] succeeded", (int) p->pid); + if ((signo == 0) && (exit_code == 0)) { + pcmk__trace("Disk write [%lld] succeeded", (long long) child->pid); } else if (signo == 0) { - pcmk__err("%s: process %d exited %d", errmsg, (int) p->pid, exitcode); + pcmk__err("%s: process %lld exited with code %d", error, + (long long) child->pid, exit_code); } else { - pcmk__err("%s: process %d terminated with signal %d (%s)%s", - errmsg, (int) p->pid, signo, strsignal(signo), + pcmk__err("%s: process %lld terminated with signal %d (%s)%s", + error, (long long) child->pid, signo, strsignal(signo), ((core != 0)? " and dumped core" : "")); } @@ -98,7 +108,7 @@ write_cib_async(gpointer user_data) if (pid > 0) { // Parent - mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete); + mainloop_child_add(pid, 0, "disk-writer", NULL, write_cib_cb); if (blackbox_state == QB_LOG_STATE_ENABLED) { qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); From 41d33da36ad529caba5a7a8836ea0e6185eed957 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 22:23:27 -0800 Subject: [PATCH 015/350] Refactor: based: Make cib_writes_enabled static to based_io.c Also use bool instead of gboolean. The --disk-writes command line option seems unnecessary, but we can deal with that later. But see T227. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 34 +++++++++++++++++++++++---------- daemons/based/based_io.h | 1 + daemons/based/pacemaker-based.c | 22 ++++++++++----------- daemons/based/pacemaker-based.h | 1 - 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 5086c1f921b..2f8748563d4 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -36,6 +36,7 @@ #include +static bool writes_enabled = true; static crm_trigger_t *write_trigger = NULL; /*! @@ -52,8 +53,8 @@ write_cib_cb(mainloop_child_t *child, int core, int signo, int exit_code) { const char *error = "Could not write CIB to disk"; - if ((exit_code != 0) && cib_writes_enabled) { - cib_writes_enabled = FALSE; + if ((exit_code != 0) && writes_enabled) { + writes_enabled = false; error = "Disabling CIB disk writes after failure"; } @@ -102,7 +103,7 @@ write_cib_async(gpointer user_data) if (pid < 0) { pcmk__err("Disabling disk writes after fork failure: %s", strerror(errno)); - cib_writes_enabled = FALSE; + writes_enabled = false; return G_SOURCE_REMOVE; } @@ -155,14 +156,27 @@ write_cib_async(gpointer user_data) /*! * \internal - * \brief Initialize data structures for \c pacemaker-based I/O + * \brief Enable CIB writes to disk (signal handler) * - * Currently there is only one, but this may be expanded later, and the name - * clarifies its purpose in the caller. + * \param[in] nsig Ignored + */ +void +based_enable_writes(int nsig) +{ + pcmk__info("(Re)enabling disk writes"); + writes_enabled = true; +} + +/*! + * \internal + * \brief Initialize data structures for \c pacemaker-based I/O */ void based_io_init(void) { + writes_enabled = !stand_alone; + mainloop_add_signal(SIGPIPE, based_enable_writes); + write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL); } @@ -179,7 +193,7 @@ cib_rename(const char *old) pcmk__err("Couldn't archive unusable file %s (disabling disk writes " "and continuing)", old); - cib_writes_enabled = FALSE; + writes_enabled = false; } else { pcmk__err("Archived unusable file %s as %s", old, new); } @@ -350,11 +364,11 @@ readCibXmlFile(const char *dir, const char *file, bool discard_status) pcmk__warn("Continuing with an empty configuration"); } - if (cib_writes_enabled + if (writes_enabled && pcmk__env_option_enabled(PCMK__SERVER_BASED, PCMK__ENV_VALGRIND_ENABLED)) { - cib_writes_enabled = FALSE; + writes_enabled = false; pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***"); } @@ -430,7 +444,7 @@ activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) pcmk__assert(new_cib != saved_cib); the_cib = new_cib; pcmk__xml_free(saved_cib); - if (cib_writes_enabled && cib_status == pcmk_rc_ok && to_disk) { + if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) { pcmk__debug("Triggering CIB write for %s op", op); mainloop_set_trigger(write_trigger); } diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h index c4cd524980c..84806aa43d9 100644 --- a/daemons/based/based_io.h +++ b/daemons/based/based_io.h @@ -11,5 +11,6 @@ #define BASED_IO__H void based_io_init(void); +void based_enable_writes(int nsig); #endif // BASED_IO__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index b614ecfe1d8..4efb88915d6 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -39,7 +39,6 @@ GMainLoop *mainloop = NULL; gchar *cib_root = NULL; static bool preserve_status = false; -gboolean cib_writes_enabled = TRUE; gboolean stand_alone = FALSE; int remote_fd = 0; @@ -53,13 +52,6 @@ static bool startCib(const char *filename); static crm_exit_t exit_code = CRM_EX_OK; -static void -cib_enable_writes(int nsig) -{ - pcmk__info("(Re)enabling disk writes"); - cib_writes_enabled = TRUE; -} - /*! * \internal * \brief Set up options, users, and groups for stand-alone mode @@ -76,7 +68,6 @@ setup_stand_alone(GError **error) int rc = pcmk_rc_ok; preserve_status = true; - cib_writes_enabled = FALSE; rc = pcmk__daemon_user(&uid, &gid); if (rc != pcmk_rc_ok) { @@ -134,12 +125,20 @@ based_metadata(pcmk__output_t *out) pcmk__opt_based); } +static gboolean +disk_writes_cb(const gchar *option_name, const gchar *optarg, gpointer data, + GError **error) +{ + based_enable_writes(0); + return TRUE; +} + static GOptionEntry entries[] = { { "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &stand_alone, "(Advanced use only) Run in stand-alone mode", NULL }, - { "disk-writes", 'w', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, - &cib_writes_enabled, + { "disk-writes", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + disk_writes_cb, "(Advanced use only) Enable disk writes (enabled by default unless in " "stand-alone mode)", NULL }, @@ -205,7 +204,6 @@ main(int argc, char **argv) } mainloop_add_signal(SIGTERM, cib_shutdown); - mainloop_add_signal(SIGPIPE, cib_enable_writes); based_io_init(); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 5f2ad5b86ee..ad8ddc9d12f 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -49,7 +49,6 @@ enum cib_client_flags { extern bool based_is_primary; extern GHashTable *config_hash; extern xmlNode *the_cib; -extern gboolean cib_writes_enabled; extern GMainLoop *mainloop; extern pcmk_cluster_t *crm_cluster; From f5cde1c39e8d588eaff23beb92f354a4d3d43fca Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 22:37:18 -0800 Subject: [PATCH 016/350] Refactor: based: Don't compare against FALSE Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 2f8748563d4..3bc3cc1c69a 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -310,8 +310,10 @@ readCibXmlFile(const char *dir, const char *file, bool discard_status) xmlNode *status = NULL; sigfile = pcmk__assert_asprintf("%s.sig", file); - if (pcmk__daemon_can_write(dir, file) == FALSE - || pcmk__daemon_can_write(dir, sigfile) == FALSE) { + + if (!pcmk__daemon_can_write(dir, file) + || !pcmk__daemon_can_write(dir, sigfile)) { + cib_status = EACCES; return NULL; } From 7c1fedb48bd6a944feeabadbfd1b82e558f8359f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 22:48:32 -0800 Subject: [PATCH 017/350] Refactor: based: Unindent retrieveCib() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 3bc3cc1c69a..d947a51873e 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -216,14 +216,15 @@ retrieveCib(const char *filename, const char *sigfile) if (rc == pcmk_ok) { pcmk__info("Loaded CIB from %s (with digest %s)", filename, sigfile); - } else { - pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s", - filename, sigfile, pcmk_strerror(rc)); - if (rc == -pcmk_err_cib_modified) { - // Archive the original files so the contents are not lost - cib_rename(filename); - cib_rename(sigfile); - } + return root; + } + + pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s", + filename, sigfile, pcmk_strerror(rc)); + if (rc == -pcmk_err_cib_modified) { + // Archive the original files so the contents are not lost + cib_rename(filename); + cib_rename(sigfile); } return root; } From d70cdd4d4ad5e643ea8f2a2016b337ebb92c921a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 22:56:52 -0800 Subject: [PATCH 018/350] Refactor: based: Clarify variable names in readCibXmlFile() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index d947a51873e..d91d1d6dabf 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -296,37 +296,37 @@ static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) } xmlNode * -readCibXmlFile(const char *dir, const char *file, bool discard_status) +readCibXmlFile(const char *dir, const char *cibfile, bool discard_status) { struct dirent **namelist = NULL; int lpc = 0; + char *cibfile_path = NULL; char *sigfile = NULL; - char *sigfilepath = NULL; - char *filename = NULL; + char *sigfile_path = NULL; const char *name = NULL; const char *value = NULL; xmlNode *root = NULL; xmlNode *status = NULL; - sigfile = pcmk__assert_asprintf("%s.sig", file); + sigfile = pcmk__assert_asprintf("%s.sig", cibfile); - if (!pcmk__daemon_can_write(dir, file) + if (!pcmk__daemon_can_write(dir, cibfile) || !pcmk__daemon_can_write(dir, sigfile)) { cib_status = EACCES; return NULL; } - filename = pcmk__assert_asprintf("%s/%s", dir, file); - sigfilepath = pcmk__assert_asprintf("%s/%s", dir, sigfile); + cibfile_path = pcmk__assert_asprintf("%s/%s", dir, cibfile); + sigfile_path = pcmk__assert_asprintf("%s/%s", dir, sigfile); free(sigfile); cib_status = pcmk_rc_ok; - root = retrieveCib(filename, sigfilepath); - free(filename); - free(sigfilepath); + root = retrieveCib(cibfile_path, sigfile_path); + free(cibfile_path); + free(sigfile_path); if (root == NULL) { lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); @@ -341,24 +341,23 @@ readCibXmlFile(const char *dir, const char *file, bool discard_status) lpc--; - filename = pcmk__assert_asprintf("%s/%s", cib_root, - namelist[lpc]->d_name); - sigfile = pcmk__assert_asprintf("%s.sig", filename); + cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, + namelist[lpc]->d_name); + sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path); - rc = cib_file_read_and_verify(filename, sigfile, &root); + rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); if (rc == pcmk_ok) { pcmk__notice("Loaded CIB from last valid backup %s (with digest " - "%s)", - filename, sigfile); + "%s)", cibfile_path, sigfile_path); } else { pcmk__warn("Not using next most recent CIB backup from %s (with " - "digest %s): %s", - filename, sigfile, pcmk_strerror(rc)); + "digest %s): %s", cibfile_path, sigfile_path, + pcmk_strerror(rc)); } free(namelist[lpc]); - free(filename); - free(sigfile); + free(cibfile_path); + free(sigfile_path); } free(namelist); From 3ff6aefe07559423bcb4c3b2b966a44b86c78dd6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 22:57:59 -0800 Subject: [PATCH 019/350] Refactor: based: Clarify argument names in retrieveCib() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index d91d1d6dabf..96691f3670e 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -209,22 +209,23 @@ cib_rename(const char *old) */ static xmlNode * -retrieveCib(const char *filename, const char *sigfile) +retrieveCib(const char *cibfile_path, const char *sigfile_path) { xmlNode *root = NULL; - int rc = cib_file_read_and_verify(filename, sigfile, &root); + int rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); if (rc == pcmk_ok) { - pcmk__info("Loaded CIB from %s (with digest %s)", filename, sigfile); + pcmk__info("Loaded CIB from %s (with digest %s)", cibfile_path, + sigfile_path); return root; } pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s", - filename, sigfile, pcmk_strerror(rc)); + cibfile_path, sigfile_path, pcmk_strerror(rc)); if (rc == -pcmk_err_cib_modified) { // Archive the original files so the contents are not lost - cib_rename(filename); - cib_rename(sigfile); + cib_rename(cibfile_path); + cib_rename(sigfile_path); } return root; } From 83036d03726c90020df03544379eafddb6e65039 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 20 Dec 2025 23:02:28 -0800 Subject: [PATCH 020/350] Refactor: based: Move more functionality into retrieveCib() This function handles the case "try to get the CIB from the requested file first." Then we'll try the directory, and finally we'll fall back to creating an empty CIB. I want to make readCibXmlFile() shorter and reduce the number of times we're allocating and freeing variables there, which is confusing. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 49 +++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 96691f3670e..500eb38950c 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -209,24 +209,44 @@ cib_rename(const char *old) */ static xmlNode * -retrieveCib(const char *cibfile_path, const char *sigfile_path) +retrieveCib(const char *dir, const char *cibfile) { + char *sigfile = pcmk__assert_asprintf("%s.sig", cibfile); + char *cibfile_path = pcmk__assert_asprintf("%s/%s", dir, cibfile); + char *sigfile_path = pcmk__assert_asprintf("%s/%s", dir, sigfile); + xmlNode *root = NULL; - int rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); + int rc = pcmk_ok; + + if (!pcmk__daemon_can_write(dir, cibfile) + || !pcmk__daemon_can_write(dir, sigfile)) { + + cib_status = EACCES; + goto done; + } + cib_status = pcmk_rc_ok; + + rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); if (rc == pcmk_ok) { pcmk__info("Loaded CIB from %s (with digest %s)", cibfile_path, sigfile_path); - return root; + goto done; } pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s", cibfile_path, sigfile_path, pcmk_strerror(rc)); + if (rc == -pcmk_err_cib_modified) { // Archive the original files so the contents are not lost cib_rename(cibfile_path); cib_rename(sigfile_path); } + +done: + free(sigfile); + free(cibfile_path); + free(sigfile_path); return root; } @@ -302,32 +322,13 @@ readCibXmlFile(const char *dir, const char *cibfile, bool discard_status) struct dirent **namelist = NULL; int lpc = 0; - char *cibfile_path = NULL; - char *sigfile = NULL; - char *sigfile_path = NULL; const char *name = NULL; const char *value = NULL; xmlNode *root = NULL; xmlNode *status = NULL; - sigfile = pcmk__assert_asprintf("%s.sig", cibfile); - - if (!pcmk__daemon_can_write(dir, cibfile) - || !pcmk__daemon_can_write(dir, sigfile)) { - - cib_status = EACCES; - return NULL; - } - - cibfile_path = pcmk__assert_asprintf("%s/%s", dir, cibfile); - sigfile_path = pcmk__assert_asprintf("%s/%s", dir, sigfile); - free(sigfile); - - cib_status = pcmk_rc_ok; - root = retrieveCib(cibfile_path, sigfile_path); - free(cibfile_path); - free(sigfile_path); + root = retrieveCib(dir, cibfile); if (root == NULL) { lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); @@ -338,6 +339,8 @@ readCibXmlFile(const char *dir, const char *cibfile, bool discard_status) } while (root == NULL && lpc > 1) { + char *cibfile_path = NULL; + char *sigfile_path = NULL; int rc = pcmk_ok; lpc--; From df034d88389ede729aee2ec91363746e539bebaa Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 00:31:37 -0800 Subject: [PATCH 021/350] Refactor: based: Drop startCib() argument Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 4efb88915d6..d75a14c0afc 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -48,7 +48,7 @@ GHashTable *config_hash = NULL; static void cib_init(void); void cib_shutdown(int nsig); -static bool startCib(const char *filename); +static bool startCib(void); static crm_exit_t exit_code = CRM_EX_OK; @@ -381,7 +381,7 @@ cib_init(void) config_hash = pcmk__strkey_table(free, free); - if (!startCib("cib.xml")) { + if (!startCib()) { pcmk__crit("Cannot start CIB... terminating"); crm_exit(CRM_EX_NOINPUT); } @@ -404,9 +404,9 @@ cib_init(void) } static bool -startCib(const char *filename) +startCib(void) { - xmlNode *cib = readCibXmlFile(cib_root, filename, !preserve_status); + xmlNode *cib = readCibXmlFile(cib_root, "cib.xml", !preserve_status); int port = 0; if (activateCibXml(cib, true, "start") != 0) { From f3339faa5541d86eeb120a34426d044e9428f363 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 00:47:06 -0800 Subject: [PATCH 022/350] Refactor: based: Drop readCibXmlFile() cibfile argument The sole call sets it to "cib.xml". Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 4 ++-- daemons/based/based_io.h | 5 +++++ daemons/based/pacemaker-based.c | 2 +- daemons/based/pacemaker-based.h | 1 - 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 500eb38950c..a517fe169a1 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -317,7 +317,7 @@ static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) } xmlNode * -readCibXmlFile(const char *dir, const char *cibfile, bool discard_status) +based_read_cib(const char *dir, bool discard_status) { struct dirent **namelist = NULL; @@ -328,7 +328,7 @@ readCibXmlFile(const char *dir, const char *cibfile, bool discard_status) xmlNode *root = NULL; xmlNode *status = NULL; - root = retrieveCib(dir, cibfile); + root = retrieveCib(dir, "cib.xml"); if (root == NULL) { lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h index 84806aa43d9..dd15edd3810 100644 --- a/daemons/based/based_io.h +++ b/daemons/based/based_io.h @@ -10,7 +10,12 @@ #ifndef BASED_IO__H #define BASED_IO__H +#include + +#include // xmlNode + void based_io_init(void); void based_enable_writes(int nsig); +xmlNode *based_read_cib(const char *dir, bool discard_status); #endif // BASED_IO__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index d75a14c0afc..55635436495 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -406,7 +406,7 @@ cib_init(void) static bool startCib(void) { - xmlNode *cib = readCibXmlFile(cib_root, "cib.xml", !preserve_status); + xmlNode *cib = based_read_cib(cib_root, !preserve_status); int port = 0; if (activateCibXml(cib, true, "start") != 0) { diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index ad8ddc9d12f..a16485320b8 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -75,7 +75,6 @@ void cib_shutdown(int nsig); void terminate_cib(int exit_status); void uninitializeCib(void); -xmlNode *readCibXmlFile(const char *dir, const char *file, bool discard_status); int activateCibXml(xmlNode *doc, bool to_disk, const char *op); int cib_process_shutdown_req(const char *op, int options, const char *section, From bec783d554e0e62e2a0df90d7b1375ef5f43fc3f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 00:49:45 -0800 Subject: [PATCH 023/350] Refactor: based: Drop based_read_cib() discard_status argument It's always set to !preserve_status, and preserve_status is always set to the same value as stand_alone. So we can use !stand_alone in place of discard_status. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 6 +++--- daemons/based/based_io.h | 4 +--- daemons/based/pacemaker-based.c | 5 +---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index a517fe169a1..2cd467f4a32 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -317,7 +317,7 @@ static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) } xmlNode * -based_read_cib(const char *dir, bool discard_status) +based_read_cib(const char *dir) { struct dirent **namelist = NULL; @@ -379,7 +379,7 @@ based_read_cib(const char *dir, bool discard_status) } status = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL); - if (discard_status && status != NULL) { + if (!stand_alone && (status != NULL)) { // Strip out the PCMK_XE_STATUS section if there is one pcmk__xml_free(status); status = NULL; @@ -414,7 +414,7 @@ based_read_cib(const char *dir, bool discard_status) // Unset (DC should set appropriate value) pcmk__xe_remove_attr(root, PCMK_XA_DC_UUID); - if (discard_status) { + if (!stand_alone) { pcmk__log_xml_trace(root, "[on-disk]"); } diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h index dd15edd3810..8f4a502523a 100644 --- a/daemons/based/based_io.h +++ b/daemons/based/based_io.h @@ -10,12 +10,10 @@ #ifndef BASED_IO__H #define BASED_IO__H -#include - #include // xmlNode void based_io_init(void); void based_enable_writes(int nsig); -xmlNode *based_read_cib(const char *dir, bool discard_status); +xmlNode *based_read_cib(const char *dir); #endif // BASED_IO__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 55635436495..3bc75be1927 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -37,7 +37,6 @@ pcmk_cluster_t *crm_cluster = NULL; GMainLoop *mainloop = NULL; gchar *cib_root = NULL; -static bool preserve_status = false; gboolean stand_alone = FALSE; @@ -67,8 +66,6 @@ setup_stand_alone(GError **error) gid_t gid = 0; int rc = pcmk_rc_ok; - preserve_status = true; - rc = pcmk__daemon_user(&uid, &gid); if (rc != pcmk_rc_ok) { exit_code = CRM_EX_FATAL; @@ -406,7 +403,7 @@ cib_init(void) static bool startCib(void) { - xmlNode *cib = based_read_cib(cib_root, !preserve_status); + xmlNode *cib = based_read_cib(cib_root); int port = 0; if (activateCibXml(cib, true, "start") != 0) { From 37a566c3989306d3381927b84d67673462562bee Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 00:53:09 -0800 Subject: [PATCH 024/350] Refactor: based: Drop retrieveCib() cibfile argument The sole call sets it to "cib.xml". Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 2cd467f4a32..711128a1780 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -208,17 +208,19 @@ cib_rename(const char *old) * It is the callers responsibility to free the output of this function */ +#define CIBFILE "cib.xml" + static xmlNode * -retrieveCib(const char *dir, const char *cibfile) +retrieveCib(const char *dir) { - char *sigfile = pcmk__assert_asprintf("%s.sig", cibfile); - char *cibfile_path = pcmk__assert_asprintf("%s/%s", dir, cibfile); + char *sigfile = pcmk__assert_asprintf("%s.sig", CIBFILE); + char *cibfile_path = pcmk__assert_asprintf("%s/%s", dir, CIBFILE); char *sigfile_path = pcmk__assert_asprintf("%s/%s", dir, sigfile); xmlNode *root = NULL; int rc = pcmk_ok; - if (!pcmk__daemon_can_write(dir, cibfile) + if (!pcmk__daemon_can_write(dir, CIBFILE) || !pcmk__daemon_can_write(dir, sigfile)) { cib_status = EACCES; @@ -328,7 +330,7 @@ based_read_cib(const char *dir) xmlNode *root = NULL; xmlNode *status = NULL; - root = retrieveCib(dir, "cib.xml"); + root = retrieveCib(dir); if (root == NULL) { lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); From 5bd82fb861fe0270aae4666e3d2cb342a9509ea6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 01:00:01 -0800 Subject: [PATCH 025/350] Refactor: based: Drop based_read_cib() and retrieveCib() dir arguments They're always set to cib_root. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 14 +++++++------- daemons/based/based_io.h | 2 +- daemons/based/pacemaker-based.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 711128a1780..b43a9219b44 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -211,17 +211,17 @@ cib_rename(const char *old) #define CIBFILE "cib.xml" static xmlNode * -retrieveCib(const char *dir) +retrieveCib(void) { char *sigfile = pcmk__assert_asprintf("%s.sig", CIBFILE); - char *cibfile_path = pcmk__assert_asprintf("%s/%s", dir, CIBFILE); - char *sigfile_path = pcmk__assert_asprintf("%s/%s", dir, sigfile); + char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, CIBFILE); + char *sigfile_path = pcmk__assert_asprintf("%s/%s", cib_root, sigfile); xmlNode *root = NULL; int rc = pcmk_ok; - if (!pcmk__daemon_can_write(dir, CIBFILE) - || !pcmk__daemon_can_write(dir, sigfile)) { + if (!pcmk__daemon_can_write(cib_root, CIBFILE) + || !pcmk__daemon_can_write(cib_root, sigfile)) { cib_status = EACCES; goto done; @@ -319,7 +319,7 @@ static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) } xmlNode * -based_read_cib(const char *dir) +based_read_cib(void) { struct dirent **namelist = NULL; @@ -330,7 +330,7 @@ based_read_cib(const char *dir) xmlNode *root = NULL; xmlNode *status = NULL; - root = retrieveCib(dir); + root = retrieveCib(); if (root == NULL) { lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h index 8f4a502523a..2c3fab0ffea 100644 --- a/daemons/based/based_io.h +++ b/daemons/based/based_io.h @@ -14,6 +14,6 @@ void based_io_init(void); void based_enable_writes(int nsig); -xmlNode *based_read_cib(const char *dir); +xmlNode *based_read_cib(void); #endif // BASED_IO__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 3bc75be1927..28662d690c0 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -403,7 +403,7 @@ cib_init(void) static bool startCib(void) { - xmlNode *cib = based_read_cib(cib_root); + xmlNode *cib = based_read_cib(); int port = 0; if (activateCibXml(cib, true, "start") != 0) { From fa90ea7f8c13685c6461a604b75158a3b55268bb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 19:24:10 -0800 Subject: [PATCH 026/350] Refactor: based: Make cib_archive_sort() behavior match comment cib_archive_sort() sorts directory entries in the reverse order from the one documented. The code still worked as intended, because the while loop iterated in reverse order. This commit sorts directory entries from newest to oldest and then iterates starting at index 0. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index b43a9219b44..f0c6b2d6983 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -306,10 +306,12 @@ static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) free(a_path); free(b_path); - if(a_age > b_age) { - rc = 1; - } else if(a_age < b_age) { + if (a_age > b_age) { + // a newer than b rc = -1; + } else if (a_age < b_age) { + // a older than b + rc = 1; } pcmk__trace("%s (%lu) vs. %s (%lu) : %d", @@ -323,7 +325,7 @@ based_read_cib(void) { struct dirent **namelist = NULL; - int lpc = 0; + int num_files = 0; const char *name = NULL; const char *value = NULL; @@ -333,22 +335,21 @@ based_read_cib(void) root = retrieveCib(); if (root == NULL) { - lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); - if (lpc < 0) { + num_files = scandir(cib_root, &namelist, cib_archive_filter, + cib_archive_sort); + if (num_files < 0) { pcmk__err("Could not check for CIB backups in %s: %s", cib_root, pcmk_rc_str(errno)); } } - while (root == NULL && lpc > 1) { + for (int i = 0; i < num_files; i++) { char *cibfile_path = NULL; char *sigfile_path = NULL; int rc = pcmk_ok; - lpc--; - cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, - namelist[lpc]->d_name); + namelist[i]->d_name); sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path); rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); @@ -361,9 +362,13 @@ based_read_cib(void) pcmk_strerror(rc)); } - free(namelist[lpc]); + free(namelist[i]); free(cibfile_path); free(sigfile_path); + + if (rc == pcmk_ok) { + break; + } } free(namelist); From 45dc84a29b34060fcb46f7ff3cf036f4195a5309 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 01:28:34 -0800 Subject: [PATCH 027/350] Refactor: based: Functionize finding and reading a backup CIB file Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 69 +++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index f0c6b2d6983..d091717b050 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -320,39 +320,34 @@ static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) return rc; } -xmlNode * -based_read_cib(void) +/*! + * \internal + * \brief Read CIB XML from the last valid backup file in \c cib_root + * + * \return CIB XML parsed from the last valid backup file, or \c NULL if none + * was found + */ +static xmlNode * +read_backup_cib(void) { + xmlNode *cib_xml = NULL; struct dirent **namelist = NULL; - - int num_files = 0; - const char *name = NULL; - const char *value = NULL; - - xmlNode *root = NULL; - xmlNode *status = NULL; - - root = retrieveCib(); - - if (root == NULL) { - num_files = scandir(cib_root, &namelist, cib_archive_filter, + int num_files = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort); - if (num_files < 0) { - pcmk__err("Could not check for CIB backups in %s: %s", cib_root, - pcmk_rc_str(errno)); - } + + if (num_files < 0) { + pcmk__err("Could not check for CIB backups in %s: %s", cib_root, + pcmk_rc_str(errno)); + goto done; } for (int i = 0; i < num_files; i++) { - char *cibfile_path = NULL; - char *sigfile_path = NULL; - int rc = pcmk_ok; + const char *cibfile = namelist[i]->d_name; + char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, cibfile); + char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path); - cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, - namelist[i]->d_name); - sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path); + int rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml); - rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); if (rc == pcmk_ok) { pcmk__notice("Loaded CIB from last valid backup %s (with digest " "%s)", cibfile_path, sigfile_path); @@ -362,7 +357,6 @@ based_read_cib(void) pcmk_strerror(rc)); } - free(namelist[i]); free(cibfile_path); free(sigfile_path); @@ -370,8 +364,31 @@ based_read_cib(void) break; } } + +done: + for (int i = 0; i < num_files; i++) { + free(namelist[i]); + } free(namelist); + return cib_xml; +} + +xmlNode * +based_read_cib(void) +{ + const char *name = NULL; + const char *value = NULL; + + xmlNode *root = NULL; + xmlNode *status = NULL; + + root = retrieveCib(); + + if (root == NULL) { + root = read_backup_cib(); + } + if (root == NULL) { root = createEmptyCib(0); pcmk__warn("Continuing with an empty configuration"); From 7f6f44fd5e563341b121c1ab10de611a33ce47a8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 01:32:55 -0800 Subject: [PATCH 028/350] Refactor: based: Rename retrieveCib() to read_current_cib() And add Doxygen. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index d091717b050..4be92a9a558 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -204,14 +204,20 @@ cib_rename(const char *old) free(new); } -/* - * It is the callers responsibility to free the output of this function - */ - #define CIBFILE "cib.xml" +/*! + * \internal + * \brief Read CIB XML from \c CIBFILE in the \c cib_root directory + * + * \return CIB XML parsed from \c CIBFILE in \c cib_root , or \c NULL if the + * file was not found or if parsing failed + * + * \note The caller is responsible for freeing the return value using + * \c pcmk__xml_free(). + */ static xmlNode * -retrieveCib(void) +read_current_cib(void) { char *sigfile = pcmk__assert_asprintf("%s.sig", CIBFILE); char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, CIBFILE); @@ -383,7 +389,7 @@ based_read_cib(void) xmlNode *root = NULL; xmlNode *status = NULL; - root = retrieveCib(); + root = read_current_cib(); if (root == NULL) { root = read_backup_cib(); From 7ca9741fb2a8b856aaf556acc0ecbfc274bf0c38 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 14:54:44 -0800 Subject: [PATCH 029/350] Refactor: based: Don't allocate sigfile string Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 4be92a9a558..238da64dc0f 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -219,9 +219,9 @@ cib_rename(const char *old) static xmlNode * read_current_cib(void) { - char *sigfile = pcmk__assert_asprintf("%s.sig", CIBFILE); char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, CIBFILE); - char *sigfile_path = pcmk__assert_asprintf("%s/%s", cib_root, sigfile); + char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path); + const char *sigfile = strrchr(sigfile_path, '/') + 1; xmlNode *root = NULL; int rc = pcmk_ok; @@ -252,7 +252,6 @@ read_current_cib(void) } done: - free(sigfile); free(cibfile_path); free(sigfile_path); return root; From 5c5b920f29ace63c04163182aa86fd1df6c3cbd2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 01:37:12 -0800 Subject: [PATCH 030/350] Refactor: based: Rename root variable to cib_xml in based_read_cib() ...and in read_current_cib(). Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 238da64dc0f..00ac427f9bf 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -223,7 +223,7 @@ read_current_cib(void) char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path); const char *sigfile = strrchr(sigfile_path, '/') + 1; - xmlNode *root = NULL; + xmlNode *cib_xml = NULL; int rc = pcmk_ok; if (!pcmk__daemon_can_write(cib_root, CIBFILE) @@ -235,7 +235,7 @@ read_current_cib(void) cib_status = pcmk_rc_ok; - rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &root); + rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml); if (rc == pcmk_ok) { pcmk__info("Loaded CIB from %s (with digest %s)", cibfile_path, sigfile_path); @@ -254,7 +254,7 @@ read_current_cib(void) done: free(cibfile_path); free(sigfile_path); - return root; + return cib_xml; } static int cib_archive_filter(const struct dirent * a) @@ -385,17 +385,17 @@ based_read_cib(void) const char *name = NULL; const char *value = NULL; - xmlNode *root = NULL; + xmlNode *cib_xml = NULL; xmlNode *status = NULL; - root = read_current_cib(); + cib_xml = read_current_cib(); - if (root == NULL) { - root = read_backup_cib(); + if (cib_xml == NULL) { + cib_xml = read_backup_cib(); } - if (root == NULL) { - root = createEmptyCib(0); + if (cib_xml == NULL) { + cib_xml = createEmptyCib(0); pcmk__warn("Continuing with an empty configuration"); } @@ -407,50 +407,50 @@ based_read_cib(void) pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***"); } - status = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL); + status = pcmk__xe_first_child(cib_xml, PCMK_XE_STATUS, NULL, NULL); if (!stand_alone && (status != NULL)) { // Strip out the PCMK_XE_STATUS section if there is one pcmk__xml_free(status); status = NULL; } if (status == NULL) { - pcmk__xe_create(root, PCMK_XE_STATUS); + pcmk__xe_create(cib_xml, PCMK_XE_STATUS); } /* Do this before schema validation happens */ /* fill in some defaults */ - value = pcmk__xe_get(root, PCMK_XA_ADMIN_EPOCH); + value = pcmk__xe_get(cib_xml, PCMK_XA_ADMIN_EPOCH); if (value == NULL) { // Not possible with schema validation enabled pcmk__warn("Defaulting missing " PCMK_XA_ADMIN_EPOCH " to 0, but " "cluster may get confused about which node's configuration " "is most recent"); - pcmk__xe_set_int(root, PCMK_XA_ADMIN_EPOCH, 0); + pcmk__xe_set_int(cib_xml, PCMK_XA_ADMIN_EPOCH, 0); } name = PCMK_XA_EPOCH; - value = pcmk__xe_get(root, name); + value = pcmk__xe_get(cib_xml, name); if (value == NULL) { - pcmk__xe_set_int(root, name, 0); + pcmk__xe_set_int(cib_xml, name, 0); } name = PCMK_XA_NUM_UPDATES; - value = pcmk__xe_get(root, name); + value = pcmk__xe_get(cib_xml, name); if (value == NULL) { - pcmk__xe_set_int(root, name, 0); + pcmk__xe_set_int(cib_xml, name, 0); } // Unset (DC should set appropriate value) - pcmk__xe_remove_attr(root, PCMK_XA_DC_UUID); + pcmk__xe_remove_attr(cib_xml, PCMK_XA_DC_UUID); if (!stand_alone) { - pcmk__log_xml_trace(root, "[on-disk]"); + pcmk__log_xml_trace(cib_xml, "[on-disk]"); } - if (!pcmk__configured_schema_validates(root)) { + if (!pcmk__configured_schema_validates(cib_xml)) { cib_status = pcmk_rc_schema_validation; } - return root; + return cib_xml; } void From 11963921f43bfee85d4ac9f3f43fbabe56e3133e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 14:58:46 -0800 Subject: [PATCH 031/350] Refactor: based: Convert to standard RC in read_{current,backup}_cib() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 00ac427f9bf..5300d65a811 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -224,7 +224,7 @@ read_current_cib(void) const char *sigfile = strrchr(sigfile_path, '/') + 1; xmlNode *cib_xml = NULL; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; if (!pcmk__daemon_can_write(cib_root, CIBFILE) || !pcmk__daemon_can_write(cib_root, sigfile)) { @@ -236,16 +236,18 @@ read_current_cib(void) cib_status = pcmk_rc_ok; rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml); - if (rc == pcmk_ok) { + rc = pcmk_legacy2rc(rc); + + if (rc == pcmk_rc_ok) { pcmk__info("Loaded CIB from %s (with digest %s)", cibfile_path, sigfile_path); goto done; } pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s", - cibfile_path, sigfile_path, pcmk_strerror(rc)); + cibfile_path, sigfile_path, pcmk_rc_str(rc)); - if (rc == -pcmk_err_cib_modified) { + if (rc == pcmk_rc_cib_modified) { // Archive the original files so the contents are not lost cib_rename(cibfile_path); cib_rename(sigfile_path); @@ -353,19 +355,21 @@ read_backup_cib(void) int rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml); - if (rc == pcmk_ok) { + rc = pcmk_legacy2rc(rc); + + if (rc == pcmk_rc_ok) { pcmk__notice("Loaded CIB from last valid backup %s (with digest " "%s)", cibfile_path, sigfile_path); } else { pcmk__warn("Not using next most recent CIB backup from %s (with " "digest %s): %s", cibfile_path, sigfile_path, - pcmk_strerror(rc)); + pcmk_rc_str(rc)); } free(cibfile_path); free(sigfile_path); - if (rc == pcmk_ok) { + if (rc == pcmk_rc_ok) { break; } } From 911297b5363b2eb23f0adc4d8d6d47f53781037c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 01:38:53 -0800 Subject: [PATCH 032/350] Refactor: based: Drop name/value variables from based_read_cib() Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 5300d65a811..466369f275a 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -386,9 +386,6 @@ read_backup_cib(void) xmlNode * based_read_cib(void) { - const char *name = NULL; - const char *value = NULL; - xmlNode *cib_xml = NULL; xmlNode *status = NULL; @@ -421,39 +418,46 @@ based_read_cib(void) pcmk__xe_create(cib_xml, PCMK_XE_STATUS); } - /* Do this before schema validation happens */ + /* Default the three version attributes to 0 if unset. The schema requires + * them to be set, so: + * * It's not possible for them to be unset if schema validation was enabled + * when the CIB file was generated, or if it was generated by Pacemaker + * and then unmodified. + * * We need to set these defaults before schema validation happens. + */ - /* fill in some defaults */ - value = pcmk__xe_get(cib_xml, PCMK_XA_ADMIN_EPOCH); - if (value == NULL) { // Not possible with schema validation enabled + if (pcmk__xe_get(cib_xml, PCMK_XA_ADMIN_EPOCH) == NULL) { pcmk__warn("Defaulting missing " PCMK_XA_ADMIN_EPOCH " to 0, but " "cluster may get confused about which node's configuration " "is most recent"); pcmk__xe_set_int(cib_xml, PCMK_XA_ADMIN_EPOCH, 0); } - name = PCMK_XA_EPOCH; - value = pcmk__xe_get(cib_xml, name); - if (value == NULL) { - pcmk__xe_set_int(cib_xml, name, 0); + if (pcmk__xe_get(cib_xml, PCMK_XA_EPOCH) == NULL) { + pcmk__warn("Defaulting missing " PCMK_XA_EPOCH " to 0, but cluster " + "may get confused about which node's configuration is most " + "recent"); + pcmk__xe_set_int(cib_xml, PCMK_XA_EPOCH, 0); } - name = PCMK_XA_NUM_UPDATES; - value = pcmk__xe_get(cib_xml, name); - if (value == NULL) { - pcmk__xe_set_int(cib_xml, name, 0); + if (pcmk__xe_get(cib_xml, PCMK_XA_NUM_UPDATES) == NULL) { + pcmk__warn("Defaulting missing " PCMK_XA_NUM_UPDATES " to 0, but " + "cluster may get confused about which node's configuration " + "is most recent"); + pcmk__xe_set_int(cib_xml, PCMK_XA_NUM_UPDATES, 0); } - // Unset (DC should set appropriate value) + // The DC should set appropriate value for PCMK_XA_DC_UUID pcmk__xe_remove_attr(cib_xml, PCMK_XA_DC_UUID); if (!stand_alone) { - pcmk__log_xml_trace(cib_xml, "[on-disk]"); + pcmk__log_xml_trace(cib_xml, "on-disk"); } if (!pcmk__configured_schema_validates(cib_xml)) { cib_status = pcmk_rc_schema_validation; } + return cib_xml; } From dc826ab47a8d167a9bea9eaa4c45cdb3ace31c85 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 03:00:35 -0800 Subject: [PATCH 033/350] Refactor: based: Functionize setting default version attributes Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 47 +++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 466369f275a..ddb65b1c44c 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -383,9 +383,34 @@ read_backup_cib(void) return cib_xml; } +/*! + * \internal + * \brief Set the given CIB version attribute to 0 if it's not already set + * + * \param[in,out] cib_xml CIB XML + * \param[in] version_attr Version attribute + */ +static void +set_default_if_unset(xmlNode *cib_xml, const char *version_attr) +{ + if (pcmk__xe_get(cib_xml, version_attr) != NULL) { + return; + } + + pcmk__warn("Defaulting missing %s to 0, but cluster may get confused about " + "which node's configuration is most recent", version_attr); + pcmk__xe_set_int(cib_xml, version_attr, 0); +} + xmlNode * based_read_cib(void) { + static const char *version_attrs[] = { + PCMK_XA_ADMIN_EPOCH, + PCMK_XA_EPOCH, + PCMK_XA_NUM_UPDATES, + }; + xmlNode *cib_xml = NULL; xmlNode *status = NULL; @@ -425,26 +450,8 @@ based_read_cib(void) * and then unmodified. * * We need to set these defaults before schema validation happens. */ - - if (pcmk__xe_get(cib_xml, PCMK_XA_ADMIN_EPOCH) == NULL) { - pcmk__warn("Defaulting missing " PCMK_XA_ADMIN_EPOCH " to 0, but " - "cluster may get confused about which node's configuration " - "is most recent"); - pcmk__xe_set_int(cib_xml, PCMK_XA_ADMIN_EPOCH, 0); - } - - if (pcmk__xe_get(cib_xml, PCMK_XA_EPOCH) == NULL) { - pcmk__warn("Defaulting missing " PCMK_XA_EPOCH " to 0, but cluster " - "may get confused about which node's configuration is most " - "recent"); - pcmk__xe_set_int(cib_xml, PCMK_XA_EPOCH, 0); - } - - if (pcmk__xe_get(cib_xml, PCMK_XA_NUM_UPDATES) == NULL) { - pcmk__warn("Defaulting missing " PCMK_XA_NUM_UPDATES " to 0, but " - "cluster may get confused about which node's configuration " - "is most recent"); - pcmk__xe_set_int(cib_xml, PCMK_XA_NUM_UPDATES, 0); + for (int i = 0; i < PCMK__NELEM(version_attrs); i++) { + set_default_if_unset(cib_xml, version_attrs[i]); } // The DC should set appropriate value for PCMK_XA_DC_UUID From 58c3dd0d6ebdfb07a30cb73bda5eee90c4b5e07c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 03:18:55 -0800 Subject: [PATCH 034/350] Refactor: based: Functionize setting empty status element Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 41 +++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index ddb65b1c44c..842499a7c7d 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -383,6 +383,32 @@ read_backup_cib(void) return cib_xml; } +/*! + * \internal + * \brief Set the CIB XML's \c PCMK_XE_STATUS element to empty if appropriate + * + * Delete the current \c PCMK_XE_STATUS element if not running in stand-alone + * mode. Then create an empty \c PCMK_XE_STATUS child if either of the following + * is true: + * * not running in stand-alone mode + * * running in stand-alone mode with no \c PCMK_XE_STATUS element + * + * \param[in,out] cib_xml CIB XML + */ +static void +set_empty_status(xmlNode *cib_xml) +{ + xmlNode *status = pcmk__xe_first_child(cib_xml, PCMK_XE_STATUS, NULL, NULL); + + if (!stand_alone) { + g_clear_pointer(&status, pcmk__xml_free); + } + + if (status == NULL) { + pcmk__xe_create(cib_xml, PCMK_XE_STATUS); + } +} + /*! * \internal * \brief Set the given CIB version attribute to 0 if it's not already set @@ -411,10 +437,7 @@ based_read_cib(void) PCMK_XA_NUM_UPDATES, }; - xmlNode *cib_xml = NULL; - xmlNode *status = NULL; - - cib_xml = read_current_cib(); + xmlNode *cib_xml = read_current_cib(); if (cib_xml == NULL) { cib_xml = read_backup_cib(); @@ -433,15 +456,7 @@ based_read_cib(void) pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***"); } - status = pcmk__xe_first_child(cib_xml, PCMK_XE_STATUS, NULL, NULL); - if (!stand_alone && (status != NULL)) { - // Strip out the PCMK_XE_STATUS section if there is one - pcmk__xml_free(status); - status = NULL; - } - if (status == NULL) { - pcmk__xe_create(cib_xml, PCMK_XE_STATUS); - } + set_empty_status(cib_xml); /* Default the three version attributes to 0 if unset. The schema requires * them to be set, so: From 4f32e5dc5ec25cca7702596b536b451cfa78395c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 03:21:29 -0800 Subject: [PATCH 035/350] Refactor: based: Move disabling writes for valgrind to based_io_init() This has nothing to do with reading the CIB from a file, so it should not be part of based_read_cib(). Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 842499a7c7d..032562c6623 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -175,6 +175,17 @@ void based_io_init(void) { writes_enabled = !stand_alone; + if (writes_enabled + && pcmk__env_option_enabled(PCMK__SERVER_BASED, + PCMK__ENV_VALGRIND_ENABLED)) { + + writes_enabled = false; + pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***"); + } + + /* @TODO Should we be setting this up if we've explicitly disabled writes + * already? + */ mainloop_add_signal(SIGPIPE, based_enable_writes); write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL); @@ -448,14 +459,6 @@ based_read_cib(void) pcmk__warn("Continuing with an empty configuration"); } - if (writes_enabled - && pcmk__env_option_enabled(PCMK__SERVER_BASED, - PCMK__ENV_VALGRIND_ENABLED)) { - - writes_enabled = false; - pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***"); - } - set_empty_status(cib_xml); /* Default the three version attributes to 0 if unset. The schema requires From 7045cc0ec6db72bc21c9af79f16ae5db09f1194d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 15:35:52 -0800 Subject: [PATCH 036/350] Refactor: based: Best practices in cib_rename() * Rename function to archive_unusable_file(). * Add Doxygen. * Improve log messages. * Rename new_fd variable to fd. * Allow fd == 0. This should never happen in practice, but it's odd to check (fd < 0) for error and (fd > 0) for success when 0 is a valid file descriptor. * Close fd sooner on success. mkstemp() is the safe and idiomatic way to generate a temp file name, because it avoids race conditions that can occur when generating a file name and then creating a file. However, we don't need the file to be open, and we don't need the fd except for the purpose of closing the file. By separating the (fd < 0) case from the (rename(...) < 0) case, we can be sure that (fd >= 0) when we call close(). * Rename old and new variables to old_file and new_file, respectively. I suspect that the use of "new" may be problematic when compiling as C++. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 52 ++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 032562c6623..f4d994d0177 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -191,28 +191,50 @@ based_io_init(void) write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL); } +/*! + * \internal + * \brief Archive a CIB file or its saved digest file + * + * When a CIB file's calculated digest doesn't match its saved one, we archive + * both the CIB file and its digest (".sig") file. This way the contents can be + * inspected for troubleshooting purposes. + * + * The file is renamed with a unique name using the \c mkstemp() template + * \c "cib.auto.XXXXXX", in the same directory (\c cib_root). + * + * \param[in] old_file Original file path + */ static void -cib_rename(const char *old) +archive_unusable_file(const char *old_file) { - int new_fd; - char *new = pcmk__assert_asprintf("%s/cib.auto.XXXXXX", cib_root); + int fd = 0; + char *new_file = pcmk__assert_asprintf("%s/cib.auto.XXXXXX", cib_root); umask(S_IWGRP | S_IWOTH | S_IROTH); - new_fd = mkstemp(new); + fd = mkstemp(new_file); - if ((new_fd < 0) || (rename(old, new) < 0)) { - pcmk__err("Couldn't archive unusable file %s (disabling disk writes " - "and continuing)", - old); + if (fd < 0) { + pcmk__err("Failed to create a temp file to archive unusable file %s: " + "%s. Disabling disk writes and continuing.", old_file, + strerror(errno)); writes_enabled = false; - } else { - pcmk__err("Archived unusable file %s as %s", old, new); + goto done; } - if (new_fd > 0) { - close(new_fd); + close(fd); + + if (rename(old_file, new_file) < 0) { + pcmk__err("Failed to archive unusable file %s as %s: %s. Disabling " + "disk writes and continuing.", old_file, new_file, + strerror(errno)); + writes_enabled = false; + goto done; } - free(new); + + pcmk__err("Archived unusable file %s as %s", old_file, new_file); + +done: + free(new_file); } #define CIBFILE "cib.xml" @@ -260,8 +282,8 @@ read_current_cib(void) if (rc == pcmk_rc_cib_modified) { // Archive the original files so the contents are not lost - cib_rename(cibfile_path); - cib_rename(sigfile_path); + archive_unusable_file(cibfile_path); + archive_unusable_file(sigfile_path); } done: From 1431f80017a3ea10ebe4e607516765391fe441ba Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 17:07:25 -0800 Subject: [PATCH 037/350] Refactor: based: Archive CIB file and digest file in subdirectory Previously, in case of a digest mismatch, we renamed the CIB file and the digest file in cib_root to new unique names generated by mkstemp(). Without checking the error logs, the file names did not make it clear that the two files were related, or which one was the CIB file and which one was the digest file. Now, we create a new subdirectory of cib_root using mkdtemp(). Then we move both the CIB file and the digest file to the new subdirectory. They keep the base names "cib.xml" and "cib.xml.sig". It's not necessary for archive_on_digest_mismatch() to take arguments, but this avoids the need to allocate redundant strings for the old paths. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 78 ++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index f4d994d0177..c6d11f5b134 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -193,52 +193,85 @@ based_io_init(void) /*! * \internal - * \brief Archive a CIB file or its saved digest file + * \brief Rename a CIB or digest file after digest mismatch + * + * This is just a wrapper for logging an error. The caller should disable writes + * on error. + * + * \param[in] old_path Original file path + * \param[in] new_path New file path + * + * \return Standard Pacemaker return code + */ +static int +rename_one(const char *old_path, const char *new_path) +{ + int rc = rename(old_path, new_path); + + if (rc == 0) { + return pcmk_rc_ok; + } + + rc = errno; + pcmk__err("Failed to rename %s to %s after digest mismatch: %s. Disabling " + "disk writes.", old_path, new_path, strerror(rc)); + return rc; +} + +#define CIBFILE "cib.xml" + +/*! + * \internal + * \brief Archive the current CIB file in \c cib_root with its saved digest file * * When a CIB file's calculated digest doesn't match its saved one, we archive * both the CIB file and its digest (".sig") file. This way the contents can be * inspected for troubleshooting purposes. * - * The file is renamed with a unique name using the \c mkstemp() template - * \c "cib.auto.XXXXXX", in the same directory (\c cib_root). + * A subdirectory with a unique name is created in \c cib_root, using the + * \c mkdtemp() template \c "cib.auto.XXXXXX". Then \c CIB_FILE and + * CIB_FILE ".sig" are moved to that directory. * - * \param[in] old_file Original file path + * \param[in] old_cibfile_path Original path of CIB file + * \param[in] old_sigfile_path Original path of digest file */ static void -archive_unusable_file(const char *old_file) +archive_on_digest_mismatch(const char *old_cibfile_path, + const char *old_sigfile_path) { - int fd = 0; - char *new_file = pcmk__assert_asprintf("%s/cib.auto.XXXXXX", cib_root); + char *new_dir = pcmk__assert_asprintf("%s/cib.auto.XXXXXX", cib_root); + char *new_cibfile_path = NULL; + char *new_sigfile_path = NULL; umask(S_IWGRP | S_IWOTH | S_IROTH); - fd = mkstemp(new_file); - if (fd < 0) { - pcmk__err("Failed to create a temp file to archive unusable file %s: " - "%s. Disabling disk writes and continuing.", old_file, - strerror(errno)); + if (mkdtemp(new_dir) == NULL) { + pcmk__err("Failed to create directory to archive %s and %s after " + "digest mismatch: %s. Disabling disk writes.", + old_cibfile_path, old_sigfile_path, strerror(errno)); writes_enabled = false; goto done; } - close(fd); + new_cibfile_path = pcmk__assert_asprintf("%s/%s", new_dir, CIBFILE); + new_sigfile_path = pcmk__assert_asprintf("%s.sig", new_cibfile_path); + + if ((rename_one(old_cibfile_path, new_cibfile_path) != pcmk_rc_ok) + || (rename_one(old_sigfile_path, new_sigfile_path) != pcmk_rc_ok)) { - if (rename(old_file, new_file) < 0) { - pcmk__err("Failed to archive unusable file %s as %s: %s. Disabling " - "disk writes and continuing.", old_file, new_file, - strerror(errno)); writes_enabled = false; goto done; } - pcmk__err("Archived unusable file %s as %s", old_file, new_file); + pcmk__err("Archived %s and %s in %s after digest mismatch", + old_cibfile_path, old_sigfile_path, new_dir); done: - free(new_file); + free(new_dir); + free(new_cibfile_path); + free(new_sigfile_path); } -#define CIBFILE "cib.xml" - /*! * \internal * \brief Read CIB XML from \c CIBFILE in the \c cib_root directory @@ -282,8 +315,7 @@ read_current_cib(void) if (rc == pcmk_rc_cib_modified) { // Archive the original files so the contents are not lost - archive_unusable_file(cibfile_path); - archive_unusable_file(sigfile_path); + archive_on_digest_mismatch(cibfile_path, sigfile_path); } done: From 6f195320f9a53e74d3533d054bf6d73a878ba103 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 18:32:03 -0800 Subject: [PATCH 038/350] Refactor: based: Simplify cib_archive_filter() Also add Doxygen and rename to backup_cib_filter(). The trace/debug messages don't seem especially useful, and we recently got rid of similar ones that were commented out of schema_filter(). Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 50 +++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index c6d11f5b134..e898530d77d 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -324,36 +324,34 @@ read_current_cib(void) return cib_xml; } -static int cib_archive_filter(const struct dirent * a) +/*! + * \internal + * \brief \c scandir() filter for backup CIB files in \c cib_root + * + * \param[in] entry Directory entry + * + * \retval 1 if the entry is a regular file whose name begins with \c "cib-" and + * does not end with ".sig" + * \retval 0 otherwise + */ +static int +backup_cib_filter(const struct dirent *entry) { - int rc = 0; - // Looking for regular files starting with "cib-" and not ending in .sig - struct stat s; - char *a_path = pcmk__assert_asprintf("%s/%s", cib_root, a->d_name); + char *path = pcmk__assert_asprintf("%s/%s", cib_root, entry->d_name); + struct stat sb; + int rc = stat(path, &sb); - if(stat(a_path, &s) != 0) { - rc = errno; - pcmk__trace("%s - stat failed: %s (%d)", a->d_name, pcmk_rc_str(rc), - rc); - rc = 0; + free(path); - } else if (!S_ISREG(s.st_mode)) { - pcmk__trace("%s - wrong type (%#o)", a->d_name, - (unsigned int) (s.st_mode & S_IFMT)); - - } else if (!g_str_has_prefix(a->d_name, "cib-")) { - pcmk__trace("%s - wrong prefix", a->d_name); - - } else if (g_str_has_suffix(a->d_name, ".sig")) { - pcmk__trace("%s - wrong suffix", a->d_name); - - } else { - pcmk__debug("%s - candidate", a->d_name); - rc = 1; + if (rc != 0) { + pcmk__warn("Filtering %s/%s during scan for backup CIB: stat() failed: " + "%s", cib_root, entry->d_name, strerror(errno)); + return 0; } - free(a_path); - return rc; + return S_ISREG(sb.st_mode) + && g_str_has_prefix(entry->d_name, "cib-") + && !g_str_has_suffix(entry->d_name, ".sig"); } static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) @@ -404,7 +402,7 @@ read_backup_cib(void) { xmlNode *cib_xml = NULL; struct dirent **namelist = NULL; - int num_files = scandir(cib_root, &namelist, cib_archive_filter, + int num_files = scandir(cib_root, &namelist, backup_cib_filter, cib_archive_sort); if (num_files < 0) { From c91a7a7fe7dc6de2239c27b011fc8d8ff3c4c99c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 19:19:11 -0800 Subject: [PATCH 039/350] Refactor: based: Simplify cib_archive_sort() Also add Doxygen, rename to compare_backup_cibs(), and create a helper function. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 85 +++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index e898530d77d..c99ff1d3641 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -354,40 +354,71 @@ backup_cib_filter(const struct dirent *entry) && !g_str_has_suffix(entry->d_name, ".sig"); } -static int cib_archive_sort(const struct dirent ** a, const struct dirent **b) +/*! + * \internal + * \brief Get a file's last change time (\c ctime) + * + * The file is assumed to be a backup CIB file in the \c cib_root directory. + * + * \param[in] file Base name of file + * + * \return Last change time of \p file, or 0 on \c stat() failure + */ +static time_t +get_backup_cib_ctime(const char *file) { - /* Order by creation date - most recently created file first */ - int rc = 0; - struct stat buf; - - time_t a_age = 0; - time_t b_age = 0; + char *path = pcmk__assert_asprintf("%s/%s", cib_root, file); + struct stat sb; + int rc = stat(path, &sb); - char *a_path = pcmk__assert_asprintf("%s/%s", cib_root, a[0]->d_name); - char *b_path = pcmk__assert_asprintf("%s/%s", cib_root, b[0]->d_name); + free(path); - if(stat(a_path, &buf) == 0) { - a_age = buf.st_ctime; - } - if(stat(b_path, &buf) == 0) { - b_age = buf.st_ctime; + if (rc != 0) { + pcmk__warn("Failed to stat() %s/%s while sorting backup CIBs: %s", + cib_root, file, strerror(errno)); + return 0; } - free(a_path); - free(b_path); + return sb.st_ctime; +} + +/*! + * \internal + * \brief Compare directory entries based on their last change times + * + * The entries are assumed to be CIB files in the \c cib_root directory. + * + * \param[in] entry1 First directory entry to compare + * \param[in] entry2 Second directory entry to compare + * + * \retval -1 if \p entry1 was changed more recently than \p entry2 + * \retval 0 if \p entry1 was last changed at the same timestamp as \p entry2 + * \retval 1 if \p entry1 was changed less recently than \p entry2 + */ +static int +compare_backup_cibs(const struct dirent **entry1, const struct dirent **entry2) +{ + time_t ctime1 = get_backup_cib_ctime((*entry1)->d_name); + time_t ctime2 = get_backup_cib_ctime((*entry2)->d_name); + + if (ctime1 > ctime2) { + pcmk__trace("%s/%s (%lld) newer than %s/%s (%lld)", + cib_root, (*entry1)->d_name, (long long) ctime1, + cib_root, (*entry2)->d_name, (long long) ctime2); + return -1; + } - if (a_age > b_age) { - // a newer than b - rc = -1; - } else if (a_age < b_age) { - // a older than b - rc = 1; + if (ctime1 < ctime2) { + pcmk__trace("%s/%s (%lld) older than %s/%s (%lld)", + cib_root, (*entry1)->d_name, (long long) ctime1, + cib_root, (*entry2)->d_name, (long long) ctime2); + return 1; } - pcmk__trace("%s (%lu) vs. %s (%lu) : %d", - a[0]->d_name, (unsigned long)a_age, - b[0]->d_name, (unsigned long)b_age, rc); - return rc; + pcmk__trace("%s/%s (%lld) same age as %s/%s (%lld)", + cib_root, (*entry1)->d_name, (long long) ctime1, + cib_root, (*entry2)->d_name, (long long) ctime2); + return 0; } /*! @@ -403,7 +434,7 @@ read_backup_cib(void) xmlNode *cib_xml = NULL; struct dirent **namelist = NULL; int num_files = scandir(cib_root, &namelist, backup_cib_filter, - cib_archive_sort); + compare_backup_cibs); if (num_files < 0) { pcmk__err("Could not check for CIB backups in %s: %s", cib_root, From 4a52ca5c820e2c77ebd3243ef4c8649be3ffffef Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 19:42:52 -0800 Subject: [PATCH 040/350] Doc: based: Add Doxygen for based_read_cib() Note that the return value is guaranteed not to be NULL, because the createEmptyCib() return value is guaranteed not to be NULL. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index c99ff1d3641..66080ffd7f3 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -522,6 +522,25 @@ set_default_if_unset(xmlNode *cib_xml, const char *version_attr) pcmk__xe_set_int(cib_xml, version_attr, 0); } +/*! + * \internal + * \brief Read the most recent CIB from a file in \c cib_root + * + * This function first tries to read the CIB from a file called \c "cib.xml" in + * the \c cib_root directory. + * + * If that fails or there is a digest mismatch, it tries all the backup CIB + * files in \c cib_root, in order from most recently changed to least, moving to + * the next backup file on failure or digest mismatch. + * + * If no valid CIB file is found, this function generates an empty CIB. + * + * \return The most current CIB XML available, or an empty CIB if none is + * available (guaranteed not to be \c NULL) + * + * \note The caller is responsible for freeing the return value using + * \c pcmk__xml_free(). + */ xmlNode * based_read_cib(void) { From 35717a9b22de5cd3c07ead6334ecd4bf748a904c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 19:44:37 -0800 Subject: [PATCH 041/350] Refactor: based: Drop uninitializeCib() As long as the_cib is a global variable, this function is unnecessary. Also, it was too complicated -- it can be a one-liner, as shown here. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_io.c | 13 ------------- daemons/based/pacemaker-based.h | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f02bfe74042..0ac24a326b6 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1363,7 +1363,7 @@ terminate_cib(int exit_status) remote_tls_fd = 0; } - uninitializeCib(); + g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error if (exit_status > CRM_EX_OK) { diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 66080ffd7f3..da77bcfdba7 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -588,19 +588,6 @@ based_read_cib(void) return cib_xml; } -void -uninitializeCib(void) -{ - xmlNode *tmp_cib = the_cib; - - if (tmp_cib == NULL) { - return; - } - - the_cib = NULL; - pcmk__xml_free(tmp_cib); -} - /* * This method will free the old CIB pointer on success and the new one * on failure. diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index a16485320b8..eb70cb4230e 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -74,7 +74,6 @@ int cib_process_request(xmlNode *request, bool privileged, void cib_shutdown(int nsig); void terminate_cib(int exit_status); -void uninitializeCib(void); int activateCibXml(xmlNode *doc, bool to_disk, const char *op); int cib_process_shutdown_req(const char *op, int options, const char *section, From da31de7e91c198cc43886dbee46aeb8d35f38efe Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 19:47:08 -0800 Subject: [PATCH 042/350] Refactor: based: activateCibXml() returns a standard return code Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 - daemons/based/based_io.c | 4 ++-- daemons/based/pacemaker-based.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0ac24a326b6..1330b1d5c87 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1128,7 +1128,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, (config_changed? " changed" : "")); rc = activateCibXml(result_cib, config_changed, op); - rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { pcmk__err("Failed to activate new CIB: %s", pcmk_rc_str(rc)); } diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index da77bcfdba7..118e67ed663 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -605,7 +605,7 @@ activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) pcmk__debug("Triggering CIB write for %s op", op); mainloop_set_trigger(write_trigger); } - return pcmk_ok; + return pcmk_rc_ok; } pcmk__err("Ignoring invalid CIB"); @@ -615,5 +615,5 @@ activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) pcmk__crit("Could not write out new CIB and no saved version to revert " "to"); } - return -ENODATA; + return ENODATA; } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 28662d690c0..49b53a6073d 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -406,7 +406,7 @@ startCib(void) xmlNode *cib = based_read_cib(); int port = 0; - if (activateCibXml(cib, true, "start") != 0) { + if (activateCibXml(cib, true, "start") != pcmk_rc_ok) { return false; } From e410cdfbffb5be1a9fb2ca078b286143caa52a34 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 19:53:39 -0800 Subject: [PATCH 043/350] Refactor: based: activateCibXml() CRM_CHECKs that new_cib != NULL There are two callers. The argument in startCib() is clearly non-NULL. The argument in cib_process_command() is unsurprisingly less clear. result_cib gets set by cib_perform_op(), and we call activateCibXml() only if we're performing an operation that modifies the CIB, rc is pcmk_rc_ok, and (result_cib != the_cib). result_cib should always be non-NULL in that case. And if it ever is NULL, then this is something I'd like to find out about via an assertion log. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 118e67ed663..1e8abafe78f 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -595,25 +595,15 @@ based_read_cib(void) int activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) { - if (new_cib) { - xmlNode *saved_cib = the_cib; - - pcmk__assert(new_cib != saved_cib); - the_cib = new_cib; - pcmk__xml_free(saved_cib); - if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) { - pcmk__debug("Triggering CIB write for %s op", op); - mainloop_set_trigger(write_trigger); - } - return pcmk_rc_ok; - } + xmlNode *saved_cib = the_cib; - pcmk__err("Ignoring invalid CIB"); - if (the_cib) { - pcmk__warn("Reverting to last known CIB"); - } else { - pcmk__crit("Could not write out new CIB and no saved version to revert " - "to"); + CRM_CHECK((new_cib != NULL) && (new_cib != the_cib), return ENODATA); + + the_cib = new_cib; + pcmk__xml_free(saved_cib); + if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) { + pcmk__debug("Triggering CIB write for %s op", op); + mainloop_set_trigger(write_trigger); } - return ENODATA; + return pcmk_rc_ok; } From 9007b5cf8efc2ab3c0e2ca082d93e4af401a5de2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 20:10:27 -0800 Subject: [PATCH 044/350] Refactor: based: Best practices in activateCibXml() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_io.c | 28 +++++++++++++++++++++------- daemons/based/based_io.h | 3 +++ daemons/based/pacemaker-based.c | 2 +- daemons/based/pacemaker-based.h | 2 -- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1330b1d5c87..c1c7d8721f0 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1127,7 +1127,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xe_get(result_cib, PCMK_XA_NUM_UPDATES), (config_changed? " changed" : "")); - rc = activateCibXml(result_cib, config_changed, op); + rc = based_activate_cib(result_cib, config_changed, op); if (rc != pcmk_rc_ok) { pcmk__err("Failed to activate new CIB: %s", pcmk_rc_str(rc)); } diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 1e8abafe78f..6eec84af668 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -588,22 +588,36 @@ based_read_cib(void) return cib_xml; } -/* - * This method will free the old CIB pointer on success and the new one - * on failure. +/*! + * \internal + * \brief Activate new CIB XML + * + * This function frees the existing \c the_cib and points it to \p new_cib. + * + * \param[in] new_cib CIB XML to activate (must not be \c NULL or equal to + * \c the_cib) + * \param[in] to_disk If \c true and if the CIB status is OK and writes are + * enabled, trigger the new CIB to be written to disk + * \param[in] op Operation that triggered the activation (for logging + * only) + * + * \return Standard Pacemaker return code + * + * \note This function takes ownership of \p new_cib by assigning it to + * \c the_cib. The caller should not free it. */ int -activateCibXml(xmlNode *new_cib, bool to_disk, const char *op) +based_activate_cib(xmlNode *new_cib, bool to_disk, const char *op) { - xmlNode *saved_cib = the_cib; - CRM_CHECK((new_cib != NULL) && (new_cib != the_cib), return ENODATA); + pcmk__xml_free(the_cib); the_cib = new_cib; - pcmk__xml_free(saved_cib); + if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) { pcmk__debug("Triggering CIB write for %s op", op); mainloop_set_trigger(write_trigger); } + return pcmk_rc_ok; } diff --git a/daemons/based/based_io.h b/daemons/based/based_io.h index 2c3fab0ffea..f9bb5011a1a 100644 --- a/daemons/based/based_io.h +++ b/daemons/based/based_io.h @@ -10,10 +10,13 @@ #ifndef BASED_IO__H #define BASED_IO__H +#include + #include // xmlNode void based_io_init(void); void based_enable_writes(int nsig); xmlNode *based_read_cib(void); +int based_activate_cib(xmlNode *new_cib, bool to_disk, const char *op); #endif // BASED_IO__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 49b53a6073d..c1da0103f5a 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -406,7 +406,7 @@ startCib(void) xmlNode *cib = based_read_cib(); int port = 0; - if (activateCibXml(cib, true, "start") != pcmk_rc_ok) { + if (based_activate_cib(cib, true, "start") != pcmk_rc_ok) { return false; } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index eb70cb4230e..8ebb8f4f4fd 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -74,8 +74,6 @@ int cib_process_request(xmlNode *request, bool privileged, void cib_shutdown(int nsig); void terminate_cib(int exit_status); -int activateCibXml(xmlNode *doc, bool to_disk, const char *op); - int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, From de5821a33515a4cda591ec2e963d80c8c9c0371b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 22:54:57 -0800 Subject: [PATCH 045/350] Refactor: based: Drop unused includes from based_operation.c Also include stddef.h for NULL, and rename cib_op_functions to op_functions so that it doesn't look like a public libcib symbol. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 6c8e93f4b83..881464517de 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2024 the Pacemaker project contributors + * Copyright 2008-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,13 +9,11 @@ #include -#include +#include // NULL -#include -#include #include -static const cib__op_fn_t cib_op_functions[] = { +static const cib__op_fn_t op_functions[] = { [cib__op_abs_delete] = cib_process_delete_absolute, [cib__op_apply_patch] = cib_server_process_diff, [cib__op_bump] = cib_process_bump, @@ -53,8 +51,8 @@ based_get_op_function(const cib__operation_t *operation) pcmk__assert(type >= 0); - if (type >= PCMK__NELEM(cib_op_functions)) { + if (type >= PCMK__NELEM(op_functions)) { return NULL; } - return cib_op_functions[type]; + return op_functions[type]; } From d5d91acbddf3a8cb68a31f96a2cc4165639fc691 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 22:59:40 -0800 Subject: [PATCH 046/350] Refactor: based: New based_operation.h Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_operation.h | 17 +++++++++++++++++ daemons/based/pacemaker-based.h | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 daemons/based/based_operation.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 4fd4a64fe97..55c833e2582 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -15,6 +15,7 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_io.h \ + based_operation.h \ based_transaction.h \ pacemaker-based.h diff --git a/daemons/based/based_operation.h b/daemons/based/based_operation.h new file mode 100644 index 00000000000..ee016466531 --- /dev/null +++ b/daemons/based/based_operation.h @@ -0,0 +1,17 @@ +/* + * Copyright 2023-2025 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_OPERATION__H +#define BASED_OPERATION__H + +#include // cib__* + +cib__op_fn_t based_get_op_function(const cib__operation_t *operation); + +#endif // BASED_OPERATION__H diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 8ebb8f4f4fd..cba8a045f5e 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -31,6 +31,7 @@ #include #include "based_io.h" +#include "based_operation.h" #include "based_transaction.h" #include @@ -118,7 +119,6 @@ int cib_process_schemas(const char *op, int options, const char *section, void send_sync_request(const char *host); int sync_our_cib(xmlNode *request, bool all); -cib__op_fn_t based_get_op_function(const cib__operation_t *operation); void cib_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *update, xmlNode *diff); From be6504a81ea7dc203c0867ffe9857caf57091e77 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 23:07:42 -0800 Subject: [PATCH 047/350] Refactor: based: Avoid xmlNodePtr and clean up includes This is just some miscellaneous cleanup. For the most part, we use xmlNode * instead of xmlNodePtr nowadays, because "const xmlNodePtr" doesn't work. Also drop some unused includes and add some that were missing in based_transaction.{c,h}. Signed-off-by: Reid Wahl --- daemons/based/based_transaction.c | 13 ++++++------- daemons/based/based_transaction.h | 11 +++++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 258861cdea6..a9df2950d53 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -11,8 +11,7 @@ #include -#include -#include +#include // xmlNode #include "pacemaker-based.h" @@ -54,8 +53,8 @@ based_transaction_source_str(const pcmk__client_t *client, const char *origin) * \return Standard Pacemaker return code */ static int -process_transaction_requests(xmlNodePtr transaction, - const pcmk__client_t *client, const char *source) +process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, + const char *source) { for (xmlNode *request = pcmk__xe_first_child(transaction, PCMK__XE_CIB_COMMAND, NULL, @@ -116,10 +115,10 @@ process_transaction_requests(xmlNodePtr transaction, * success, and for freeing it on failure. */ int -based_commit_transaction(xmlNodePtr transaction, const pcmk__client_t *client, - const char *origin, xmlNodePtr *result_cib) +based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, + const char *origin, xmlNode **result_cib) { - xmlNodePtr saved_cib = the_cib; + xmlNode *saved_cib = the_cib; int rc = pcmk_rc_ok; char *source = NULL; diff --git a/daemons/based/based_transaction.h b/daemons/based/based_transaction.h index 9935c736a68..19dc01ba529 100644 --- a/daemons/based/based_transaction.h +++ b/daemons/based/based_transaction.h @@ -1,5 +1,5 @@ /* - * Copyright 2023 the Pacemaker project contributors + * Copyright 2023-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,15 +10,14 @@ #ifndef BASED_TRANSACTION__H #define BASED_TRANSACTION__H -#include +#include // xmlNode -#include +#include // pcmk__client_t char *based_transaction_source_str(const pcmk__client_t *client, const char *origin); -int based_commit_transaction(xmlNodePtr transaction, - const pcmk__client_t *client, - const char *origin, xmlNodePtr *result_cib); +int based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, + const char *origin, xmlNode **result_cib); #endif // BASED_TRANSACTION__H From d88b0745af72cea4b6ab20e1ce6b7127de0f4ab0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 23:16:58 -0800 Subject: [PATCH 048/350] Refactor: based: New based_notify.h Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_callbacks.c | 4 ++-- daemons/based/based_notify.c | 6 +++--- daemons/based/based_notify.h | 19 +++++++++++++++++++ daemons/based/pacemaker-based.h | 5 +---- 5 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 daemons/based/based_notify.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 55c833e2582..62c5d88a7b5 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -15,6 +15,7 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_io.h \ + based_notify.h \ based_operation.h \ based_transaction.h \ pacemaker-based.h diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c1c7d8721f0..b06e3e4d576 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1182,8 +1182,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, cib_dryrun|cib_inhibit_notify|cib_transaction)) { pcmk__trace("Sending notifications %d", pcmk__is_set(call_options, cib_dryrun)); - cib_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, client_name, - originator, input, cib_diff); + based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, + client_name, originator, input, cib_diff); } pcmk__log_xml_patchset(LOG_TRACE, cib_diff); diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index dba81da44c3..91dc28e9b3c 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -150,9 +150,9 @@ cib_notify_send(const xmlNode *xml) } void -cib_diff_notify(const char *op, int result, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *update, xmlNode *diff) +based_diff_notify(const char *op, int result, const char *call_id, + const char *client_id, const char *client_name, + const char *origin, xmlNode *update, xmlNode *diff) { int add_updates = 0; int add_epoch = 0; diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h new file mode 100644 index 00000000000..0943d8d9dd3 --- /dev/null +++ b/daemons/based/based_notify.h @@ -0,0 +1,19 @@ +/* + * Copyright 2025 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_NOTIFY__H +#define BASED_NOTIFY__H + +#include // xmlNode + +void based_diff_notify(const char *op, int result, const char *call_id, + const char *client_id, const char *client_name, + const char *origin, xmlNode *update, xmlNode *diff); + +#endif // BASED_NOTIFY__H diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index cba8a045f5e..78b5f9655b5 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -32,6 +32,7 @@ #include "based_io.h" #include "based_operation.h" +#include "based_notify.h" #include "based_transaction.h" #include @@ -119,10 +120,6 @@ int cib_process_schemas(const char *op, int options, const char *section, void send_sync_request(const char *host); int sync_our_cib(xmlNode *request, bool all); -void cib_diff_notify(const char *op, int result, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *update, xmlNode *diff); - static inline const char * cib_config_lookup(const char *opt) { From ae7f7f0b6764ff39e3f29e5671ae92423dfa2316 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 23:37:22 -0800 Subject: [PATCH 049/350] Refactor: libcib: Drop cib_diff_version_details() It's a simple wrapper around pcmk__xml_patchset_versions but requires seven arguments. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 17 +++++------------ daemons/based/based_notify.c | 34 +++++++++++++--------------------- include/crm/cib/internal.h | 3 --- lib/cib/cib_utils.c | 20 -------------------- 4 files changed, 18 insertions(+), 56 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 0aaedec5001..0e4dd7b8bfc 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -307,23 +307,16 @@ cib_server_process_diff(const char *op, int options, const char *section, xmlNod // The primary instance should never ignore a diff if (sync_in_progress && !based_is_primary) { - int diff_add_updates = 0; - int diff_add_epoch = 0; - int diff_add_admin_epoch = 0; + int source[] = { 0, 0, 0 }; + int target[] = { 0, 0, 0 }; - int diff_del_updates = 0; - int diff_del_epoch = 0; - int diff_del_admin_epoch = 0; - - cib_diff_version_details(input, - &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, - &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + pcmk__xml_patchset_versions(input, source, target); sync_in_progress++; pcmk__notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in " "progress)", - diff_del_admin_epoch, diff_del_epoch, diff_del_updates, - diff_add_admin_epoch, diff_add_epoch, diff_add_updates); + source[0], source[1], source[2], + target[0], target[1], target[2]); return -pcmk_err_diff_resync; } diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 91dc28e9b3c..c00c35a4f8c 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -154,13 +154,8 @@ based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *update, xmlNode *diff) { - int add_updates = 0; - int add_epoch = 0; - int add_admin_epoch = 0; - - int del_updates = 0; - int del_epoch = 0; - int del_admin_epoch = 0; + int source[] = { 0, 0, 0 }; + int target[] = { 0, 0, 0 }; uint8_t log_level = LOG_TRACE; @@ -175,31 +170,28 @@ based_diff_notify(const char *op, int result, const char *call_id, log_level = LOG_WARNING; } - cib_diff_version_details(diff, &add_admin_epoch, &add_epoch, &add_updates, - &del_admin_epoch, &del_epoch, &del_updates); + /* @TODO Check return code? How should we handle an error? Are these log + * messages even useful? + */ + pcmk__xml_patchset_versions(diff, source, target); - if ((add_admin_epoch != del_admin_epoch) - || (add_epoch != del_epoch) - || (add_updates != del_updates)) { + if ((source[0] != target[0]) + || (source[1] != target[1]) + || (source[2] != target[2])) { do_crm_log(log_level, "Updated CIB generation %d.%d.%d to %d.%d.%d from client " "%s%s%s (%s) (%s)", - del_admin_epoch, del_epoch, del_updates, - add_admin_epoch, add_epoch, add_updates, - client_name, + source[0], source[1], source[2], + target[0], target[1], target[2], client_name, ((call_id != NULL)? " call " : ""), pcmk__s(call_id, ""), pcmk__s(origin, "unspecified peer"), pcmk_strerror(result)); - } else if ((add_admin_epoch != 0) - || (add_epoch != 0) - || (add_updates != 0)) { - + } else if ((target[0] != 0) || (target[1] != 0) || (target[2] != 0)) { do_crm_log(log_level, "Local-only change to CIB generation %d.%d.%d from client " "%s%s%s (%s) (%s)", - add_admin_epoch, add_epoch, add_updates, - client_name, + target[0], target[1], target[2], client_name, ((call_id != NULL)? " call " : ""), pcmk__s(call_id, ""), pcmk__s(origin, "unspecified peer"), pcmk_strerror(result)); } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 4c4c54fd473..d493b69e2a2 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -95,9 +95,6 @@ enum cib__op_type { cib__op_schemas, }; -gboolean cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates, - int *_admin_epoch, int *_epoch, int *_updates); - gboolean cib_read_config(GHashTable * options, xmlNode * current_cib); typedef int (*cib__op_fn_t)(const char *, int, const char *, xmlNode *, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 043c57e14ec..70c409ba1f8 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -39,26 +39,6 @@ cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates) return TRUE; } -gboolean -cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates, - int *_admin_epoch, int *_epoch, int *_updates) -{ - int add[] = { 0, 0, 0 }; - int del[] = { 0, 0, 0 }; - - pcmk__xml_patchset_versions(diff, del, add); - - *admin_epoch = add[0]; - *epoch = add[1]; - *updates = add[2]; - - *_admin_epoch = del[0]; - *_epoch = del[1]; - *_updates = del[2]; - - return TRUE; -} - /*! * \internal * \brief Get the XML patchset from a CIB diff notification From 65cd2a7839cef26055daa4fcf2a09e6a46274111 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:08:13 -0800 Subject: [PATCH 050/350] Log: based: Drop redundant messages from based_diff_notify() There's a message near the end of cib_process_request() that tells us almost exactly the same things. The only thing that seems to be missing is the pre-operation CIB versions. I don't think that's a big loss. The only caller of based_diff_notify() is cib_process_command(). The message in cib_process_request() gets logged shortly after the sole call to cib_process_command(). So any time the based_diff_notify() message would have been logged, the remaining one in cib_process_request() will be too. Signed-off-by: Reid Wahl --- daemons/based/based_notify.c | 37 +----------------------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index c00c35a4f8c..a05eb1066c5 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.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. * @@ -154,11 +154,6 @@ based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *update, xmlNode *diff) { - int source[] = { 0, 0, 0 }; - int target[] = { 0, 0, 0 }; - - uint8_t log_level = LOG_TRACE; - xmlNode *update_msg = NULL; xmlNode *wrapper = NULL; @@ -166,36 +161,6 @@ based_diff_notify(const char *op, int result, const char *call_id, return; } - if (result != pcmk_ok) { - log_level = LOG_WARNING; - } - - /* @TODO Check return code? How should we handle an error? Are these log - * messages even useful? - */ - pcmk__xml_patchset_versions(diff, source, target); - - if ((source[0] != target[0]) - || (source[1] != target[1]) - || (source[2] != target[2])) { - - do_crm_log(log_level, - "Updated CIB generation %d.%d.%d to %d.%d.%d from client " - "%s%s%s (%s) (%s)", - source[0], source[1], source[2], - target[0], target[1], target[2], client_name, - ((call_id != NULL)? " call " : ""), pcmk__s(call_id, ""), - pcmk__s(origin, "unspecified peer"), pcmk_strerror(result)); - - } else if ((target[0] != 0) || (target[1] != 0) || (target[2] != 0)) { - do_crm_log(log_level, - "Local-only change to CIB generation %d.%d.%d from client " - "%s%s%s (%s) (%s)", - target[0], target[1], target[2], client_name, - ((call_id != NULL)? " call " : ""), pcmk__s(call_id, ""), - pcmk__s(origin, "unspecified peer"), pcmk_strerror(result)); - } - update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY); pcmk__xe_set(update_msg, PCMK__XA_T, PCMK__VALUE_CIB_NOTIFY); From f786bc8645e5ed786bc8a97bba9271979bef1ed9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 11:22:57 -0800 Subject: [PATCH 051/350] Refactor: libcrmcommon: New pcmk__mainloop_child_exit_fn_t typedef Signed-off-by: Reid Wahl --- include/crm/common/mainloop.h | 21 +++++++++------------ include/crm/common/mainloop_internal.h | 2 +- lib/common/mainloop.c | 18 +++++++++--------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h index a680c44bd04..e10e0625976 100644 --- a/include/crm/common/mainloop.h +++ b/include/crm/common/mainloop.h @@ -44,6 +44,10 @@ typedef struct mainloop_child_s mainloop_child_t; // NOTE: sbd (as of at least 1.5.2) uses this typedef struct mainloop_timer_s mainloop_timer_t; +//! \deprecated This has been for internal use only since its creation. +typedef void (*pcmk__mainloop_child_exit_fn_t)(mainloop_child_t *p, int core, + int signo, int exitcode); + void mainloop_cleanup(void); // NOTE: sbd (as of at least 1.5.2) uses this @@ -167,20 +171,13 @@ void mainloop_del_fd(mainloop_io_t * client); * Create a new tracked process * To track a process group, use -pid */ -void mainloop_child_add(pid_t pid, - int timeout, - const char *desc, +void mainloop_child_add(pid_t pid, int timeout, const char *desc, void *userdata, - void (*callback)(mainloop_child_t *p, int core, - int signo, int exitcode)); + pcmk__mainloop_child_exit_fn_t exit_fn); -void mainloop_child_add_with_flags(pid_t pid, - int timeout, - const char *desc, - void *userdata, - enum mainloop_child_flags, - void (*callback)(mainloop_child_t *p, int core, - int signo, int exitcode)); +void mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, + void *userdata, enum mainloop_child_flags, + pcmk__mainloop_child_exit_fn_t exit_fn); void *mainloop_child_userdata(mainloop_child_t * child); int mainloop_child_timeout(mainloop_child_t * child); diff --git a/include/crm/common/mainloop_internal.h b/include/crm/common/mainloop_internal.h index 05c3f45672e..db3a49b64b4 100644 --- a/include/crm/common/mainloop_internal.h +++ b/include/crm/common/mainloop_internal.h @@ -35,7 +35,7 @@ struct mainloop_child_s { enum mainloop_child_flags flags; /* Called when a process dies */ - void (*callback)(mainloop_child_t *p, int core, int signo, int exitcode); + pcmk__mainloop_child_exit_fn_t exit_fn; }; int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 5652fe89bb7..4d3dd9ae6ac 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -1158,8 +1158,8 @@ child_waitpid(mainloop_child_t *child, int flags) callback_needed = false; } - if (callback_needed && child->callback) { - child->callback(child, core, signo, exitcode); + if (callback_needed && child->exit_fn) { + child->exit_fn(child, core, signo, exitcode); } return callback_needed; } @@ -1251,9 +1251,10 @@ mainloop_child_kill(pid_t pid) * completed process. */ void -mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *privatedata, enum mainloop_child_flags flags, - void (*callback)(mainloop_child_t *p, int core, - int signo, int exitcode)) +mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, + void *privatedata, + enum mainloop_child_flags flags, + pcmk__mainloop_child_exit_fn_t exit_fn) { static bool need_init = TRUE; mainloop_child_t *child = pcmk__assert_alloc(1, sizeof(mainloop_child_t)); @@ -1262,7 +1263,7 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *pr child->timerid = 0; child->timeout = FALSE; child->privatedata = privatedata; - child->callback = callback; + child->exit_fn = exit_fn; child->flags = flags; child->desc = pcmk__str_copy(desc); @@ -1284,10 +1285,9 @@ mainloop_child_add_with_flags(pid_t pid, int timeout, const char *desc, void *pr void mainloop_child_add(pid_t pid, int timeout, const char *desc, void *privatedata, - void (*callback)(mainloop_child_t *p, int core, int signo, - int exitcode)) + pcmk__mainloop_child_exit_fn_t exit_fn) { - mainloop_child_add_with_flags(pid, timeout, desc, privatedata, 0, callback); + mainloop_child_add_with_flags(pid, timeout, desc, privatedata, 0, exit_fn); } static gboolean From e156ecd11dc9e642210b6b31853ecad5b83deb6a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:09:03 -0800 Subject: [PATCH 052/350] Refactor: based: Clean up includes in based_callbacks.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 52 ++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b06e3e4d576..847fc4a7ff0 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.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,27 +9,37 @@ #include -#include -#include -#include -#include - +#include // EACCES, ECONNREFUSED +#include // PRIu64 #include -#include -#include // uint32_t, uint64_t, UINT64_C() -#include -#include -#include // PRIu64 - -#include -#include -#include // xmlXPathObject, etc. - -#include -#include -#include - -#include +#include // NULL, size_t +#include // snprintf +#include // free +#include // u?int*_t, UINT64_C +#include // gid_t, uid_t +#include // LOG_INFO, LOG_DEBUG +#include // time_t +#include // close + +#include // gboolean, gpointer, g_*, etc. +#include // xmlNode +#include // xmlXPath* +#include // QB_FALSE +#include // qb_ipcs_connection_t +#include // LOG_TRACE + +#include // cib_call_options values +#include // cib__* +#include // pcmk_cluster_disconnect +#include // pcmk__cluster_send_message +#include // pcmk_find_cib_element +#include // pcmk__s, pcmk__str_eq +#include // crm_ipc_*, pcmk_ipc_* +#include // CRM_LOG_ASSERT, CRM_CHECK +#include // mainloop_* +#include // pcmk_rc_* +#include // PCMK_XA_*, PCMK_XE_* +#include // CRM_OP_* #include From 62ccd5dea44ad62b3012df607ba83fe791830c63 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 13:55:36 -0800 Subject: [PATCH 053/350] Refactor: based: Clean up includes in based_io.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_io.c | 48 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c index 6eec84af668..4df713eaaea 100644 --- a/daemons/based/based_io.c +++ b/daemons/based/based_io.c @@ -9,30 +9,32 @@ #include +#include // dirent, scandir +#include // errno, EACCES, ENODATA +#include // SIGPIPE #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include +#include // NULL +#include // rename +#include // free, mkdtemp +#include // strerror, strrchr, etc. +#include // stat, umask, etc. +#include // pid_t +#include // time_t +#include // _exit, fork + +#include // g_*, G_* +#include // xmlNode +#include // QB_FALSE, QB_TRUE +#include // qb_log_* + +#include // cib_file_* +#include // createEmptyCib +#include // pcmk__assert_asprintf, PCMK__XE_*, etc. +#include // CRM_CHECK +#include // mainloop_add_signal +#include // pcmk_legacy2rc, pcmk_rc_* +#include // pcmk_common_cleanup +#include // PCMK_XA_*, PCMK_XE_* #include From c3bd5aa40d6603b78ef01c53323ed65e6e565e1f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:15:14 -0800 Subject: [PATCH 054/350] Refactor: based: Clean up includes in based_messages.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 36 ++++++++++++++++------------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 0e4dd7b8bfc..1c623ec349b 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.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,25 +9,23 @@ #include +#include // EINVAL, ENOTCONN, EPROTO #include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include -#include +#include // NULL +#include // free + +#include // g_list_free_full, GList +#include // xmlNode +#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 +#include // pcmk_err, pcmk_ok, pcmk_rc* +#include // PCMK_XA_*, PCMK_XE_* +#include // CRM_FEATURE_SET #include From adea5725961e917af5ca135b07d5423eeeeba6f9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:20:57 -0800 Subject: [PATCH 055/350] Refactor: based: Clean up includes in based_notify.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_notify.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index a05eb1066c5..83849415174 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -9,26 +9,22 @@ #include -#include +#include // EAGAIN +#include // PRIx64 #include -#include -#include -#include -#include // PRIx64 +#include // NULL +#include // int32_t, uint16_t +#include // ssize_t -#include -#include -#include +#include // gpointer, g_string_free +#include // xmlNode +#include // QB_XS -#include +#include // pcmk__client_t, etc. +#include // pcmk_free_ipc_event +#include // CRM_LOG_ASSERT +#include // pcmk_rc_* -#include -#include - -#include -#include - -#include #include struct cib_notification_s { From a43e7077b262f44f2de59120e64e8c84b2d1762e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:23:27 -0800 Subject: [PATCH 056/350] Refactor: based: Clean up includes in based_operation.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 881464517de..e3e7d3bd133 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2025 the Pacemaker project contributors + * Copyright 2008-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,7 +9,10 @@ #include -#include // NULL +#include // NULL + +#include // cib__* +#include // pcmk__assert, PCMK__NELEM #include From 2f042ff037fd5dd7ebae9037ce66037ca9b74348 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:35:07 -0800 Subject: [PATCH 057/350] Refactor: based: Clean up includes in based_remote.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 55 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index c51d788d7f7..5b5e0cf35f8 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.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. * @@ -8,41 +8,40 @@ */ #include -#include -#include +#include // htons +#include // errno, EAGAIN +#include // getgrgid, getgrnam, group +#include // PRIx64 +#include // sockaddr_in, INADDR_ANY #include -#include -#include -#include -#include -#include // PRIx64 -#include -#include - -#include - -#include -#include - -#include -#include - -#include -#include -#include +#include // NULL +#include // calloc, free, getenv +#include // memset +#include // sockaddr{,_storage}, AF_INET, etc. +#include // gid_t +#include // close + +#include // gboolean, gpointer, g_source_remove, etc. +#include // gnutls_bye, gnutls_deinit +#include // xmlNode +#include // QB_XS + +#include // CRM_DAEMON_GROUP +#include // pcmk__client_t, etc. +#include // CRM_CHECK +#include // mainloop_* +#include // pcmk_rc_* +#include // PCMK_XA_* +#include // CRM_OP_REGISTER #include "pacemaker-based.h" -#include - -#include -#include #if HAVE_SECURITY_PAM_APPL_H -# include +# include // pam_*, PAM_* # define HAVE_PAM 1 #elif HAVE_PAM_PAM_APPL_H -# include +# include // pam_*, PAM_* # define HAVE_PAM 1 #endif From edae06b56a39ad537fc1b7ad64179f041307d860 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:38:15 -0800 Subject: [PATCH 058/350] Refactor: based: Clean up includes in based_transaction.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/based_transaction.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index a9df2950d53..021e9073798 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.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. * @@ -9,10 +9,18 @@ #include +#include // EOPNOTSUPP #include +#include // NULL +#include // free #include // xmlNode +#include // cib__* +#include // pcmk__client_t, pcmk__s, pcmk__xe_*, etc. +#include // CRM_CHECK +#include // pcmk_rc_* + #include "pacemaker-based.h" /*! From 8ed34a642397e7873136d702aa5cdf2e0f7a6d4c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 14:49:15 -0800 Subject: [PATCH 059/350] Refactor: based: Clean up includes in pacemaker-based.c Using include-what-you-use. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 37 ++++++++++++++++++++------------- daemons/based/pacemaker-based.h | 28 +++++++------------------ 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index c1da0103f5a..6cf491e89b4 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -9,22 +9,29 @@ #include +#include // errno +#include // initgroups +#include // SIGTERM #include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include +#include // NULL, size_t +#include // free +#include // LOG_INFO +#include // gid_t, uid_t +#include // setgid, setuid + +#include // cpg_* +#include // g_*, G_*, etc. +#include // xmlNode + +#include // CRM_CONFIG_DIR, CRM_DAEMON_USER +#include // cib_read_config +#include // pcmk_cluster_* +#include // pcmk__node_update, etc. +#include // crm_ipc_* +#include // crm_log_* +#include // mainloop_add_signal +#include // CRM_EX_*, pcmk_rc_* +#include // PCMK_XA_REMOTE_*_PORT #include diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 78b5f9655b5..2e018ec77b4 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -11,32 +11,20 @@ # define PACEMAKER_BASED__H #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include +#include // uint32_t, UINT64_C + +#include // GHashTable, g_hash_table_lookup +#include // xmlNode +#include // qb_ipcs_service_t + +#include // pcmk_cluster_t +#include // pcmk__client_t #include "based_io.h" #include "based_operation.h" #include "based_notify.h" #include "based_transaction.h" -#include - #define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->priv->node_name) // CIB-specific client flags From 0d3a55b6a67614dcb20bfabbef3095ee95bc87f5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 23:43:57 -0800 Subject: [PATCH 060/350] Refactor: based: New based_messages.h Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_messages.h | 75 +++++++++++++++++++++++++++++++++ daemons/based/pacemaker-based.h | 47 +-------------------- 3 files changed, 77 insertions(+), 46 deletions(-) create mode 100644 daemons/based/based_messages.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 62c5d88a7b5..7a322fb01a6 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -15,6 +15,7 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_io.h \ + based_messages.h \ based_notify.h \ based_operation.h \ based_transaction.h \ diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h new file mode 100644 index 00000000000..1ea21e314c6 --- /dev/null +++ b/daemons/based/based_messages.h @@ -0,0 +1,75 @@ +/* + * Copyright 2004-2025 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_MESSAGES__H +#define BASED_MESSAGES__H + +#include + +#include // xmlNode * + +extern bool based_is_primary; +extern xmlNode *the_cib; + +int cib_process_shutdown_req(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); + +int cib_process_noop(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_ping(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_readwrite(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_replace_svr(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_server_process_diff(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_sync(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_sync_one(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_delete_absolute(const char *op, int options, + const char *section, xmlNode *req, + xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_upgrade_server(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); + +int cib_process_commit_transaction(const char *op, int options, + const char *section, xmlNode *req, + xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +int cib_process_schemas(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + +void send_sync_request(const char *host); +int sync_our_cib(xmlNode *request, bool all); + +#endif // BASED_MESSAGES__H diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 2e018ec77b4..7bd1f28da1c 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -21,6 +21,7 @@ #include // pcmk__client_t #include "based_io.h" +#include "based_messages.h" #include "based_operation.h" #include "based_notify.h" #include "based_transaction.h" @@ -36,9 +37,7 @@ enum cib_client_flags { cib_notify_diff = (UINT64_C(1) << 4), }; -extern bool based_is_primary; extern GHashTable *config_hash; -extern xmlNode *the_cib; extern GMainLoop *mainloop; extern pcmk_cluster_t *crm_cluster; @@ -64,50 +63,6 @@ int cib_process_request(xmlNode *request, bool privileged, void cib_shutdown(int nsig); void terminate_cib(int exit_status); -int cib_process_shutdown_req(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer); -int cib_process_noop(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_ping(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_readwrite(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_replace_svr(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_server_process_diff(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_sync(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_sync_one(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_delete_absolute(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_upgrade_server(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer); -int cib_process_commit_transaction(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); -int cib_process_schemas(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - -void send_sync_request(const char *host); -int sync_our_cib(xmlNode *request, bool all); - static inline const char * cib_config_lookup(const char *opt) { From b4647088d4e1dcabc24cb524a3d04e213686902b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 21 Dec 2025 23:57:38 -0800 Subject: [PATCH 061/350] Refactor: libcib: Return void from cib_read_config() Nothing uses the return value. Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 2 +- lib/cib/cib_utils.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index d493b69e2a2..d35f4fade24 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -95,7 +95,7 @@ enum cib__op_type { cib__op_schemas, }; -gboolean cib_read_config(GHashTable * options, xmlNode * current_cib); +void cib_read_config(GHashTable *options, xmlNode *current_cib); typedef int (*cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 70c409ba1f8..03f667eea86 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -730,14 +730,14 @@ cib_native_notify(gpointer data, gpointer user_data) pcmk__trace("Callback invoked..."); } -gboolean +void cib_read_config(GHashTable * options, xmlNode * current_cib) { xmlNode *config = NULL; crm_time_t *now = NULL; if (options == NULL || current_cib == NULL) { - return FALSE; + return; } now = crm_time_new(NULL); @@ -758,8 +758,6 @@ cib_read_config(GHashTable * options, xmlNode * current_cib) pcmk__validate_cluster_options(options); crm_time_free(now); - - return TRUE; } int From adad8f9e0a183545e516ef553ab3f45bb9efdd7f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 00:03:51 -0800 Subject: [PATCH 062/350] Refactor: based: Best practices for cib_process_delete_absolute() * Use "based_" prefix. * Add Doxygen. * Move definition to align with order in based_operation.c table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 39 +++++++++++++++++++++++---------- daemons/based/based_messages.h | 10 ++++----- daemons/based/based_operation.c | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 1c623ec349b..edaa224fdd1 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -36,6 +36,34 @@ bool based_is_primary = false; xmlNode *the_cib = NULL; +/*! + * \internal + * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE + * + * \param[in] op Ignored + * \param[in] options Ignored + * \param[in] section Ignored + * \param[in] req Ignored + * \param[in] input Ignored + * \param[in] existing_cib Ignored + * \param[in] result_cib Ignored + * \param[in] answer Ignored + * + * \return \c -EINVAL + * + * \note This is unimplemented and simply returns an error. + */ +int +based_process_abs_delete(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_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 cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -351,17 +379,6 @@ cib_process_replace_svr(const char *op, int options, const char *section, xmlNod return rc; } -/* @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed - * (At least external client code <3.0.0 can send it) - */ -int -cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - return -EINVAL; -} - static xmlNode * cib_msg_copy(xmlNode *msg) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 1ea21e314c6..97eb6cac969 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,6 +17,11 @@ extern bool based_is_primary; extern xmlNode *the_cib; +int based_process_abs_delete(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, @@ -50,11 +55,6 @@ int cib_process_sync_one(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_delete_absolute(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index e3e7d3bd133..0b46095dbbb 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -17,7 +17,7 @@ #include static const cib__op_fn_t op_functions[] = { - [cib__op_abs_delete] = cib_process_delete_absolute, + [cib__op_abs_delete] = based_process_abs_delete, [cib__op_apply_patch] = cib_server_process_diff, [cib__op_bump] = cib_process_bump, [cib__op_commit_transact] = cib_process_commit_transaction, From a7ea3c2c305d0da8b058447175470fe0416935cf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 00:43:41 -0800 Subject: [PATCH 063/350] Refactor: based: cib_server_process_diff -> based_process_apply_patch Rename the function to match the request name. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 106 ++++++++++++++++---------------- daemons/based/based_messages.h | 9 +-- daemons/based/based_operation.c | 2 +- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index edaa224fdd1..1b95382d6a2 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -36,6 +36,11 @@ bool based_is_primary = false; xmlNode *the_cib = NULL; +/* Set to 1 when a sync is requested, incremented when a diff is ignored, + * reset to 0 when a sync is received + */ +static int sync_in_progress = 0; + /*! * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE @@ -64,6 +69,54 @@ based_process_abs_delete(const char *op, int options, const char *section, return -EINVAL; } +int +based_process_apply_patch(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + int rc = pcmk_ok; + + if (sync_in_progress > MAX_DIFF_RETRY) { + /* Don't ignore diffs forever; the last request may have been lost. + * If the diff fails, we'll ask for another full resync. + */ + sync_in_progress = 0; + } + + // The primary instance should never ignore a diff + if (sync_in_progress && !based_is_primary) { + int source[] = { 0, 0, 0 }; + int target[] = { 0, 0, 0 }; + + pcmk__xml_patchset_versions(input, source, target); + + sync_in_progress++; + pcmk__notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in " + "progress)", + source[0], source[1], source[2], + target[0], target[1], target[2]); + return -pcmk_err_diff_resync; + } + + rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); + pcmk__trace("result: %s (%d), %s", pcmk_strerror(rc), rc, + (based_is_primary? "primary": "secondary")); + + if ((rc == -pcmk_err_diff_resync) && !based_is_primary) { + pcmk__xml_free(*result_cib); + *result_cib = NULL; + send_sync_request(NULL); + + } else if (rc == -pcmk_err_diff_resync) { + rc = -pcmk_err_diff_failed; + if (options & cib_force_diff) { + pcmk__warn("Not requesting full refresh in R/W mode"); + } + } + + return rc; +} + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -134,11 +187,6 @@ cib_process_readwrite(const char *op, int options, const char *section, xmlNode return result; } -/* Set to 1 when a sync is requested, incremented when a diff is ignored, - * reset to 0 when a sync is received - */ -static int sync_in_progress = 0; - void send_sync_request(const char *host) { @@ -317,54 +365,6 @@ cib_process_sync_one(const char *op, int options, const char *section, xmlNode * return sync_our_cib(req, false); } -int -cib_server_process_diff(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - int rc = pcmk_ok; - - if (sync_in_progress > MAX_DIFF_RETRY) { - /* Don't ignore diffs forever; the last request may have been lost. - * If the diff fails, we'll ask for another full resync. - */ - sync_in_progress = 0; - } - - // The primary instance should never ignore a diff - if (sync_in_progress && !based_is_primary) { - int source[] = { 0, 0, 0 }; - int target[] = { 0, 0, 0 }; - - pcmk__xml_patchset_versions(input, source, target); - - sync_in_progress++; - pcmk__notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in " - "progress)", - source[0], source[1], source[2], - target[0], target[1], target[2]); - return -pcmk_err_diff_resync; - } - - rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); - pcmk__trace("result: %s (%d), %s", pcmk_strerror(rc), rc, - (based_is_primary? "primary": "secondary")); - - if ((rc == -pcmk_err_diff_resync) && !based_is_primary) { - pcmk__xml_free(*result_cib); - *result_cib = NULL; - send_sync_request(NULL); - - } else if (rc == -pcmk_err_diff_resync) { - rc = -pcmk_err_diff_failed; - if (options & cib_force_diff) { - pcmk__warn("Not requesting full refresh in R/W mode"); - } - } - - return rc; -} - int cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 97eb6cac969..46e69068afc 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -22,6 +22,11 @@ int based_process_abs_delete(const char *op, int options, const char *section, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_apply_patch(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, @@ -43,10 +48,6 @@ int cib_process_replace_svr(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_server_process_diff(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_sync(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 0b46095dbbb..146c3a95e84 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -18,7 +18,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_abs_delete] = based_process_abs_delete, - [cib__op_apply_patch] = cib_server_process_diff, + [cib__op_apply_patch] = based_process_apply_patch, [cib__op_bump] = cib_process_bump, [cib__op_commit_transact] = cib_process_commit_transaction, [cib__op_create] = cib_process_create, From 9a2710dba96cd854cd11685ec098f4baadbf26b3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 00:46:01 -0800 Subject: [PATCH 064/350] Refactor: based: Drop send_sync_request() argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_messages.c | 9 +++------ daemons/based/based_messages.h | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 847fc4a7ff0..4f74516c6a5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -653,7 +653,7 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { pcmk__info("Detected legacy %s global update from %s", op, originator); - send_sync_request(NULL); + send_sync_request(); return false; } else if (is_reply diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 1b95382d6a2..9969a939dc6 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -105,7 +105,7 @@ based_process_apply_patch(const char *op, int options, const char *section, if ((rc == -pcmk_err_diff_resync) && !based_is_primary) { pcmk__xml_free(*result_cib); *result_cib = NULL; - send_sync_request(NULL); + send_sync_request(); } else if (rc == -pcmk_err_diff_resync) { rc = -pcmk_err_diff_failed; @@ -188,21 +188,18 @@ cib_process_readwrite(const char *op, int options, const char *section, xmlNode } void -send_sync_request(const char *host) +send_sync_request(void) { xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me"); pcmk__node_status_t *peer = NULL; - pcmk__info("Requesting re-sync from %s", (host? host : "all peers")); + pcmk__info("Requesting re-sync from all peers"); sync_in_progress = 1; pcmk__xe_set(sync_me, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE); pcmk__xe_set(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); - if (host != NULL) { - peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); - } pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me); pcmk__xml_free(sync_me); } diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 46e69068afc..b5b75e15442 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -70,7 +70,7 @@ int cib_process_schemas(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -void send_sync_request(const char *host); +void send_sync_request(void); int sync_our_cib(xmlNode *request, bool all); #endif // BASED_MESSAGES__H From 8f84be7b78b365a8e7106616c3d9077528ea8662 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 00:52:41 -0800 Subject: [PATCH 065/350] Refactor: libcib: cib_process_diff -> cib__process_apply_patch Rename the function to match the request name and to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 3 ++- include/crm/cib/internal.h | 8 +++--- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 45 +++++++++++++++++----------------- lib/cib/cib_utils.c | 4 +-- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 9969a939dc6..d0e3ba2cbc4 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -98,7 +98,8 @@ based_process_apply_patch(const char *op, int options, const char *section, return -pcmk_err_diff_resync; } - rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); + rc = cib__process_apply_patch(op, options, section, req, input, + existing_cib, result_cib, answer); pcmk__trace("result: %s (%d), %s", pcmk_strerror(rc), rc, (based_is_primary? "primary": "secondary")); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index d35f4fade24..69a31d6fd58 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -200,6 +200,10 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); +int cib__process_apply_patch(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); @@ -228,10 +232,6 @@ int cib_process_delete(const char *op, int options, const char *section, xmlNode xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); -int cib_process_diff(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); - int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index d827e582bd3..02276dff5d5 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -119,7 +119,7 @@ get_client(const char *client_id) } static const cib__op_fn_t cib_op_functions[] = { - [cib__op_apply_patch] = cib_process_diff, + [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib_process_bump, [cib__op_commit_transact] = cib_file_process_commit_transaction, [cib__op_create] = cib_process_create, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index c390f71a6d2..e9c8d21e5c8 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -165,6 +165,29 @@ cib__get_operation(const char *op, const cib__operation_t **operation) return pcmk_rc_ok; } +int +cib__process_apply_patch(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + const bool force = pcmk__is_set(options, cib_force_diff); + const char *originator = NULL; + + if (req != NULL) { + originator = pcmk__xe_get(req, PCMK__XA_SRC); + } + + pcmk__trace("Processing \"%s\" event from %s%s", op, originator, + (force? " (global update)" : "")); + + if (*result_cib != existing_cib) { + pcmk__xml_free(*result_cib); + } + *result_cib = pcmk__xml_copy(NULL, existing_cib); + + return xml_apply_patchset(*result_cib, input, TRUE); +} + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -638,28 +661,6 @@ cib_process_create(const char *op, int options, const char *section, xmlNode * r return result; } -int -cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - const bool force = pcmk__is_set(options, cib_force_diff); - const char *originator = NULL; - - if (req != NULL) { - originator = pcmk__xe_get(req, PCMK__XA_SRC); - } - - pcmk__trace("Processing \"%s\" event from %s%s", op, originator, - (force? " (global update)" : "")); - - if (*result_cib != existing_cib) { - pcmk__xml_free(*result_cib); - } - *result_cib = pcmk__xml_copy(NULL, existing_cib); - - return xml_apply_patchset(*result_cib, input, TRUE); -} - int cib_process_xpath(const char *op, int options, const char *section, const xmlNode *req, xmlNode *input, xmlNode *existing_cib, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 03f667eea86..4ec2255cdef 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -822,8 +822,8 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, } if (input != NULL) { - rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output, - NULL); + rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, + output, NULL); if (rc != pcmk_ok) { pcmk__debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), From 80f24414014dfb44e3dcb79dd163b2a9f05aed07 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 00:56:03 -0800 Subject: [PATCH 066/350] Refactor: libcib: cib_process_bump() -> cib__process_bump() Rename the function to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 8 ++-- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 85 +++++++++++++++++---------------- 4 files changed, 49 insertions(+), 48 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 146c3a95e84..960781f752a 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -19,7 +19,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_abs_delete] = based_process_abs_delete, [cib__op_apply_patch] = based_process_apply_patch, - [cib__op_bump] = cib_process_bump, + [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = cib_process_commit_transaction, [cib__op_create] = cib_process_create, [cib__op_delete] = cib_process_delete, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 69a31d6fd58..ae8750924d2 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -204,6 +204,10 @@ int cib__process_apply_patch(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int cib__process_bump(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); @@ -212,10 +216,6 @@ int cib_process_erase(const char *op, int options, const char *section, xmlNode xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); -int cib_process_bump(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); - int cib_process_replace(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 02276dff5d5..3b9da7412a6 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -120,7 +120,7 @@ get_client(const char *client_id) static const cib__op_fn_t cib_op_functions[] = { [cib__op_apply_patch] = cib__process_apply_patch, - [cib__op_bump] = cib_process_bump, + [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = cib_file_process_commit_transaction, [cib__op_create] = cib_process_create, [cib__op_delete] = cib_process_delete, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index e9c8d21e5c8..bc8a2e7676c 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -188,6 +188,49 @@ cib__process_apply_patch(const char *op, int options, const char *section, return xml_apply_patchset(*result_cib, input, TRUE); } +static int +update_counter(xmlNode *xml_obj, const char *field, bool reset) +{ + char *new_value = NULL; + char *old_value = NULL; + int int_value = -1; + + if (!reset && pcmk__xe_get(xml_obj, field) != NULL) { + old_value = pcmk__xe_get_copy(xml_obj, field); + } + if (old_value != NULL) { + int_value = atoi(old_value); + new_value = pcmk__itoa(++int_value); + } else { + new_value = pcmk__str_copy("1"); + } + + pcmk__trace("Update %s from %s to %s", field, pcmk__s(old_value, "unset"), + new_value); + pcmk__xe_set(xml_obj, field, new_value); + + free(new_value); + free(old_value); + + return pcmk_ok; +} + +int +cib__process_bump(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + int result = pcmk_ok; + + pcmk__trace("Processing %s for epoch='%s'", op, + pcmk__s(pcmk__xe_get(existing_cib, PCMK_XA_EPOCH), "")); + + *answer = NULL; + update_counter(*result_cib, PCMK_XA_EPOCH, false); + + return result; +} + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -234,33 +277,6 @@ cib_process_query(const char *op, int options, const char *section, xmlNode * re return result; } -static int -update_counter(xmlNode *xml_obj, const char *field, bool reset) -{ - char *new_value = NULL; - char *old_value = NULL; - int int_value = -1; - - if (!reset && pcmk__xe_get(xml_obj, field) != NULL) { - old_value = pcmk__xe_get_copy(xml_obj, field); - } - if (old_value != NULL) { - int_value = atoi(old_value); - new_value = pcmk__itoa(++int_value); - } else { - new_value = pcmk__str_copy("1"); - } - - pcmk__trace("Update %s from %s to %s", field, pcmk__s(old_value, "unset"), - new_value); - pcmk__xe_set(xml_obj, field, new_value); - - free(new_value); - free(old_value); - - return pcmk_ok; -} - int cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -309,21 +325,6 @@ cib_process_upgrade(const char *op, int options, const char *section, xmlNode * return rc; } -int -cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - int result = pcmk_ok; - - pcmk__trace("Processing %s for epoch='%s'", op, - pcmk__s(pcmk__xe_get(existing_cib, PCMK_XA_EPOCH), "")); - - *answer = NULL; - update_counter(*result_cib, PCMK_XA_EPOCH, false); - - return result; -} - int cib_process_replace(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, From c9eff08ba9c5e72c3c3f0c2cec880f022a29ea6d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 00:59:10 -0800 Subject: [PATCH 067/350] Refactor: based: Rename cib_process_commit_transaction() ...to based_process_commit_transact(). Rename the function to match the request name. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 54 +++++++++++++++---------------- daemons/based/based_messages.h | 10 +++--- daemons/based/based_operation.c | 2 +- daemons/based/based_transaction.c | 2 +- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d0e3ba2cbc4..ce5f5695d83 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -118,6 +118,33 @@ based_process_apply_patch(const char *op, int options, const char *section, return rc; } +int +based_process_commit_transact(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer) +{ + /* On success, our caller will activate *result_cib locally, trigger a + * replace notification if appropriate, and sync *result_cib to all nodes. + * On failure, our caller will free *result_cib. + */ + int rc = pcmk_rc_ok; + const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); + const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); + pcmk__client_t *client = pcmk__find_client_by_id(client_id); + + rc = based_commit_transaction(input, client, origin, result_cib); + + if (rc != pcmk_rc_ok) { + char *source = based_transaction_source_str(client, origin); + + pcmk__err("Could not commit transaction for %s: %s", source, + pcmk_rc_str(rc)); + free(source); + } + return pcmk_rc2legacy(rc); +} + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -460,33 +487,6 @@ sync_our_cib(xmlNode *request, bool all) return result; } -int -cib_process_commit_transaction(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer) -{ - /* On success, our caller will activate *result_cib locally, trigger a - * replace notification if appropriate, and sync *result_cib to all nodes. - * On failure, our caller will free *result_cib. - */ - int rc = pcmk_rc_ok; - const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); - const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); - pcmk__client_t *client = pcmk__find_client_by_id(client_id); - - rc = based_commit_transaction(input, client, origin, result_cib); - - if (rc != pcmk_rc_ok) { - char *source = based_transaction_source_str(client, origin); - - pcmk__err("Could not commit transaction for %s: %s", source, - pcmk_rc_str(rc)); - free(source); - } - return pcmk_rc2legacy(rc); -} - int cib_process_schemas(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index b5b75e15442..ddcc8dd561a 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -27,6 +27,11 @@ int based_process_apply_patch(const char *op, int options, const char *section, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_commit_transact(const char *op, int options, + const char *section, xmlNode *req, + xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, @@ -61,11 +66,6 @@ int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_commit_transaction(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_schemas(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 960781f752a..e2797330ce0 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -20,7 +20,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_abs_delete] = based_process_abs_delete, [cib__op_apply_patch] = based_process_apply_patch, [cib__op_bump] = cib__process_bump, - [cib__op_commit_transact] = cib_process_commit_transaction, + [cib__op_commit_transact] = based_process_commit_transact, [cib__op_create] = cib_process_create, [cib__op_delete] = cib_process_delete, [cib__op_erase] = cib_process_erase, diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 021e9073798..6ee1f793308 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -116,7 +116,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, * \return Standard Pacemaker return code * * \note This function is expected to be called only by - * \p cib_process_commit_transaction(). + * \p based_process_commit_transact(). * \note \p result_cib is expected to be a copy of the current CIB as created by * \p cib_perform_op(). * \note The caller is responsible for activating and syncing \p result_cib on From c7b2ef54fbeec1e0f74ef33ae2f8c26ddd9347a4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:06:21 -0800 Subject: [PATCH 068/350] Refactor: libcib: Stop using "cib_" prefix in static cib_file.c funcs To avoid using the public API prefix. Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 94 ++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 3b9da7412a6..49f26aa1737 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -51,12 +51,10 @@ typedef struct cib_file_opaque_s { xmlNode *cib_xml; } cib_file_opaque_t; -static int cib_file_process_commit_transaction(const char *op, int options, - const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, - xmlNode **result_cib, - xmlNode **answer); +static int process_commit_transact(const char *op, int options, + const char *section, xmlNode *req, + xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); /*! * \internal @@ -121,7 +119,7 @@ get_client(const char *client_id) static const cib__op_fn_t cib_op_functions[] = { [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib__process_bump, - [cib__op_commit_transact] = cib_file_process_commit_transaction, + [cib__op_commit_transact] = process_commit_transact, [cib__op_create] = cib_process_create, [cib__op_delete] = cib_process_delete, [cib__op_erase] = cib_process_erase, @@ -131,7 +129,7 @@ static const cib__op_fn_t cib_op_functions[] = { [cib__op_upgrade] = cib_process_upgrade, }; -/* cib_file_backup() and cib_file_write_with_digest() need to chown the +/* backup_cib_file() and cib_file_write_with_digest() need to chown the * written files only in limited circumstances, so these variables allow * that to be indicated without affecting external callers */ @@ -187,7 +185,7 @@ file_get_op_function(const cib__operation_t *operation) * \return TRUE if file exists and its real path is same as live CIB's */ static gboolean -cib_file_is_live(const char *filename) +is_live(const char *filename) { gboolean same = FALSE; @@ -210,7 +208,7 @@ cib_file_is_live(const char *filename) } static int -cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output) +process_request(cib_t *cib, xmlNode *request, xmlNode **output) { int rc = pcmk_ok; const cib__operation_t *operation = NULL; @@ -284,10 +282,10 @@ cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output) } static int -cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host, - const char *section, xmlNode *data, - xmlNode **output_data, int call_options, - const char *user_name) +file_perform_op_delegate(cib_t *cib, const char *op, const char *host, + const char *section, xmlNode *data, + xmlNode **output_data, int call_options, + const char *user_name) { int rc = pcmk_ok; xmlNode *request = NULL; @@ -336,7 +334,7 @@ cib_file_perform_op_delegate(cib_t *cib, const char *op, const char *host, goto done; } - rc = cib_file_process_request(cib, request, &output); + rc = process_request(cib, request, &output); if ((output_data != NULL) && (output != NULL)) { if (output->doc == private->cib_xml->doc) { @@ -406,7 +404,7 @@ load_file_cib(const char *filename, xmlNode **output) } static int -cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type) +file_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; cib_file_opaque_t *private = cib->variant_opaque; @@ -443,7 +441,7 @@ cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type) * \return Standard Pacemaker return code */ static int -cib_file_write_live(xmlNode *cib_root, char *path) +write_live(xmlNode *cib_root, char *path) { uid_t euid = geteuid(); uid_t daemon_uid = 0; @@ -525,7 +523,7 @@ cib_file_write_live(xmlNode *cib_root, char *path) * running. */ static int -cib_file_signoff(cib_t *cib) +file_signoff(cib_t *cib) { int rc = pcmk_ok; cib_file_opaque_t *private = cib->variant_opaque; @@ -541,7 +539,7 @@ cib_file_signoff(cib_t *cib) /* If this is the live CIB, write it out with a digest */ if (pcmk__is_set(private->flags, cib_file_flag_live)) { - rc = cib_file_write_live(private->cib_xml, private->filename); + rc = write_live(private->cib_xml, private->filename); rc = pcmk_rc2legacy(rc); /* Otherwise, it's a simple write */ @@ -569,12 +567,12 @@ cib_file_signoff(cib_t *cib) } static int -cib_file_free(cib_t *cib) +file_free(cib_t *cib) { int rc = pcmk_ok; if (cib->state != cib_disconnected) { - rc = cib_file_signoff(cib); + rc = file_signoff(cib); } if (rc == pcmk_ok) { @@ -595,14 +593,13 @@ cib_file_free(cib_t *cib) } static int -cib_file_register_notification(cib_t *cib, const char *callback, int enabled) +file_register_notification(cib_t *cib, const char *callback, int enabled) { return -EPROTONOSUPPORT; } static int -cib_file_set_connection_dnotify(cib_t *cib, - void (*dnotify) (gpointer user_data)) +file_set_connection_dnotify(cib_t *cib, void (*dnotify)(gpointer user_data)) { return -EPROTONOSUPPORT; } @@ -621,8 +618,7 @@ cib_file_set_connection_dnotify(cib_t *cib, * \p cib_api_operations_t:client_id(). */ static int -cib_file_client_id(const cib_t *cib, const char **async_id, - const char **sync_id) +file_client_id(const cib_t *cib, const char **async_id, const char **sync_id) { cib_file_opaque_t *private = cib->variant_opaque; @@ -674,20 +670,20 @@ cib_file_new(const char *cib_location) cib->variant_opaque = private; private->flags = 0; - if (cib_file_is_live(cib_location)) { + if (is_live(cib_location)) { cib_set_file_flags(private, cib_file_flag_live); pcmk__trace("File %s detected as live CIB", cib_location); } /* assign variant specific ops */ - cib->delegate_fn = cib_file_perform_op_delegate; - cib->cmds->signon = cib_file_signon; - cib->cmds->signoff = cib_file_signoff; - cib->cmds->free = cib_file_free; - cib->cmds->register_notification = cib_file_register_notification; - cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify; + cib->delegate_fn = file_perform_op_delegate; + cib->cmds->signon = file_signon; + cib->cmds->signoff = file_signoff; + cib->cmds->free = file_free; + cib->cmds->register_notification = file_register_notification; + cib->cmds->set_connection_dnotify = file_set_connection_dnotify; - cib->cmds->client_id = cib_file_client_id; + cib->cmds->client_id = file_client_id; return cib; } @@ -702,7 +698,7 @@ cib_file_new(const char *cib_location) * \return TRUE if digests match or signature file does not exist, else FALSE */ static gboolean -cib_file_verify_digest(xmlNode *root, const char *sigfile) +verify_digest(xmlNode *root, const char *sigfile) { gboolean passed = FALSE; char *expected; @@ -784,7 +780,7 @@ cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **ro } /* Verify that digests match */ - if (cib_file_verify_digest(local_root, sigfile) == FALSE) { + if (!verify_digest(local_root, sigfile)) { free(local_sigfile); pcmk__xml_free(local_root); return -pcmk_err_cib_modified; @@ -809,7 +805,7 @@ cib_file_read_and_verify(const char *filename, const char *sigfile, xmlNode **ro * \return 0 on success, -1 on error */ static int -cib_file_backup(const char *cib_dirname, const char *cib_filename) +backup_cib_file(const char *cib_dirname, const char *cib_filename) { int rc = 0; unsigned int seq = 0U; @@ -896,7 +892,7 @@ cib_file_backup(const char *cib_dirname, const char *cib_filename) * \return void */ static void -cib_file_prepare_xml(xmlNode *root) +prepare_xml(xmlNode *root) { xmlNode *cib_status_root = NULL; @@ -955,14 +951,14 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, } /* Back up the existing CIB */ - if (cib_file_backup(cib_dirname, cib_filename) < 0) { + if (backup_cib_file(cib_dirname, cib_filename) < 0) { exit_rc = pcmk_err_cib_backup; goto cleanup; } pcmk__debug("Writing CIB to disk"); umask(S_IWGRP | S_IWOTH | S_IROTH); - cib_file_prepare_xml(cib_root); + prepare_xml(cib_root); /* Write the CIB to a temporary file, so we can deploy (near) atomically */ fd = mkstemp(tmp_cib); @@ -1067,7 +1063,7 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, * \return Standard Pacemaker return code */ static int -cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction) +process_transaction_requests(cib_t *cib, xmlNode *transaction) { cib_file_opaque_t *private = cib->variant_opaque; @@ -1080,7 +1076,7 @@ cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction) xmlNode *output = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - int rc = cib_file_process_request(cib, request, &output); + int rc = process_request(cib, request, &output); rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { @@ -1115,8 +1111,7 @@ cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction) * \p result_cib using \p pcmk__xml_free() on failure. */ static int -cib_file_commit_transaction(cib_t *cib, xmlNode *transaction, - xmlNode **result_cib) +commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) { int rc = pcmk_rc_ok; cib_file_opaque_t *private = cib->variant_opaque; @@ -1142,7 +1137,7 @@ cib_file_commit_transaction(cib_t *cib, xmlNode *transaction, // Apply all changes to a working copy of the CIB private->cib_xml = *result_cib; - rc = cib_file_process_transaction_requests(cib, transaction); + rc = process_transaction_requests(cib, transaction); pcmk__trace("Transaction commit %s for CIB file client (%s) on file '%s'", ((rc == pcmk_rc_ok)? "succeeded" : "failed"), @@ -1163,10 +1158,9 @@ cib_file_commit_transaction(cib_t *cib, xmlNode *transaction, } static int -cib_file_process_commit_transaction(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) +process_commit_transact(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { int rc = pcmk_rc_ok; const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); @@ -1177,7 +1171,7 @@ cib_file_process_commit_transaction(const char *op, int options, cib = get_client(client_id); CRM_CHECK(cib != NULL, return -EINVAL); - rc = cib_file_commit_transaction(cib, input, result_cib); + rc = commit_transaction(cib, input, result_cib); if (rc != pcmk_rc_ok) { cib_file_opaque_t *private = cib->variant_opaque; From d189fa9e4a14a7e5497700a55ef7b9c1305ee717 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:29:18 -0800 Subject: [PATCH 069/350] Refactor: libcib: Drop "cib_" from some internal cib_file.c symbols To avoid using the public API prefix. Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 84 +++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 49f26aa1737..6b4c90a5523 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -39,17 +39,17 @@ // key: client ID (const char *) -> value: client (cib_t *) static GHashTable *client_table = NULL; -enum cib_file_flags { - cib_file_flag_dirty = (UINT32_C(1) << 0), - cib_file_flag_live = (UINT32_C(1) << 1), +enum file_flags { + file_flag_dirty = (UINT32_C(1) << 0), + file_flag_live = (UINT32_C(1) << 1), }; -typedef struct cib_file_opaque_s { +typedef struct { char *id; char *filename; - uint32_t flags; // Group of enum cib_file_flags + uint32_t flags; // Group of enum file_flags xmlNode *cib_xml; -} cib_file_opaque_t; +} file_opaque_t; static int process_commit_transact(const char *op, int options, const char *section, xmlNode *req, @@ -65,7 +65,7 @@ static int process_commit_transact(const char *op, int options, static void register_client(const cib_t *cib) { - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; if (client_table == NULL) { client_table = pcmk__strkey_table(NULL, NULL); @@ -82,7 +82,7 @@ register_client(const cib_t *cib) static void unregister_client(const cib_t *cib) { - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; if (client_table == NULL) { return; @@ -116,7 +116,7 @@ get_client(const char *client_id) return g_hash_table_lookup(client_table, (gpointer) client_id); } -static const cib__op_fn_t cib_op_functions[] = { +static const cib__op_fn_t op_functions[] = { [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = process_commit_transact, @@ -133,11 +133,11 @@ static const cib__op_fn_t cib_op_functions[] = { * written files only in limited circumstances, so these variables allow * that to be indicated without affecting external callers */ -static uid_t cib_file_owner = 0; -static uid_t cib_file_group = 0; -static gboolean cib_do_chown = FALSE; +static uid_t file_owner = 0; +static uid_t file_group = 0; +static gboolean do_chown = FALSE; -#define cib_set_file_flags(cibfile, flags_to_set) do { \ +#define set_file_flags(cibfile, flags_to_set) do { \ (cibfile)->flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_TRACE, "CIB file", \ cibfile->filename, \ @@ -146,7 +146,7 @@ static gboolean cib_do_chown = FALSE; #flags_to_set); \ } while (0) -#define cib_clear_file_flags(cibfile, flags_to_clear) do { \ +#define clear_file_flags(cibfile, flags_to_clear) do { \ (cibfile)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ LOG_TRACE, "CIB file", \ cibfile->filename, \ @@ -170,10 +170,10 @@ file_get_op_function(const cib__operation_t *operation) pcmk__assert(type >= 0); - if (type >= PCMK__NELEM(cib_op_functions)) { + if (type >= PCMK__NELEM(op_functions)) { return NULL; } - return cib_op_functions[type]; + return op_functions[type]; } /*! @@ -227,7 +227,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) xmlNode *result_cib = NULL; xmlNode *cib_diff = NULL; - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; // We error checked these in callers cib__get_operation(op, &operation); @@ -270,7 +270,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) pcmk__xml_free(private->cib_xml); private->cib_xml = result_cib; } - cib_set_file_flags(private, cib_file_flag_dirty); + set_file_flags(private, file_flag_dirty); } done: @@ -290,7 +290,7 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, int rc = pcmk_ok; xmlNode *request = NULL; xmlNode *output = NULL; - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; const cib__operation_t *operation = NULL; @@ -407,7 +407,7 @@ static int file_signon(cib_t *cib, const char *name, enum cib_conn_type type) { int rc = pcmk_ok; - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; if (private->filename == NULL) { rc = -EINVAL; @@ -487,9 +487,9 @@ write_live(xmlNode *cib_root, char *path) /* if we're root, we want to update the file ownership */ if (euid == 0) { - cib_file_owner = daemon_uid; - cib_file_group = daemon_gid; - cib_do_chown = TRUE; + file_owner = daemon_uid; + file_group = daemon_gid; + do_chown = TRUE; } /* write the file */ @@ -498,7 +498,7 @@ write_live(xmlNode *cib_root, char *path) /* turn off file ownership changes, for other callers */ if (euid == 0) { - cib_do_chown = FALSE; + do_chown = FALSE; } /* undo fancy stuff */ @@ -526,7 +526,7 @@ static int file_signoff(cib_t *cib) { int rc = pcmk_ok; - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; pcmk__debug("Disconnecting from the CIB manager"); cib->state = cib_disconnected; @@ -535,10 +535,10 @@ file_signoff(cib_t *cib) cib->cmds->end_transaction(cib, false, cib_none); /* If the in-memory CIB has been changed, write it to disk */ - if (pcmk__is_set(private->flags, cib_file_flag_dirty)) { + if (pcmk__is_set(private->flags, file_flag_dirty)) { /* If this is the live CIB, write it out with a digest */ - if (pcmk__is_set(private->flags, cib_file_flag_live)) { + if (pcmk__is_set(private->flags, file_flag_live)) { rc = write_live(private->cib_xml, private->filename); rc = pcmk_rc2legacy(rc); @@ -554,7 +554,7 @@ file_signoff(cib_t *cib) if (rc == pcmk_ok) { pcmk__info("Wrote CIB to %s", private->filename); - cib_clear_file_flags(private, cib_file_flag_dirty); + clear_file_flags(private, file_flag_dirty); } else { pcmk__err("Could not write CIB to %s", private->filename); } @@ -576,7 +576,7 @@ file_free(cib_t *cib) } if (rc == pcmk_ok) { - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; free(private->id); free(private->filename); @@ -620,7 +620,7 @@ file_set_connection_dnotify(cib_t *cib, void (*dnotify)(gpointer user_data)) static int file_client_id(const cib_t *cib, const char **async_id, const char **sync_id) { - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; if (async_id != NULL) { *async_id = private->id; @@ -635,7 +635,7 @@ cib_t * cib_file_new(const char *cib_location) { cib_t *cib = NULL; - cib_file_opaque_t *private = NULL; + file_opaque_t *private = NULL; char *filename = NULL; if (cib_location == NULL) { @@ -656,7 +656,7 @@ cib_file_new(const char *cib_location) return NULL; } - private = calloc(1, sizeof(cib_file_opaque_t)); + private = calloc(1, sizeof(file_opaque_t)); if (private == NULL) { free(cib); free(filename); @@ -671,7 +671,7 @@ cib_file_new(const char *cib_location) private->flags = 0; if (is_live(cib_location)) { - cib_set_file_flags(private, cib_file_flag_live); + set_file_flags(private, file_flag_live); pcmk__trace("File %s detected as live CIB", cib_location); } @@ -844,17 +844,17 @@ backup_cib_file(const char *cib_dirname, const char *cib_filename) } else { pcmk__write_series_sequence(cib_dirname, CIB_SERIES, ++seq, CIB_SERIES_MAX); - if (cib_do_chown) { + if (do_chown) { int rc2; - if ((chown(backup_path, cib_file_owner, cib_file_group) < 0) + if ((chown(backup_path, file_owner, file_group) < 0) && (errno != ENOENT)) { pcmk__err("Could not set owner of %s: %s", backup_path, strerror(errno)); rc = -1; } - if ((chown(backup_digest, cib_file_owner, cib_file_group) < 0) + if ((chown(backup_digest, file_owner, file_group) < 0) && (errno != ENOENT)) { pcmk__err("Could not set owner of %s: %s", backup_digest, @@ -862,7 +862,7 @@ backup_cib_file(const char *cib_dirname, const char *cib_filename) rc = -1; } rc2 = pcmk__chown_series_sequence(cib_dirname, CIB_SERIES, - cib_file_owner, cib_file_group); + file_owner, file_group); if (rc2 != pcmk_rc_ok) { pcmk__err("Could not set owner of sequence file in %s: %s", cib_dirname, pcmk_rc_str(rc2)); @@ -976,7 +976,7 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, exit_rc = pcmk_err_cib_save; goto cleanup; } - if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) { + if (do_chown && (fchown(fd, file_owner, file_group) < 0)) { pcmk__err("Couldn't protect temporary file %s for writing CIB: %s", tmp_cib, strerror(errno)); exit_rc = pcmk_err_cib_save; @@ -1004,7 +1004,7 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, exit_rc = pcmk_err_cib_save; goto cleanup; } - if (cib_do_chown && (fchown(fd, cib_file_owner, cib_file_group) < 0)) { + if (do_chown && (fchown(fd, file_owner, file_group) < 0)) { pcmk__err("Couldn't protect temporary file %s for writing CIB: %s", tmp_cib, strerror(errno)); exit_rc = pcmk_err_cib_save; @@ -1065,7 +1065,7 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, static int process_transaction_requests(cib_t *cib, xmlNode *transaction) { - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; for (xmlNode *request = pcmk__xe_first_child(transaction, PCMK__XE_CIB_COMMAND, NULL, @@ -1114,7 +1114,7 @@ static int commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) { int rc = pcmk_rc_ok; - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; xmlNode *saved_cib = private->cib_xml; CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION), @@ -1173,7 +1173,7 @@ process_commit_transact(const char *op, int options, const char *section, rc = commit_transaction(cib, input, result_cib); if (rc != pcmk_rc_ok) { - cib_file_opaque_t *private = cib->variant_opaque; + file_opaque_t *private = cib->variant_opaque; pcmk__err("Could not commit transaction for CIB file client (%s) on " "file '%s': %s", From baca455db3e2f9ff925eca1f773fc6a168bc4a4c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:33:31 -0800 Subject: [PATCH 070/350] Refactor: libcib: Move commit_transact handler above functions table We can't avoid a forward declaration altogether, but forward-declare get_op_functions() instead. It seems more reasonable for the commit_transact handler to be defined above the table that uses it. No code changes (aside from renaming file_get_op_functions() to get_op_functions()); it only moves. Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 453 ++++++++++++++++++++++----------------------- 1 file changed, 225 insertions(+), 228 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 6b4c90a5523..d6f4df018cc 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -51,10 +51,25 @@ typedef struct { xmlNode *cib_xml; } file_opaque_t; -static int process_commit_transact(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); +static cib__op_fn_t get_op_function(const cib__operation_t *operation); + +#define set_file_flags(cibfile, flags_to_set) do { \ + (cibfile)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "CIB file", \ + cibfile->filename, \ + (cibfile)->flags, \ + (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define clear_file_flags(cibfile, flags_to_clear) do { \ + (cibfile)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "CIB file", \ + cibfile->filename, \ + (cibfile)->flags, \ + (flags_to_clear), \ + #flags_to_clear); \ + } while (0) /*! * \internal @@ -116,97 +131,6 @@ get_client(const char *client_id) return g_hash_table_lookup(client_table, (gpointer) client_id); } -static const cib__op_fn_t op_functions[] = { - [cib__op_apply_patch] = cib__process_apply_patch, - [cib__op_bump] = cib__process_bump, - [cib__op_commit_transact] = process_commit_transact, - [cib__op_create] = cib_process_create, - [cib__op_delete] = cib_process_delete, - [cib__op_erase] = cib_process_erase, - [cib__op_modify] = cib_process_modify, - [cib__op_query] = cib_process_query, - [cib__op_replace] = cib_process_replace, - [cib__op_upgrade] = cib_process_upgrade, -}; - -/* backup_cib_file() and cib_file_write_with_digest() need to chown the - * written files only in limited circumstances, so these variables allow - * that to be indicated without affecting external callers - */ -static uid_t file_owner = 0; -static uid_t file_group = 0; -static gboolean do_chown = FALSE; - -#define set_file_flags(cibfile, flags_to_set) do { \ - (cibfile)->flags = pcmk__set_flags_as(__func__, __LINE__, \ - LOG_TRACE, "CIB file", \ - cibfile->filename, \ - (cibfile)->flags, \ - (flags_to_set), \ - #flags_to_set); \ - } while (0) - -#define clear_file_flags(cibfile, flags_to_clear) do { \ - (cibfile)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ - LOG_TRACE, "CIB file", \ - cibfile->filename, \ - (cibfile)->flags, \ - (flags_to_clear), \ - #flags_to_clear); \ - } while (0) - -/*! - * \internal - * \brief Get the function that performs a given CIB file operation - * - * \param[in] operation Operation whose function to look up - * - * \return Function that performs \p operation for a CIB file client - */ -static cib__op_fn_t -file_get_op_function(const cib__operation_t *operation) -{ - enum cib__op_type type = operation->type; - - pcmk__assert(type >= 0); - - if (type >= PCMK__NELEM(op_functions)) { - return NULL; - } - return op_functions[type]; -} - -/*! - * \internal - * \brief Check whether a file is the live CIB - * - * \param[in] filename Name of file to check - * - * \return TRUE if file exists and its real path is same as live CIB's - */ -static gboolean -is_live(const char *filename) -{ - gboolean same = FALSE; - - if (filename != NULL) { - // Canonicalize file names for true comparison - char *real_filename = NULL; - - if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) { - char *real_livename = NULL; - - if (pcmk__real_path(CRM_CONFIG_DIR "/" CIB_LIVE_NAME, - &real_livename) == pcmk_rc_ok) { - same = !strcmp(real_filename, real_livename); - free(real_livename); - } - free(real_filename); - } - } - return same; -} - static int process_request(cib_t *cib, xmlNode *request, xmlNode **output) { @@ -231,7 +155,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) // We error checked these in callers cib__get_operation(op, &operation); - op_function = file_get_op_function(operation); + op_function = get_op_function(operation); pcmk__xe_get_int(request, PCMK__XA_CIB_CALLID, &call_id); rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, @@ -281,6 +205,210 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) return rc; } +/*! + * \internal + * \brief Process requests in a CIB transaction + * + * Stop when a request fails or when all requests have been processed. + * + * \param[in,out] cib CIB client + * \param[in,out] transaction CIB transaction + * + * \return Standard Pacemaker return code + */ +static int +process_transaction_requests(cib_t *cib, xmlNode *transaction) +{ + file_opaque_t *private = cib->variant_opaque; + + 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)) { + + xmlNode *output = NULL; + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + + int rc = process_request(cib, request, &output); + + rc = pcmk_legacy2rc(rc); + if (rc != pcmk_rc_ok) { + pcmk__err("Aborting transaction for CIB file client (%s) on file " + "'%s' due to failed %s request: %s", + private->id, private->filename, op, pcmk_rc_str(rc)); + pcmk__log_xml_info(request, "Failed request"); + return rc; + } + + pcmk__trace("Applied %s request to transaction working CIB for CIB " + "file client (%s) on file '%s'", + op, private->id, private->filename); + pcmk__log_xml_trace(request, "Successful request"); + } + + return pcmk_rc_ok; +} + +/*! + * \internal + * \brief Commit a given CIB file client's transaction to a working CIB copy + * + * \param[in,out] cib CIB file client + * \param[in] transaction CIB transaction + * \param[in,out] result_cib Where to store result CIB + * + * \return Standard Pacemaker return code + * + * \note The caller is responsible for replacing the \p cib argument's + * \p private->cib_xml with \p result_cib on success, and for freeing + * \p result_cib using \p pcmk__xml_free() on failure. + */ +static int +commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) +{ + int rc = pcmk_rc_ok; + file_opaque_t *private = cib->variant_opaque; + xmlNode *saved_cib = private->cib_xml; + + CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION), + return pcmk_rc_no_transaction); + + /* *result_cib should be a copy of private->cib_xml (created by + * cib_perform_op()). If not, make a copy now. Change tracking isn't + * strictly required here because: + * * Each request in the transaction will have changes tracked and ACLs + * checked if appropriate. + * * cib_perform_op() will infer changes for the commit request at the end. + */ + CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml), + *result_cib = pcmk__xml_copy(NULL, private->cib_xml)); + + pcmk__trace("Committing transaction for CIB file client (%s) on file '%s' " + "to working CIB", + private->id, private->filename); + + // Apply all changes to a working copy of the CIB + private->cib_xml = *result_cib; + + rc = process_transaction_requests(cib, transaction); + + pcmk__trace("Transaction commit %s for CIB file client (%s) on file '%s'", + ((rc == pcmk_rc_ok)? "succeeded" : "failed"), + private->id, private->filename); + + /* Some request types (for example, erase) may have freed private->cib_xml + * (the working copy) and pointed it at a new XML object. In that case, it + * follows that *result_cib (the working copy) was freed. + * + * Point *result_cib at the updated working copy stored in private->cib_xml. + */ + *result_cib = private->cib_xml; + + // Point private->cib_xml back to the unchanged original copy + private->cib_xml = saved_cib; + + return rc; +} + +static int +process_commit_transact(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + int rc = pcmk_rc_ok; + const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); + cib_t *cib = NULL; + + CRM_CHECK(client_id != NULL, return -EINVAL); + + cib = get_client(client_id); + CRM_CHECK(cib != NULL, return -EINVAL); + + rc = commit_transaction(cib, input, result_cib); + if (rc != pcmk_rc_ok) { + file_opaque_t *private = cib->variant_opaque; + + pcmk__err("Could not commit transaction for CIB file client (%s) on " + "file '%s': %s", + private->id, private->filename, pcmk_rc_str(rc)); + } + return pcmk_rc2legacy(rc); +} + +static const cib__op_fn_t op_functions[] = { + [cib__op_apply_patch] = cib__process_apply_patch, + [cib__op_bump] = cib__process_bump, + [cib__op_commit_transact] = process_commit_transact, + [cib__op_create] = cib_process_create, + [cib__op_delete] = cib_process_delete, + [cib__op_erase] = cib_process_erase, + [cib__op_modify] = cib_process_modify, + [cib__op_query] = cib_process_query, + [cib__op_replace] = cib_process_replace, + [cib__op_upgrade] = cib_process_upgrade, +}; + +/* backup_cib_file() and cib_file_write_with_digest() need to chown the + * written files only in limited circumstances, so these variables allow + * that to be indicated without affecting external callers + */ +static uid_t file_owner = 0; +static uid_t file_group = 0; +static gboolean do_chown = FALSE; + +/*! + * \internal + * \brief Get the function that performs a given CIB file operation + * + * \param[in] operation Operation whose function to look up + * + * \return Function that performs \p operation for a CIB file client + */ +static cib__op_fn_t +get_op_function(const cib__operation_t *operation) +{ + enum cib__op_type type = operation->type; + + pcmk__assert(type >= 0); + + if (type >= PCMK__NELEM(op_functions)) { + return NULL; + } + return op_functions[type]; +} + +/*! + * \internal + * \brief Check whether a file is the live CIB + * + * \param[in] filename Name of file to check + * + * \return TRUE if file exists and its real path is same as live CIB's + */ +static gboolean +is_live(const char *filename) +{ + gboolean same = FALSE; + + if (filename != NULL) { + // Canonicalize file names for true comparison + char *real_filename = NULL; + + if (pcmk__real_path(filename, &real_filename) == pcmk_rc_ok) { + char *real_livename = NULL; + + if (pcmk__real_path(CRM_CONFIG_DIR "/" CIB_LIVE_NAME, + &real_livename) == pcmk_rc_ok) { + same = !strcmp(real_filename, real_livename); + free(real_livename); + } + free(real_filename); + } + } + return same; +} + static int file_perform_op_delegate(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, @@ -313,7 +441,7 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, return -EPROTONOSUPPORT; } - if (file_get_op_function(operation) == NULL) { + if (get_op_function(operation) == NULL) { // @COMPAT: At compatibility break, use EOPNOTSUPP pcmk__err("Operation %s is not supported by CIB file clients", op); return -EPROTONOSUPPORT; @@ -1050,134 +1178,3 @@ cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, free(tmp_cib); return exit_rc; } - -/*! - * \internal - * \brief Process requests in a CIB transaction - * - * Stop when a request fails or when all requests have been processed. - * - * \param[in,out] cib CIB client - * \param[in,out] transaction CIB transaction - * - * \return Standard Pacemaker return code - */ -static int -process_transaction_requests(cib_t *cib, xmlNode *transaction) -{ - file_opaque_t *private = cib->variant_opaque; - - 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)) { - - xmlNode *output = NULL; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - - int rc = process_request(cib, request, &output); - - rc = pcmk_legacy2rc(rc); - if (rc != pcmk_rc_ok) { - pcmk__err("Aborting transaction for CIB file client (%s) on file " - "'%s' due to failed %s request: %s", - private->id, private->filename, op, pcmk_rc_str(rc)); - pcmk__log_xml_info(request, "Failed request"); - return rc; - } - - pcmk__trace("Applied %s request to transaction working CIB for CIB " - "file client (%s) on file '%s'", - op, private->id, private->filename); - pcmk__log_xml_trace(request, "Successful request"); - } - - return pcmk_rc_ok; -} - -/*! - * \internal - * \brief Commit a given CIB file client's transaction to a working CIB copy - * - * \param[in,out] cib CIB file client - * \param[in] transaction CIB transaction - * \param[in,out] result_cib Where to store result CIB - * - * \return Standard Pacemaker return code - * - * \note The caller is responsible for replacing the \p cib argument's - * \p private->cib_xml with \p result_cib on success, and for freeing - * \p result_cib using \p pcmk__xml_free() on failure. - */ -static int -commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) -{ - int rc = pcmk_rc_ok; - file_opaque_t *private = cib->variant_opaque; - xmlNode *saved_cib = private->cib_xml; - - CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION), - return pcmk_rc_no_transaction); - - /* *result_cib should be a copy of private->cib_xml (created by - * cib_perform_op()). If not, make a copy now. Change tracking isn't - * strictly required here because: - * * Each request in the transaction will have changes tracked and ACLs - * checked if appropriate. - * * cib_perform_op() will infer changes for the commit request at the end. - */ - CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml), - *result_cib = pcmk__xml_copy(NULL, private->cib_xml)); - - pcmk__trace("Committing transaction for CIB file client (%s) on file '%s' " - "to working CIB", - private->id, private->filename); - - // Apply all changes to a working copy of the CIB - private->cib_xml = *result_cib; - - rc = process_transaction_requests(cib, transaction); - - pcmk__trace("Transaction commit %s for CIB file client (%s) on file '%s'", - ((rc == pcmk_rc_ok)? "succeeded" : "failed"), - private->id, private->filename); - - /* Some request types (for example, erase) may have freed private->cib_xml - * (the working copy) and pointed it at a new XML object. In that case, it - * follows that *result_cib (the working copy) was freed. - * - * Point *result_cib at the updated working copy stored in private->cib_xml. - */ - *result_cib = private->cib_xml; - - // Point private->cib_xml back to the unchanged original copy - private->cib_xml = saved_cib; - - return rc; -} - -static int -process_commit_transact(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) -{ - int rc = pcmk_rc_ok; - const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); - cib_t *cib = NULL; - - CRM_CHECK(client_id != NULL, return -EINVAL); - - cib = get_client(client_id); - CRM_CHECK(cib != NULL, return -EINVAL); - - rc = commit_transaction(cib, input, result_cib); - if (rc != pcmk_rc_ok) { - file_opaque_t *private = cib->variant_opaque; - - pcmk__err("Could not commit transaction for CIB file client (%s) on " - "file '%s': %s", - private->id, private->filename, pcmk_rc_str(rc)); - } - return pcmk_rc2legacy(rc); -} From ff478a7f9a174269f4e1e1e7b8d58ecb89c21f9a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:39:55 -0800 Subject: [PATCH 071/350] Refactor: libcib: Don't use gboolean in cib_file.c Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index d6f4df018cc..f98ce911d6a 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -30,7 +30,7 @@ #define CIB_SERIES "cib" #define CIB_SERIES_MAX 100 -#define CIB_SERIES_BZIP FALSE /* Must be false because archived copies are +#define CIB_SERIES_BZIP false /* Must be false because archived copies are created with hard links */ @@ -355,7 +355,7 @@ static const cib__op_fn_t op_functions[] = { */ static uid_t file_owner = 0; static uid_t file_group = 0; -static gboolean do_chown = FALSE; +static bool do_chown = false; /*! * \internal @@ -384,12 +384,13 @@ get_op_function(const cib__operation_t *operation) * * \param[in] filename Name of file to check * - * \return TRUE if file exists and its real path is same as live CIB's + * \return \c true if file exists and its real path is same as the live CIB's, + * or \c false otherwise */ -static gboolean +static bool is_live(const char *filename) { - gboolean same = FALSE; + bool same = false; if (filename != NULL) { // Canonicalize file names for true comparison @@ -617,7 +618,7 @@ write_live(xmlNode *cib_root, char *path) if (euid == 0) { file_owner = daemon_uid; file_group = daemon_gid; - do_chown = TRUE; + do_chown = true; } /* write the file */ @@ -626,7 +627,7 @@ write_live(xmlNode *cib_root, char *path) /* turn off file ownership changes, for other callers */ if (euid == 0) { - do_chown = FALSE; + do_chown = false; } /* undo fancy stuff */ @@ -823,12 +824,13 @@ cib_file_new(const char *cib_location) * \param[in] root Root of XML tree to compare * \param[in] sigfile Name of signature file containing digest to compare * - * \return TRUE if digests match or signature file does not exist, else FALSE + * \return \c true if digests match or signature file does not exist, or + * \c false otherwise */ -static gboolean +static bool verify_digest(xmlNode *root, const char *sigfile) { - gboolean passed = FALSE; + bool passed = false; char *expected; int rc = pcmk__file_contents(sigfile, &expected); @@ -836,16 +838,16 @@ verify_digest(xmlNode *root, const char *sigfile) case pcmk_rc_ok: if (expected == NULL) { pcmk__err("On-disk digest at %s is empty", sigfile); - return FALSE; + return false; } break; case ENOENT: pcmk__warn("No on-disk digest present at %s", sigfile); - return TRUE; + return true; default: pcmk__err("Could not read on-disk digest from %s: %s", sigfile, pcmk_rc_str(rc)); - return FALSE; + return false; } passed = pcmk__verify_digest(root, expected); free(expected); From 5774017ab5a37ddbf7dfb145812f59408a057ae9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:41:13 -0800 Subject: [PATCH 072/350] Refactor: libcib: Move some static variable declarations to top of file Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index f98ce911d6a..19d789e7d8a 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -51,6 +51,14 @@ typedef struct { xmlNode *cib_xml; } file_opaque_t; +/* backup_cib_file() and cib_file_write_with_digest() need to chown the + * written files only in limited circumstances, so these variables allow + * that to be indicated without affecting external callers + */ +static uid_t file_owner = 0; +static uid_t file_group = 0; +static bool do_chown = false; + static cib__op_fn_t get_op_function(const cib__operation_t *operation); #define set_file_flags(cibfile, flags_to_set) do { \ @@ -349,14 +357,6 @@ static const cib__op_fn_t op_functions[] = { [cib__op_upgrade] = cib_process_upgrade, }; -/* backup_cib_file() and cib_file_write_with_digest() need to chown the - * written files only in limited circumstances, so these variables allow - * that to be indicated without affecting external callers - */ -static uid_t file_owner = 0; -static uid_t file_group = 0; -static bool do_chown = false; - /*! * \internal * \brief Get the function that performs a given CIB file operation From 5dbcdb6704e44fdeafcd15b1c3bffa9b5d01c6b4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:42:32 -0800 Subject: [PATCH 073/350] Refactor: libcib: Drop CIB_SERIES_BZIP Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 19d789e7d8a..4e26515d6bb 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -30,10 +30,6 @@ #define CIB_SERIES "cib" #define CIB_SERIES_MAX 100 -#define CIB_SERIES_BZIP false /* Must be false because archived copies are - created with hard links - */ - #define CIB_LIVE_NAME CIB_SERIES ".xml" // key: client ID (const char *) -> value: client (cib_t *) @@ -950,8 +946,9 @@ backup_cib_file(const char *cib_dirname, const char *cib_filename) // @TODO maybe handle errors better ... seq = 0U; } - backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq, - CIB_SERIES_BZIP); + + // Must pass false because archived copies are created with hard links + backup_path = pcmk__series_filename(cib_dirname, CIB_SERIES, seq, false); backup_digest = pcmk__assert_asprintf("%s.sig", backup_path); /* Remove the old backups if they exist */ From 66ffc25f4e7ca6d25548c81d1c8334a324cdb3eb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:49:46 -0800 Subject: [PATCH 074/350] Refactor: libcib: cib_process_create() -> cib__process_create() Rename the function to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 8 +- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 263 ++++++++++++++++---------------- 4 files changed, 138 insertions(+), 137 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index e2797330ce0..291b6280b9d 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -21,7 +21,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_apply_patch] = based_process_apply_patch, [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = based_process_commit_transact, - [cib__op_create] = cib_process_create, + [cib__op_create] = cib__process_create, [cib__op_delete] = cib_process_delete, [cib__op_erase] = cib_process_erase, [cib__op_is_primary] = cib_process_readwrite, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index ae8750924d2..dbd39c27edb 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -208,6 +208,10 @@ int cib__process_bump(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int cib__process_create(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); @@ -220,10 +224,6 @@ int cib_process_replace(const char *op, int options, const char *section, xmlNod xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); -int cib_process_create(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); - int cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 4e26515d6bb..cf66f8cf5ec 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -344,7 +344,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = process_commit_transact, - [cib__op_create] = cib_process_create, + [cib__op_create] = cib__process_create, [cib__op_delete] = cib_process_delete, [cib__op_erase] = cib_process_erase, [cib__op_modify] = cib_process_modify, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index bc8a2e7676c..7c116b5b211 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -231,6 +231,138 @@ cib__process_bump(const char *op, int options, const char *section, return result; } +static int +add_cib_object(xmlNode *parent, xmlNode *new_obj) +{ + const char *object_name = NULL; + const char *object_id = NULL; + + if ((parent == NULL) || (new_obj == NULL)) { + return -EINVAL; + } + + object_name = (const char *) new_obj->name; + if (object_name == NULL) { + return -EINVAL; + } + + object_id = pcmk__xe_id(new_obj); + if (pcmk__xe_first_child(parent, object_name, + ((object_id != NULL)? PCMK_XA_ID : NULL), + object_id)) { + return -EEXIST; + } + + if (object_id != NULL) { + pcmk__trace("Processing creation of <%s " PCMK_XA_ID "='%s'>", + object_name, object_id); + } else { + pcmk__trace("Processing creation of <%s>", object_name); + } + + /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use + * case, PCMK__XA_REPLACE has special meaning and should not be included in + * the newly created object until we can break behavioral backward + * compatibility. + * + * At a compatibility break, drop this and drop the definition of + * PCMK__XA_REPLACE. Treat it like any other attribute. + */ + pcmk__xml_tree_foreach(new_obj, pcmk__xe_remove_attr_cb, + (void *) PCMK__XA_REPLACE); + + pcmk__xml_copy(parent, new_obj); + return pcmk_ok; +} + +static void +update_results(xmlNode *failed, xmlNode *target, const char *operation, int rc) +{ + xmlNode *failed_update = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE); + + pcmk__xml_copy(failed_update, target); + + pcmk__xe_set(failed_update, PCMK_XA_ID, pcmk__xe_id(target)); + pcmk__xe_set(failed_update, PCMK_XA_OBJECT_TYPE, + (const char *) target->name); + pcmk__xe_set(failed_update, PCMK_XA_OPERATION, operation); + pcmk__xe_set(failed_update, PCMK_XA_REASON, pcmk_rc_str(rc)); + + pcmk__warn("Action %s failed: %s", operation, pcmk_rc_str(rc)); +} + +int +cib__process_create(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *failed = NULL; + int result = pcmk_ok; + xmlNode *update_section = NULL; + + pcmk__trace("Processing %s for %s section", op, + pcmk__s(section, "unspecified")); + if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { + section = NULL; + + } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) { + section = NULL; + + } else if (pcmk__xe_is(input, PCMK_XE_CIB)) { + section = NULL; + } + + CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL); + + if (input == NULL) { + pcmk__err("Cannot perform modification with no data"); + return -EINVAL; + } + + if (section == NULL) { + return cib_process_modify(op, options, section, req, input, existing_cib, result_cib, + answer); + } + + // @COMPAT Deprecated since 2.1.8 + failed = pcmk__xe_create(NULL, PCMK__XE_FAILED); + + update_section = pcmk_find_cib_element(*result_cib, section); + if (pcmk__xe_is(input, section)) { + xmlNode *a_child = NULL; + + for (a_child = pcmk__xml_first_child(input); a_child != NULL; + a_child = pcmk__xml_next(a_child)) { + + result = add_cib_object(update_section, a_child); + if (result != pcmk_ok) { + update_results(failed, a_child, op, pcmk_legacy2rc(result)); + break; + } + } + + } else { + result = add_cib_object(update_section, input); + if (result != pcmk_ok) { + update_results(failed, input, op, pcmk_legacy2rc(result)); + } + } + + if ((result == pcmk_ok) && (failed->children != NULL)) { + result = -EINVAL; + } + + if (result != pcmk_ok) { + pcmk__log_xml_err(failed, "CIB Update failures"); + *answer = failed; + + } else { + pcmk__xml_free(failed); + } + + return result; +} + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -531,137 +663,6 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r return pcmk_ok; } -static int -add_cib_object(xmlNode * parent, xmlNode * new_obj) -{ - const char *object_name = NULL; - const char *object_id = NULL; - - if ((parent == NULL) || (new_obj == NULL)) { - return -EINVAL; - } - - object_name = (const char *) new_obj->name; - if (object_name == NULL) { - return -EINVAL; - } - - object_id = pcmk__xe_id(new_obj); - if (pcmk__xe_first_child(parent, object_name, - ((object_id != NULL)? PCMK_XA_ID : NULL), - object_id)) { - return -EEXIST; - } - - if (object_id != NULL) { - pcmk__trace("Processing creation of <%s " PCMK_XA_ID "='%s'>", - object_name, object_id); - } else { - pcmk__trace("Processing creation of <%s>", object_name); - } - - /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use - * case, PCMK__XA_REPLACE has special meaning and should not be included in - * the newly created object until we can break behavioral backward - * compatibility. - * - * At a compatibility break, drop this and drop the definition of - * PCMK__XA_REPLACE. Treat it like any other attribute. - */ - pcmk__xml_tree_foreach(new_obj, pcmk__xe_remove_attr_cb, - (void *) PCMK__XA_REPLACE); - - pcmk__xml_copy(parent, new_obj); - return pcmk_ok; -} - -static void -update_results(xmlNode *failed, xmlNode *target, const char *operation, int rc) -{ - xmlNode *failed_update = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE); - - pcmk__xml_copy(failed_update, target); - - pcmk__xe_set(failed_update, PCMK_XA_ID, pcmk__xe_id(target)); - pcmk__xe_set(failed_update, PCMK_XA_OBJECT_TYPE, - (const char *) target->name); - pcmk__xe_set(failed_update, PCMK_XA_OPERATION, operation); - pcmk__xe_set(failed_update, PCMK_XA_REASON, pcmk_rc_str(rc)); - - pcmk__warn("Action %s failed: %s", operation, pcmk_rc_str(rc)); -} - -int -cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - xmlNode *failed = NULL; - int result = pcmk_ok; - xmlNode *update_section = NULL; - - pcmk__trace("Processing %s for %s section", op, - pcmk__s(section, "unspecified")); - if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { - section = NULL; - - } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) { - section = NULL; - - } else if (pcmk__xe_is(input, PCMK_XE_CIB)) { - section = NULL; - } - - CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL); - - if (input == NULL) { - pcmk__err("Cannot perform modification with no data"); - return -EINVAL; - } - - if (section == NULL) { - return cib_process_modify(op, options, section, req, input, existing_cib, result_cib, - answer); - } - - // @COMPAT Deprecated since 2.1.8 - failed = pcmk__xe_create(NULL, PCMK__XE_FAILED); - - update_section = pcmk_find_cib_element(*result_cib, section); - if (pcmk__xe_is(input, section)) { - xmlNode *a_child = NULL; - - for (a_child = pcmk__xml_first_child(input); a_child != NULL; - a_child = pcmk__xml_next(a_child)) { - - result = add_cib_object(update_section, a_child); - if (result != pcmk_ok) { - update_results(failed, a_child, op, pcmk_legacy2rc(result)); - break; - } - } - - } else { - result = add_cib_object(update_section, input); - if (result != pcmk_ok) { - update_results(failed, input, op, pcmk_legacy2rc(result)); - } - } - - if ((result == pcmk_ok) && (failed->children != NULL)) { - result = -EINVAL; - } - - if (result != pcmk_ok) { - pcmk__log_xml_err(failed, "CIB Update failures"); - *answer = failed; - - } else { - pcmk__xml_free(failed); - } - - return result; -} - int cib_process_xpath(const char *op, int options, const char *section, const xmlNode *req, xmlNode *input, xmlNode *existing_cib, From aa60ba47985006d18033aac63be92dc02ed23893 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:55:31 -0800 Subject: [PATCH 075/350] Refactor: libcib: cib_process_delete() -> cib__process_delete() Rename the function to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 8 +- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 83 ++++++++++--------- lib/common/xml_element.c | 2 +- .../pcmk_resource/pcmk_resource_delete_test.c | 9 +- 6 files changed, 53 insertions(+), 53 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 291b6280b9d..74c0b5bb4c4 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -22,7 +22,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = based_process_commit_transact, [cib__op_create] = cib__process_create, - [cib__op_delete] = cib_process_delete, + [cib__op_delete] = cib__process_delete, [cib__op_erase] = cib_process_erase, [cib__op_is_primary] = cib_process_readwrite, [cib__op_modify] = cib_process_modify, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index dbd39c27edb..7d5675c8b6e 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -212,6 +212,10 @@ int cib__process_create(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int cib__process_delete(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); @@ -228,10 +232,6 @@ int cib_process_modify(const char *op, int options, const char *section, xmlNode xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); -int cib_process_delete(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); - int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index cf66f8cf5ec..676850c63dc 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -345,7 +345,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = process_commit_transact, [cib__op_create] = cib__process_create, - [cib__op_delete] = cib_process_delete, + [cib__op_delete] = cib__process_delete, [cib__op_erase] = cib_process_erase, [cib__op_modify] = cib_process_modify, [cib__op_query] = cib_process_query, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 7c116b5b211..dedbe3e0461 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -363,6 +363,48 @@ cib__process_create(const char *op, int options, const char *section, return result; } +static int +delete_child(xmlNode *child, void *userdata) +{ + xmlNode *obj_root = userdata; + + if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) { + pcmk__trace("No matching object to delete: %s=%s", child->name, + pcmk__xe_id(child)); + } + + return pcmk_rc_ok; +} + +int +cib__process_delete(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *obj_root = NULL; + + pcmk__trace("Processing \"%s\" event", op); + + if (options & cib_xpath) { + return cib_process_xpath(op, options, section, req, input, + existing_cib, result_cib, answer); + } + + if (input == NULL) { + pcmk__err("Cannot perform modification with no data"); + return -EINVAL; + } + + obj_root = pcmk_find_cib_element(*result_cib, section); + if (pcmk__xe_is(input, section)) { + pcmk__xe_foreach_child(input, NULL, delete_child, obj_root); + } else { + delete_child(input, obj_root); + } + + return pcmk_ok; +} + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -569,47 +611,6 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * return result; } -static int -delete_child(xmlNode *child, void *userdata) -{ - xmlNode *obj_root = userdata; - - if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) { - pcmk__trace("No matching object to delete: %s=%s", child->name, - pcmk__xe_id(child)); - } - - return pcmk_rc_ok; -} - -int -cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - xmlNode *obj_root = NULL; - - pcmk__trace("Processing \"%s\" event", op); - - if (options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); - } - - if (input == NULL) { - pcmk__err("Cannot perform modification with no data"); - return -EINVAL; - } - - obj_root = pcmk_find_cib_element(*result_cib, section); - if (pcmk__xe_is(input, section)) { - pcmk__xe_foreach_child(input, NULL, delete_child, obj_root); - } else { - delete_child(input, obj_root); - } - - return pcmk_ok; -} - int cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) diff --git a/lib/common/xml_element.c b/lib/common/xml_element.c index 82acc186b81..bd4e4a1436e 100644 --- a/lib/common/xml_element.c +++ b/lib/common/xml_element.c @@ -834,7 +834,7 @@ pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace) /* @COMPAT Some of this behavior (like not matching the tree root, which is * allowed by pcmk__xe_update_match()) is questionable for general use but * required for backward compatibility by cib_process_replace() and - * cib_process_delete(). Behavior can change at a major version release if + * cib__process_delete(). Behavior can change at a major version release if * desired. */ CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL); diff --git a/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c b/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c index 7bfc751d30c..2492a5a411d 100644 --- a/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c +++ b/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 the Pacemaker project contributors + * Copyright 2024-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -95,9 +95,8 @@ incorrect_type(void **state) xmlNode *xml = NULL; xmlNode *result = NULL; - /* cib_process_delete returns pcmk_ok even if given the wrong type so - * we have to do an xpath query of the CIB to make sure it's still - * there. + /* cib__process_delete() returns pcmk_ok even if given the wrong type, so we + * have to do an xpath query of the CIB to make sure it's still there. */ assert_int_equal(pcmk_resource_delete(&xml, "Fencing", "clone"), pcmk_rc_ok); pcmk__assert_validates(xml); @@ -130,7 +129,7 @@ unknown_resource(void **state) { xmlNode *xml = NULL; - /* cib_process_delete returns pcmk_ok even if asked to delete something + /* cib__process_delete() returns pcmk_ok even if asked to delete something * that doesn't exist. */ assert_int_equal(pcmk_resource_delete(&xml, "no_such_resource", "primitive"), pcmk_rc_ok); From 9c75558ff1dc397ce4444afb6486e268ebde9209 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 01:59:26 -0800 Subject: [PATCH 076/350] Refactor: libcib: Make cib_process_xpath() static And rename to process_xpath(), to avoid using the public API prefix. Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 26 --- lib/cib/cib_ops.c | 384 ++++++++++++++++++++----------------- 2 files changed, 203 insertions(+), 207 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 7d5675c8b6e..f32bc60bc87 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -236,32 +236,6 @@ int cib_process_upgrade(const char *op, int options, const char *section, xmlNod xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); -/*! - * \internal - * \brief Query or modify a CIB - * - * \param[in] op PCMK__CIB_REQUEST_* operation to be performed - * \param[in] options Flag set of \c cib_call_options - * \param[in] section XPath to query or modify - * \param[in] req unused - * \param[in] input Portion of CIB to modify (used with - * PCMK__CIB_REQUEST_CREATE, - * PCMK__CIB_REQUEST_MODIFY, and - * PCMK__CIB_REQUEST_REPLACE) - * \param[in,out] existing_cib Input CIB (used with PCMK__CIB_REQUEST_QUERY) - * \param[in,out] result_cib CIB copy to make changes in (used with - * PCMK__CIB_REQUEST_CREATE, - * PCMK__CIB_REQUEST_MODIFY, - * PCMK__CIB_REQUEST_DELETE, and - * PCMK__CIB_REQUEST_REPLACE) - * \param[out] answer Query result (used with PCMK__CIB_REQUEST_QUERY) - * - * \return Legacy Pacemaker return code - */ -int cib_process_xpath(const char *op, int options, const char *section, - const xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode ** answer); - int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, xmlNode ** output_data, int call_options, const char *user_name); diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index dedbe3e0461..60d3a4c977d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -363,6 +363,199 @@ cib__process_create(const char *op, int options, const char *section, return result; } +/*! + * \internal + * \brief Query or modify a CIB + * + * \param[in] op PCMK__CIB_REQUEST_* operation to be performed + * \param[in] options Flag set of \c cib_call_options + * \param[in] section XPath to query or modify + * \param[in] req unused + * \param[in] input Portion of CIB to modify (used with + * PCMK__CIB_REQUEST_CREATE, + * PCMK__CIB_REQUEST_MODIFY, and + * PCMK__CIB_REQUEST_REPLACE) + * \param[in,out] existing_cib Input CIB (used with PCMK__CIB_REQUEST_QUERY) + * \param[in,out] result_cib CIB copy to make changes in (used with + * PCMK__CIB_REQUEST_CREATE, + * PCMK__CIB_REQUEST_MODIFY, + * PCMK__CIB_REQUEST_DELETE, and + * PCMK__CIB_REQUEST_REPLACE) + * \param[out] answer Query result (used with PCMK__CIB_REQUEST_QUERY) + * + * \return Legacy Pacemaker return code + */ +static int +process_xpath(const char *op, int options, const char *section, + const xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + int num_results = 0; + int rc = pcmk_ok; + bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none); + bool delete_multiple = pcmk__is_set(options, cib_multiple) + && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, + pcmk__str_none); + xmlXPathObject *xpathObj = NULL; + + pcmk__trace("Processing \"%s\" event", op); + + if (is_query) { + xpathObj = pcmk__xpath_search(existing_cib->doc, section); + } else { + xpathObj = pcmk__xpath_search((*result_cib)->doc, section); + } + + num_results = pcmk__xpath_num_results(xpathObj); + if (num_results == 0) { + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { + pcmk__debug("%s was already removed", section); + + } else { + pcmk__debug("%s: %s does not exist", op, section); + rc = -ENXIO; + } + goto done; + } + + if (is_query && (num_results > 1)) { + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); + } + + for (int i = 0; i < num_results; i++) { + xmlNode *match = NULL; + xmlChar *path = NULL; + + /* If we're deleting multiple nodes, go in reverse document order. + * If we go in forward order and the node set contains both a parent and + * its descendant, then deleting the parent frees the descendant before + * the loop reaches the descendant. This is a use-after-free error. + * + * @COMPAT cib_multiple is only ever used with delete operations. The + * correct order to process multiple nodes for operations other than + * query (forward) and delete (reverse) is less clear but likely should + * be reverse. If we ever replace the CIB public API with libpacemaker + * functions, revisit this. For now, we keep forward order for other + * operations to preserve backward compatibility, even though external + * callers of other ops with cib_multiple might segfault. + * + * For more info, see comment in xpath2.c:update_xpath_nodes() in + * libxml2. + */ + if (delete_multiple) { + match = pcmk__xpath_result(xpathObj, num_results - 1 - i); + } else { + match = pcmk__xpath_result(xpathObj, i); + } + + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, section, path); + free(path); + + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { + if (match == *result_cib) { + /* Attempting to delete the whole "/cib" */ + pcmk__warn("Cannot perform %s for %s: The xpath is addressing " + "the whole /cib", + op, section); + rc = -EINVAL; + break; + } + + pcmk__xml_free(match); + if ((options & cib_multiple) == 0) { + break; + } + + } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) { + uint32_t flags = pcmk__xaf_none; + + if (pcmk__is_set(options, cib_score_update)) { + flags |= pcmk__xaf_score_update; + } + + if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { + rc = -ENXIO; + } else if ((options & cib_multiple) == 0) { + break; + } + + } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) { + pcmk__xml_copy(match, input); + break; + + } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { + + if (options & cib_no_children) { + xmlNode *shallow = pcmk__xe_create(*answer, + (const char *) match->name); + + pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); + + if (*answer == NULL) { + *answer = shallow; + } + + } else if (options & cib_xpath_address) { + // @COMPAT cib_xpath_address is deprecated since 3.0.2 + char *path = NULL; + xmlNode *parent = match; + + while (parent && parent->type == XML_ELEMENT_NODE) { + const char *id = pcmk__xe_get(parent, PCMK_XA_ID); + char *new_path = NULL; + + if (id) { + new_path = + pcmk__assert_asprintf("/%s[@" PCMK_XA_ID "='%s']%s", + parent->name, id, + pcmk__s(path, "")); + } else { + new_path = pcmk__assert_asprintf("/%s%s", parent->name, + pcmk__s(path, "")); + } + free(path); + path = new_path; + parent = parent->parent; + } + pcmk__trace("Got: %s", path); + + if (*answer == NULL) { + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); + } + parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH); + pcmk__xe_set(parent, PCMK_XA_ID, path); + free(path); + + } else if (*answer) { + pcmk__xml_copy(*answer, match); + + } else { + *answer = match; + } + + } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, + pcmk__str_none)) { + xmlNode *parent = match->parent; + + pcmk__xml_free(match); + pcmk__xml_copy(parent, input); + + if ((options & cib_multiple) == 0) { + break; + } + } + } + +done: + xmlXPathFreeObject(xpathObj); + return rc; +} + static int delete_child(xmlNode *child, void *userdata) { @@ -386,8 +579,8 @@ cib__process_delete(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); + return process_xpath(op, options, section, req, input, existing_cib, + result_cib, answer); } if (input == NULL) { @@ -416,8 +609,8 @@ cib_process_query(const char *op, int options, const char *section, xmlNode * re pcmk__s(section, "unspecified")); if (options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); + return process_xpath(op, options, section, req, input, existing_cib, + result_cib, answer); } CRM_CHECK(*answer == NULL, pcmk__xml_free(*answer)); @@ -510,8 +703,8 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * pcmk__s(section, "unspecified")); if (options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); + return process_xpath(op, options, section, req, input, existing_cib, + result_cib, answer); } *answer = NULL; @@ -621,8 +814,8 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r pcmk__trace("Processing \"%s\" event", op); if (options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); + return process_xpath(op, options, section, req, input, existing_cib, + result_cib, answer); } if (input == NULL) { @@ -640,8 +833,8 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r } tmp_section = pcmk__xe_create(NULL, section); - cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section, - NULL, result_cib, answer); + process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section, + NULL, result_cib, answer); pcmk__xml_free(tmp_section); obj_root = pcmk_find_cib_element(*result_cib, section); @@ -663,174 +856,3 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r return pcmk_ok; } - -int -cib_process_xpath(const char *op, int options, const char *section, - const xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) -{ - int num_results = 0; - int rc = pcmk_ok; - bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none); - bool delete_multiple = pcmk__is_set(options, cib_multiple) - && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, - pcmk__str_none); - xmlXPathObject *xpathObj = NULL; - - pcmk__trace("Processing \"%s\" event", op); - - if (is_query) { - xpathObj = pcmk__xpath_search(existing_cib->doc, section); - } else { - xpathObj = pcmk__xpath_search((*result_cib)->doc, section); - } - - num_results = pcmk__xpath_num_results(xpathObj); - if (num_results == 0) { - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { - pcmk__debug("%s was already removed", section); - - } else { - pcmk__debug("%s: %s does not exist", op, section); - rc = -ENXIO; - } - goto done; - } - - if (is_query && (num_results > 1)) { - *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); - } - - for (int i = 0; i < num_results; i++) { - xmlNode *match = NULL; - xmlChar *path = NULL; - - /* If we're deleting multiple nodes, go in reverse document order. - * If we go in forward order and the node set contains both a parent and - * its descendant, then deleting the parent frees the descendant before - * the loop reaches the descendant. This is a use-after-free error. - * - * @COMPAT cib_multiple is only ever used with delete operations. The - * correct order to process multiple nodes for operations other than - * query (forward) and delete (reverse) is less clear but likely should - * be reverse. If we ever replace the CIB public API with libpacemaker - * functions, revisit this. For now, we keep forward order for other - * operations to preserve backward compatibility, even though external - * callers of other ops with cib_multiple might segfault. - * - * For more info, see comment in xpath2.c:update_xpath_nodes() in - * libxml2. - */ - if (delete_multiple) { - match = pcmk__xpath_result(xpathObj, num_results - 1 - i); - } else { - match = pcmk__xpath_result(xpathObj, i); - } - - if (match == NULL) { - continue; - } - - path = xmlGetNodePath(match); - pcmk__debug("Processing %s op for %s with %s", op, section, path); - free(path); - - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { - if (match == *result_cib) { - /* Attempting to delete the whole "/cib" */ - pcmk__warn("Cannot perform %s for %s: The xpath is addressing " - "the whole /cib", - op, section); - rc = -EINVAL; - break; - } - - pcmk__xml_free(match); - if ((options & cib_multiple) == 0) { - break; - } - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) { - uint32_t flags = pcmk__xaf_none; - - if (pcmk__is_set(options, cib_score_update)) { - flags |= pcmk__xaf_score_update; - } - - if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { - rc = -ENXIO; - } else if ((options & cib_multiple) == 0) { - break; - } - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) { - pcmk__xml_copy(match, input); - break; - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { - - if (options & cib_no_children) { - xmlNode *shallow = pcmk__xe_create(*answer, - (const char *) match->name); - - pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); - - if (*answer == NULL) { - *answer = shallow; - } - - } else if (options & cib_xpath_address) { - // @COMPAT cib_xpath_address is deprecated since 3.0.2 - char *path = NULL; - xmlNode *parent = match; - - while (parent && parent->type == XML_ELEMENT_NODE) { - const char *id = pcmk__xe_get(parent, PCMK_XA_ID); - char *new_path = NULL; - - if (id) { - new_path = - pcmk__assert_asprintf("/%s[@" PCMK_XA_ID "='%s']%s", - parent->name, id, - pcmk__s(path, "")); - } else { - new_path = pcmk__assert_asprintf("/%s%s", parent->name, - pcmk__s(path, "")); - } - free(path); - path = new_path; - parent = parent->parent; - } - pcmk__trace("Got: %s", path); - - if (*answer == NULL) { - *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); - } - parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH); - pcmk__xe_set(parent, PCMK_XA_ID, path); - free(path); - - } else if (*answer) { - pcmk__xml_copy(*answer, match); - - } else { - *answer = match; - } - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, - pcmk__str_none)) { - xmlNode *parent = match->parent; - - pcmk__xml_free(match); - pcmk__xml_copy(parent, input); - - if ((options & cib_multiple) == 0) { - break; - } - } - } - -done: - xmlXPathFreeObject(xpathObj); - return rc; -} From f05caae0816fc947472c39579b4d4f5b0f8985c6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:02:03 -0800 Subject: [PATCH 077/350] Doc: various: Update a few copyright dates These were missed in recent commits. Signed-off-by: Reid Wahl --- lib/common/tests/io/pcmk__get_tmpdir_test.c | 2 +- lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c | 2 +- tools/crm_verify.c | 2 +- tools/crmadmin.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/common/tests/io/pcmk__get_tmpdir_test.c b/lib/common/tests/io/pcmk__get_tmpdir_test.c index 7a0cf901a80..e9ccf901a9d 100644 --- a/lib/common/tests/io/pcmk__get_tmpdir_test.c +++ b/lib/common/tests/io/pcmk__get_tmpdir_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the Pacemaker project contributors + * Copyright 2021-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c index 76e0839c0bc..c25ac96b109 100644 --- a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_delete_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 the Pacemaker project contributors + * Copyright 2024-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * diff --git a/tools/crm_verify.c b/tools/crm_verify.c index 6da34e0259d..145fb1d0220 100644 --- a/tools/crm_verify.c +++ b/tools/crm_verify.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2024 the Pacemaker project contributors + * Copyright 2004-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * diff --git a/tools/crmadmin.c b/tools/crmadmin.c index 281705327d2..62b8aadcdfc 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2024 the Pacemaker project contributors + * Copyright 2004-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * From a1903feeb30a0dd625e6b6e48af93f2f270c594e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:04:58 -0800 Subject: [PATCH 078/350] Refactor: libcib: cib_process_erase() -> cib__process_erase() Rename the function to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 8 +++---- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 39 +++++++++++++++++---------------- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 74c0b5bb4c4..ff00c74595d 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -23,7 +23,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_commit_transact] = based_process_commit_transact, [cib__op_create] = cib__process_create, [cib__op_delete] = cib__process_delete, - [cib__op_erase] = cib_process_erase, + [cib__op_erase] = cib__process_erase, [cib__op_is_primary] = cib_process_readwrite, [cib__op_modify] = cib_process_modify, [cib__op_noop] = cib_process_noop, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index f32bc60bc87..e879fda58ba 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -216,11 +216,11 @@ int cib__process_delete(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_query(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); +int cib__process_erase(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); -int cib_process_erase(const char *op, int options, const char *section, xmlNode * req, +int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 676850c63dc..56e69d92cd2 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -346,7 +346,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_commit_transact] = process_commit_transact, [cib__op_create] = cib__process_create, [cib__op_delete] = cib__process_delete, - [cib__op_erase] = cib_process_erase, + [cib__op_erase] = cib__process_erase, [cib__op_modify] = cib_process_modify, [cib__op_query] = cib_process_query, [cib__op_replace] = cib_process_replace, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 60d3a4c977d..12f72c9b70a 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -598,6 +598,26 @@ cib__process_delete(const char *op, int options, const char *section, return pcmk_ok; } +int +cib__process_erase(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + int result = pcmk_ok; + + pcmk__trace("Processing \"%s\" event", op); + + if (*result_cib != existing_cib) { + pcmk__xml_free(*result_cib); + } + *result_cib = createEmptyCib(0); + pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none); + update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); + *answer = NULL; + + return result; +} + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -644,25 +664,6 @@ cib_process_query(const char *op, int options, const char *section, xmlNode * re return result; } -int -cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - int result = pcmk_ok; - - pcmk__trace("Processing \"%s\" event", op); - - if (*result_cib != existing_cib) { - pcmk__xml_free(*result_cib); - } - *result_cib = createEmptyCib(0); - pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none); - update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); - *answer = NULL; - - return result; -} - int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, From 46237f2aed4dd7895d25ac1a32e54bf3eb909094 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:09:11 -0800 Subject: [PATCH 079/350] Refactor: based: New based_process_is_primary() The new function's name matches the request name. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 21 +++++++++++---------- daemons/based/based_messages.h | 5 +++++ daemons/based/based_operation.c | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index ce5f5695d83..bfc21402fe7 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -180,6 +180,17 @@ cib_process_noop(const char *op, int options, const char *section, xmlNode *req, return pcmk_ok; } +int +based_process_is_primary(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + pcmk__trace("Processing \"%s\" event", op); + + // @COMPAT Pacemaker Remote clients <3.0.0 may send this + return (based_is_primary? pcmk_ok : -EPERM); +} + int cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -189,16 +200,6 @@ cib_process_readwrite(const char *op, int options, const char *section, xmlNode pcmk__trace("Processing \"%s\" event", op); - // @COMPAT Pacemaker Remote clients <3.0.0 may send this - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_IS_PRIMARY, pcmk__str_none)) { - if (based_is_primary) { - result = pcmk_ok; - } else { - result = -EPERM; - } - return result; - } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index ddcc8dd561a..6207e074e24 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -32,6 +32,11 @@ int based_process_commit_transact(const char *op, int options, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_is_primary(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index ff00c74595d..a97559f81d2 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -24,7 +24,7 @@ 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] = cib_process_readwrite, + [cib__op_is_primary] = based_process_is_primary, [cib__op_modify] = cib_process_modify, [cib__op_noop] = cib_process_noop, [cib__op_ping] = cib_process_ping, From 345124c8d15331ed52e1c539dd4f1aafcccfc8a1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:15:22 -0800 Subject: [PATCH 080/350] Refactor: libcib: cib_process_modify() -> cib__process_modify() Rename the function to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 8 +-- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 111 ++++++++++++++++---------------- 4 files changed, 62 insertions(+), 61 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index a97559f81d2..4b268764050 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -25,7 +25,7 @@ static const cib__op_fn_t op_functions[] = { [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_modify] = cib__process_modify, [cib__op_noop] = cib_process_noop, [cib__op_ping] = cib_process_ping, [cib__op_primary] = cib_process_readwrite, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index e879fda58ba..aa9455c0fc5 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -220,6 +220,10 @@ int cib__process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int cib__process_modify(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); @@ -228,10 +232,6 @@ int cib_process_replace(const char *op, int options, const char *section, xmlNod xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); -int cib_process_modify(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); - int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 56e69d92cd2..c0b79549849 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -347,7 +347,7 @@ 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_modify] = cib_process_modify, + [cib__op_modify] = cib__process_modify, [cib__op_query] = cib_process_query, [cib__op_replace] = cib_process_replace, [cib__op_upgrade] = cib_process_upgrade, diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 12f72c9b70a..8c8225c27b9 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -320,8 +320,8 @@ cib__process_create(const char *op, int options, const char *section, } if (section == NULL) { - return cib_process_modify(op, options, section, req, input, existing_cib, result_cib, - answer); + return cib__process_modify(op, options, section, req, input, + existing_cib, result_cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -618,6 +618,60 @@ cib__process_erase(const char *op, int options, const char *section, return result; } +int +cib__process_modify(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *obj_root = NULL; + uint32_t flags = pcmk__xaf_none; + + pcmk__trace("Processing \"%s\" event", op); + + if (options & cib_xpath) { + return process_xpath(op, options, section, req, input, existing_cib, + result_cib, answer); + } + + if (input == NULL) { + pcmk__err("Cannot perform modification with no data"); + return -EINVAL; + } + + obj_root = pcmk_find_cib_element(*result_cib, section); + if (obj_root == NULL) { + xmlNode *tmp_section = NULL; + const char *path = pcmk_cib_parent_name_for(section); + + if (path == NULL) { + return -EINVAL; + } + + tmp_section = pcmk__xe_create(NULL, section); + process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section, + NULL, result_cib, answer); + pcmk__xml_free(tmp_section); + + obj_root = pcmk_find_cib_element(*result_cib, section); + } + + CRM_CHECK(obj_root != NULL, return -EINVAL); + + if (pcmk__is_set(options, cib_score_update)) { + flags |= pcmk__xaf_score_update; + } + + if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) { + if (options & cib_can_create) { + pcmk__xml_copy(obj_root, input); + } else { + return -ENXIO; + } + } + + return pcmk_ok; +} + int cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) @@ -804,56 +858,3 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * return result; } - -int -cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - xmlNode *obj_root = NULL; - uint32_t flags = pcmk__xaf_none; - - pcmk__trace("Processing \"%s\" event", op); - - if (options & cib_xpath) { - return process_xpath(op, options, section, req, input, existing_cib, - result_cib, answer); - } - - if (input == NULL) { - pcmk__err("Cannot perform modification with no data"); - return -EINVAL; - } - - obj_root = pcmk_find_cib_element(*result_cib, section); - if (obj_root == NULL) { - xmlNode *tmp_section = NULL; - const char *path = pcmk_cib_parent_name_for(section); - - if (path == NULL) { - return -EINVAL; - } - - tmp_section = pcmk__xe_create(NULL, section); - process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section, - NULL, result_cib, answer); - pcmk__xml_free(tmp_section); - - obj_root = pcmk_find_cib_element(*result_cib, section); - } - - CRM_CHECK(obj_root != NULL, return -EINVAL); - - if (pcmk__is_set(options, cib_score_update)) { - flags |= pcmk__xaf_score_update; - } - - if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) { - if (options & cib_can_create) { - pcmk__xml_copy(obj_root, input); - } else { - return -ENXIO; - } - } - - return pcmk_ok; -} From 7d739e789a26d132d0ad7ab51ac347df1d32794b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:19:00 -0800 Subject: [PATCH 081/350] Refactor: based: cib_process_noop() -> based_process_noop() Avoid using the public API prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 22 +++++++++++----------- daemons/based/based_messages.h | 8 ++++---- daemons/based/based_operation.c | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index bfc21402fe7..61f89a98e3e 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -169,17 +169,6 @@ cib_process_shutdown_req(const char *op, int options, const char *section, xmlNo return pcmk_ok; } -// @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed -int -cib_process_noop(const char *op, int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer) -{ - pcmk__trace("Processing \"%s\" event", op); - *answer = NULL; - return pcmk_ok; -} - int based_process_is_primary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -191,6 +180,17 @@ based_process_is_primary(const char *op, int options, const char *section, return (based_is_primary? pcmk_ok : -EPERM); } +// @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed +int +based_process_noop(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + pcmk__trace("Processing \"%s\" event", op); + *answer = NULL; + return pcmk_ok; +} + int cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 6207e074e24..e42661c1dc0 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -37,15 +37,15 @@ int based_process_is_primary(const char *op, int options, const char *section, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_noop(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_noop(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_ping(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 4b268764050..65c19f540f8 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -26,7 +26,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_erase] = cib__process_erase, [cib__op_is_primary] = based_process_is_primary, [cib__op_modify] = cib__process_modify, - [cib__op_noop] = cib_process_noop, + [cib__op_noop] = based_process_noop, [cib__op_ping] = cib_process_ping, [cib__op_primary] = cib_process_readwrite, [cib__op_query] = cib_process_query, From 6cb54ee948a0a692d7917f882d3a2871d0f799af Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:21:07 -0800 Subject: [PATCH 082/350] Refactor: based: cib_process_ping() -> based_process_ping() Avoid using the public API prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 89 +++++++++++++++++---------------- daemons/based/based_messages.h | 8 +-- daemons/based/based_operation.c | 2 +- 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 61f89a98e3e..1ae6a7faf3d 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -192,50 +192,9 @@ based_process_noop(const char *op, int options, const char *section, } int -cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - int result = pcmk_ok; - - pcmk__trace("Processing \"%s\" event", op); - - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) { - if (!based_is_primary) { - pcmk__info("We are now in R/W mode"); - based_is_primary = true; - } else { - pcmk__debug("We are still in R/W mode"); - } - - } else if (based_is_primary) { - pcmk__info("We are now in R/O mode"); - based_is_primary = false; - } - - return result; -} - -void -send_sync_request(void) -{ - xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me"); - pcmk__node_status_t *peer = NULL; - - pcmk__info("Requesting re-sync from all peers"); - sync_in_progress = 1; - - pcmk__xe_set(sync_me, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE); - pcmk__xe_set(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); - - pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me); - pcmk__xml_free(sync_me); -} - -int -cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) +based_process_ping(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); @@ -281,6 +240,48 @@ cib_process_ping(const char *op, int options, const char *section, xmlNode * req return pcmk_ok; } +int +cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer) +{ + int result = pcmk_ok; + + pcmk__trace("Processing \"%s\" event", op); + + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) { + if (!based_is_primary) { + pcmk__info("We are now in R/W mode"); + based_is_primary = true; + } else { + pcmk__debug("We are still in R/W mode"); + } + + } else if (based_is_primary) { + pcmk__info("We are now in R/O mode"); + based_is_primary = false; + } + + return result; +} + +void +send_sync_request(void) +{ + xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me"); + pcmk__node_status_t *peer = NULL; + + pcmk__info("Requesting re-sync from all peers"); + sync_in_progress = 1; + + pcmk__xe_set(sync_me, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE); + pcmk__xe_set(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); + + pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me); + pcmk__xml_free(sync_me); +} + int cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index e42661c1dc0..4f6e4b89dec 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -41,15 +41,15 @@ int based_process_noop(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_ping(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_ping(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_readwrite(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 65c19f540f8..13b7dba14fb 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -27,7 +27,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_is_primary] = based_process_is_primary, [cib__op_modify] = cib__process_modify, [cib__op_noop] = based_process_noop, - [cib__op_ping] = cib_process_ping, + [cib__op_ping] = based_process_ping, [cib__op_primary] = cib_process_readwrite, [cib__op_query] = cib_process_query, [cib__op_replace] = cib_process_replace_svr, From bd0f2904d5538fb8f0605990e05fb6dce84a9dc0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:24:28 -0800 Subject: [PATCH 083/350] Refactor: based: New based_process_primary() The new function's name matches the request name. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 24 ++++++++++++++++++------ daemons/based/based_messages.h | 4 ++++ daemons/based/based_operation.c | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 1ae6a7faf3d..dc982e19afc 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -240,6 +240,24 @@ based_process_ping(const char *op, int options, const char *section, return pcmk_ok; } +int +based_process_primary(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode ** answer) +{ + pcmk__trace("Processing \"%s\" event", op); + + if (!based_is_primary) { + pcmk__info("We are now in R/W mode"); + based_is_primary = true; + + } else { + pcmk__debug("We are still in R/W mode"); + } + + return pcmk_ok; +} + int cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -250,12 +268,6 @@ cib_process_readwrite(const char *op, int options, const char *section, xmlNode pcmk__trace("Processing \"%s\" event", op); if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) { - if (!based_is_primary) { - pcmk__info("We are now in R/W mode"); - based_is_primary = true; - } else { - pcmk__debug("We are still in R/W mode"); - } } else if (based_is_primary) { pcmk__info("We are now in R/O mode"); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 4f6e4b89dec..1abdf7f67ac 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -45,6 +45,10 @@ int based_process_ping(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_primary(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 13b7dba14fb..e985d76afcb 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -28,7 +28,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_modify] = cib__process_modify, [cib__op_noop] = based_process_noop, [cib__op_ping] = based_process_ping, - [cib__op_primary] = cib_process_readwrite, + [cib__op_primary] = based_process_primary, [cib__op_query] = cib_process_query, [cib__op_replace] = cib_process_replace_svr, [cib__op_secondary] = cib_process_readwrite, From 0cd53809f01a9be8bd378c9c36a17e5453a6fc41 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:26:19 -0800 Subject: [PATCH 084/350] Refactor: libcib: cib_process_query() -> cib__process_query() Rename the function to use the internal prefix. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 6 +++--- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 5 +++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index e985d76afcb..1e56c41d4fb 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -29,7 +29,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_noop] = based_process_noop, [cib__op_ping] = based_process_ping, [cib__op_primary] = based_process_primary, - [cib__op_query] = cib_process_query, + [cib__op_query] = cib__process_query, [cib__op_replace] = cib_process_replace_svr, [cib__op_secondary] = cib_process_readwrite, [cib__op_shutdown] = cib_process_shutdown_req, diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index aa9455c0fc5..75fdba592ab 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -224,9 +224,9 @@ int cib__process_modify(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_query(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); +int cib__process_query(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); int cib_process_replace(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index c0b79549849..af2bd68c2f3 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -348,7 +348,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_delete] = cib__process_delete, [cib__op_erase] = cib__process_erase, [cib__op_modify] = cib__process_modify, - [cib__op_query] = cib_process_query, + [cib__op_query] = cib__process_query, [cib__op_replace] = cib_process_replace, [cib__op_upgrade] = cib_process_upgrade, }; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 8c8225c27b9..09c9624a892 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -673,8 +673,9 @@ cib__process_modify(const char *op, int options, const char *section, } int -cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) +cib__process_query(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { xmlNode *obj_root = NULL; int result = pcmk_ok; From 304152273d9dcc0f51ba81d01ef32902710610d4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:29:14 -0800 Subject: [PATCH 085/350] Refactor: based: cib_process_replace_svr() -> based_process_replace() Avoid using the public API prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 28 ++++++++++++++-------------- daemons/based/based_messages.h | 8 ++++---- daemons/based/based_operation.c | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index dc982e19afc..533d6234e8e 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -258,6 +258,20 @@ based_process_primary(const char *op, int options, const char *section, return pcmk_ok; } +int +based_process_replace(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + int rc = + cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer); + + if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { + sync_in_progress = 0; + } + return rc; +} + int cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, @@ -404,20 +418,6 @@ cib_process_sync_one(const char *op, int options, const char *section, xmlNode * return sync_our_cib(req, false); } -int -cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - int rc = - cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer); - - if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { - sync_in_progress = 0; - } - return rc; -} - static xmlNode * cib_msg_copy(xmlNode *msg) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 1abdf7f67ac..06d44a7e44b 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -49,6 +49,10 @@ int based_process_primary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_replace(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, @@ -58,10 +62,6 @@ int cib_process_readwrite(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_replace_svr(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_sync(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 1e56c41d4fb..2aeac8bd670 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -30,7 +30,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_ping] = based_process_ping, [cib__op_primary] = based_process_primary, [cib__op_query] = cib__process_query, - [cib__op_replace] = cib_process_replace_svr, + [cib__op_replace] = based_process_replace, [cib__op_secondary] = cib_process_readwrite, [cib__op_shutdown] = cib_process_shutdown_req, [cib__op_sync_all] = cib_process_sync, From 29edf7ea6a866c4020151ddd26fcde984c7b6875 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:32:08 -0800 Subject: [PATCH 086/350] Refactor: based: cib_process_readwrite() -> based_process_secondary() Avoid using the public API prefix. The new function's name also matches the request name. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 17 ++++++++--------- daemons/based/based_messages.h | 8 ++++---- daemons/based/based_operation.c | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 533d6234e8e..3d4edceb0cc 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -273,22 +273,21 @@ based_process_replace(const char *op, int options, const char *section, } int -cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) +based_process_secondary(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { - int result = pcmk_ok; - pcmk__trace("Processing \"%s\" event", op); - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) { - - } else if (based_is_primary) { + if (based_is_primary) { pcmk__info("We are now in R/O mode"); based_is_primary = false; + + } else { + pcmk__debug("We are still in R/O mode"); } - return result; + return pcmk_ok; } void diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 06d44a7e44b..20874fdf006 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -53,15 +53,15 @@ int based_process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_secondary(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_readwrite(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - int cib_process_sync(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 2aeac8bd670..057161f3ae5 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -31,7 +31,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_primary] = based_process_primary, [cib__op_query] = cib__process_query, [cib__op_replace] = based_process_replace, - [cib__op_secondary] = cib_process_readwrite, + [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = cib_process_shutdown_req, [cib__op_sync_all] = cib_process_sync, [cib__op_sync_one] = cib_process_sync_one, From 9e2b0285d723b04fe08adcc14dd9bc5a71c2ded1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:34:26 -0800 Subject: [PATCH 087/350] Refactor: based: cib_process_shutdown_req() -> based_process_shutdown() Avoid using the public API prefix. Rename to match request name. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 48 ++++++++++++++++----------------- daemons/based/based_messages.h | 7 +++-- daemons/based/based_operation.c | 2 +- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 3d4edceb0cc..17b78b08958 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -145,30 +145,6 @@ based_process_commit_transact(const char *op, int options, const char *section, return pcmk_rc2legacy(rc); } -int -cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - - *answer = NULL; - - if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { - pcmk__info("Peer %s is requesting to shut down", host); - return pcmk_ok; - } - - if (!cib_shutdown_flag) { - pcmk__err("Peer %s mistakenly thinks we wanted to shut down", host); - return -EINVAL; - } - - pcmk__info("Exiting after %s acknowledged our shutdown request", host); - terminate_cib(CRM_EX_OK); - return pcmk_ok; -} - int based_process_is_primary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -290,6 +266,30 @@ based_process_secondary(const char *op, int options, const char *section, return pcmk_ok; } +int +based_process_shutdown(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + const char *host = pcmk__xe_get(req, PCMK__XA_SRC); + + *answer = NULL; + + if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { + pcmk__info("Peer %s is requesting to shut down", host); + return pcmk_ok; + } + + if (!cib_shutdown_flag) { + pcmk__err("Peer %s mistakenly thinks we wanted to shut down", host); + return -EINVAL; + } + + pcmk__info("Exiting after %s acknowledged our shutdown request", host); + terminate_cib(CRM_EX_OK); + return pcmk_ok; +} + void send_sync_request(void) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 20874fdf006..dcc742b229e 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -57,10 +57,9 @@ int based_process_secondary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_shutdown_req(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer); +int based_process_shutdown(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); int cib_process_sync(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 057161f3ae5..b1799fc97f3 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -32,7 +32,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_query] = cib__process_query, [cib__op_replace] = based_process_replace, [cib__op_secondary] = based_process_secondary, - [cib__op_shutdown] = cib_process_shutdown_req, + [cib__op_shutdown] = based_process_shutdown, [cib__op_sync_all] = cib_process_sync, [cib__op_sync_one] = cib_process_sync_one, [cib__op_upgrade] = cib_process_upgrade_server, From 55ba68b32ca9527b2df15f965aec8bbf8484e3a7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:36:59 -0800 Subject: [PATCH 088/350] Refactor: libcib: cib__op_sync_{all,one} -> cib__op_sync_to_{all,one} To match the request names. Signed-off-by: Reid Wahl --- daemons/based/based_operation.c | 4 ++-- include/crm/cib/internal.h | 6 +++--- lib/cib/cib_ops.c | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index b1799fc97f3..e7ec6ef3cbe 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -33,8 +33,8 @@ static const cib__op_fn_t op_functions[] = { [cib__op_replace] = based_process_replace, [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, - [cib__op_sync_all] = cib_process_sync, - [cib__op_sync_one] = cib_process_sync_one, + [cib__op_sync_to_all] = cib_process_sync, + [cib__op_sync_to_one] = cib_process_sync_one, [cib__op_upgrade] = cib_process_upgrade_server, [cib__op_schemas] = cib_process_schemas, }; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 75fdba592ab..cb7a4817406 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -87,12 +87,12 @@ enum cib__op_type { cib__op_primary, cib__op_query, cib__op_replace, + cib__op_schemas, cib__op_secondary, cib__op_shutdown, - cib__op_sync_all, - cib__op_sync_one, + cib__op_sync_to_all, + cib__op_sync_to_one, cib__op_upgrade, - cib__op_schemas, }; void cib_read_config(GHashTable *options, xmlNode *current_cib); diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 09c9624a892..b7fddfbe016 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -115,10 +115,12 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_privileged }, { - PCMK__CIB_REQUEST_SYNC_TO_ALL, cib__op_sync_all, cib__op_attr_privileged + PCMK__CIB_REQUEST_SYNC_TO_ALL, cib__op_sync_to_all, + cib__op_attr_privileged }, { - PCMK__CIB_REQUEST_SYNC_TO_ONE, cib__op_sync_one, cib__op_attr_privileged + PCMK__CIB_REQUEST_SYNC_TO_ONE, cib__op_sync_to_one, + cib__op_attr_privileged }, { PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, From 060a577a4913a305e16f9ba8789764d6b2bb7ff1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:39:25 -0800 Subject: [PATCH 089/350] Refactor: based: cib_process_sync() -> based_process_sync_to_all() Avoid using the public API prefix. Rename to match request name. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 15 ++++++++------- daemons/based/based_messages.h | 7 ++++--- daemons/based/based_operation.c | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 17b78b08958..d3f78c3042c 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -290,6 +290,14 @@ based_process_shutdown(const char *op, int options, const char *section, return pcmk_ok; } +int +based_process_sync_to_all(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + return sync_our_cib(req, true); +} + void send_sync_request(void) { @@ -307,13 +315,6 @@ send_sync_request(void) pcmk__xml_free(sync_me); } -int -cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) -{ - return sync_our_cib(req, true); -} - int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index dcc742b229e..4d4e62bbfdc 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -61,9 +61,10 @@ int based_process_shutdown(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_sync(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); +int based_process_sync_to_all(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); int cib_process_sync_one(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index e7ec6ef3cbe..c936e095d23 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -33,7 +33,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_replace] = based_process_replace, [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, - [cib__op_sync_to_all] = cib_process_sync, + [cib__op_sync_to_all] = based_process_sync_to_all, [cib__op_sync_to_one] = cib_process_sync_one, [cib__op_upgrade] = cib_process_upgrade_server, [cib__op_schemas] = cib_process_schemas, From e3909f333de1947b10c90bebe791e7ab98374520 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:40:59 -0800 Subject: [PATCH 090/350] Refactor: based: cib_process_sync_one() -> based_process_sync_to_one() Avoid using the public API prefix. Rename to match request name. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 16 ++++++++-------- daemons/based/based_messages.h | 7 ++++--- daemons/based/based_operation.c | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d3f78c3042c..864c797fc31 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -298,6 +298,14 @@ based_process_sync_to_all(const char *op, int options, const char *section, return sync_our_cib(req, true); } +int +based_process_sync_to_one(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + return sync_our_cib(req, false); +} + void send_sync_request(void) { @@ -410,14 +418,6 @@ cib_process_upgrade_server(const char *op, int options, const char *section, xml return rc; } -int -cib_process_sync_one(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - return sync_our_cib(req, false); -} - static xmlNode * cib_msg_copy(xmlNode *msg) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 4d4e62bbfdc..235b7fe0954 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -66,9 +66,10 @@ int based_process_sync_to_all(const char *op, int options, const char *section, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_sync_one(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); +int based_process_sync_to_one(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer); int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index c936e095d23..e31e65aeceb 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -34,7 +34,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, [cib__op_sync_to_all] = based_process_sync_to_all, - [cib__op_sync_to_one] = cib_process_sync_one, + [cib__op_sync_to_one] = based_process_sync_to_one, [cib__op_upgrade] = cib_process_upgrade_server, [cib__op_schemas] = cib_process_schemas, }; From ace1fe50364cb5466b60761d602afac15d26452c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:42:58 -0800 Subject: [PATCH 091/350] Refactor: based: cib_process_upgrade_server() -> based_process_upgrade() Avoid using the public API prefix. Rename to match request name. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 39 +++++++++++++++++---------------- daemons/based/based_messages.h | 7 +++--- daemons/based/based_operation.c | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 864c797fc31..35f541cb146 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -306,26 +306,10 @@ based_process_sync_to_one(const char *op, int options, const char *section, return sync_our_cib(req, false); } -void -send_sync_request(void) -{ - xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me"); - pcmk__node_status_t *peer = NULL; - - pcmk__info("Requesting re-sync from all peers"); - sync_in_progress = 1; - - pcmk__xe_set(sync_me, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE); - pcmk__xe_set(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); - - pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me); - pcmk__xml_free(sync_me); -} - int -cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, - xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer) +based_process_upgrade(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { int rc = pcmk_ok; @@ -418,6 +402,23 @@ cib_process_upgrade_server(const char *op, int options, const char *section, xml return rc; } +void +send_sync_request(void) +{ + xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me"); + pcmk__node_status_t *peer = NULL; + + pcmk__info("Requesting re-sync from all peers"); + sync_in_progress = 1; + + pcmk__xe_set(sync_me, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE); + pcmk__xe_set(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); + + pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me); + pcmk__xml_free(sync_me); +} + static xmlNode * cib_msg_copy(xmlNode *msg) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 235b7fe0954..4c18de9a1ec 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -71,10 +71,9 @@ int based_process_sync_to_one(const char *op, int options, const char *section, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_upgrade_server(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer); +int based_process_upgrade(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); int cib_process_schemas(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index e31e65aeceb..982e9aa6206 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -35,7 +35,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_shutdown] = based_process_shutdown, [cib__op_sync_to_all] = based_process_sync_to_all, [cib__op_sync_to_one] = based_process_sync_to_one, - [cib__op_upgrade] = cib_process_upgrade_server, + [cib__op_upgrade] = based_process_upgrade, [cib__op_schemas] = cib_process_schemas, }; From 4bfefab5e1780342d65b57f666075d433741ea4a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:45:37 -0800 Subject: [PATCH 092/350] Refactor: based: cib_process_schemas() -> based_process_schemas() Avoid using the public API prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 92 ++++++++++++++++----------------- daemons/based/based_messages.h | 8 +-- daemons/based/based_operation.c | 2 +- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 35f541cb146..910a47442fa 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -248,6 +248,52 @@ based_process_replace(const char *op, int options, const char *section, return rc; } +int +based_process_schemas(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *wrapper = NULL; + xmlNode *data = NULL; + + const char *after_ver = NULL; + GList *schemas = NULL; + GList *already_included = NULL; + + *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + + wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL); + data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + if (data == NULL) { + pcmk__warn("No data specified in request"); + return -EPROTO; + } + + after_ver = pcmk__xe_get(data, PCMK_XA_VERSION); + if (after_ver == NULL) { + pcmk__warn("No version specified in request"); + return -EPROTO; + } + + /* The client requested all schemas after the latest one we know about, which + * means the client is fully up-to-date. Return a properly formatted reply + * with no schemas. + */ + if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) { + return pcmk_ok; + } + + schemas = pcmk__schema_files_later_than(after_ver); + + for (GList *iter = schemas; iter != NULL; iter = iter->next) { + pcmk__build_schema_xml_node(*answer, iter->data, &already_included); + } + + g_list_free_full(schemas, free); + g_list_free_full(already_included, free); + return pcmk_ok; +} + int based_process_secondary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -501,49 +547,3 @@ sync_our_cib(xmlNode *request, bool all) free(digest); return result; } - -int -cib_process_schemas(const char *op, int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer) -{ - xmlNode *wrapper = NULL; - xmlNode *data = NULL; - - const char *after_ver = NULL; - GList *schemas = NULL; - GList *already_included = NULL; - - *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); - - wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL); - data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - if (data == NULL) { - pcmk__warn("No data specified in request"); - return -EPROTO; - } - - after_ver = pcmk__xe_get(data, PCMK_XA_VERSION); - if (after_ver == NULL) { - pcmk__warn("No version specified in request"); - return -EPROTO; - } - - /* The client requested all schemas after the latest one we know about, which - * means the client is fully up-to-date. Return a properly formatted reply - * with no schemas. - */ - if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) { - return pcmk_ok; - } - - schemas = pcmk__schema_files_later_than(after_ver); - - for (GList *iter = schemas; iter != NULL; iter = iter->next) { - pcmk__build_schema_xml_node(*answer, iter->data, &already_included); - } - - g_list_free_full(schemas, free); - g_list_free_full(already_included, free); - return pcmk_ok; -} diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 4c18de9a1ec..fee6e089cb8 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -53,6 +53,10 @@ int based_process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); +int based_process_schemas(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); + int based_process_secondary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); @@ -75,10 +79,6 @@ int based_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_schemas(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); - void send_sync_request(void); int sync_our_cib(xmlNode *request, bool all); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 982e9aa6206..f7e58a5ea11 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -31,12 +31,12 @@ static const cib__op_fn_t op_functions[] = { [cib__op_primary] = based_process_primary, [cib__op_query] = cib__process_query, [cib__op_replace] = based_process_replace, + [cib__op_schemas] = based_process_schemas, [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, [cib__op_sync_to_all] = based_process_sync_to_all, [cib__op_sync_to_one] = based_process_sync_to_one, [cib__op_upgrade] = based_process_upgrade, - [cib__op_schemas] = cib_process_schemas, }; /*! From 33e4c0e7f5ec0f676bc970334c404e2d9b9ab9f5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:48:33 -0800 Subject: [PATCH 093/350] Refactor: libcib: cib_process_replace() -> cib__process_replace() Rename the function to use the internal prefix. Also move the definition to match the order in the operation table. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 4 +-- include/crm/cib/internal.h | 6 ++-- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 64 +++++++++++++++++----------------- lib/common/xml_element.c | 2 +- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 910a47442fa..71f3c66cb06 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -239,8 +239,8 @@ based_process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int rc = - cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer); + int rc = cib__process_replace(op, options, section, req, input, + existing_cib, result_cib, answer); if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { sync_in_progress = 0; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index cb7a4817406..1c7b972320b 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -228,9 +228,9 @@ int cib__process_query(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_replace(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); +int cib__process_replace(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index af2bd68c2f3..5001beda821 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -349,7 +349,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_erase] = cib__process_erase, [cib__op_modify] = cib__process_modify, [cib__op_query] = cib__process_query, - [cib__op_replace] = cib_process_replace, + [cib__op_replace] = cib__process_replace, [cib__op_upgrade] = cib_process_upgrade, }; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index b7fddfbe016..d826eb8d856 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -722,38 +722,9 @@ cib__process_query(const char *op, int options, const char *section, } int -cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) -{ - int rc = 0; - const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); - const char *original_schema = NULL; - const char *new_schema = NULL; - - *answer = NULL; - pcmk__trace("Processing \"%s\" event with max=%s", op, max_schema); - - original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); - rc = pcmk__update_schema(result_cib, max_schema, true, - !pcmk__is_set(options, cib_verbose)); - rc = pcmk_rc2legacy(rc); - new_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); - - if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); - update_counter(*result_cib, PCMK_XA_EPOCH, true); - update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true); - return pcmk_ok; - } - - return rc; -} - -int -cib_process_replace(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) +cib__process_replace(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { int result = pcmk_ok; @@ -861,3 +832,32 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode * return result; } + +int +cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer) +{ + int rc = 0; + const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); + const char *original_schema = NULL; + const char *new_schema = NULL; + + *answer = NULL; + pcmk__trace("Processing \"%s\" event with max=%s", op, max_schema); + + original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); + rc = pcmk__update_schema(result_cib, max_schema, true, + !pcmk__is_set(options, cib_verbose)); + rc = pcmk_rc2legacy(rc); + new_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); + + if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { + update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); + update_counter(*result_cib, PCMK_XA_EPOCH, true); + update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true); + return pcmk_ok; + } + + return rc; +} diff --git a/lib/common/xml_element.c b/lib/common/xml_element.c index bd4e4a1436e..6863471c181 100644 --- a/lib/common/xml_element.c +++ b/lib/common/xml_element.c @@ -833,7 +833,7 @@ pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace) { /* @COMPAT Some of this behavior (like not matching the tree root, which is * allowed by pcmk__xe_update_match()) is questionable for general use but - * required for backward compatibility by cib_process_replace() and + * required for backward compatibility by cib__process_replace() and * cib__process_delete(). Behavior can change at a major version release if * desired. */ From 45f4c99f33b9f326fddfae30daeb542e6f9e783b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 02:50:43 -0800 Subject: [PATCH 094/350] Refactor: libcib: cib_process_upgrade() -> cib__process_upgrade() Rename the function to use the internal prefix. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 4 ++-- include/crm/cib/internal.h | 6 +++--- lib/cib/cib_file.c | 2 +- lib/cib/cib_ops.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 71f3c66cb06..30a72064b3e 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -367,8 +367,8 @@ based_process_upgrade(const char *op, int options, const char *section, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib_process_upgrade( - op, options, section, req, input, existing_cib, result_cib, answer); + return cib__process_upgrade(op, options, section, req, input, + existing_cib, result_cib, answer); } else { xmlNode *scratch = pcmk__xml_copy(NULL, existing_cib); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 1c7b972320b..8715785e44a 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -232,9 +232,9 @@ int cib__process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer); +int cib__process_upgrade(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 5001beda821..0ca37782a53 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -350,7 +350,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_modify] = cib__process_modify, [cib__op_query] = cib__process_query, [cib__op_replace] = cib__process_replace, - [cib__op_upgrade] = cib_process_upgrade, + [cib__op_upgrade] = cib__process_upgrade, }; /*! diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index d826eb8d856..18b9191c539 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -834,9 +834,9 @@ cib__process_replace(const char *op, int options, const char *section, } int -cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, - xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, - xmlNode ** answer) +cib__process_upgrade(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) { int rc = 0; const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); From 152082748af4214c659b651aaa582caa5d90708d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 11:10:31 -0800 Subject: [PATCH 095/350] Refactor: based: New based_remote.h Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_remote.c | 2 +- daemons/based/based_remote.h | 17 +++++++++++++++++ daemons/based/pacemaker-based.c | 4 ++-- daemons/based/pacemaker-based.h | 3 +-- 5 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 daemons/based/based_remote.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 7a322fb01a6..92b6490e3e3 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -18,6 +18,7 @@ noinst_HEADERS = based_io.h \ based_messages.h \ based_notify.h \ based_operation.h \ + based_remote.h \ based_transaction.h \ pacemaker-based.h diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 5b5e0cf35f8..d49d8b34e50 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -67,7 +67,7 @@ remote_connection_destroy(gpointer user_data) } int -init_remote_listener(int port, bool encrypted) +based_init_remote_listener(int port, bool encrypted) { int rc; int *ssock = NULL; diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h new file mode 100644 index 00000000000..bb6c0b5852f --- /dev/null +++ b/daemons/based/based_remote.h @@ -0,0 +1,17 @@ +/* + * Copyright 2025 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_REMOTE__H +#define BASED_REMOTE__H + +#include + +int based_init_remote_listener(int port, bool encrypted); + +#endif // BASED_REMOTE__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 6cf491e89b4..f5f1cfc039b 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -421,12 +421,12 @@ startCib(void) pcmk__scan_port(pcmk__xe_get(cib, PCMK_XA_REMOTE_TLS_PORT), &port); if (port >= 0) { - remote_tls_fd = init_remote_listener(port, true); + remote_tls_fd = based_init_remote_listener(port, true); } pcmk__scan_port(pcmk__xe_get(cib, PCMK_XA_REMOTE_CLEAR_PORT), &port); if (port >= 0) { - remote_fd = init_remote_listener(port, false); + remote_fd = based_init_remote_listener(port, false); } return true; diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 7bd1f28da1c..81566e16451 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -24,6 +24,7 @@ #include "based_messages.h" #include "based_operation.h" #include "based_notify.h" +#include "based_remote.h" #include "based_transaction.h" #define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->priv->node_name) @@ -52,8 +53,6 @@ extern qb_ipcs_service_t *ipcs_ro; extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; -int init_remote_listener(int port, bool encrypted); - void cib_peer_callback(xmlNode *msg, void *private_data); void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *cib_client, From 081573030514c679f0d57b7825d3f09935482d27 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 11:13:58 -0800 Subject: [PATCH 096/350] Refactor: based: Remove remote FD variables to based_remote.c With extern declarations in based_remote.h. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 3 --- daemons/based/based_remote.c | 3 ++- daemons/based/based_remote.h | 3 +++ daemons/based/pacemaker-based.c | 3 --- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4f74516c6a5..a4a1e1fa3c1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1350,9 +1350,6 @@ cib_shutdown(int nsig) } } -extern int remote_fd; -extern int remote_tls_fd; - /*! * \internal * \brief Close remote sockets, free the global CIB and quit diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index d49d8b34e50..6801bb21203 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -47,7 +47,8 @@ static pcmk__tls_t *tls = NULL; -extern int remote_tls_fd; +int remote_fd = 0; +int remote_tls_fd = 0; void cib_remote_connection_destroy(gpointer user_data); diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index bb6c0b5852f..09760865594 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -12,6 +12,9 @@ #include +extern int remote_fd; +extern int remote_tls_fd; + int based_init_remote_listener(int port, bool encrypted); #endif // BASED_REMOTE__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index f5f1cfc039b..52ffd4d325f 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -47,9 +47,6 @@ gchar *cib_root = NULL; gboolean stand_alone = FALSE; -int remote_fd = 0; -int remote_tls_fd = 0; - GHashTable *config_hash = NULL; static void cib_init(void); From d7f1db52e259481f283021368ebcf47ab0e5b6ae Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 11:18:30 -0800 Subject: [PATCH 097/350] Refactor: based: New based_remote_init() based_init_remote_listener() becomes static. Note that the_cib was already set to cib when startCib() called pcmk__xe_get(). Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 24 ++++++++++++++++++++++-- daemons/based/based_remote.h | 2 +- daemons/based/pacemaker-based.c | 14 +------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 6801bb21203..3bf897f5dd3 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -67,8 +67,8 @@ remote_connection_destroy(gpointer user_data) return; } -int -based_init_remote_listener(int port, bool encrypted) +static int +init_remote_listener(int port, bool encrypted) { int rc; int *ssock = NULL; @@ -143,6 +143,26 @@ based_init_remote_listener(int port, bool encrypted) return *ssock; } +/*! + * \internal + * \brief Initialize remote listeners using ports configured in the CIB + */ +void +based_remote_init(void) +{ + int port = 0; + + pcmk__scan_port(pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT), &port); + if (port >= 0) { + remote_tls_fd = init_remote_listener(port, true); + } + + pcmk__scan_port(pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT), &port); + if (port >= 0) { + remote_fd = init_remote_listener(port, false); + } +} + static bool is_daemon_group_member(const char *usr) { diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index 09760865594..f75661c93ac 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -15,6 +15,6 @@ extern int remote_fd; extern int remote_tls_fd; -int based_init_remote_listener(int port, bool encrypted); +void based_remote_init(void); #endif // BASED_REMOTE__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 52ffd4d325f..189d79132d4 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -31,7 +31,6 @@ #include // crm_log_* #include // mainloop_add_signal #include // CRM_EX_*, pcmk_rc_* -#include // PCMK_XA_REMOTE_*_PORT #include @@ -408,23 +407,12 @@ static bool startCib(void) { xmlNode *cib = based_read_cib(); - int port = 0; if (based_activate_cib(cib, true, "start") != pcmk_rc_ok) { return false; } cib_read_config(config_hash, cib); - - pcmk__scan_port(pcmk__xe_get(cib, PCMK_XA_REMOTE_TLS_PORT), &port); - if (port >= 0) { - remote_tls_fd = based_init_remote_listener(port, true); - } - - pcmk__scan_port(pcmk__xe_get(cib, PCMK_XA_REMOTE_CLEAR_PORT), &port); - if (port >= 0) { - remote_fd = based_init_remote_listener(port, false); - } - + based_remote_init(); return true; } From fb831ecf4fa696481903b73dfe9a3bfd69a1a1f9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 11:46:11 -0800 Subject: [PATCH 098/350] Refactor: based: Drop cib variable from startCib() Since based_read_cib() returns non-NULL and based_activate_cib() assigns its argument to the_cib, we can simply pass the return value of based_read_cib() directly into based_activate_cib(). Then pass the_cib to cib_read_config(). Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 189d79132d4..757377602a6 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -406,13 +406,12 @@ cib_init(void) static bool startCib(void) { - xmlNode *cib = based_read_cib(); - - if (based_activate_cib(cib, true, "start") != pcmk_rc_ok) { + // based_read_cib() returns new, non-NULL XML, so this should always succeed + if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { return false; } - cib_read_config(config_hash, cib); + cib_read_config(config_hash, the_cib); based_remote_init(); return true; } From e5dd108306a94bf915df6d775ccf68a98cdbb49a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 11:42:08 -0800 Subject: [PATCH 099/350] Refactor: based: Drop startCib() It was short and simple enough. There's also no harm in moving its contents above the pcmk_cluster_new() call, as there are no dependencies between these parts. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 757377602a6..e2448aabd17 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -50,7 +50,6 @@ GHashTable *config_hash = NULL; static void cib_init(void); void cib_shutdown(int nsig); -static bool startCib(void); static crm_exit_t exit_code = CRM_EX_OK; @@ -369,6 +368,18 @@ cib_peer_update_callback(enum pcmk__node_update type, static void cib_init(void) { + // based_read_cib() returns new, non-NULL XML, so this should always succeed + if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { + pcmk__crit("Bug: failed to activate CIB. Terminating %s.", + pcmk__server_log_name(pcmk_ipc_based)); + crm_exit(CRM_EX_SOFTWARE); + } + + config_hash = pcmk__strkey_table(free, free); + cib_read_config(config_hash, the_cib); + + based_remote_init(); + crm_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC @@ -379,13 +390,6 @@ cib_init(void) } #endif // SUPPORT_COROSYNC - config_hash = pcmk__strkey_table(free, free); - - if (!startCib()) { - pcmk__crit("Cannot start CIB... terminating"); - crm_exit(CRM_EX_NOINPUT); - } - if (!stand_alone) { pcmk__cluster_set_status_callback(&cib_peer_update_callback); @@ -402,16 +406,3 @@ cib_init(void) based_is_primary = true; } } - -static bool -startCib(void) -{ - // based_read_cib() returns new, non-NULL XML, so this should always succeed - if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { - return false; - } - - cib_read_config(config_hash, the_cib); - based_remote_init(); - return true; -} From 9dd105c4d584030d54897b9673f3e094332f435a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 14:43:43 -0800 Subject: [PATCH 100/350] Refactor: based: Drop num_clients variable The only thing it's used for is a trace message that logs how many clients are still active after one's connection is destroyed. That doesn't seem useful. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 3bf897f5dd3..9d354a53c14 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -55,7 +55,6 @@ void cib_remote_connection_destroy(gpointer user_data); // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 -int num_clients; static bool authenticate_user(const char *user, const char *passwd); static int cib_remote_listen(gpointer data); static int cib_remote_msg(gpointer data); @@ -304,8 +303,6 @@ cib_remote_listen(gpointer data) return 0; } - num_clients++; - new_client = pcmk__new_unauth_client(NULL); new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t)); @@ -351,9 +348,6 @@ cib_remote_connection_destroy(gpointer user_data) pcmk__trace("Cleaning up after client %s disconnect", pcmk__client_name(client)); - num_clients--; - pcmk__trace("Num unfree'd clients: %d", num_clients); - switch (PCMK__CLIENT_TYPE(client)) { case pcmk__client_tcp: csock = client->remote->tcp_socket; From 18f3ee80e220b298a2fc768cbd6b715ad976d24b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 14:53:37 -0800 Subject: [PATCH 101/350] Refactor: based: Check pcmk__scan_port() return code for remote ports This shouldn't change behavior but is a good practice. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 9d354a53c14..74b5beb08a7 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -79,11 +79,6 @@ init_remote_listener(int port, bool encrypted) .destroy = remote_connection_destroy, }; - if (port <= 0) { - /* don't start it */ - return 0; - } - if (encrypted) { bool use_cert = pcmk__x509_enabled(); @@ -149,15 +144,18 @@ init_remote_listener(int port, bool encrypted) void based_remote_init(void) { + const char *port_s = NULL; int port = 0; - pcmk__scan_port(pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT), &port); - if (port >= 0) { + port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT); + + if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { remote_tls_fd = init_remote_listener(port, true); } - pcmk__scan_port(pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT), &port); - if (port >= 0) { + port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT); + + if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { remote_fd = init_remote_listener(port, false); } } From 9403dab2fef4c6f9e318c6ed51fc11ff221424cf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:24:26 -0800 Subject: [PATCH 102/350] Refactor: libcrmcommon: Simplify pcmk__init_tls() cred type selection We only use three possible cred types. We always use certificate if they're configured. Otherwise, some callers default to pre-shared keys, while remote CIB clients and listeners default to anonymous authentication. Also improve the doc comments, since it was not obvious to me how this worked and why. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 5 ++-- daemons/execd/remoted_tls.c | 5 ++-- include/crm/common/tls_internal.h | 17 +---------- lib/cib/cib_remote.c | 4 +-- lib/common/tls.c | 47 +++++++++++++++++++++++++++---- lib/lrmd/lrmd_client.c | 10 +++---- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 74b5beb08a7..fe8e56dfa65 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -80,11 +80,10 @@ init_remote_listener(int port, bool encrypted) }; if (encrypted) { - bool use_cert = pcmk__x509_enabled(); - pcmk__notice("Starting TLS listener on port %d", port); - rc = pcmk__init_tls(&tls, true, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_ANON); + // @TODO Implement pre-shared key authentication (see T961) + rc = pcmk__init_tls(&tls, true, false); if (rc != pcmk_rc_ok) { return -1; } diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c index 818b48caa4d..c9b2df13bd9 100644 --- a/daemons/execd/remoted_tls.c +++ b/daemons/execd/remoted_tls.c @@ -335,7 +335,6 @@ lrmd_init_remote_tls_server(void) int port = crm_default_remote_port(); struct addrinfo *res = NULL, *iter; const char *bind_name = pcmk__env_option(PCMK__ENV_REMOTE_ADDRESS); - bool use_cert = pcmk__x509_enabled(); static struct mainloop_fd_callbacks remote_listen_fd_callbacks = { .dispatch = lrmd_remote_listen, @@ -347,12 +346,12 @@ lrmd_init_remote_tls_server(void) pcmk__debug("Starting TLS listener on %s port %d", pcmk__s(bind_name, "all addresses on"), port); - rc = pcmk__init_tls(&tls, true, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_PSK); + rc = pcmk__init_tls(&tls, true, true); if (rc != pcmk_rc_ok) { return -1; } - if (!use_cert) { + if (!pcmk__x509_enabled()) { gnutls_datum_t psk_key = { NULL, 0 }; pcmk__tls_add_psk_callback(tls, lrmd_tls_server_key_cb); diff --git a/include/crm/common/tls_internal.h b/include/crm/common/tls_internal.h index 819dbbd5fbe..700efb8632f 100644 --- a/include/crm/common/tls_internal.h +++ b/include/crm/common/tls_internal.h @@ -52,22 +52,7 @@ typedef struct { */ void pcmk__free_tls(pcmk__tls_t *tls); -/*! - * \internal - * \brief Initialize a new TLS object - * - * Unlike \p pcmk__new_tls_session, this function is used for creating the - * global environment for TLS connections. - * - * \param[in,out] tls The object to be allocated and initialized - * \param[in] server Is this a server or not? - * \param[in] cred_type What type of gnutls credentials are in use? - * (GNUTLS_CRD_* constants) - * - * \returns Standard Pacemaker return code - */ -int pcmk__init_tls(pcmk__tls_t **tls, bool server, - gnutls_credentials_type_t cred_type); +int pcmk__init_tls(pcmk__tls_t **tls, bool server, bool psk_fallback); /*! * \internal diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index d812857bf6b..d25e8008231 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -375,10 +375,10 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel) } if (private->encrypted) { - bool use_cert = pcmk__x509_enabled(); int tls_rc = GNUTLS_E_SUCCESS; - rc = pcmk__init_tls(&tls, false, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_ANON); + // @TODO Implement pre-shared key authentication (see T961) + rc = pcmk__init_tls(&tls, false, false); if (rc != pcmk_rc_ok) { return -1; } diff --git a/lib/common/tls.c b/lib/common/tls.c index 0fa7973cde9..f20d5518753 100644 --- a/lib/common/tls.c +++ b/lib/common/tls.c @@ -135,8 +135,35 @@ pcmk__free_tls(pcmk__tls_t *tls) tls = NULL; } +/*! + * \internal + * \brief Initialize a new TLS object + * + * This function initializes \p tls as an environment for TLS connections. This + * is in contrast to \c pcmk__new_tls_session(), which initializes a single + * session within that environment. + * + * X.509 certificates are used if configured via environment variables. + * Otherwise, we fall back to either pre-shared keys (PSK) or anonymous + * authentication, depending on the value of \p have_psk. + * + * \param[out] tls Where to store new TLS object + * \param[in] server Current process is a server if \c true or a client if + * \c false + * \param[in] have_psk If X.509 certificates are not enabled, then use + * \c GNUTLS_CRD_PSK (pre-shared keys) if this is \c true + * or \c GNUTLS_CRD_ANON (anonymous authentication) if + * this is \c false + * + * \return Standard Pacemaker return code + * + * \note CIB remote clients and the CIB manager's remote listener are the only + * things that use anonymous authentication when X.509 is disabled. Task + * T961 is open to implement PSK for those. The only other callers are + * executor clients and listeners, which already use PSK. + */ int -pcmk__init_tls(pcmk__tls_t **tls, bool server, gnutls_credentials_type_t cred_type) +pcmk__init_tls(pcmk__tls_t **tls, bool server, bool have_psk) { int rc = pcmk_rc_ok; @@ -160,10 +187,19 @@ pcmk__init_tls(pcmk__tls_t **tls, bool server, gnutls_credentials_type_t cred_ty } } - (*tls)->cred_type = cred_type; + if (pcmk__x509_enabled()) { + (*tls)->cred_type = GNUTLS_CRD_CERTIFICATE; + + } else if (have_psk) { + (*tls)->cred_type = GNUTLS_CRD_PSK; + + } else { + (*tls)->cred_type = GNUTLS_CRD_ANON; + } + (*tls)->server = server; - if (cred_type == GNUTLS_CRD_ANON) { + if ((*tls)->cred_type == GNUTLS_CRD_ANON) { if (server) { gnutls_anon_allocate_server_credentials(&(*tls)->credentials.anon_s); gnutls_anon_set_server_dh_params((*tls)->credentials.anon_s, @@ -171,7 +207,8 @@ pcmk__init_tls(pcmk__tls_t **tls, bool server, gnutls_credentials_type_t cred_ty } else { gnutls_anon_allocate_client_credentials(&(*tls)->credentials.anon_c); } - } else if (cred_type == GNUTLS_CRD_CERTIFICATE) { + + } else if ((*tls)->cred_type == GNUTLS_CRD_CERTIFICATE) { /* Try the PCMK_ version of each environment variable first, and if * it's not set then try the CIB_ version. */ @@ -209,7 +246,7 @@ pcmk__init_tls(pcmk__tls_t **tls, bool server, gnutls_credentials_type_t cred_ty *tls = NULL; return rc; } - } else if (cred_type == GNUTLS_CRD_PSK) { + } else { // GNUTLS_CRD_PSK if (server) { gnutls_psk_allocate_server_credentials(&(*tls)->credentials.psk_s); gnutls_psk_set_server_dh_params((*tls)->credentials.psk_s, diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c index 54a65c41b13..73f164f9375 100644 --- a/lib/lrmd/lrmd_client.c +++ b/lib/lrmd/lrmd_client.c @@ -1518,7 +1518,6 @@ lrmd_tcp_connect_cb(void *userdata, int rc, int sock) lrmd_t *lrmd = userdata; lrmd_private_t *native = lrmd->lrmd_private; int tls_rc = GNUTLS_E_SUCCESS; - bool use_cert = pcmk__x509_enabled(); native->async_timer = 0; @@ -1536,7 +1535,7 @@ lrmd_tcp_connect_cb(void *userdata, int rc, int sock) native->sock = sock; if (native->tls == NULL) { - rc = pcmk__init_tls(&native->tls, false, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_PSK); + rc = pcmk__init_tls(&native->tls, false, true); if ((rc != pcmk_rc_ok) || (native->tls == NULL)) { lrmd_tls_connection_destroy(lrmd); @@ -1545,7 +1544,7 @@ lrmd_tcp_connect_cb(void *userdata, int rc, int sock) } } - if (!use_cert) { + if (!pcmk__x509_enabled()) { gnutls_datum_t psk_key = { NULL, 0 }; rc = lrmd__init_remote_key(&psk_key); @@ -1621,7 +1620,6 @@ static int lrmd_tls_connect(lrmd_t * lrmd, int *fd) { int rc = pcmk_rc_ok; - bool use_cert = pcmk__x509_enabled(); lrmd_private_t *native = lrmd->lrmd_private; native->sock = -1; @@ -1636,7 +1634,7 @@ lrmd_tls_connect(lrmd_t * lrmd, int *fd) } if (native->tls == NULL) { - rc = pcmk__init_tls(&native->tls, false, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_PSK); + rc = pcmk__init_tls(&native->tls, false, true); if (rc != pcmk_rc_ok) { lrmd_tls_connection_destroy(lrmd); @@ -1644,7 +1642,7 @@ lrmd_tls_connect(lrmd_t * lrmd, int *fd) } } - if (!use_cert) { + if (!pcmk__x509_enabled()) { gnutls_datum_t psk_key = { NULL, 0 }; rc = lrmd__init_remote_key(&psk_key); From f76a215038d8d662cd1a613ae1f0d3be9969e6cf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:33:50 -0800 Subject: [PATCH 103/350] Refactor: based: Drop init_remote_listener() encrypted argument Also add an error message on TLS init failure, don't log "Starting TLS listener..." until we've initialized TLS, and warn that the clear-text listener is not recommended. It seems cleaner to handle this little piece of condtional code in the caller, rather than passing a boolean argument. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index fe8e56dfa65..02d48567cbc 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -67,7 +67,7 @@ remote_connection_destroy(gpointer user_data) } static int -init_remote_listener(int port, bool encrypted) +init_remote_listener(int port) { int rc; int *ssock = NULL; @@ -79,17 +79,6 @@ init_remote_listener(int port, bool encrypted) .destroy = remote_connection_destroy, }; - if (encrypted) { - pcmk__notice("Starting TLS listener on port %d", port); - - // @TODO Implement pre-shared key authentication (see T961) - rc = pcmk__init_tls(&tls, true, false); - if (rc != pcmk_rc_ok) { - return -1; - } - } else { - pcmk__warn("Starting plain-text listener on port %d", port); - } #ifndef HAVE_PAM pcmk__warn("This build does not support remote administrators because PAM " "support is not available"); @@ -149,13 +138,26 @@ based_remote_init(void) port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT); if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { - remote_tls_fd = init_remote_listener(port, true); + // @TODO Implement pre-shared key authentication (see T961) + int rc = pcmk__init_tls(&tls, true, false); + + if (rc != pcmk_rc_ok) { + pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ", + "on port %d", pcmk_rc_str(rc), port); + remote_tls_fd = -1; + + } else { + pcmk__notice("Starting TLS listener on port %d", port); + remote_tls_fd = init_remote_listener(port); + } } port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT); if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { - remote_fd = init_remote_listener(port, false); + pcmk__warn("Starting clear-text listener on port %d. This is insecure; " + PCMK_XA_REMOTE_TLS_PORT " is recommended instead.", port); + remote_fd = init_remote_listener(port); } } From 817aa97a0d4599a7758126e87d5d63973933d5c1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:41:37 -0800 Subject: [PATCH 104/350] Refactor: based: Drop forward declaration for authenticate_user() No other code changes, only moving functions around. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 305 +++++++++++++++++------------------ 1 file changed, 152 insertions(+), 153 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 02d48567cbc..d2d76d51f81 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -55,7 +55,6 @@ void cib_remote_connection_destroy(gpointer user_data); // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 -static bool authenticate_user(const char *user, const char *passwd); static int cib_remote_listen(gpointer data); static int cib_remote_msg(gpointer data); @@ -205,6 +204,158 @@ is_daemon_group_member(const char *usr) return false; } +#ifdef HAVE_PAM +/*! + * \internal + * \brief Pass remote user's password to PAM + * + * \param[in] num_msg Number of entries in \p msg + * \param[in] msg Array of PAM messages + * \param[out] response Where to set response to PAM + * \param[in] data User data (the password string) + * + * \return PAM return code (PAM_BUF_ERR for memory errors, PAM_CONV_ERR for all + * other errors, or PAM_SUCCESS on success) + * \note See pam_conv(3) for more explanation + */ +static int +construct_pam_passwd(int num_msg, const struct pam_message **msg, + struct pam_response **response, void *data) +{ + /* In theory, multiple messages are allowed, but due to OS compatibility + * issues, PAM implementations are recommended to only send one message at a + * time. We can require that here for simplicity. + */ + CRM_CHECK((num_msg == 1) && (msg != NULL) && (response != NULL) + && (data != NULL), return PAM_CONV_ERR); + + switch (msg[0]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + // Password requested + break; + case PAM_TEXT_INFO: + pcmk__info("PAM: %s", msg[0]->msg); + data = NULL; + break; + case PAM_ERROR_MSG: + /* In theory we should show msg[0]->msg, but that might + * contain the password, which we don't want in the logs + */ + pcmk__err("PAM reported an error"); + data = NULL; + break; + default: + pcmk__warn("Ignoring PAM message of unrecognized type %d", + msg[0]->msg_style); + return PAM_CONV_ERR; + } + + *response = calloc(1, sizeof(struct pam_response)); + if (*response == NULL) { + return PAM_BUF_ERR; + } + (*response)->resp_retcode = 0; + (*response)->resp = pcmk__str_copy((const char *) data); // Caller will free + return PAM_SUCCESS; +} +#endif + +/*! + * \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 + * + * \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) +{ +#ifdef HAVE_PAM + int rc = 0; + bool pass = false; + const void *p_user = NULL; + struct pam_conv p_conv; + struct pam_handle *pam_h = NULL; + + static const char *pam_name = NULL; + + if (pam_name == NULL) { + pam_name = getenv("CIB_pam_service"); + if (pam_name == NULL) { + pam_name = "login"; + } + } + + p_conv.conv = construct_pam_passwd; + p_conv.appdata_ptr = (void *) 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)); + 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, + pam_strerror(pam_h, rc)); + goto bail; + } + + /* Get the authenticated user name (PAM modules can map the original name to + * something else). Since the CIB manager runs as the daemon user (not + * root), that is the only user that can be successfully authenticated. + */ + 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", + 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); + 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); + 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)); + goto bail; + } + pass = true; + +bail: + pam_end(pam_h, rc); + return pass; +#else + // @TODO Implement for non-PAM environments + pcmk__warn("Rejecting remote user %s because this build does not have PAM " + "support", + user); + return false; +#endif +} + static bool cib_remote_auth(xmlNode * login) { @@ -526,155 +677,3 @@ cib_remote_msg(gpointer data) return 0; } - -#ifdef HAVE_PAM -/*! - * \internal - * \brief Pass remote user's password to PAM - * - * \param[in] num_msg Number of entries in \p msg - * \param[in] msg Array of PAM messages - * \param[out] response Where to set response to PAM - * \param[in] data User data (the password string) - * - * \return PAM return code (PAM_BUF_ERR for memory errors, PAM_CONV_ERR for all - * other errors, or PAM_SUCCESS on success) - * \note See pam_conv(3) for more explanation - */ -static int -construct_pam_passwd(int num_msg, const struct pam_message **msg, - struct pam_response **response, void *data) -{ - /* In theory, multiple messages are allowed, but due to OS compatibility - * issues, PAM implementations are recommended to only send one message at a - * time. We can require that here for simplicity. - */ - CRM_CHECK((num_msg == 1) && (msg != NULL) && (response != NULL) - && (data != NULL), return PAM_CONV_ERR); - - switch (msg[0]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - // Password requested - break; - case PAM_TEXT_INFO: - pcmk__info("PAM: %s", msg[0]->msg); - data = NULL; - break; - case PAM_ERROR_MSG: - /* In theory we should show msg[0]->msg, but that might - * contain the password, which we don't want in the logs - */ - pcmk__err("PAM reported an error"); - data = NULL; - break; - default: - pcmk__warn("Ignoring PAM message of unrecognized type %d", - msg[0]->msg_style); - return PAM_CONV_ERR; - } - - *response = calloc(1, sizeof(struct pam_response)); - if (*response == NULL) { - return PAM_BUF_ERR; - } - (*response)->resp_retcode = 0; - (*response)->resp = pcmk__str_copy((const char *) data); // Caller will free - return PAM_SUCCESS; -} -#endif - -/*! - * \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 - * - * \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) -{ -#ifdef HAVE_PAM - int rc = 0; - bool pass = false; - const void *p_user = NULL; - struct pam_conv p_conv; - struct pam_handle *pam_h = NULL; - - static const char *pam_name = NULL; - - if (pam_name == NULL) { - pam_name = getenv("CIB_pam_service"); - if (pam_name == NULL) { - pam_name = "login"; - } - } - - p_conv.conv = construct_pam_passwd; - p_conv.appdata_ptr = (void *) 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)); - 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, - pam_strerror(pam_h, rc)); - goto bail; - } - - /* Get the authenticated user name (PAM modules can map the original name to - * something else). Since the CIB manager runs as the daemon user (not - * root), that is the only user that can be successfully authenticated. - */ - 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", - 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); - 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); - 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)); - goto bail; - } - pass = true; - -bail: - pam_end(pam_h, rc); - return pass; -#else - // @TODO Implement for non-PAM environments - pcmk__warn("Rejecting remote user %s because this build does not have PAM " - "support", - user); - return false; -#endif -} From 25aff0f9f09a941361e1d5c86e078ec0e6ea6cfb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:42:56 -0800 Subject: [PATCH 105/350] Refactor: based: Drop forward declaration for cib_remote_listen() No other code changes, only moving functions around. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 175 +++++++++++++++++------------------ 1 file changed, 87 insertions(+), 88 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index d2d76d51f81..0b4e05d5130 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -55,9 +55,95 @@ void cib_remote_connection_destroy(gpointer user_data); // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 -static int cib_remote_listen(gpointer data); static int cib_remote_msg(gpointer data); +static gboolean +remote_auth_timeout_cb(gpointer data) +{ + pcmk__client_t *client = data; + + client->remote->auth_timeout = 0; + + if (pcmk__is_set(client->flags, pcmk__client_authenticated)) { + return FALSE; + } + + mainloop_del_fd(client->remote->source); + pcmk__err("Remote client authentication timed out"); + + return FALSE; +} + +static int +cib_remote_listen(gpointer data) +{ + int csock = -1; + unsigned laddr; + struct sockaddr_storage addr; + char ipstr[INET6_ADDRSTRLEN]; + int ssock = *(int *)data; + int rc; + + pcmk__client_t *new_client = NULL; + + static struct mainloop_fd_callbacks remote_client_fd_callbacks = { + .dispatch = cib_remote_msg, + .destroy = cib_remote_connection_destroy, + }; + + /* accept the connection */ + laddr = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + csock = accept(ssock, (struct sockaddr *)&addr, &laddr); + if (csock == -1) { + pcmk__warn("Could not accept remote connection: %s", + pcmk_rc_str(errno)); + return 0; + } + + pcmk__sockaddr2str(&addr, ipstr); + + rc = pcmk__set_nonblocking(csock); + if (rc != pcmk_rc_ok) { + pcmk__warn("Dropping remote connection from %s because it could not be " + "set to non-blocking: %s", + ipstr, pcmk_rc_str(rc)); + close(csock); + return 0; + } + + new_client = pcmk__new_unauth_client(NULL); + new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t)); + + if (ssock == remote_tls_fd) { + pcmk__set_client_flags(new_client, pcmk__client_tls); + + /* create gnutls session for the server socket */ + new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); + if (new_client->remote->tls_session == NULL) { + close(csock); + return 0; + } + } else { + pcmk__set_client_flags(new_client, pcmk__client_tcp); + new_client->remote->tcp_socket = csock; + } + + // Require the client to authenticate within this time + new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, + remote_auth_timeout_cb, + new_client); + pcmk__info("%s connection from %s pending authentication for client %s", + ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"), ipstr, + new_client->id); + + new_client->remote->source = + mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client, + &remote_client_fd_callbacks); + + return 0; +} + static void remote_connection_destroy(gpointer user_data) { @@ -398,93 +484,6 @@ cib_remote_auth(xmlNode * login) return is_daemon_group_member(user) && authenticate_user(user, pass); } -static gboolean -remote_auth_timeout_cb(gpointer data) -{ - pcmk__client_t *client = data; - - client->remote->auth_timeout = 0; - - if (pcmk__is_set(client->flags, pcmk__client_authenticated)) { - return FALSE; - } - - mainloop_del_fd(client->remote->source); - pcmk__err("Remote client authentication timed out"); - - return FALSE; -} - -static int -cib_remote_listen(gpointer data) -{ - int csock = -1; - unsigned laddr; - struct sockaddr_storage addr; - char ipstr[INET6_ADDRSTRLEN]; - int ssock = *(int *)data; - int rc; - - pcmk__client_t *new_client = NULL; - - static struct mainloop_fd_callbacks remote_client_fd_callbacks = { - .dispatch = cib_remote_msg, - .destroy = cib_remote_connection_destroy, - }; - - /* accept the connection */ - laddr = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - csock = accept(ssock, (struct sockaddr *)&addr, &laddr); - if (csock == -1) { - pcmk__warn("Could not accept remote connection: %s", - pcmk_rc_str(errno)); - return 0; - } - - pcmk__sockaddr2str(&addr, ipstr); - - rc = pcmk__set_nonblocking(csock); - if (rc != pcmk_rc_ok) { - pcmk__warn("Dropping remote connection from %s because it could not be " - "set to non-blocking: %s", - ipstr, pcmk_rc_str(rc)); - close(csock); - return 0; - } - - new_client = pcmk__new_unauth_client(NULL); - new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t)); - - if (ssock == remote_tls_fd) { - pcmk__set_client_flags(new_client, pcmk__client_tls); - - /* create gnutls session for the server socket */ - new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); - if (new_client->remote->tls_session == NULL) { - close(csock); - return 0; - } - } else { - pcmk__set_client_flags(new_client, pcmk__client_tcp); - new_client->remote->tcp_socket = csock; - } - - // Require the client to authenticate within this time - new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, - remote_auth_timeout_cb, - new_client); - pcmk__info("%s connection from %s pending authentication for client %s", - ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"), ipstr, - new_client->id); - - new_client->remote->source = - mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client, - &remote_client_fd_callbacks); - - return 0; -} - void cib_remote_connection_destroy(gpointer user_data) { From 817b9550694287b1633d143a29e0231b3cea87f9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:47:19 -0800 Subject: [PATCH 106/350] Refactor: based: Drop forward declaration for cib_remote_msg() No other code changes, only moving functions around. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 444 +++++++++++++++++------------------ 1 file changed, 221 insertions(+), 223 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 0b4e05d5130..43a054418d0 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -55,8 +55,6 @@ void cib_remote_connection_destroy(gpointer user_data); // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 -static int cib_remote_msg(gpointer data); - static gboolean remote_auth_timeout_cb(gpointer data) { @@ -74,178 +72,6 @@ remote_auth_timeout_cb(gpointer data) return FALSE; } -static int -cib_remote_listen(gpointer data) -{ - int csock = -1; - unsigned laddr; - struct sockaddr_storage addr; - char ipstr[INET6_ADDRSTRLEN]; - int ssock = *(int *)data; - int rc; - - pcmk__client_t *new_client = NULL; - - static struct mainloop_fd_callbacks remote_client_fd_callbacks = { - .dispatch = cib_remote_msg, - .destroy = cib_remote_connection_destroy, - }; - - /* accept the connection */ - laddr = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - csock = accept(ssock, (struct sockaddr *)&addr, &laddr); - if (csock == -1) { - pcmk__warn("Could not accept remote connection: %s", - pcmk_rc_str(errno)); - return 0; - } - - pcmk__sockaddr2str(&addr, ipstr); - - rc = pcmk__set_nonblocking(csock); - if (rc != pcmk_rc_ok) { - pcmk__warn("Dropping remote connection from %s because it could not be " - "set to non-blocking: %s", - ipstr, pcmk_rc_str(rc)); - close(csock); - return 0; - } - - new_client = pcmk__new_unauth_client(NULL); - new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t)); - - if (ssock == remote_tls_fd) { - pcmk__set_client_flags(new_client, pcmk__client_tls); - - /* create gnutls session for the server socket */ - new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); - if (new_client->remote->tls_session == NULL) { - close(csock); - return 0; - } - } else { - pcmk__set_client_flags(new_client, pcmk__client_tcp); - new_client->remote->tcp_socket = csock; - } - - // Require the client to authenticate within this time - new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, - remote_auth_timeout_cb, - new_client); - pcmk__info("%s connection from %s pending authentication for client %s", - ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"), ipstr, - new_client->id); - - new_client->remote->source = - mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client, - &remote_client_fd_callbacks); - - return 0; -} - -static void -remote_connection_destroy(gpointer user_data) -{ - pcmk__info("No longer listening for remote connections"); - return; -} - -static int -init_remote_listener(int port) -{ - int rc; - int *ssock = NULL; - struct sockaddr_in saddr; - int optval; - - static struct mainloop_fd_callbacks remote_listen_fd_callbacks = { - .dispatch = cib_remote_listen, - .destroy = remote_connection_destroy, - }; - -#ifndef HAVE_PAM - pcmk__warn("This build does not support remote administrators because PAM " - "support is not available"); -#endif - - /* create server socket */ - ssock = pcmk__assert_alloc(1, sizeof(int)); - *ssock = socket(AF_INET, SOCK_STREAM, 0); - if (*ssock == -1) { - pcmk__err("Listener socket creation failed: %s", pcmk_rc_str(errno)); - free(ssock); - return -1; - } - - /* reuse address */ - optval = 1; - rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - if (rc < 0) { - pcmk__err("Local address reuse not allowed on listener socket: %s", - pcmk_rc_str(errno)); - } - - /* bind server socket */ - memset(&saddr, '\0', sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = INADDR_ANY; - saddr.sin_port = htons(port); - if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { - pcmk__err("Cannot bind to listener socket: %s", pcmk_rc_str(errno)); - close(*ssock); - free(ssock); - return -2; - } - if (listen(*ssock, 10) == -1) { - pcmk__err("Cannot listen on socket: %s", pcmk_rc_str(errno)); - close(*ssock); - free(ssock); - return -3; - } - - mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, *ssock, ssock, &remote_listen_fd_callbacks); - pcmk__debug("Started listener on port %d", port); - - return *ssock; -} - -/*! - * \internal - * \brief Initialize remote listeners using ports configured in the CIB - */ -void -based_remote_init(void) -{ - const char *port_s = NULL; - int port = 0; - - port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT); - - if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { - // @TODO Implement pre-shared key authentication (see T961) - int rc = pcmk__init_tls(&tls, true, false); - - if (rc != pcmk_rc_ok) { - pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ", - "on port %d", pcmk_rc_str(rc), port); - remote_tls_fd = -1; - - } else { - pcmk__notice("Starting TLS listener on port %d", port); - remote_tls_fd = init_remote_listener(port); - } - } - - port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT); - - if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { - pcmk__warn("Starting clear-text listener on port %d. This is insecure; " - PCMK_XA_REMOTE_TLS_PORT " is recommended instead.", port); - remote_fd = init_remote_listener(port); - } -} - static bool is_daemon_group_member(const char *usr) { @@ -484,55 +310,6 @@ cib_remote_auth(xmlNode * login) return is_daemon_group_member(user) && authenticate_user(user, pass); } -void -cib_remote_connection_destroy(gpointer user_data) -{ - pcmk__client_t *client = user_data; - int csock = -1; - - if (client == NULL) { - return; - } - - pcmk__trace("Cleaning up after client %s disconnect", - pcmk__client_name(client)); - - switch (PCMK__CLIENT_TYPE(client)) { - case pcmk__client_tcp: - csock = client->remote->tcp_socket; - break; - case pcmk__client_tls: - if (client->remote->tls_session) { - csock = pcmk__tls_get_client_sock(client->remote); - - if (pcmk__is_set(client->flags, - pcmk__client_tls_handshake_complete)) { - gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR); - } - gnutls_deinit(client->remote->tls_session); - client->remote->tls_session = NULL; - } - break; - default: - pcmk__warn("Unknown transport for client %s " - QB_XS " flags=%#016" PRIx64, - pcmk__client_name(client), client->flags); - } - - if (csock >= 0) { - close(csock); - } - - pcmk__free_client(client); - - pcmk__trace("Freed the cib client"); - - if (cib_shutdown_flag) { - cib_shutdown(0); - } - return; -} - static void cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) { @@ -676,3 +453,224 @@ cib_remote_msg(gpointer data) return 0; } + +static int +cib_remote_listen(gpointer data) +{ + int csock = -1; + unsigned laddr; + struct sockaddr_storage addr; + char ipstr[INET6_ADDRSTRLEN]; + int ssock = *(int *)data; + int rc; + + pcmk__client_t *new_client = NULL; + + static struct mainloop_fd_callbacks remote_client_fd_callbacks = { + .dispatch = cib_remote_msg, + .destroy = cib_remote_connection_destroy, + }; + + /* accept the connection */ + laddr = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + csock = accept(ssock, (struct sockaddr *)&addr, &laddr); + if (csock == -1) { + pcmk__warn("Could not accept remote connection: %s", + pcmk_rc_str(errno)); + return 0; + } + + pcmk__sockaddr2str(&addr, ipstr); + + rc = pcmk__set_nonblocking(csock); + if (rc != pcmk_rc_ok) { + pcmk__warn("Dropping remote connection from %s because it could not be " + "set to non-blocking: %s", + ipstr, pcmk_rc_str(rc)); + close(csock); + return 0; + } + + new_client = pcmk__new_unauth_client(NULL); + new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t)); + + if (ssock == remote_tls_fd) { + pcmk__set_client_flags(new_client, pcmk__client_tls); + + /* create gnutls session for the server socket */ + new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); + if (new_client->remote->tls_session == NULL) { + close(csock); + return 0; + } + } else { + pcmk__set_client_flags(new_client, pcmk__client_tcp); + new_client->remote->tcp_socket = csock; + } + + // Require the client to authenticate within this time + new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT, + remote_auth_timeout_cb, + new_client); + pcmk__info("%s connection from %s pending authentication for client %s", + ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"), ipstr, + new_client->id); + + new_client->remote->source = + mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client, + &remote_client_fd_callbacks); + + return 0; +} + +static void +remote_connection_destroy(gpointer user_data) +{ + pcmk__info("No longer listening for remote connections"); + return; +} + +static int +init_remote_listener(int port) +{ + int rc; + int *ssock = NULL; + struct sockaddr_in saddr; + int optval; + + static struct mainloop_fd_callbacks remote_listen_fd_callbacks = { + .dispatch = cib_remote_listen, + .destroy = remote_connection_destroy, + }; + +#ifndef HAVE_PAM + pcmk__warn("This build does not support remote administrators because PAM " + "support is not available"); +#endif + + /* create server socket */ + ssock = pcmk__assert_alloc(1, sizeof(int)); + *ssock = socket(AF_INET, SOCK_STREAM, 0); + if (*ssock == -1) { + pcmk__err("Listener socket creation failed: %s", pcmk_rc_str(errno)); + free(ssock); + return -1; + } + + /* reuse address */ + optval = 1; + rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if (rc < 0) { + pcmk__err("Local address reuse not allowed on listener socket: %s", + pcmk_rc_str(errno)); + } + + /* bind server socket */ + memset(&saddr, '\0', sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = INADDR_ANY; + saddr.sin_port = htons(port); + if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { + pcmk__err("Cannot bind to listener socket: %s", pcmk_rc_str(errno)); + close(*ssock); + free(ssock); + return -2; + } + if (listen(*ssock, 10) == -1) { + pcmk__err("Cannot listen on socket: %s", pcmk_rc_str(errno)); + close(*ssock); + free(ssock); + return -3; + } + + mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, *ssock, ssock, &remote_listen_fd_callbacks); + pcmk__debug("Started listener on port %d", port); + + return *ssock; +} + +/*! + * \internal + * \brief Initialize remote listeners using ports configured in the CIB + */ +void +based_remote_init(void) +{ + const char *port_s = NULL; + int port = 0; + + port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT); + + if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { + // @TODO Implement pre-shared key authentication (see T961) + int rc = pcmk__init_tls(&tls, true, false); + + if (rc != pcmk_rc_ok) { + pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ", + "on port %d", pcmk_rc_str(rc), port); + remote_tls_fd = -1; + + } else { + pcmk__notice("Starting TLS listener on port %d", port); + remote_tls_fd = init_remote_listener(port); + } + } + + port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT); + + if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) { + pcmk__warn("Starting clear-text listener on port %d. This is insecure; " + PCMK_XA_REMOTE_TLS_PORT " is recommended instead.", port); + remote_fd = init_remote_listener(port); + } +} + +void +cib_remote_connection_destroy(gpointer user_data) +{ + pcmk__client_t *client = user_data; + int csock = -1; + + if (client == NULL) { + return; + } + + pcmk__trace("Cleaning up after client %s disconnect", + pcmk__client_name(client)); + + switch (PCMK__CLIENT_TYPE(client)) { + case pcmk__client_tcp: + csock = client->remote->tcp_socket; + break; + case pcmk__client_tls: + if (client->remote->tls_session) { + csock = pcmk__tls_get_client_sock(client->remote); + + if (pcmk__is_set(client->flags, + pcmk__client_tls_handshake_complete)) { + gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR); + } + gnutls_deinit(client->remote->tls_session); + client->remote->tls_session = NULL; + } + break; + default: + pcmk__warn("Unknown transport for client %s " + QB_XS " flags=%#016" PRIx64, + pcmk__client_name(client), client->flags); + } + + if (csock >= 0) { + close(csock); + } + + pcmk__free_client(client); + + pcmk__trace("Freed the cib client"); + + if (cib_shutdown_flag) { + cib_shutdown(0); + } + return; +} From eea619cb906459f5016b4ef0922643db9b480a06 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:52:01 -0800 Subject: [PATCH 107/350] Refactor: based: Drop cib_remote_connection_destroy forward declaration Also rename the two destroy callbacks so that their purposes are clearer. Typically we wouldn't use the "based_" prefix since these are static. However, there are similarly named callbacks in liblrmd (using the "lrmd_" prefix) and we typically don't like having multiple functions with the same name even if they're static (due to difficulty of grepping through source code). No code changes aside from the renames, moves, and dropping "return" statements at the end of two void functions. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 106 +++++++++++++++++------------------ 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 43a054418d0..6a3b066c800 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -50,8 +50,6 @@ static pcmk__tls_t *tls = NULL; int remote_fd = 0; int remote_tls_fd = 0; -void cib_remote_connection_destroy(gpointer user_data); - // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 @@ -454,6 +452,54 @@ cib_remote_msg(gpointer data) return 0; } +static void +based_remote_client_destroy(gpointer user_data) +{ + pcmk__client_t *client = user_data; + int csock = -1; + + if (client == NULL) { + return; + } + + pcmk__trace("Cleaning up after client %s disconnect", + pcmk__client_name(client)); + + switch (PCMK__CLIENT_TYPE(client)) { + case pcmk__client_tcp: + csock = client->remote->tcp_socket; + break; + case pcmk__client_tls: + if (client->remote->tls_session) { + csock = pcmk__tls_get_client_sock(client->remote); + + if (pcmk__is_set(client->flags, + pcmk__client_tls_handshake_complete)) { + gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR); + } + gnutls_deinit(client->remote->tls_session); + client->remote->tls_session = NULL; + } + break; + default: + pcmk__warn("Unknown transport for client %s " + QB_XS " flags=%#016" PRIx64, + pcmk__client_name(client), client->flags); + } + + if (csock >= 0) { + close(csock); + } + + pcmk__free_client(client); + + pcmk__trace("Freed the cib client"); + + if (cib_shutdown_flag) { + cib_shutdown(0); + } +} + static int cib_remote_listen(gpointer data) { @@ -468,7 +514,7 @@ cib_remote_listen(gpointer data) static struct mainloop_fd_callbacks remote_client_fd_callbacks = { .dispatch = cib_remote_msg, - .destroy = cib_remote_connection_destroy, + .destroy = based_remote_client_destroy, }; /* accept the connection */ @@ -525,10 +571,9 @@ cib_remote_listen(gpointer data) } static void -remote_connection_destroy(gpointer user_data) +based_remote_listener_destroy(gpointer user_data) { pcmk__info("No longer listening for remote connections"); - return; } static int @@ -541,7 +586,7 @@ init_remote_listener(int port) static struct mainloop_fd_callbacks remote_listen_fd_callbacks = { .dispatch = cib_remote_listen, - .destroy = remote_connection_destroy, + .destroy = based_remote_listener_destroy, }; #ifndef HAVE_PAM @@ -625,52 +670,3 @@ based_remote_init(void) remote_fd = init_remote_listener(port); } } - -void -cib_remote_connection_destroy(gpointer user_data) -{ - pcmk__client_t *client = user_data; - int csock = -1; - - if (client == NULL) { - return; - } - - pcmk__trace("Cleaning up after client %s disconnect", - pcmk__client_name(client)); - - switch (PCMK__CLIENT_TYPE(client)) { - case pcmk__client_tcp: - csock = client->remote->tcp_socket; - break; - case pcmk__client_tls: - if (client->remote->tls_session) { - csock = pcmk__tls_get_client_sock(client->remote); - - if (pcmk__is_set(client->flags, - pcmk__client_tls_handshake_complete)) { - gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR); - } - gnutls_deinit(client->remote->tls_session); - client->remote->tls_session = NULL; - } - break; - default: - pcmk__warn("Unknown transport for client %s " - QB_XS " flags=%#016" PRIx64, - pcmk__client_name(client), client->flags); - } - - if (csock >= 0) { - close(csock); - } - - pcmk__free_client(client); - - pcmk__trace("Freed the cib client"); - - if (cib_shutdown_flag) { - cib_shutdown(0); - } - return; -} From 152ccfb85a6b250b1047244b8358890a108ddd5d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 15:57:52 -0800 Subject: [PATCH 108/350] Refactor: based: Use G_SOURCE_REMOVE in remote_auth_timeout_cb() This is a GSourceFunc, so use G_SOURCE_REMOVE for clarity. Also add Doxygen. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 6a3b066c800..66355a564c8 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -53,6 +53,21 @@ int remote_tls_fd = 0; // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 +/*! + * \internal + * \brief Destroy a client if not authenticated after the timeout has expired + * + * This is used as a callback that runs \c REMOTE_AUTH_TIMEOUT milliseconds + * after a new remote CIB client connects. + * + * If the client is not authenticated, delete it from the mainloop. It will then + * be freed by \c based_remote_client_destroy() via + * \c remote_client_fd_callbacks. + * + * \param[in,out] data Remote CIB client (\c pcmk__client_t) + * + * \return \c G_SOURCE_REMOVE (to destroy the timeout) + */ static gboolean remote_auth_timeout_cb(gpointer data) { @@ -61,13 +76,12 @@ remote_auth_timeout_cb(gpointer data) client->remote->auth_timeout = 0; if (pcmk__is_set(client->flags, pcmk__client_authenticated)) { - return FALSE; + return G_SOURCE_REMOVE; } mainloop_del_fd(client->remote->source); pcmk__err("Remote client authentication timed out"); - - return FALSE; + return G_SOURCE_REMOVE; } static bool From fd9cd4d8570af2b67db94b3123f856ebfa1c4993 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 16:32:08 -0800 Subject: [PATCH 109/350] Doc: libcrmcommon: Add a clarifying note to pcmk__create_timer() I got confused for a little while about remote_auth_timeout_cb(). I thought returning G_SOURCE_REMOVE would cause g_source_remove() to get called on client->remote->source. But that is not the case. Signed-off-by: Reid Wahl --- lib/common/utils.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/common/utils.c b/lib/common/utils.c index 79b3d43ca0b..2872fb80e31 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.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. * @@ -372,6 +372,11 @@ pcmk__sleep_ms(unsigned int ms) * \param[in] data Data to be passed to fn (can be NULL) * * \return The ID of the event source + * + * \note If \p fn returns \c G_SOURCE_CONTINUE, then it will be called again + * after \p interval_ms. If \p fn returns \c G_SOURCE_REMOVE, then the + * timeout is destroyed and \c fn will not be called again. Note that no + * \c GDestroyNotify function is set, so only the timeout is destroyed. */ guint pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data) From d22fdb5e6427db0b6e946f5260893c396800fe38 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 16:03:49 -0800 Subject: [PATCH 110/350] Refactor: libcrmcommon: Move pcmk__remote_t def to remote_internal.h This is a more logical place for it. We drop the ipc_internal.h include from remote_internal.h to avoid circular includes. Signed-off-by: Reid Wahl --- include/crm/common/ipc_internal.h | 26 ++-------------------- include/crm/common/remote_internal.h | 32 ++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h index 911979ebe2a..d5fde69841d 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -19,8 +19,6 @@ #include // struct iovec #include // uid_t, gid_t, pid_t, size_t -#include // gnutls_session_t - #include // guint, gpointer, GQueue, ... #include // xmlNode #include // qb_ipcs_connection_t, ... @@ -30,7 +28,7 @@ #include #include // pcmk_controld_api_reply #include // pcmk_pacemakerd_{api_reply,state} -#include // mainloop_io_t +#include // pcmk__remote_t #ifdef __cplusplus extern "C" { @@ -106,26 +104,6 @@ int pcmk__connect_ipc_retry_conrefused(pcmk_ipc_api_t *api, typedef struct pcmk__client_s pcmk__client_t; -struct pcmk__remote_s { - /* Shared */ - char *buffer; - size_t buffer_size; - size_t buffer_offset; - int auth_timeout; - int tcp_socket; - mainloop_io_t *source; - time_t uptime; - char *start_state; - - /* CIB-only */ - char *token; - - /* TLS only */ - - // Must be created by pcmk__new_tls_session() - gnutls_session_t tls_session; -}; - enum pcmk__client_flags { // Lower 32 bits are reserved for server (not library) use @@ -189,7 +167,7 @@ struct pcmk__client_s { qb_ipcs_connection_t *ipcs; /* IPC */ - struct pcmk__remote_s *remote; /* TCP/TLS */ + pcmk__remote_t *remote; // TCP/TLS unsigned int queue_backlog; /* IPC queue length after last flush */ unsigned int queue_max; /* Evict client whose queue grows this big */ diff --git a/include/crm/common/remote_internal.h b/include/crm/common/remote_internal.h index 6656d9c0d1b..51a99354d0c 100644 --- a/include/crm/common/remote_internal.h +++ b/include/crm/common/remote_internal.h @@ -14,13 +14,15 @@ #ifndef PCMK__CRM_COMMON_REMOTE_INTERNAL__H #define PCMK__CRM_COMMON_REMOTE_INTERNAL__H -#include // NULL -#include // bool -#include // xmlNode +#include // bool +#include // NULL +#include // xmlNode -#include // pcmk__client_t +#include // gnutls_session_t + +#include // mainloop_io_t #include // pcmk__node_variant_remote, etc. -#include // struct pcmk__remote_private +#include // launcher #include // pcmk_node_t #ifdef __cplusplus @@ -29,7 +31,25 @@ extern "C" { // internal functions from remote.c -typedef struct pcmk__remote_s pcmk__remote_t; +typedef struct { + // Shared + char *buffer; + size_t buffer_size; + size_t buffer_offset; + int auth_timeout; + int tcp_socket; + mainloop_io_t *source; + time_t uptime; + char *start_state; + + // CIB-only + char *token; + + // TLS-only + + // Must be created by pcmk__new_tls_session() + gnutls_session_t tls_session; +} pcmk__remote_t; int pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg); int pcmk__remote_ready(const pcmk__remote_t *remote, int timeout_ms); From ec583265c081e40e68cb352a18599242972a9630 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 16:05:43 -0800 Subject: [PATCH 111/350] Refactor: libcrmcommon: Drop struct pcmk__client_s An anonymous struct is fine for the typedef. Signed-off-by: Reid Wahl --- include/crm/common/ipc_internal.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h index d5fde69841d..e96e8b1bd57 100644 --- a/include/crm/common/ipc_internal.h +++ b/include/crm/common/ipc_internal.h @@ -102,8 +102,6 @@ int pcmk__connect_ipc_retry_conrefused(pcmk_ipc_api_t *api, * Server-related */ -typedef struct pcmk__client_s pcmk__client_t; - enum pcmk__client_flags { // Lower 32 bits are reserved for server (not library) use @@ -142,7 +140,7 @@ enum pcmk__client_flags { #define PCMK__CLIENT_TYPE(client) ((client)->flags & UINT64_C(0xff00000000)) -struct pcmk__client_s { +typedef struct { unsigned int pid; char *id; @@ -171,7 +169,7 @@ struct pcmk__client_s { unsigned int queue_backlog; /* IPC queue length after last flush */ unsigned int queue_max; /* Evict client whose queue grows this big */ -}; +} pcmk__client_t; #define pcmk__set_client_flags(client, flags_to_set) do { \ (client)->flags = pcmk__set_flags_as(__func__, __LINE__, \ From f06bfd31d012a42ddb8bfd9fc0fcaefda05aa91d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 16:20:26 -0800 Subject: [PATCH 112/350] Refactor: libcrmcommon: Unindent mainloop_del_fd() Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 4d3dd9ae6ac..a63854c8d9d 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -995,17 +995,16 @@ mainloop_add_fd(const char *name, int priority, int fd, void *userdata, } void -mainloop_del_fd(mainloop_io_t * client) +mainloop_del_fd(mainloop_io_t *client) { - if (client != NULL) { - pcmk__trace("Removing client %s[%p]", client->name, client); - if (client->source) { - /* Results in mainloop_gio_destroy() being called just - * before the source is removed from mainloop - */ - g_source_remove(client->source); - } + if ((client == NULL) || (client->source == 0)) { + return; } + + pcmk__trace("Removing client %s[%p]", client->name, client); + + // mainloop_gio_destroy() gets called during source removal + g_source_remove(client->source); } static GList *child_list = NULL; From 5f747ade2c2f4f93e74f180ae89e9489a933fd4b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 22:49:37 -0800 Subject: [PATCH 113/350] Refactor: based: Drop redundant checks in is_daemon_group_member() Previously, we were looking up the user, making sure we found it, looking up the user's primary group, and checking whether it was CRM_DAEMON_GROUP. If not, then we fell back to looking up CRM_DAEMON_GROUP and checking whether user was in its member list. That fallback is all we need. The additional checks were added by commit 14d9ae4. No reasoning was provided. I'm guessing they were added in the name of efficiency in the common case. But they required two additional system calls, so I doubt that was any more efficient than iterating over the list of group members and doing string comparisons. And since this is done only once per remote client, efficiency gains probably don't matter much. Also add Doxygen. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 44 +++++++++++++----------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 66355a564c8..168b3b3994e 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -19,7 +19,6 @@ #include // calloc, free, getenv #include // memset #include // sockaddr{,_storage}, AF_INET, etc. -#include // gid_t #include // close #include // gboolean, gpointer, g_source_remove, etc. @@ -84,47 +83,36 @@ remote_auth_timeout_cb(gpointer data) return G_SOURCE_REMOVE; } +/*! + * \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 *usr) +is_daemon_group_member(const char *user) { - int index = 0; - gid_t gid = 0; - struct group *group = NULL; - int rc = pcmk_rc_ok; + const struct group *group = getgrnam(CRM_DAEMON_GROUP); - rc = pcmk__lookup_user(usr, NULL, &gid); - if (rc != pcmk_rc_ok) { - pcmk__notice("Rejecting remote client: could not find user '%s': %s", - usr, pcmk_rc_str(rc)); - return false; - } - - group = getgrgid(gid); - if ((group != NULL) - && pcmk__str_eq(group->gr_name, CRM_DAEMON_GROUP, pcmk__str_none)) { - return true; - } - - group = getgrnam(CRM_DAEMON_GROUP); if (group == NULL) { pcmk__err("Rejecting remote client: " CRM_DAEMON_GROUP " is not a " "valid group"); return false; } - while (true) { - char *member = group->gr_mem[index++]; - - if (member == NULL) { - break; + for (const char *const *member = (const char *const *) group->gr_mem; + *member != NULL; member++) { - } else if (pcmk__str_eq(usr, member, pcmk__str_none)) { + if (pcmk__str_eq(user, *member, pcmk__str_none)) { return true; } } - pcmk__notice("Rejecting remote client: User %s is not a member of group " - CRM_DAEMON_GROUP, usr); + pcmk__notice("Rejecting remote client: User %s is not a member of group %s", + user, CRM_DAEMON_GROUP); return false; } From 60f8d091cb1ca1e0058a363e6501db4e77c4d2e7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 23:52:07 -0800 Subject: [PATCH 114/350] Refactor: based: Unindent contains_config_change() And other minor changes Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a4a1e1fa3c1..b97c5abf6f1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1008,24 +1008,23 @@ prepare_input(const xmlNode *request, enum cib__op_type type, return input; } -#define XPATH_CONFIG_CHANGE \ - "//" PCMK_XE_CHANGE \ - "[contains(@" PCMK_XA_PATH ",'/" PCMK_XE_CRM_CONFIG "/')]" - static bool contains_config_change(xmlNode *diff) { + xmlXPathObject *xpath_obj = NULL; bool changed = false; - if (diff) { - xmlXPathObject *xpathObj = pcmk__xpath_search(diff->doc, - XPATH_CONFIG_CHANGE); - - if (pcmk__xpath_num_results(xpathObj) > 0) { - changed = true; - } - xmlXPathFreeObject(xpathObj); + if (diff == NULL) { + return false; } + + xpath_obj = pcmk__xpath_search(diff->doc, + "//" PCMK_XE_CHANGE + "[contains(@" PCMK_XA_PATH ", " + "'/" PCMK_XE_CRM_CONFIG "/')]"); + + changed = (pcmk__xpath_num_results(xpath_obj) > 0); + xmlXPathFreeObject(xpath_obj); return changed; } From 2e64f32ebc7961b1ecdef464a4cc927a1466327b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 23:55:06 -0800 Subject: [PATCH 115/350] Refactor: based: Drop forward declaration for cib_process_command() No code changes other than moving function definitions. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 437 ++++++++++++++++---------------- 1 file changed, 216 insertions(+), 221 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b97c5abf6f1..bd675d8a795 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -49,11 +49,6 @@ qb_ipcs_service_t *ipcs_ro = NULL; qb_ipcs_service_t *ipcs_rw = NULL; qb_ipcs_service_t *ipcs_shm = NULL; -static int cib_process_command(xmlNode *request, - const cib__operation_t *operation, - cib__op_fn_t op_function, xmlNode **reply, - bool privileged); - static int32_t cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privileged); @@ -757,222 +752,6 @@ forward_request(xmlNode *request) pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM); } -static void -send_peer_reply(xmlNode *msg, const char *originator) -{ - const pcmk__node_status_t *node = NULL; - - if ((msg == NULL) || (originator == NULL)) { - return; - } - - // Send reply via cluster to originating node - node = pcmk__get_node(0, originator, NULL, - pcmk__node_search_cluster_member); - - pcmk__trace("Sending request result to %s only", originator); - pcmk__xe_set(msg, PCMK__XA_CIB_ISREPLYTO, originator); - pcmk__cluster_send_message(node, pcmk_ipc_based, msg); -} - -/*! - * \internal - * \brief Handle an IPC or CPG message containing a request - * - * \param[in,out] request Request XML - * \param[in] privileged Whether privileged commands may be run - * (see cib_server_ops[] definition) - * \param[in] cib_client IPC client that sent request (or NULL if CPG) - * - * \return Standard Pacemaker return code - */ -int -cib_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *cib_client) -{ - // @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 - bool needs_forward = false; // Whether to forward request somewhere else - - xmlNode *op_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), - "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; - - 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 ((host != NULL) && (*host == '\0')) { - host = NULL; - } - - if (cib_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); - } else { - pcmk__xe_set(request, PCMK__XA_SRC, OUR_NODENAME); - pcmk__trace("Processing local %s operation from %s/%s intended for %s", - op, client_name, call_id, pcmk__s(host, "all")); - } - - rc = cib__get_operation(op, &operation); - if (rc != pcmk_rc_ok) { - /* TODO: construct error reply? */ - pcmk__err("Pre-processing of command failed: %s", pcmk_rc_str(rc)); - return rc; - } - - op_function = based_get_op_function(operation); - if (op_function == NULL) { - pcmk__err("Operation %s not supported by CIB manager", op); - return EOPNOTSUPP; - } - - if (cib_client != NULL) { - parse_local_options(cib_client, operation, host, op, - &local_notify, &needs_reply, &process, - &needs_forward); - - } else if (!parse_peer_options(operation, request, &local_notify, - &needs_reply, &process)) { - return pcmk_rc_ok; - } - - if (pcmk__is_set(call_options, cib_transaction)) { - /* All requests in a transaction are processed locally against a working - * CIB copy, and we don't notify for individual requests because the - * entire transaction is atomic. - * - * We still call the option parser functions above, for the sake of log - * messages and checking whether we're the target for peer requests. - */ - process = true; - needs_reply = false; - local_notify = false; - needs_forward = false; - } - - if (pcmk__is_set(call_options, cib_discard_reply)) { - /* If the request will modify the CIB, and we are in legacy mode, we - * need to build a reply so we can broadcast a diff, even if the - * requester doesn't want one. - */ - needs_reply = false; - local_notify = false; - pcmk__trace("Client is not interested in the reply"); - } - - if (needs_forward) { - forward_request(request); - return pcmk_rc_ok; - } - - if (cib_status != pcmk_rc_ok) { - rc = cib_status; - pcmk__err("Ignoring request because cluster configuration is invalid " - "(please repair and restart): %s", pcmk_rc_str(rc)); - op_reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), the_cib); - - } else if (process) { - time_t finished = 0; - time_t now = time(NULL); - int level = LOG_INFO; - const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; - - rc = cib_process_command(request, operation, op_function, &op_reply, - privileged); - - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - level = LOG_TRACE; - - } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { - switch (rc) { - case pcmk_rc_ok: - level = LOG_INFO; - break; - case pcmk_rc_old_data: - case pcmk_rc_diff_resync: - case pcmk_rc_diff_failed: - level = LOG_TRACE; - break; - default: - level = LOG_ERR; - } - - } else if (rc != pcmk_rc_ok) { - level = LOG_WARNING; - } - - if (the_cib != NULL) { - admin_epoch_s = pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(the_cib, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES); - } - - do_crm_log(level, - "Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)", - op, pcmk__s(section, "'all'"), pcmk_rc_str(rc), rc, - pcmk__s(originator, "local"), client_name, call_id, - pcmk__s(admin_epoch_s, "0"), pcmk__s(epoch_s, "0"), - pcmk__s(num_updates_s, "0")); - - finished = time(NULL); - if ((finished - now) > 3) { - pcmk__trace("%s operation took %llds to complete", op, - (long long) (finished - now)); - crm_write_blackbox(0, NULL); - } - - if (op_reply == NULL && (needs_reply || local_notify)) { - pcmk__err("Unexpected NULL reply to message"); - pcmk__log_xml_err(request, "null reply"); - goto done; - } - } - - if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - pcmk__trace("Completed pre-sync update from %s/%s/%s%s", - pcmk__s(originator, "local"), client_name, call_id, - (local_notify? " with local notification" : "")); - - } else if (needs_reply && !stand_alone && (cib_client == NULL) - && !pcmk__is_set(call_options, cib_discard_reply)) { - send_peer_reply(op_reply, originator); - } - - if (local_notify && client_id) { - do_local_notify(process ? op_reply : request, client_id, - pcmk__is_set(call_options, cib_sync_call), - (cib_client == NULL)); - } - -done: - pcmk__xml_free(op_reply); - return rc; -} - /*! * \internal * \brief Get a CIB operation's input from the request XML @@ -1212,6 +991,222 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, return rc; } +static void +send_peer_reply(xmlNode *msg, const char *originator) +{ + const pcmk__node_status_t *node = NULL; + + if ((msg == NULL) || (originator == NULL)) { + return; + } + + // Send reply via cluster to originating node + node = pcmk__get_node(0, originator, NULL, + pcmk__node_search_cluster_member); + + pcmk__trace("Sending request result to %s only", originator); + pcmk__xe_set(msg, PCMK__XA_CIB_ISREPLYTO, originator); + pcmk__cluster_send_message(node, pcmk_ipc_based, msg); +} + +/*! + * \internal + * \brief Handle an IPC or CPG message containing a request + * + * \param[in,out] request Request XML + * \param[in] privileged Whether privileged commands may be run + * (see cib_server_ops[] definition) + * \param[in] cib_client IPC client that sent request (or NULL if CPG) + * + * \return Standard Pacemaker return code + */ +int +cib_process_request(xmlNode *request, bool privileged, + const pcmk__client_t *cib_client) +{ + // @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 + bool needs_forward = false; // Whether to forward request somewhere else + + xmlNode *op_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), + "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; + + 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 ((host != NULL) && (*host == '\0')) { + host = NULL; + } + + if (cib_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); + } else { + pcmk__xe_set(request, PCMK__XA_SRC, OUR_NODENAME); + pcmk__trace("Processing local %s operation from %s/%s intended for %s", + op, client_name, call_id, pcmk__s(host, "all")); + } + + rc = cib__get_operation(op, &operation); + if (rc != pcmk_rc_ok) { + /* TODO: construct error reply? */ + pcmk__err("Pre-processing of command failed: %s", pcmk_rc_str(rc)); + return rc; + } + + op_function = based_get_op_function(operation); + if (op_function == NULL) { + pcmk__err("Operation %s not supported by CIB manager", op); + return EOPNOTSUPP; + } + + if (cib_client != NULL) { + parse_local_options(cib_client, operation, host, op, + &local_notify, &needs_reply, &process, + &needs_forward); + + } else if (!parse_peer_options(operation, request, &local_notify, + &needs_reply, &process)) { + return pcmk_rc_ok; + } + + if (pcmk__is_set(call_options, cib_transaction)) { + /* All requests in a transaction are processed locally against a working + * CIB copy, and we don't notify for individual requests because the + * entire transaction is atomic. + * + * We still call the option parser functions above, for the sake of log + * messages and checking whether we're the target for peer requests. + */ + process = true; + needs_reply = false; + local_notify = false; + needs_forward = false; + } + + if (pcmk__is_set(call_options, cib_discard_reply)) { + /* If the request will modify the CIB, and we are in legacy mode, we + * need to build a reply so we can broadcast a diff, even if the + * requester doesn't want one. + */ + needs_reply = false; + local_notify = false; + pcmk__trace("Client is not interested in the reply"); + } + + if (needs_forward) { + forward_request(request); + return pcmk_rc_ok; + } + + if (cib_status != pcmk_rc_ok) { + rc = cib_status; + pcmk__err("Ignoring request because cluster configuration is invalid " + "(please repair and restart): %s", pcmk_rc_str(rc)); + op_reply = create_cib_reply(op, call_id, client_id, call_options, + pcmk_rc2legacy(rc), the_cib); + + } else if (process) { + time_t finished = 0; + time_t now = time(NULL); + int level = LOG_INFO; + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + const char *admin_epoch_s = NULL; + const char *epoch_s = NULL; + const char *num_updates_s = NULL; + + rc = cib_process_command(request, operation, op_function, &op_reply, + privileged); + + if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + level = LOG_TRACE; + + } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { + switch (rc) { + case pcmk_rc_ok: + level = LOG_INFO; + break; + case pcmk_rc_old_data: + case pcmk_rc_diff_resync: + case pcmk_rc_diff_failed: + level = LOG_TRACE; + break; + default: + level = LOG_ERR; + } + + } else if (rc != pcmk_rc_ok) { + level = LOG_WARNING; + } + + if (the_cib != NULL) { + admin_epoch_s = pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH); + epoch_s = pcmk__xe_get(the_cib, PCMK_XA_EPOCH); + num_updates_s = pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES); + } + + do_crm_log(level, + "Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)", + op, pcmk__s(section, "'all'"), pcmk_rc_str(rc), rc, + pcmk__s(originator, "local"), client_name, call_id, + pcmk__s(admin_epoch_s, "0"), pcmk__s(epoch_s, "0"), + pcmk__s(num_updates_s, "0")); + + finished = time(NULL); + if ((finished - now) > 3) { + pcmk__trace("%s operation took %llds to complete", op, + (long long) (finished - now)); + crm_write_blackbox(0, NULL); + } + + if (op_reply == NULL && (needs_reply || local_notify)) { + pcmk__err("Unexpected NULL reply to message"); + pcmk__log_xml_err(request, "null reply"); + goto done; + } + } + + if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + pcmk__trace("Completed pre-sync update from %s/%s/%s%s", + pcmk__s(originator, "local"), client_name, call_id, + (local_notify? " with local notification" : "")); + + } else if (needs_reply && !stand_alone && (cib_client == NULL) + && !pcmk__is_set(call_options, cib_discard_reply)) { + send_peer_reply(op_reply, originator); + } + + if (local_notify && client_id) { + do_local_notify(process ? op_reply : request, client_id, + pcmk__is_set(call_options, cib_sync_call), + (cib_client == NULL)); + } + +done: + pcmk__xml_free(op_reply); + return rc; +} + void cib_peer_callback(xmlNode * msg, void *private_data) { From 74678a64853558743653e37295e2647fc9737d28 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 22 Dec 2025 23:56:35 -0800 Subject: [PATCH 116/350] Refactor: based: Drop forward declaration for cib_common_callback() No code changes other than moving a function definition. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 191 ++++++++++++++++---------------- 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index bd675d8a795..c36d58dca0f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -49,9 +49,6 @@ qb_ipcs_service_t *ipcs_ro = NULL; qb_ipcs_service_t *ipcs_rw = NULL; qb_ipcs_service_t *ipcs_shm = NULL; -static int32_t cib_common_callback(qb_ipcs_connection_t *c, void *data, - size_t size, bool privileged); - static int32_t cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) { @@ -67,6 +64,100 @@ cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) return 0; } +static int32_t +cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privileged) +{ + int rc = pcmk_rc_ok; + uint32_t id = 0; + uint32_t flags = 0; + uint32_t call_options = cib_none; + pcmk__client_t *cib_client = pcmk__find_client(c); + xmlNode *op_request = NULL; + + // Sanity-check, and parse XML from IPC data + CRM_CHECK(cib_client != NULL, return 0); + if (data == NULL) { + pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); + return 0; + } + + pcmk__trace("Dispatching %sprivileged request from client %s", + (privileged? "" : "un"), cib_client->id); + + rc = pcmk__ipc_msg_append(&cib_client->buffer, data); + + if (rc == pcmk_rc_ipc_more) { + /* We haven't read the complete message yet, so just return. */ + return 0; + + } else if (rc == pcmk_rc_ok) { + /* We've read the complete message and there's already a header on + * the front. Pass it off for processing. + */ + op_request = pcmk__client_data2xml(cib_client, &id, &flags); + g_byte_array_free(cib_client->buffer, TRUE); + cib_client->buffer = NULL; + + } else { + /* Some sort of error occurred reassembling the message. All we can + * do is clean up, log an error and return. + */ + pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc)); + + if (cib_client->buffer != NULL) { + g_byte_array_free(cib_client->buffer, TRUE); + cib_client->buffer = NULL; + } + + return 0; + } + + if (op_request) { + int rc = pcmk_rc_ok; + + rc = pcmk__xe_get_flags(op_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 (op_request == NULL) { + pcmk__trace("Invalid message from %p", c); + pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL, + CRM_EX_PROTOCOL); + return 0; + } + + if (pcmk__is_set(call_options, cib_sync_call)) { + CRM_LOG_ASSERT(flags & crm_ipc_client_response); + CRM_LOG_ASSERT(cib_client->request_id == 0); /* This means the client has two synchronous events in-flight */ + cib_client->request_id = id; /* Reply only to the last one */ + } + + if (cib_client->name == NULL) { + const char *value = pcmk__xe_get(op_request, PCMK__XA_CIB_CLIENTNAME); + + if (value == NULL) { + cib_client->name = pcmk__itoa(cib_client->pid); + } else { + cib_client->name = pcmk__str_copy(value); + } + } + + pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id); + pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name); + + CRM_LOG_ASSERT(cib_client->user != NULL); + pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user); + + cib_common_callback_worker(id, flags, op_request, cib_client, privileged); + pcmk__xml_free(op_request); + + return 0; +} + static int32_t cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size) { @@ -300,100 +391,6 @@ cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request, cib_process_request(op_request, privileged, cib_client); } -static int32_t -cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privileged) -{ - int rc = pcmk_rc_ok; - uint32_t id = 0; - uint32_t flags = 0; - uint32_t call_options = cib_none; - pcmk__client_t *cib_client = pcmk__find_client(c); - xmlNode *op_request = NULL; - - // Sanity-check, and parse XML from IPC data - CRM_CHECK(cib_client != NULL, return 0); - if (data == NULL) { - pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); - return 0; - } - - pcmk__trace("Dispatching %sprivileged request from client %s", - (privileged? "" : "un"), cib_client->id); - - rc = pcmk__ipc_msg_append(&cib_client->buffer, data); - - if (rc == pcmk_rc_ipc_more) { - /* We haven't read the complete message yet, so just return. */ - return 0; - - } else if (rc == pcmk_rc_ok) { - /* We've read the complete message and there's already a header on - * the front. Pass it off for processing. - */ - op_request = pcmk__client_data2xml(cib_client, &id, &flags); - g_byte_array_free(cib_client->buffer, TRUE); - cib_client->buffer = NULL; - - } else { - /* Some sort of error occurred reassembling the message. All we can - * do is clean up, log an error and return. - */ - pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc)); - - if (cib_client->buffer != NULL) { - g_byte_array_free(cib_client->buffer, TRUE); - cib_client->buffer = NULL; - } - - return 0; - } - - if (op_request) { - int rc = pcmk_rc_ok; - - rc = pcmk__xe_get_flags(op_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 (op_request == NULL) { - pcmk__trace("Invalid message from %p", c); - pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL, - CRM_EX_PROTOCOL); - return 0; - } - - if (pcmk__is_set(call_options, cib_sync_call)) { - CRM_LOG_ASSERT(flags & crm_ipc_client_response); - CRM_LOG_ASSERT(cib_client->request_id == 0); /* This means the client has two synchronous events in-flight */ - cib_client->request_id = id; /* Reply only to the last one */ - } - - if (cib_client->name == NULL) { - const char *value = pcmk__xe_get(op_request, PCMK__XA_CIB_CLIENTNAME); - - if (value == NULL) { - cib_client->name = pcmk__itoa(cib_client->pid); - } else { - cib_client->name = pcmk__str_copy(value); - } - } - - pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id); - pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name); - - CRM_LOG_ASSERT(cib_client->user != NULL); - pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user); - - cib_common_callback_worker(id, flags, op_request, cib_client, privileged); - pcmk__xml_free(op_request); - - return 0; -} - static uint64_t ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; From d454230de76285113c763848e265e615e97624b4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:04:47 -0800 Subject: [PATCH 117/350] Refactor: based: Rename cib_process_request() cib_client arg to client To be more consistent with other daemons' message-processing functions. Also improve Doxygen. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 24 ++++++++++++------------ daemons/based/pacemaker-based.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c36d58dca0f..459aa2b4a14 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1010,16 +1010,17 @@ 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 Whether privileged commands may be run - * (see cib_server_ops[] definition) - * \param[in] cib_client IPC client that sent request (or NULL if CPG) + * \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) * * \return Standard Pacemaker return code */ int cib_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *cib_client) + const pcmk__client_t *client) { // @TODO: Break into multiple smaller functions uint32_t call_options = cib_none; @@ -1054,7 +1055,7 @@ cib_process_request(xmlNode *request, bool privileged, host = NULL; } - if (cib_client == NULL) { + if (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); @@ -1077,10 +1078,9 @@ cib_process_request(xmlNode *request, bool privileged, return EOPNOTSUPP; } - if (cib_client != NULL) { - parse_local_options(cib_client, operation, host, op, - &local_notify, &needs_reply, &process, - &needs_forward); + if (client != NULL) { + parse_local_options(client, operation, host, op, &local_notify, + &needs_reply, &process, &needs_forward); } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { @@ -1188,7 +1188,7 @@ cib_process_request(xmlNode *request, bool privileged, pcmk__s(originator, "local"), client_name, call_id, (local_notify? " with local notification" : "")); - } else if (needs_reply && !stand_alone && (cib_client == NULL) + } else if (needs_reply && !stand_alone && (client == NULL) && !pcmk__is_set(call_options, cib_discard_reply)) { send_peer_reply(op_reply, originator); } @@ -1196,7 +1196,7 @@ cib_process_request(xmlNode *request, bool privileged, if (local_notify && client_id) { do_local_notify(process ? op_reply : request, client_id, pcmk__is_set(call_options, cib_sync_call), - (cib_client == NULL)); + (client == NULL)); } done: diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 81566e16451..51816eab68c 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -58,7 +58,7 @@ void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *cib_client, bool privileged); int cib_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *cib_client); + const pcmk__client_t *client); void cib_shutdown(int nsig); void terminate_cib(int exit_status); From 02bd92fc90d5b00f1ea0e534e3cfd2f06101aea3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:14:22 -0800 Subject: [PATCH 118/350] Refactor: based: Rename cib_process_request() to based_process_request() Avoid using the public API prefix. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 12 ++++++------ daemons/based/based_transaction.c | 2 +- daemons/based/pacemaker-based.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 459aa2b4a14..cdc13405a9c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -388,7 +388,7 @@ cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request, return; } - cib_process_request(op_request, privileged, cib_client); + based_process_request(op_request, privileged, cib_client); } static uint64_t ping_seq = 0; @@ -527,8 +527,8 @@ parse_local_options(const pcmk__client_t *cib_client, /* Always process locally if cib__op_attr_local is set. * * @COMPAT: Currently host is ignored. At a compatibility break, throw - * an error (from cib_process_request() or earlier) if host is not NULL or - * OUR_NODENAME. + * an error (from based_process_request() or earlier) if host is not + * NULL or OUR_NODENAME. */ pcmk__trace("Processing always-local %s op from client %s", op, pcmk__client_name(cib_client)); @@ -1019,8 +1019,8 @@ send_peer_reply(xmlNode *msg, const char *originator) * \return Standard Pacemaker return code */ int -cib_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client) +based_process_request(xmlNode *request, bool privileged, + const pcmk__client_t *client) { // @TODO: Break into multiple smaller functions uint32_t call_options = cib_none; @@ -1219,7 +1219,7 @@ cib_peer_callback(xmlNode * msg, void *private_data) pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); } - cib_process_request(msg, true, NULL); + based_process_request(msg, true, NULL); return; bail: diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index 6ee1f793308..d6d7a2b3678 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -84,7 +84,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, /* Commit-transaction is a privileged operation. If we reached * this point, the request came from a privileged connection. */ - rc = cib_process_request(request, true, client); + rc = based_process_request(request, true, client); } } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 51816eab68c..d62a4484bbf 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -57,8 +57,8 @@ void cib_peer_callback(xmlNode *msg, void *private_data); void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *cib_client, bool privileged); -int cib_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client); +int based_process_request(xmlNode *request, bool privileged, + const pcmk__client_t *client); void cib_shutdown(int nsig); void terminate_cib(int exit_status); From 2ab0117bbdc8bb28dabe5bad7aeac355327ae621 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:15:53 -0800 Subject: [PATCH 119/350] Refactor: based: Rename cib_peer_callback() to based_peer_callback() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/pacemaker-based.c | 2 +- daemons/based/pacemaker-based.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cdc13405a9c..676947c3662 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1205,7 +1205,7 @@ based_process_request(xmlNode *request, bool privileged, } void -cib_peer_callback(xmlNode * msg, void *private_data) +based_peer_callback(xmlNode *msg, void *private_data) { const char *reason = NULL; const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index e2448aabd17..a9e8dd430b4 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -326,7 +326,7 @@ cib_cs_dispatch(cpg_handle_t handle, return; } pcmk__xe_set(xml, PCMK__XA_SRC, from); - cib_peer_callback(xml, NULL); + based_peer_callback(xml, NULL); pcmk__xml_free(xml); free(data); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index d62a4484bbf..33f975d40d9 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -53,7 +53,7 @@ extern qb_ipcs_service_t *ipcs_ro; extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; -void cib_peer_callback(xmlNode *msg, void *private_data); +void based_peer_callback(xmlNode *msg, void *private_data); void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *cib_client, bool privileged); From 7ac450830c5267f7d85183a6a51064cfcff2e30d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:17:15 -0800 Subject: [PATCH 120/350] Refactor: based: Rename cib_common_callback_worker() ...to based_common_callback_worker(), to avoid using public API prefix. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +++--- daemons/based/based_remote.c | 2 +- daemons/based/pacemaker-based.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 676947c3662..6bc7556b643 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -152,7 +152,7 @@ cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privi CRM_LOG_ASSERT(cib_client->user != NULL); pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user); - cib_common_callback_worker(id, flags, op_request, cib_client, privileged); + based_common_callback_worker(id, flags, op_request, cib_client, privileged); pcmk__xml_free(op_request); return 0; @@ -313,8 +313,8 @@ do_local_notify(const xmlNode *notify_src, const char *client_id, } void -cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request, - pcmk__client_t *cib_client, bool privileged) +based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, + pcmk__client_t *cib_client, bool privileged) { const char *op = pcmk__xe_get(op_request, PCMK__XA_CIB_OP); uint32_t call_options = cib_none; diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 168b3b3994e..fca0d40f6fa 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -345,7 +345,7 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) } pcmk__log_xml_trace(command, "Remote command: "); - cib_common_callback_worker(0, 0, command, client, true); + based_common_callback_worker(0, 0, command, client, true); } static int diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 33f975d40d9..6be879e2df8 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -54,9 +54,9 @@ extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; void based_peer_callback(xmlNode *msg, void *private_data); -void cib_common_callback_worker(uint32_t id, uint32_t flags, - xmlNode *op_request, pcmk__client_t *cib_client, - bool privileged); +void based_common_callback_worker(uint32_t id, uint32_t flags, + xmlNode *op_request, + pcmk__client_t *cib_client, bool privileged); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void cib_shutdown(int nsig); From 610b5799aaf86f9aab4eda496fb83c7af60e1a30 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:19:16 -0800 Subject: [PATCH 121/350] Refactor: based: Rename cib_shutdown() to based_shutdown() Avoid using the public API prefix. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- daemons/based/based_remote.c | 2 +- daemons/based/pacemaker-based.c | 3 +-- daemons/based/pacemaker-based.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 6bc7556b643..cec93eabbcc 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -190,7 +190,7 @@ cib_ipc_destroy(qb_ipcs_connection_t * c) pcmk__trace("Connection %p", c); cib_ipc_closed(c); if (cib_shutdown_flag) { - cib_shutdown(0); + based_shutdown(0); } } @@ -1275,7 +1275,7 @@ initiate_exit(void) } void -cib_shutdown(int nsig) +based_shutdown(int nsig) { struct qb_ipcs_stats srv_stats; diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index fca0d40f6fa..f9781c61e43 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -498,7 +498,7 @@ based_remote_client_destroy(gpointer user_data) pcmk__trace("Freed the cib client"); if (cib_shutdown_flag) { - cib_shutdown(0); + based_shutdown(0); } } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index a9e8dd430b4..b6dbf1415e0 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -49,7 +49,6 @@ gboolean stand_alone = FALSE; GHashTable *config_hash = NULL; static void cib_init(void); -void cib_shutdown(int nsig); static crm_exit_t exit_code = CRM_EX_OK; @@ -202,7 +201,7 @@ main(int argc, char **argv) goto done; } - mainloop_add_signal(SIGTERM, cib_shutdown); + mainloop_add_signal(SIGTERM, based_shutdown); based_io_init(); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 6be879e2df8..3363235aad0 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -59,7 +59,7 @@ void based_common_callback_worker(uint32_t id, uint32_t flags, pcmk__client_t *cib_client, bool privileged); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); -void cib_shutdown(int nsig); +void based_shutdown(int nsig); void terminate_cib(int exit_status); static inline const char * From 5256736efd1a97efaf59cab81a95ca1e38a00d26 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:21:29 -0800 Subject: [PATCH 122/350] Refactor: based: Rename terminate_cib() to based_terminate() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +++--- daemons/based/based_messages.c | 2 +- daemons/based/pacemaker-based.c | 6 +++--- daemons/based/pacemaker-based.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cec93eabbcc..4d82e3efb08 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1235,7 +1235,7 @@ cib_force_exit(gpointer data) { pcmk__notice("Exiting immediately after %s without shutdown acknowledgment", pcmk__readable_interval(EXIT_ESCALATION_MS)); - terminate_cib(CRM_EX_ERROR); + based_terminate(CRM_EX_ERROR); return FALSE; } @@ -1258,7 +1258,7 @@ initiate_exit(void) if (active < 2) { // This is the last active node pcmk__info("Exiting without sending shutdown request (no active " "peers)"); - terminate_cib(CRM_EX_OK); + based_terminate(CRM_EX_OK); return; } @@ -1349,7 +1349,7 @@ based_shutdown(int nsig) * skip disconnecting from the cluster layer) */ void -terminate_cib(int exit_status) +based_terminate(int exit_status) { if (remote_fd > 0) { close(remote_fd); diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 30a72064b3e..382e69c548a 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -332,7 +332,7 @@ based_process_shutdown(const char *op, int options, const char *section, } pcmk__info("Exiting after %s acknowledged our shutdown request", host); - terminate_cib(CRM_EX_OK); + based_terminate(CRM_EX_OK); return pcmk_ok; } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index b6dbf1415e0..5013d581aad 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -276,7 +276,7 @@ main(int argc, char **argv) g_main_loop_run(mainloop); /* If main loop returned, clean up and exit. We disconnect in case - * terminate_cib(-1) was called. + * based_terminate(-1) was called. */ pcmk_cluster_disconnect(crm_cluster); pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); @@ -339,7 +339,7 @@ cib_cs_destroy(gpointer user_data) } else { pcmk__crit("Exiting immediately after losing connection to cluster " "layer"); - terminate_cib(CRM_EX_DISCONNECT); + based_terminate(CRM_EX_DISCONNECT); } } #endif @@ -355,7 +355,7 @@ cib_peer_update_callback(enum pcmk__node_update type, && (pcmk__ipc_client_count() == 0)) { pcmk__info("Exiting after no more peers or clients remain"); - terminate_cib(-1); + based_terminate(-1); } break; diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 3363235aad0..0e8d2663861 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -60,7 +60,7 @@ void based_common_callback_worker(uint32_t id, uint32_t flags, int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_shutdown(int nsig); -void terminate_cib(int exit_status); +void based_terminate(int exit_status); static inline const char * cib_config_lookup(const char *opt) From 78297dd48b35de7f933c33da32674adaaf2efa85 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:13:19 -0800 Subject: [PATCH 123/350] Refactor: based: New based_callbacks.h This will probably get renamed and/or split up further when we implement IPC/message-related best practices in pacemaker-based. However, for now, simply make the header name match the .c file name. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 3 ++- daemons/based/based_callbacks.h | 37 +++++++++++++++++++++++++++++++++ daemons/based/pacemaker-based.h | 24 +++------------------ 3 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 daemons/based/based_callbacks.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 92b6490e3e3..711f3559e80 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -14,7 +14,8 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based -noinst_HEADERS = based_io.h \ +noinst_HEADERS = based_callbacks.h \ + based_io.h \ based_messages.h \ based_notify.h \ based_operation.h \ diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h new file mode 100644 index 00000000000..9c67a84f2e1 --- /dev/null +++ b/daemons/based/based_callbacks.h @@ -0,0 +1,37 @@ +/* + * Copyright 2025-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_CALLBACKS__H +#define BASED_CALLBACKS__H + +#include +#include // uint32_t + +#include // xmlNode +#include // qb_* + +#include // pcmk__client_t + +extern struct qb_ipcs_service_handlers ipc_ro_callbacks; +extern struct qb_ipcs_service_handlers ipc_rw_callbacks; + +extern qb_ipcs_service_t *ipcs_ro; +extern qb_ipcs_service_t *ipcs_rw; +extern qb_ipcs_service_t *ipcs_shm; + +void based_peer_callback(xmlNode *msg, void *private_data); +void based_common_callback_worker(uint32_t id, uint32_t flags, + xmlNode *op_request, + pcmk__client_t *cib_client, bool privileged); +int based_process_request(xmlNode *request, bool privileged, + const pcmk__client_t *client); +void based_shutdown(int nsig); +void based_terminate(int exit_status); + +#endif // BASED_CALLBACKS__H diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 0e8d2663861..965543c7fb7 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -8,18 +8,15 @@ */ #ifndef PACEMAKER_BASED__H -# define PACEMAKER_BASED__H +#define PACEMAKER_BASED__H #include -#include // uint32_t, UINT64_C -#include // GHashTable, g_hash_table_lookup -#include // xmlNode -#include // qb_ipcs_service_t +#include // gboolean, gchar, GHashTable, etc. #include // pcmk_cluster_t -#include // pcmk__client_t +#include "based_callbacks.h" #include "based_io.h" #include "based_messages.h" #include "based_operation.h" @@ -47,21 +44,6 @@ extern bool cib_shutdown_flag; extern gchar *cib_root; extern int cib_status; -extern struct qb_ipcs_service_handlers ipc_ro_callbacks; -extern struct qb_ipcs_service_handlers ipc_rw_callbacks; -extern qb_ipcs_service_t *ipcs_ro; -extern qb_ipcs_service_t *ipcs_rw; -extern qb_ipcs_service_t *ipcs_shm; - -void based_peer_callback(xmlNode *msg, void *private_data); -void based_common_callback_worker(uint32_t id, uint32_t flags, - xmlNode *op_request, - pcmk__client_t *cib_client, bool privileged); -int based_process_request(xmlNode *request, bool privileged, - const pcmk__client_t *client); -void based_shutdown(int nsig); -void based_terminate(int exit_status); - static inline const char * cib_config_lookup(const char *opt) { From 848267aedb03be8f7b4dc5f0fdc110d52ed614b7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 00:32:34 -0800 Subject: [PATCH 124/350] Refactor: based: Drop cib_config_lookup() Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 965543c7fb7..c931bf25d94 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -12,7 +12,7 @@ #include -#include // gboolean, gchar, GHashTable, etc. +#include // gboolean, gchar, GHashTable, GMainLoop #include // pcmk_cluster_t @@ -44,10 +44,4 @@ extern bool cib_shutdown_flag; extern gchar *cib_root; extern int cib_status; -static inline const char * -cib_config_lookup(const char *opt) -{ - return g_hash_table_lookup(config_hash, opt); -} - #endif // PACEMAKER_BASED__H From b3f2f66edce307556d4efd52f1e022f4cea7cd78 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 01:51:00 -0800 Subject: [PATCH 125/350] Refactor: based: Functionize logging result of cib_process_command() Make based_process_request() a bit more legible. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 121 +++++++++++++++++++------------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4d82e3efb08..9044e3b24b2 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -988,6 +988,75 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, return rc; } +/*! + * \internal + * \brief Log the result of processing a CIB request locally + * + * \param[in] request Request XML + * \param[in] operation Operation info + * \param[in] rc Return code from processing the request + * \param[in] elapsed How long processing took in seconds + */ +static void +log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, + long long elapsed) +{ + int level = LOG_INFO; + + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); + const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); + const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); + + int admin_epoch = 0; + int epoch = 0; + int num_updates = 0; + + if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + level = LOG_TRACE; + + } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { + switch (rc) { + case pcmk_rc_ok: + level = LOG_INFO; + break; + + case pcmk_rc_old_data: + case pcmk_rc_diff_resync: + case pcmk_rc_diff_failed: + level = LOG_TRACE; + break; + + default: + level = LOG_ERR; + } + + } else if (rc != pcmk_rc_ok) { + level = LOG_WARNING; + } + + section = pcmk__s(section, "'all'"); + originator = pcmk__s(originator, "local"); + client_name = pcmk__s(client_name, "client"); + + pcmk__xe_get_int(the_cib, PCMK_XA_ADMIN_EPOCH, &admin_epoch); + pcmk__xe_get_int(the_cib, PCMK_XA_EPOCH, &epoch); + pcmk__xe_get_int(the_cib, PCMK_XA_NUM_UPDATES, &num_updates); + + do_crm_log(level, + "Completed %s operation for section %s: %s (rc=%d, " + "origin=%s/%s/%s, version=%d.%d.%d)", + op, section, pcmk_rc_str(rc), rc, + originator, client_name, call_id, + admin_epoch, epoch, num_updates); + + if (elapsed > 3) { + pcmk__trace("%s operation took %llds to complete", op, elapsed); + crm_write_blackbox(0, NULL); + } +} + static void send_peer_reply(xmlNode *msg, const char *originator) { @@ -1124,59 +1193,13 @@ based_process_request(xmlNode *request, bool privileged, pcmk_rc2legacy(rc), the_cib); } else if (process) { - time_t finished = 0; - time_t now = time(NULL); - int level = LOG_INFO; - const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; + time_t start_time = time(NULL); rc = cib_process_command(request, operation, op_function, &op_reply, privileged); + log_op_result(request, operation, rc, (time(NULL) - start_time)); - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - level = LOG_TRACE; - - } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { - switch (rc) { - case pcmk_rc_ok: - level = LOG_INFO; - break; - case pcmk_rc_old_data: - case pcmk_rc_diff_resync: - case pcmk_rc_diff_failed: - level = LOG_TRACE; - break; - default: - level = LOG_ERR; - } - - } else if (rc != pcmk_rc_ok) { - level = LOG_WARNING; - } - - if (the_cib != NULL) { - admin_epoch_s = pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(the_cib, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES); - } - - do_crm_log(level, - "Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)", - op, pcmk__s(section, "'all'"), pcmk_rc_str(rc), rc, - pcmk__s(originator, "local"), client_name, call_id, - pcmk__s(admin_epoch_s, "0"), pcmk__s(epoch_s, "0"), - pcmk__s(num_updates_s, "0")); - - finished = time(NULL); - if ((finished - now) > 3) { - pcmk__trace("%s operation took %llds to complete", op, - (long long) (finished - now)); - crm_write_blackbox(0, NULL); - } - - if (op_reply == NULL && (needs_reply || local_notify)) { + if ((op_reply == NULL) && (needs_reply || local_notify)) { pcmk__err("Unexpected NULL reply to message"); pcmk__log_xml_err(request, "null reply"); goto done; From 61f0b4e7598a45f7f928cf688810af0387883797 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 02:40:47 -0800 Subject: [PATCH 126/350] Refactor: based: Very minor cleanups in based_process_request() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 9044e3b24b2..faf87b8ce57 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1099,7 +1099,7 @@ based_process_request(xmlNode *request, bool privileged, bool local_notify = false; // Whether to notify (local) requester bool needs_forward = false; // Whether to forward request somewhere else - xmlNode *op_reply = NULL; + xmlNode *reply = NULL; int rc = pcmk_rc_ok; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); @@ -1120,7 +1120,7 @@ based_process_request(xmlNode *request, bool privileged, pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); } - if ((host != NULL) && (*host == '\0')) { + if (pcmk__str_empty(host)) { host = NULL; } @@ -1189,17 +1189,17 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - op_reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), the_cib); + reply = create_cib_reply(op, call_id, client_id, call_options, + pcmk_rc2legacy(rc), the_cib); } else if (process) { time_t start_time = time(NULL); - rc = cib_process_command(request, operation, op_function, &op_reply, + rc = cib_process_command(request, operation, op_function, &reply, privileged); log_op_result(request, operation, rc, (time(NULL) - start_time)); - if ((op_reply == NULL) && (needs_reply || local_notify)) { + if ((reply == NULL) && (needs_reply || local_notify)) { pcmk__err("Unexpected NULL reply to message"); pcmk__log_xml_err(request, "null reply"); goto done; @@ -1213,17 +1213,17 @@ based_process_request(xmlNode *request, bool privileged, } else if (needs_reply && !stand_alone && (client == NULL) && !pcmk__is_set(call_options, cib_discard_reply)) { - send_peer_reply(op_reply, originator); + send_peer_reply(reply, originator); } - if (local_notify && client_id) { - do_local_notify(process ? op_reply : request, client_id, + if (local_notify && (client_id != NULL)) { + do_local_notify((process? reply : request), client_id, pcmk__is_set(call_options, cib_sync_call), (client == NULL)); } done: - pcmk__xml_free(op_reply); + pcmk__xml_free(reply); return rc; } From 0488891af4ad3b8765921dce28a7d34790310be3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 03:03:21 -0800 Subject: [PATCH 127/350] Refactor: based: Reduce duplication in do_local_notify() And change/add some variable names. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 67 +++++++++++++++++---------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index faf87b8ce57..ff5b6536565 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -251,64 +251,67 @@ create_cib_reply(const char *op, const char *call_id, const char *client_id, } static void -do_local_notify(const xmlNode *notify_src, const char *client_id, - bool sync_reply, bool from_peer) +do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, + bool from_peer) { - int msg_id = 0; + int call_id = 0; int rc = pcmk_rc_ok; - pcmk__client_t *client_obj = NULL; + pcmk__client_t *client = NULL; uint32_t flags = crm_ipc_server_event; + const char *client_type = NULL; + const char *client_name = NULL; + const char *client_desc = ""; + const char *sync_s = (sync_reply? "synchronous" : "asynchronous"); - CRM_CHECK((notify_src != NULL) && (client_id != NULL), return); + CRM_CHECK((xml != NULL) && (client_id != NULL), return); - pcmk__trace("Performing local %ssync notification for %s", - sync_reply ? "" : "a", client_id); + if (from_peer) { + client_desc = " (originator of delegated_request)"; + } + + pcmk__trace("Performing local %s notification for %s", sync_s, client_id); - pcmk__xe_get_int(notify_src, PCMK__XA_CIB_CALLID, &msg_id); + pcmk__xe_get_int(xml, PCMK__XA_CIB_CALLID, &call_id); - client_obj = pcmk__find_client_by_id(client_id); - if (client_obj == NULL) { - pcmk__debug("Could not notify client %s%s %s of call %d result: " - "client no longer exists", - client_id, - (from_peer? " (originator of delegated request)" : ""), - (sync_reply? "synchronously" : "asynchronously"), msg_id); + client = pcmk__find_client_by_id(client_id); + if (client == NULL) { + pcmk__debug("Could not notify client %s%s %sly of call %d result: " + "client no longer exists", client_id, client_desc, sync_s, + call_id); return; } + client_type = pcmk__client_type_str(PCMK__CLIENT_TYPE(client)); + client_name = pcmk__client_name(client); + if (sync_reply) { flags = crm_ipc_flags_none; - if (client_obj->ipcs != NULL) { - msg_id = client_obj->request_id; - client_obj->request_id = 0; + if (client->ipcs != NULL) { + call_id = client->request_id; + client->request_id = 0; } } - switch (PCMK__CLIENT_TYPE(client_obj)) { + switch (PCMK__CLIENT_TYPE(client)) { case pcmk__client_ipc: - rc = pcmk__ipc_send_xml(client_obj, msg_id, notify_src, flags); + rc = pcmk__ipc_send_xml(client, call_id, xml, flags); break; case pcmk__client_tls: case pcmk__client_tcp: - rc = pcmk__remote_send_xml(client_obj->remote, notify_src); + rc = pcmk__remote_send_xml(client->remote, xml); break; default: rc = EPROTONOSUPPORT; break; } + if (rc == pcmk_rc_ok) { - pcmk__trace("Notified %s client %s%s %s of call %d result", - pcmk__client_type_str(PCMK__CLIENT_TYPE(client_obj)), - pcmk__client_name(client_obj), - (from_peer? " (originator of delegated request)" : ""), - (sync_reply? "synchronously" : "asynchronously"), msg_id); + pcmk__trace("Notified %s client %s%s %sly of call %d result", + client_type, client_name, client_desc, sync_s, call_id); } else { - pcmk__warn("Could not notify %s client %s%s %s of call %d result: %s", - pcmk__client_type_str(PCMK__CLIENT_TYPE(client_obj)), - pcmk__client_name(client_obj), - (from_peer? " (originator of delegated request)" : ""), - (sync_reply? "synchronously" : "asynchronously"), msg_id, - pcmk_rc_str(rc)); + pcmk__warn("Could not notify %s client %s%s %ssynchronously of call %d " + "result: %s", client_type, client_name, client_desc, sync_s, + call_id, pcmk_rc_str(rc)); } } From 154ae512edfbcaba904ca5cef14a08b9402d9ef5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 14:06:53 -0800 Subject: [PATCH 128/350] Refactor: based: Split IPC callbacks into their own file There are still plenty of IPC-specific pieces in based_callbacks.c. However, this pulls out all of the libqb IPC callbacks, which can be easily isolated. Pulling out the rest of the IPC-specific code will require more refactoring. This is based on commit 24d4455 and similar ones for other daemons. Ref T566 Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_callbacks.c | 166 --------------------------- daemons/based/based_ipc.c | 193 ++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 166 deletions(-) create mode 100644 daemons/based/based_ipc.c diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index 711f3559e80..f814ce8b478 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -34,6 +34,7 @@ pacemaker_based_LDADD += $(CLUSTERLIBS) $(PAM_LIBS) pacemaker_based_SOURCES = pacemaker-based.c \ based_callbacks.c \ based_io.c \ + based_ipc.c \ based_messages.c \ based_notify.c \ based_operation.c \ diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ff5b6536565..1c6108ab096 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -16,7 +16,6 @@ #include // snprintf #include // free #include // u?int*_t, UINT64_C -#include // gid_t, uid_t #include // LOG_INFO, LOG_DEBUG #include // time_t #include // close @@ -45,171 +44,6 @@ #define EXIT_ESCALATION_MS 10000 -qb_ipcs_service_t *ipcs_ro = NULL; -qb_ipcs_service_t *ipcs_rw = NULL; -qb_ipcs_service_t *ipcs_shm = NULL; - -static int32_t -cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) -{ - if (cib_shutdown_flag) { - pcmk__info("Ignoring new IPC client [%d] during shutdown", - pcmk__client_pid(c)); - return -ECONNREFUSED; - } - - if (pcmk__new_client(c, uid, gid) == NULL) { - return -ENOMEM; - } - return 0; -} - -static int32_t -cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privileged) -{ - int rc = pcmk_rc_ok; - uint32_t id = 0; - uint32_t flags = 0; - uint32_t call_options = cib_none; - pcmk__client_t *cib_client = pcmk__find_client(c); - xmlNode *op_request = NULL; - - // Sanity-check, and parse XML from IPC data - CRM_CHECK(cib_client != NULL, return 0); - if (data == NULL) { - pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); - return 0; - } - - pcmk__trace("Dispatching %sprivileged request from client %s", - (privileged? "" : "un"), cib_client->id); - - rc = pcmk__ipc_msg_append(&cib_client->buffer, data); - - if (rc == pcmk_rc_ipc_more) { - /* We haven't read the complete message yet, so just return. */ - return 0; - - } else if (rc == pcmk_rc_ok) { - /* We've read the complete message and there's already a header on - * the front. Pass it off for processing. - */ - op_request = pcmk__client_data2xml(cib_client, &id, &flags); - g_byte_array_free(cib_client->buffer, TRUE); - cib_client->buffer = NULL; - - } else { - /* Some sort of error occurred reassembling the message. All we can - * do is clean up, log an error and return. - */ - pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc)); - - if (cib_client->buffer != NULL) { - g_byte_array_free(cib_client->buffer, TRUE); - cib_client->buffer = NULL; - } - - return 0; - } - - if (op_request) { - int rc = pcmk_rc_ok; - - rc = pcmk__xe_get_flags(op_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 (op_request == NULL) { - pcmk__trace("Invalid message from %p", c); - pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL, - CRM_EX_PROTOCOL); - return 0; - } - - if (pcmk__is_set(call_options, cib_sync_call)) { - CRM_LOG_ASSERT(flags & crm_ipc_client_response); - CRM_LOG_ASSERT(cib_client->request_id == 0); /* This means the client has two synchronous events in-flight */ - cib_client->request_id = id; /* Reply only to the last one */ - } - - if (cib_client->name == NULL) { - const char *value = pcmk__xe_get(op_request, PCMK__XA_CIB_CLIENTNAME); - - if (value == NULL) { - cib_client->name = pcmk__itoa(cib_client->pid); - } else { - cib_client->name = pcmk__str_copy(value); - } - } - - pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id); - pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name); - - CRM_LOG_ASSERT(cib_client->user != NULL); - pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user); - - based_common_callback_worker(id, flags, op_request, cib_client, privileged); - pcmk__xml_free(op_request); - - return 0; -} - -static int32_t -cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size) -{ - return cib_common_callback(c, data, size, true); -} - -static int32_t -cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size) -{ - return cib_common_callback(c, data, size, false); -} - -/* Error code means? */ -static int32_t -cib_ipc_closed(qb_ipcs_connection_t * c) -{ - pcmk__client_t *client = pcmk__find_client(c); - - if (client == NULL) { - return 0; - } - pcmk__trace("Connection %p", c); - pcmk__free_client(client); - return 0; -} - -static void -cib_ipc_destroy(qb_ipcs_connection_t * c) -{ - pcmk__trace("Connection %p", c); - cib_ipc_closed(c); - if (cib_shutdown_flag) { - based_shutdown(0); - } -} - -struct qb_ipcs_service_handlers ipc_ro_callbacks = { - .connection_accept = cib_ipc_accept, - .connection_created = NULL, - .msg_process = cib_ipc_dispatch_ro, - .connection_closed = cib_ipc_closed, - .connection_destroyed = cib_ipc_destroy -}; - -struct qb_ipcs_service_handlers ipc_rw_callbacks = { - .connection_accept = cib_ipc_accept, - .connection_created = NULL, - .msg_process = cib_ipc_dispatch_rw, - .connection_closed = cib_ipc_closed, - .connection_destroyed = cib_ipc_destroy -}; - /*! * \internal * \brief Create reply XML for a CIB request diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c new file mode 100644 index 00000000000..191f9004122 --- /dev/null +++ b/daemons/based/based_ipc.c @@ -0,0 +1,193 @@ +/* + * Copyright 2004-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include + +#include // ECONNREFUSED, ENOMEM +#include +#include // size_t +#include // int32_t, uint32_t +#include // uid_t, gid_t + +#include // g_byte_array_free(), TRUE +#include // xmlNode +#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_* + +#include "pacemaker-based.h" + +qb_ipcs_service_t *ipcs_ro = NULL; +qb_ipcs_service_t *ipcs_rw = NULL; +qb_ipcs_service_t *ipcs_shm = NULL; + +static int32_t +cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) +{ + if (cib_shutdown_flag) { + pcmk__info("Ignoring new IPC client [%d] during shutdown", + pcmk__client_pid(c)); + return -ECONNREFUSED; + } + + if (pcmk__new_client(c, uid, gid) == NULL) { + return -ENOMEM; + } + return 0; +} + +static int32_t +cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privileged) +{ + int rc = pcmk_rc_ok; + uint32_t id = 0; + uint32_t flags = 0; + uint32_t call_options = cib_none; + pcmk__client_t *cib_client = pcmk__find_client(c); + xmlNode *op_request = NULL; + + // Sanity-check, and parse XML from IPC data + CRM_CHECK(cib_client != NULL, return 0); + if (data == NULL) { + pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); + return 0; + } + + pcmk__trace("Dispatching %sprivileged request from client %s", + (privileged? "" : "un"), cib_client->id); + + rc = pcmk__ipc_msg_append(&cib_client->buffer, data); + + if (rc == pcmk_rc_ipc_more) { + /* We haven't read the complete message yet, so just return. */ + return 0; + + } else if (rc == pcmk_rc_ok) { + /* We've read the complete message and there's already a header on + * the front. Pass it off for processing. + */ + op_request = pcmk__client_data2xml(cib_client, &id, &flags); + g_byte_array_free(cib_client->buffer, TRUE); + cib_client->buffer = NULL; + + } else { + /* Some sort of error occurred reassembling the message. All we can + * do is clean up, log an error and return. + */ + pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc)); + + if (cib_client->buffer != NULL) { + g_byte_array_free(cib_client->buffer, TRUE); + cib_client->buffer = NULL; + } + + return 0; + } + + if (op_request) { + int rc = pcmk_rc_ok; + + rc = pcmk__xe_get_flags(op_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 (op_request == NULL) { + pcmk__trace("Invalid message from %p", c); + pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL, + CRM_EX_PROTOCOL); + return 0; + } + + if (pcmk__is_set(call_options, cib_sync_call)) { + CRM_LOG_ASSERT(flags & crm_ipc_client_response); + CRM_LOG_ASSERT(cib_client->request_id == 0); /* This means the client has two synchronous events in-flight */ + cib_client->request_id = id; /* Reply only to the last one */ + } + + if (cib_client->name == NULL) { + const char *value = pcmk__xe_get(op_request, PCMK__XA_CIB_CLIENTNAME); + + if (value == NULL) { + cib_client->name = pcmk__itoa(cib_client->pid); + } else { + cib_client->name = pcmk__str_copy(value); + } + } + + pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id); + pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name); + + CRM_LOG_ASSERT(cib_client->user != NULL); + pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user); + + based_common_callback_worker(id, flags, op_request, cib_client, privileged); + pcmk__xml_free(op_request); + + return 0; +} + +static int32_t +cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size) +{ + return cib_common_callback(c, data, size, true); +} + +static int32_t +cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size) +{ + return cib_common_callback(c, data, size, false); +} + +/* Error code means? */ +static int32_t +cib_ipc_closed(qb_ipcs_connection_t * c) +{ + pcmk__client_t *client = pcmk__find_client(c); + + if (client == NULL) { + return 0; + } + pcmk__trace("Connection %p", c); + pcmk__free_client(client); + return 0; +} + +static void +cib_ipc_destroy(qb_ipcs_connection_t * c) +{ + pcmk__trace("Connection %p", c); + cib_ipc_closed(c); + if (cib_shutdown_flag) { + based_shutdown(0); + } +} + +struct qb_ipcs_service_handlers ipc_ro_callbacks = { + .connection_accept = cib_ipc_accept, + .connection_created = NULL, + .msg_process = cib_ipc_dispatch_ro, + .connection_closed = cib_ipc_closed, + .connection_destroyed = cib_ipc_destroy +}; + +struct qb_ipcs_service_handlers ipc_rw_callbacks = { + .connection_accept = cib_ipc_accept, + .connection_created = NULL, + .msg_process = cib_ipc_dispatch_rw, + .connection_closed = cib_ipc_closed, + .connection_destroyed = cib_ipc_destroy +}; From b962c00bdcb3dc53fccef976adc72da5a86acafd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 14:13:59 -0800 Subject: [PATCH 129/350] Refactor: based: Rename functions in based_ipc.c Also drop the unused size argument from dispatch_common(). This is based on commit 4f8e790 and similar ones for other daemons. Ref T566 Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 191f9004122..a01fe22892f 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -32,7 +32,7 @@ qb_ipcs_service_t *ipcs_rw = NULL; qb_ipcs_service_t *ipcs_shm = NULL; static int32_t -cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) +based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { if (cib_shutdown_flag) { pcmk__info("Ignoring new IPC client [%d] during shutdown", @@ -47,7 +47,7 @@ cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid) } static int32_t -cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privileged) +dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) { int rc = pcmk_rc_ok; uint32_t id = 0; @@ -141,20 +141,20 @@ cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, bool privi } static int32_t -cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size) +based_ipc_dispatch_rw(qb_ipcs_connection_t *c, void *data, size_t size) { - return cib_common_callback(c, data, size, true); + return dispatch_common(c, data, true); } static int32_t -cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size) +based_ipc_dispatch_ro(qb_ipcs_connection_t *c, void *data, size_t size) { - return cib_common_callback(c, data, size, false); + return dispatch_common(c, data, false); } /* Error code means? */ static int32_t -cib_ipc_closed(qb_ipcs_connection_t * c) +based_ipc_closed(qb_ipcs_connection_t *c) { pcmk__client_t *client = pcmk__find_client(c); @@ -167,27 +167,27 @@ cib_ipc_closed(qb_ipcs_connection_t * c) } static void -cib_ipc_destroy(qb_ipcs_connection_t * c) +based_ipc_destroy(qb_ipcs_connection_t *c) { pcmk__trace("Connection %p", c); - cib_ipc_closed(c); + based_ipc_closed(c); if (cib_shutdown_flag) { based_shutdown(0); } } struct qb_ipcs_service_handlers ipc_ro_callbacks = { - .connection_accept = cib_ipc_accept, + .connection_accept = based_ipc_accept, .connection_created = NULL, - .msg_process = cib_ipc_dispatch_ro, - .connection_closed = cib_ipc_closed, - .connection_destroyed = cib_ipc_destroy + .msg_process = based_ipc_dispatch_ro, + .connection_closed = based_ipc_closed, + .connection_destroyed = based_ipc_destroy, }; struct qb_ipcs_service_handlers ipc_rw_callbacks = { - .connection_accept = cib_ipc_accept, + .connection_accept = based_ipc_accept, .connection_created = NULL, - .msg_process = cib_ipc_dispatch_rw, - .connection_closed = cib_ipc_closed, - .connection_destroyed = cib_ipc_destroy + .msg_process = based_ipc_dispatch_rw, + .connection_closed = based_ipc_closed, + .connection_destroyed = based_ipc_destroy, }; From d4228e4101fd35949e9969ca7e35f48a50b6c29f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 14:48:03 -0800 Subject: [PATCH 130/350] Refactor: based: Standardize functions in based_ipc.c * Add Doxygen. * Standardize log messages with what's happening in the same functions in other daemons. * Standardize variable names (cib_client becomes client, op_request becomes msg). * Un-indent "msg != NULL" block after moving the "msg == NULL" block upward and standardizing it with other daemons. * Make sure return values and comments match what libqb documents. The "Dispatching (un)privileged request" message is out of alignment with the common code in other daemons. I've left it for now, because the alternative is to fetch the client in based_ipc_dispatch_rw() and based_ipc_dispatch_ro(), log the message, and then fetch the client again in dispatch_common() (or pass it as an argument, which is also non-standard). Also add a TODO for based_ipc_destroy(). The other daemons have comments in their DAEMON_ipc_closed() doxygen that say: "We handle a destroyed connection the same as a closed one, but we need a separate handler because the return type is different." Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 151 +++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 42 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index a01fe22892f..c351a0f7a96 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -31,6 +31,16 @@ qb_ipcs_service_t *ipcs_ro = NULL; qb_ipcs_service_t *ipcs_rw = NULL; qb_ipcs_service_t *ipcs_shm = NULL; +/*! + * \internal + * \brief Accept a new client IPC connection + * + * \param[in,out] c New connection + * \param[in] uid Client user id + * \param[in] gid Client group id + * + * \return 0 on success, \c -errno otherwise + */ static int32_t based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) { @@ -40,12 +50,26 @@ based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) return -ECONNREFUSED; } + pcmk__trace("New client connection %p", c); if (pcmk__new_client(c, uid, gid) == NULL) { return -ENOMEM; } return 0; } +/*! + * \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 + * + * \return 0 in all cases + */ static int32_t dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) { @@ -53,20 +77,20 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) uint32_t id = 0; uint32_t flags = 0; uint32_t call_options = cib_none; - pcmk__client_t *cib_client = pcmk__find_client(c); - xmlNode *op_request = NULL; + xmlNode *msg = NULL; + pcmk__client_t *client = pcmk__find_client(c); // Sanity-check, and parse XML from IPC data - CRM_CHECK(cib_client != NULL, return 0); + CRM_CHECK(client != NULL, return 0); if (data == NULL) { pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c)); return 0; } pcmk__trace("Dispatching %sprivileged request from client %s", - (privileged? "" : "un"), cib_client->id); + (privileged? "" : "un"), client->id); - rc = pcmk__ipc_msg_append(&cib_client->buffer, data); + 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. */ @@ -76,9 +100,9 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) /* We've read the complete message and there's already a header on * the front. Pass it off for processing. */ - op_request = pcmk__client_data2xml(cib_client, &id, &flags); - g_byte_array_free(cib_client->buffer, TRUE); - cib_client->buffer = NULL; + msg = pcmk__client_data2xml(client, &id, &flags); + g_byte_array_free(client->buffer, TRUE); + client->buffer = NULL; } else { /* Some sort of error occurred reassembling the message. All we can @@ -86,91 +110,134 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) */ pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc)); - if (cib_client->buffer != NULL) { - g_byte_array_free(cib_client->buffer, TRUE); - cib_client->buffer = NULL; + if (client->buffer != NULL) { + g_byte_array_free(client->buffer, TRUE); + client->buffer = NULL; } return 0; } - if (op_request) { - int rc = pcmk_rc_ok; - - rc = pcmk__xe_get_flags(op_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 (op_request == NULL) { - pcmk__trace("Invalid message from %p", c); - pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL, + if (msg == NULL) { + pcmk__debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c)); + pcmk__ipc_send_ack(client, id, flags, PCMK__XE_NACK, NULL, CRM_EX_PROTOCOL); return 0; } + 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)); + } + if (pcmk__is_set(call_options, cib_sync_call)) { CRM_LOG_ASSERT(flags & crm_ipc_client_response); - CRM_LOG_ASSERT(cib_client->request_id == 0); /* This means the client has two synchronous events in-flight */ - cib_client->request_id = id; /* Reply only to the last one */ + + // If false, the client has two synchronous events in flight + CRM_LOG_ASSERT(client->request_id == 0); + + // Reply only to the last one + client->request_id = id; } - if (cib_client->name == NULL) { - const char *value = pcmk__xe_get(op_request, PCMK__XA_CIB_CLIENTNAME); + if (client->name == NULL) { + const char *value = pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME); if (value == NULL) { - cib_client->name = pcmk__itoa(cib_client->pid); + client->name = pcmk__itoa(client->pid); } else { - cib_client->name = pcmk__str_copy(value); + client->name = pcmk__str_copy(value); } } - pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id); - pcmk__xe_set(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); - CRM_LOG_ASSERT(cib_client->user != NULL); - pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user); + CRM_LOG_ASSERT(client->user != NULL); + pcmk__update_acl_user(msg, PCMK__XA_CIB_USER, client->user); - based_common_callback_worker(id, flags, op_request, cib_client, privileged); - pcmk__xml_free(op_request); + based_common_callback_worker(id, flags, msg, client, privileged); + pcmk__xml_free(msg); return 0; } +/*! + * \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 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); } -/* Error code means? */ +/*! + * \internal + * \brief Destroy a client IPC connection + * + * \param[in] c Connection to destroy + * + * \return 0 (do not re-run this callback) + */ static int32_t based_ipc_closed(qb_ipcs_connection_t *c) { pcmk__client_t *client = pcmk__find_client(c); if (client == NULL) { - return 0; + pcmk__trace("Ignoring request to clean up unknown connection %p", c); + } else { + pcmk__trace("Cleaning up closed client connection %p", c); + pcmk__free_client(client); } - pcmk__trace("Connection %p", c); - pcmk__free_client(client); + return 0; } +/*! + * \internal + * \brief Destroy a client IPC connection + * + * \param[in] c Connection to destroy + */ static void based_ipc_destroy(qb_ipcs_connection_t *c) { - pcmk__trace("Connection %p", c); + pcmk__trace("Destroying client connection %p", c); based_ipc_closed(c); + + /* Shut down if this was the last client to leave. + * + * @TODO Is it correct to do this for destroy but not for closed? Other + * daemons handle closed and destroyed connections in the same way. + */ if (cib_shutdown_flag) { based_shutdown(0); } From aaa9450fefa7323e80711ae958aa159b42370f6b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 17:11:19 -0800 Subject: [PATCH 131/350] Feature: libcib: {add,del}_notify_callback support only cib_diff_notify Now, if cib_api_operations_t:add_notify_callback or cib_api_operations_t:del_notify_callback is called with the event argument set to any value other than "cib_diff_notify", it will result in an error. Previously, the following were also treated as valid: * "cib_pre_notify" * "cib_post_notify" * "cib_update_confirmation" However, at least as far back as Pacemaker 2.0.0, Pacemaker has not sent any notifications for those events. So if a client added or deleted a notify callback for them, the addition or deletion would succeed, but the callback would never run. Likewise, at least as far back as Pacemaker 2.0.0, no internal clients have added or deleted callbacks for those events. So this change should not risk breaking compatibility with older internal clients. External clients are probably not registering CIB callbacks. But in theory they could be, since these functions are public API. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +------------- daemons/based/based_notify.c | 24 ++---------------------- daemons/based/pacemaker-based.h | 3 --- include/crm/common/options_internal.h | 3 --- 4 files changed, 3 insertions(+), 41 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1c6108ab096..02255512be7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -194,19 +194,7 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__debug("Setting %s callbacks %s for client %s", type, (on_off? "on" : "off"), pcmk__client_name(cib_client)); - if (pcmk__str_eq(type, PCMK__VALUE_CIB_POST_NOTIFY, pcmk__str_none)) { - bit = cib_notify_post; - - } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_PRE_NOTIFY, - pcmk__str_none)) { - bit = cib_notify_pre; - - } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_UPDATE_CONFIRMATION, - pcmk__str_none)) { - bit = cib_notify_confirm; - - } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, - pcmk__str_none)) { + if (pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { bit = cib_notify_diff; } else { diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 83849415174..e25399d7828 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -37,7 +37,6 @@ static void cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) { const char *type = NULL; - bool do_send = false; int rc = pcmk_rc_ok; pcmk__client_t *client = value; @@ -51,28 +50,9 @@ cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) type = pcmk__xe_get(update->msg, PCMK__XA_SUBT); CRM_LOG_ASSERT(type != NULL); - if (pcmk__is_set(client->flags, cib_notify_diff) - && pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { + if (!pcmk__is_set(client->flags, cib_notify_diff) + || !pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { - do_send = true; - - } else if (pcmk__is_set(client->flags, cib_notify_confirm) - && pcmk__str_eq(type, PCMK__VALUE_CIB_UPDATE_CONFIRMATION, - pcmk__str_none)) { - do_send = true; - - } else if (pcmk__is_set(client->flags, cib_notify_pre) - && pcmk__str_eq(type, PCMK__VALUE_CIB_PRE_NOTIFY, - pcmk__str_none)) { - do_send = true; - - } else if (pcmk__is_set(client->flags, cib_notify_post) - && pcmk__str_eq(type, PCMK__VALUE_CIB_POST_NOTIFY, - pcmk__str_none)) { - do_send = true; - } - - if (!do_send) { return; } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index c931bf25d94..82ba60451ab 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -29,9 +29,6 @@ // CIB-specific client flags enum cib_client_flags { // Notifications - cib_notify_pre = (UINT64_C(1) << 0), - cib_notify_post = (UINT64_C(1) << 1), - cib_notify_confirm = (UINT64_C(1) << 3), cib_notify_diff = (UINT64_C(1) << 4), }; diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h index 1cf836e7feb..9d4247c6349 100644 --- a/include/crm/common/options_internal.h +++ b/include/crm/common/options_internal.h @@ -212,9 +212,6 @@ bool pcmk__valid_fencing_watchdog_timeout(const char *value); #define PCMK__VALUE_CIB "cib" #define PCMK__VALUE_CIB_DIFF_NOTIFY "cib_diff_notify" #define PCMK__VALUE_CIB_NOTIFY "cib_notify" -#define PCMK__VALUE_CIB_POST_NOTIFY "cib_post_notify" -#define PCMK__VALUE_CIB_PRE_NOTIFY "cib_pre_notify" -#define PCMK__VALUE_CIB_UPDATE_CONFIRMATION "cib_update_confirmation" #define PCMK__VALUE_CLUSTER "cluster" #define PCMK__VALUE_CRMD "crmd" #define PCMK__VALUE_EN "en" From 879333f032948d739990d4734fb7d2f1c3f5fce2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 16:59:16 -0800 Subject: [PATCH 132/350] Refactor: based: Rename cib_client_flags There's only one flag now, but we still need this instead of a boolean, since we're setting it in pcmk__client_t:flags. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_notify.c | 2 +- daemons/based/based_notify.h | 11 +++++++++++ daemons/based/pacemaker-based.h | 6 ------ 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 02255512be7..7bcff5e5f1d 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -195,7 +195,7 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, (on_off? "on" : "off"), pcmk__client_name(cib_client)); if (pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { - bit = cib_notify_diff; + bit = based_nf_diff; } else { status = CRM_EX_INVALID_PARAM; diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index e25399d7828..8d6a84b6d50 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -50,7 +50,7 @@ cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) type = pcmk__xe_get(update->msg, PCMK__XA_SUBT); CRM_LOG_ASSERT(type != NULL); - if (!pcmk__is_set(client->flags, cib_notify_diff) + if (!pcmk__is_set(client->flags, based_nf_diff) || !pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { return; diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index 0943d8d9dd3..d1cab2026ab 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -12,6 +12,17 @@ #include // xmlNode +/*! + * \internal + * \brief Flags for CIB manager client notification types + * + * These are used for setting the \c flags field of a \c pcmk__client_t. + */ +enum based_notify_flags { + //! Notify when the CIB changes + based_nf_diff = (UINT64_C(1) << 0), +}; + void based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *update, xmlNode *diff); diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 82ba60451ab..1e8fe9303fd 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -26,12 +26,6 @@ #define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->priv->node_name) -// CIB-specific client flags -enum cib_client_flags { - // Notifications - cib_notify_diff = (UINT64_C(1) << 4), -}; - extern GHashTable *config_hash; extern GMainLoop *mainloop; From aa8a47298759dfecdd38dc78790f1c2ad648b2cc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 17:48:30 -0800 Subject: [PATCH 133/350] Refactor: based: New based_parse_notify_flag() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 24 ++++++++++++------------ daemons/based/based_notify.c | 18 ++++++++++++++++++ daemons/based/based_notify.h | 5 +++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 7bcff5e5f1d..c05f06a24b8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -186,27 +186,27 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, /* Update the notify filters for this client */ int on_off = 0; crm_exit_t status = CRM_EX_OK; - uint64_t bit = UINT64_C(0); const char *type = pcmk__xe_get(op_request, PCMK__XA_CIB_NOTIFY_TYPE); + enum based_notify_flags notify_flag = based_parse_notify_flag(type); pcmk__xe_get_int(op_request, PCMK__XA_CIB_NOTIFY_ACTIVATE, &on_off); pcmk__debug("Setting %s callbacks %s for client %s", type, (on_off? "on" : "off"), pcmk__client_name(cib_client)); - if (pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { - bit = based_nf_diff; - - } else { + if (notify_flag == based_nf_none) { + /* Don't log an assertion. Technically this could come from an + * external client. + */ + pcmk__err("Ignoring unknown CIB manager event notification type %s", + type); status = CRM_EX_INVALID_PARAM; - } - if (bit != 0) { - if (on_off) { - pcmk__set_client_flags(cib_client, bit); - } else { - pcmk__clear_client_flags(cib_client, bit); - } + } else if (on_off) { + pcmk__set_client_flags(cib_client, notify_flag); + + } else { + pcmk__clear_client_flags(cib_client, notify_flag); } pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_ACK, NULL, status); diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 8d6a84b6d50..d3cbb48b405 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -33,6 +33,24 @@ struct cib_notification_s { int32_t iov_size; }; +/*! + * \internal + * \brief Parse a CIB manager client notification type string to a flag + * + * \param[in] text Notification type string + * + * \return Flag corresponding to \p text, or \c based_nf_none if none exists + */ +enum based_notify_flags +based_parse_notify_flag(const char *text) +{ + if (pcmk__str_eq(text, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { + return based_nf_diff; + } + + return based_nf_none; +} + static void cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) { diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index d1cab2026ab..3d5dcee1c4e 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -19,10 +19,15 @@ * These are used for setting the \c flags field of a \c pcmk__client_t. */ enum based_notify_flags { + //! This flag has no effect + based_nf_none = UINT64_C(0), + //! Notify when the CIB changes based_nf_diff = (UINT64_C(1) << 0), }; +enum based_notify_flags based_parse_notify_flag(const char *text); + void based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *update, xmlNode *diff); From 2b36047d67db0a26bbb1c15bd8c2690331394e58 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 18:10:30 -0800 Subject: [PATCH 134/350] Refactor: libcrmcommon: Correct the Doxygen for pcmk__xe_attr_is_true() And simplify the body. The value doesn't have to be "true" (PCMK_VALUE_TRUE); it can be anything that pcmk__parse_bool() parses to true. Signed-off-by: Reid Wahl --- lib/common/xml_element.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/common/xml_element.c b/lib/common/xml_element.c index 6863471c181..7bd3f70d477 100644 --- a/lib/common/xml_element.c +++ b/lib/common/xml_element.c @@ -1530,17 +1530,17 @@ pcmk__xe_set_bool(xmlNode *xml, const char *attr, bool value) * \param[in] node XML node to get attribute from * \param[in] name XML attribute to get * - * \return True if the given \p name is an attribute on \p node and has - * the value \c PCMK_VALUE_TRUE, False in all other cases + * \return \c true if the given \p name is an attribute on \p node whose value + * parses to \c true (see \c pcmk__parse_bool()), or \c false otherwise */ bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name) { bool value = false; - int rc; - rc = pcmk__xe_get_bool(node, name, &value); - return rc == pcmk_rc_ok && value == true; + // value remains false on error, so don't check return value + pcmk__xe_get_bool(node, name, &value); + return value; } // Deprecated functions kept only for backward API compatibility From a465327ff44a83b53f5a2287c4593241fc3ebf6d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 16:18:46 -0800 Subject: [PATCH 135/350] Refactor: based: Functionize updating client notify flags Also move enum based_notify_flags and based_parse_notify_flags into based_notify.c. No other file needs them now. Technically we don't need based_nf_none or based_parse_notify_flags() since we only have one meaningful flag. Those seemed clearer than checking directly for PCMK__VALUE_CIB_DIFF_NOTIFY and setting the based_nf_diff flag directly in based_update_notify_flags(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 29 ++++------------- daemons/based/based_notify.c | 57 ++++++++++++++++++++++++++++++++- daemons/based/based_notify.h | 18 ++--------- 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c05f06a24b8..9b6d6150d10 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -181,34 +181,19 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__xml_free(ack); } return; + } - } else if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { - /* Update the notify filters for this client */ - int on_off = 0; + if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { crm_exit_t status = CRM_EX_OK; - const char *type = pcmk__xe_get(op_request, PCMK__XA_CIB_NOTIFY_TYPE); - enum based_notify_flags notify_flag = based_parse_notify_flag(type); - - pcmk__xe_get_int(op_request, PCMK__XA_CIB_NOTIFY_ACTIVATE, &on_off); - pcmk__debug("Setting %s callbacks %s for client %s", type, - (on_off? "on" : "off"), pcmk__client_name(cib_client)); - - if (notify_flag == based_nf_none) { - /* Don't log an assertion. Technically this could come from an - * external client. - */ - pcmk__err("Ignoring unknown CIB manager event notification type %s", - type); + rc = based_update_notify_flags(op_request, cib_client); + if (rc != pcmk_rc_ok) { status = CRM_EX_INVALID_PARAM; - - } else if (on_off) { - pcmk__set_client_flags(cib_client, notify_flag); - - } else { - pcmk__clear_client_flags(cib_client, notify_flag); } + /* This is a no-op if crm_ipc_client_response is not set, so it's OK for + * remote clients. This could be much clearer, however. + */ pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_ACK, NULL, status); return; } diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index d3cbb48b405..8620bab953a 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -33,6 +33,20 @@ struct cib_notification_s { int32_t iov_size; }; +/*! + * \internal + * \brief Flags for CIB manager client notification types + * + * These are used for setting the \c flags field of a \c pcmk__client_t. + */ +enum based_notify_flags { + //! This flag has no effect + based_nf_none = UINT64_C(0), + + //! Notify when the CIB changes + based_nf_diff = (UINT64_C(1) << 0), +}; + /*! * \internal * \brief Parse a CIB manager client notification type string to a flag @@ -41,7 +55,7 @@ struct cib_notification_s { * * \return Flag corresponding to \p text, or \c based_nf_none if none exists */ -enum based_notify_flags +static enum based_notify_flags based_parse_notify_flag(const char *text) { if (pcmk__str_eq(text, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) { @@ -51,6 +65,47 @@ based_parse_notify_flag(const char *text) return based_nf_none; } +/*! + * \internal + * \brief Set or clear a notify flag in a client based on request XML + * + * \param[in] xml Request XML + * \param[in,out] client Client + * + * \return Standard Pacemaker return code + */ +int +based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client) +{ + int rc = pcmk_rc_ok; + bool enable = false; + enum based_notify_flags notify_flag = based_nf_none; + const char *type = pcmk__xe_get(xml, PCMK__XA_CIB_NOTIFY_TYPE); + + rc = pcmk__xe_get_bool(xml, PCMK__XA_CIB_NOTIFY_ACTIVATE, &enable); + if (rc != pcmk_rc_ok) { + return pcmk_rc_bad_input; + } + + notify_flag = based_parse_notify_flag(type); + if (notify_flag == based_nf_none) { + return pcmk_rc_bad_input; + } + + if (enable) { + pcmk__debug("Enabling %s callbacks for client %s", type, + pcmk__client_name(client)); + pcmk__set_client_flags(client, notify_flag); + + } else { + pcmk__debug("Disabling %s callbacks for client %s", type, + pcmk__client_name(client)); + pcmk__clear_client_flags(client, notify_flag); + } + + return pcmk_rc_ok; +} + static void cib_notify_send_one(gpointer key, gpointer value, gpointer user_data) { diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index 3d5dcee1c4e..1735cac54d9 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -1,5 +1,5 @@ /* - * Copyright 2025 the Pacemaker project contributors + * Copyright 2025-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -12,21 +12,9 @@ #include // xmlNode -/*! - * \internal - * \brief Flags for CIB manager client notification types - * - * These are used for setting the \c flags field of a \c pcmk__client_t. - */ -enum based_notify_flags { - //! This flag has no effect - based_nf_none = UINT64_C(0), - - //! Notify when the CIB changes - based_nf_diff = (UINT64_C(1) << 0), -}; +#include // pcmk__client_t -enum based_notify_flags based_parse_notify_flag(const char *text); +int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); void based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, From b29d9802f84dbb1f46f2945279bade6d174f0dfe Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 19:20:51 -0800 Subject: [PATCH 136/350] Refactor: based: Add client type guards in based_common_callback_worker These don't affect behavior, but they make the code clearer. We should not be calling IPC functions for remote clients. I spent longer than I'd like to admit, trying to figure out how the notify update ack worked for remote clients, before I noticed the crm_ipc_client_response guard in pcmk__ipc_create_ack_as(). Only cib_native.c sets that flag for CIB clients. It's never set for remote clients as far as I can tell (which makes sense). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 9b6d6150d10..a500d36dbcf 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -171,7 +171,9 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, } if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { - if (flags & crm_ipc_client_response) { + if ((PCMK__CLIENT_TYPE(cib_client) == pcmk__client_ipc) + && pcmk__is_set(flags, crm_ipc_client_response)) { + xmlNode *ack = pcmk__xe_create(NULL, __func__); pcmk__xe_set(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER); @@ -187,13 +189,15 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, crm_exit_t status = CRM_EX_OK; rc = based_update_notify_flags(op_request, cib_client); + + if (PCMK__CLIENT_TYPE(cib_client) != pcmk__client_ipc) { + return; + } + if (rc != pcmk_rc_ok) { status = CRM_EX_INVALID_PARAM; } - /* This is a no-op if crm_ipc_client_response is not set, so it's OK for - * remote clients. This could be much clearer, however. - */ pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_ACK, NULL, status); return; } From 5bfc56d1f3cd3f1ce794a1b9a371b71fdac2b4f8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 19:23:58 -0800 Subject: [PATCH 137/350] Refactor: based: Rename cib_client to client in based_callbacks.c For consistency with other IPC/message-related code. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 28 ++++++++++++++-------------- daemons/based/based_callbacks.h | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a500d36dbcf..1847edf5247 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -151,7 +151,7 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, void based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, - pcmk__client_t *cib_client, bool privileged) + pcmk__client_t *client, bool privileged) { const char *op = pcmk__xe_get(op_request, PCMK__XA_CIB_OP); uint32_t call_options = cib_none; @@ -171,15 +171,15 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, } if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { - if ((PCMK__CLIENT_TYPE(cib_client) == pcmk__client_ipc) + if ((PCMK__CLIENT_TYPE(client) == pcmk__client_ipc) && pcmk__is_set(flags, crm_ipc_client_response)) { xmlNode *ack = pcmk__xe_create(NULL, __func__); pcmk__xe_set(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER); - pcmk__xe_set(ack, PCMK__XA_CIB_CLIENTID, cib_client->id); - pcmk__ipc_send_xml(cib_client, id, ack, flags); - cib_client->request_id = 0; + pcmk__xe_set(ack, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__ipc_send_xml(client, id, ack, flags); + client->request_id = 0; pcmk__xml_free(ack); } return; @@ -188,9 +188,9 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { crm_exit_t status = CRM_EX_OK; - rc = based_update_notify_flags(op_request, cib_client); + rc = based_update_notify_flags(op_request, client); - if (PCMK__CLIENT_TYPE(cib_client) != pcmk__client_ipc) { + if (PCMK__CLIENT_TYPE(client) != pcmk__client_ipc) { return; } @@ -198,11 +198,11 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, status = CRM_EX_INVALID_PARAM; } - pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_ACK, NULL, status); + pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL, status); return; } - based_process_request(op_request, privileged, cib_client); + based_process_request(op_request, privileged, client); } static uint64_t ping_seq = 0; @@ -326,7 +326,7 @@ process_ping_reply(xmlNode *reply) } static void -parse_local_options(const pcmk__client_t *cib_client, +parse_local_options(const pcmk__client_t *client, const cib__operation_t *operation, const char *host, const char *op, bool *local_notify, bool *needs_reply, bool *process, bool *needs_forward) @@ -345,7 +345,7 @@ parse_local_options(const pcmk__client_t *cib_client, * NULL or OUR_NODENAME. */ pcmk__trace("Processing always-local %s op from client %s", op, - pcmk__client_name(cib_client)); + pcmk__client_name(client)); if (!pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei|pcmk__str_null_matches)) { @@ -368,18 +368,18 @@ parse_local_options(const pcmk__client_t *cib_client, *needs_forward = true; pcmk__trace("%s op from %s needs to be forwarded to %s", op, - pcmk__client_name(cib_client), pcmk__s(host, "all nodes")); + pcmk__client_name(client), pcmk__s(host, "all nodes")); return; } if (stand_alone) { pcmk__trace("Processing %s op from client %s (stand-alone)", op, - pcmk__client_name(cib_client)); + pcmk__client_name(client)); } else { pcmk__trace("Processing %saddressed %s op from client %s", ((host != NULL)? "locally " : "un"), op, - pcmk__client_name(cib_client)); + pcmk__client_name(client)); } } diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 9c67a84f2e1..d4203d25289 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -27,8 +27,8 @@ extern qb_ipcs_service_t *ipcs_shm; void based_peer_callback(xmlNode *msg, void *private_data); void based_common_callback_worker(uint32_t id, uint32_t flags, - xmlNode *op_request, - pcmk__client_t *cib_client, bool privileged); + xmlNode *op_request, pcmk__client_t *client, + bool privileged); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_shutdown(int nsig); From edd331e02d179fa1d028f8caf91f2323c065d35b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 20:34:41 -0800 Subject: [PATCH 138/350] Refactor: based: Clarify that CRM_OP_REGISTER response is only for IPC ...clients. CIB IPC clients send an initial request with PCMK__XA_CIB_OP set to CRM_OP_REGISTER (in cib_native_signon()). **For some reason**, CIB remote clients don't do that. Instead, they send an initial request with PCMK_XA_OP (a different attribute) set to "authenticate" (a different value). The PCMK__XA_CIB_OP attribute is not set at all. If the CIB manager receives a request with PCMK__XA_CIB_OP set to CRM_OP_REGISTER, then it sends an ack in based_common_callback_worker(). As discussed above, that will happen only for an IPC client. It will never happen for a remote client. The CIB manager sends a CRM_OP_REGISTER ack to a CIB remote client in cib_remote_msg(), after authentication. It would be nice to isolate the IPC code from the remote code better. This small change highlights that this block is needed only for IPC clients. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1847edf5247..1ec63a820fc 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -170,10 +170,10 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, return; } - if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { - if ((PCMK__CLIENT_TYPE(client) == pcmk__client_ipc) - && pcmk__is_set(flags, crm_ipc_client_response)) { + if ((PCMK__CLIENT_TYPE(client) == pcmk__client_ipc) + && pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { + if (pcmk__is_set(flags, crm_ipc_client_response)) { xmlNode *ack = pcmk__xe_create(NULL, __func__); pcmk__xe_set(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER); From dcd42a067de0407829a34c1ed8ff37cb2004a111 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 21:25:36 -0800 Subject: [PATCH 139/350] Refactor: based: Improve a log message for IPC call options Log the client name, and also log the bad request at info level. Signed-off-by: Reid Wahl --- daemons/based/based_ipc.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index c351a0f7a96..79ac74fcd6a 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -125,9 +125,21 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) return 0; } + 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: %s", pcmk_rc_str(rc)); + 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"); } if (pcmk__is_set(call_options, cib_sync_call)) { @@ -140,16 +152,6 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) client->request_id = id; } - 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); - } - } - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTID, client->id); pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, client->name); From e97eef0171de6efd95cdab7eca15a8ea44762882 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 21:24:43 -0800 Subject: [PATCH 140/350] Refactor: based: Move cib_transaction checks out of based_callbacks.c ..and into based_ipc.c and based_remote.c. This introduces some duplication, but we want to disentangle the IPC code from the remote code. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 22 +--------------------- daemons/based/based_ipc.c | 13 +++++++++++++ daemons/based/based_remote.c | 24 +++++++++++++++++++++--- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1ec63a820fc..3ffdabd7fba 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -154,21 +154,6 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *client, bool privileged) { const char *op = pcmk__xe_get(op_request, PCMK__XA_CIB_OP); - uint32_t call_options = cib_none; - int rc = pcmk_rc_ok; - - rc = pcmk__xe_get_flags(op_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)); - } - - /* Requests with cib_transaction set should not be sent to based directly - * (outside of a commit-transaction request) - */ - if (pcmk__is_set(call_options, cib_transaction)) { - return; - } if ((PCMK__CLIENT_TYPE(client) == pcmk__client_ipc) && pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { @@ -187,12 +172,7 @@ based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { crm_exit_t status = CRM_EX_OK; - - rc = based_update_notify_flags(op_request, client); - - if (PCMK__CLIENT_TYPE(client) != pcmk__client_ipc) { - return; - } + int rc = based_update_notify_flags(op_request, client); if (rc != pcmk_rc_ok) { status = CRM_EX_INVALID_PARAM; diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 79ac74fcd6a..1673b295a97 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -142,6 +142,17 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) pcmk__log_xml_info(msg, "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 IPC client %s with " + "cib_transaction flag set outside of any transaction", + client->name); + pcmk__log_xml_info(msg, "no-transaction"); + return 0; + } + if (pcmk__is_set(call_options, cib_sync_call)) { CRM_LOG_ASSERT(flags & crm_ipc_client_response); @@ -158,6 +169,8 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) CRM_LOG_ASSERT(client->user != NULL); pcmk__update_acl_user(msg, PCMK__XA_CIB_USER, client->user); + pcmk__log_xml_trace(msg, "ipc-request"); + based_common_callback_worker(id, flags, msg, client, privileged); pcmk__xml_free(msg); diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index f9781c61e43..15852a11e8d 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -313,6 +313,9 @@ cib_remote_auth(xmlNode * login) static void cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) { + int rc = pcmk_rc_ok; + uint32_t call_options = cib_none; + if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) { pcmk__log_xml_trace(command, "bad"); return; @@ -340,11 +343,26 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) free(call_uuid); } - if (pcmk__xe_get(command, PCMK__XA_CIB_CALLOPT) == NULL) { - pcmk__xe_set_int(command, PCMK__XA_CIB_CALLOPT, 0); + 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__log_xml_trace(command, "Remote command: "); + pcmk__log_xml_trace(command, "remote-request"); based_common_callback_worker(0, 0, command, client, true); } From 4b45d2e65956b97995894e52d4a03c5b1fd1d9f9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 21:56:26 -0800 Subject: [PATCH 141/350] Refactor: based: Drop based_common_callback_worker() Move the remaining functionality into based_ipc.c:dispatch_common() and based_remote.c:cib_handle_remote_msg(). There is a bit of duplication, but it's worth it to finally separate IPC and remote request processing, up to the point of based_process_request(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 36 --------------------------------- daemons/based/based_callbacks.h | 4 ---- daemons/based/based_ipc.c | 36 +++++++++++++++++++++++++++++++-- daemons/based/based_remote.c | 8 +++++++- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3ffdabd7fba..67968b9cd42 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -149,42 +149,6 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, } } -void -based_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, - pcmk__client_t *client, bool privileged) -{ - const char *op = pcmk__xe_get(op_request, PCMK__XA_CIB_OP); - - if ((PCMK__CLIENT_TYPE(client) == pcmk__client_ipc) - && pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { - - if (pcmk__is_set(flags, crm_ipc_client_response)) { - xmlNode *ack = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER); - pcmk__xe_set(ack, PCMK__XA_CIB_CLIENTID, client->id); - pcmk__ipc_send_xml(client, id, ack, flags); - client->request_id = 0; - pcmk__xml_free(ack); - } - return; - } - - if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { - crm_exit_t status = CRM_EX_OK; - int rc = based_update_notify_flags(op_request, client); - - if (rc != pcmk_rc_ok) { - status = CRM_EX_INVALID_PARAM; - } - - pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL, status); - return; - } - - based_process_request(op_request, privileged, client); -} - static uint64_t ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index d4203d25289..d52bf52431e 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -11,7 +11,6 @@ #define BASED_CALLBACKS__H #include -#include // uint32_t #include // xmlNode #include // qb_* @@ -26,9 +25,6 @@ extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; void based_peer_callback(xmlNode *msg, void *private_data); -void based_common_callback_worker(uint32_t id, uint32_t flags, - xmlNode *op_request, pcmk__client_t *client, - bool privileged); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_shutdown(int nsig); diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 1673b295a97..11ecb399706 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -24,6 +24,7 @@ #include // crm_ipc_client_response #include // CRM_CHECK(), CRM_LOG_ASSERT() #include // CRM_EX_PROTOCOL, pcmk_rc_* +#include // CRM_OP_REGISTER #include "pacemaker-based.h" @@ -79,6 +80,7 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) uint32_t call_options = cib_none; xmlNode *msg = NULL; pcmk__client_t *client = pcmk__find_client(c); + const char *op = NULL; // Sanity-check, and parse XML from IPC data CRM_CHECK(client != NULL, return 0); @@ -171,9 +173,39 @@ dispatch_common(qb_ipcs_connection_t *c, void *data, bool privileged) pcmk__log_xml_trace(msg, "ipc-request"); - based_common_callback_worker(id, flags, msg, client, privileged); - pcmk__xml_free(msg); + op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); + + if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { + xmlNode *ack = NULL; + + if (!pcmk__is_set(flags, crm_ipc_client_response)) { + return 0; + } + + ack = pcmk__xe_create(NULL, __func__); + pcmk__xe_set(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER); + pcmk__xe_set(ack, PCMK__XA_CIB_CLIENTID, client->id); + pcmk__ipc_send_xml(client, id, ack, flags); + + client->request_id = 0; + pcmk__xml_free(ack); + return 0; + } + if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { + crm_exit_t status = CRM_EX_OK; + int rc = based_update_notify_flags(msg, client); + + if (rc != pcmk_rc_ok) { + status = CRM_EX_INVALID_PARAM; + } + + pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL, status); + return 0; + } + + based_process_request(msg, privileged, client); + pcmk__xml_free(msg); return 0; } diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 15852a11e8d..c52e99de50d 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -315,6 +315,7 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) { int rc = pcmk_rc_ok; uint32_t call_options = cib_none; + const char *op = pcmk__xe_get(command, PCMK__XA_CIB_OP); if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) { pcmk__log_xml_trace(command, "bad"); @@ -363,7 +364,12 @@ cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command) } pcmk__log_xml_trace(command, "remote-request"); - based_common_callback_worker(0, 0, command, client, true); + + if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) { + based_update_notify_flags(command, client); + } + + based_process_request(command, true, client); } static int From 940e2b7299884473eae477aa1e2843b643217c09 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 22:04:34 -0800 Subject: [PATCH 142/350] Refactor: based: Unindent cib_digester_cb() and return G_SOURCE_REMOVE Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 38 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 67968b9cd42..edc649e9b7d 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -156,28 +156,32 @@ static bool ping_modified_since = false; static gboolean cib_digester_cb(gpointer data) { - if (based_is_primary) { - char buffer[32]; - xmlNode *ping = pcmk__xe_create(NULL, PCMK__XE_PING); + char buffer[32] = { '\0' }; + xmlNode *ping = NULL; - ping_seq++; - free(ping_digest); - ping_digest = NULL; - ping_modified_since = false; - pcmk__assert(snprintf(buffer, 32, "%" PRIu64, ping_seq) >= 0); + if (!based_is_primary) { + return G_SOURCE_REMOVE; + } - pcmk__trace("Requesting peer digests (%s)", buffer); + ping = pcmk__xe_create(NULL, PCMK__XE_PING); - pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); - pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buffer); + ping_seq++; + free(ping_digest); + ping_digest = NULL; + ping_modified_since = false; + pcmk__assert(snprintf(buffer, 32, "%" PRIu64, ping_seq) >= 0); - pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); + pcmk__trace("Requesting peer digests (%s)", buffer); - pcmk__xml_free(ping); - } - return FALSE; + pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); + pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buffer); + + pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); + pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); + + pcmk__xml_free(ping); + return G_SOURCE_REMOVE; } static void From 38fc20edf0c0b1102ab9389edbb1cdcaccca269a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 22:33:48 -0800 Subject: [PATCH 143/350] Refactor: based: cib_digester_cb() Doxygen and small improvements Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 48 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index edc649e9b7d..1524e0b0d27 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -13,7 +13,6 @@ #include // PRIu64 #include #include // NULL, size_t -#include // snprintf #include // free #include // u?int*_t, UINT64_C #include // LOG_INFO, LOG_DEBUG @@ -44,6 +43,10 @@ #define EXIT_ESCALATION_MS 10000 +static uint64_t ping_seq = 0; +static char *ping_digest = NULL; +static bool ping_modified_since = false; + /*! * \internal * \brief Create reply XML for a CIB request @@ -149,37 +152,46 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, } } -static uint64_t ping_seq = 0; -static char *ping_digest = NULL; -static bool ping_modified_since = false; - +/*! + * \internal + * \brief Request CIB digests from all peer nodes + * + * This is used as a callback that runs 5 seconds after we modify the CIB. It + * sends a ping request to all cluster nodes. They will respond by sending their + * current digests and version info, which we will validate in + * process_ping_reply(). This serves as a check of consistency across the + * cluster after a CIB update. + * + * \param[in] data Ignored + * + * \return \c G_SOURCE_REMOVE (to destroy the timeout) + */ static gboolean -cib_digester_cb(gpointer data) +digest_timer_cb(gpointer data) { - char buffer[32] = { '\0' }; + char *buf = NULL; xmlNode *ping = NULL; if (!based_is_primary) { return G_SOURCE_REMOVE; } - ping = pcmk__xe_create(NULL, PCMK__XE_PING); - ping_seq++; - free(ping_digest); - ping_digest = NULL; + g_clear_pointer(&ping_digest, free); ping_modified_since = false; - pcmk__assert(snprintf(buffer, 32, "%" PRIu64, ping_seq) >= 0); - pcmk__trace("Requesting peer digests (%s)", buffer); + buf = pcmk__assert_asprintf("%" PRIu64, ping_seq); + ping = pcmk__xe_create(NULL, PCMK__XE_PING); pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); - pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buffer); - + pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buf); pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); + + pcmk__trace("Requesting peer digests (%s)", buf); pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); + free(buf); pcmk__xml_free(ping); return G_SOURCE_REMOVE; } @@ -593,8 +605,9 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__assert(cib_status == pcmk_rc_ok); - if(digest_timer == NULL) { - digest_timer = mainloop_timer_add("digester", 5000, FALSE, cib_digester_cb, NULL); + if (digest_timer == NULL) { + digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, + digest_timer_cb, NULL); } *reply = NULL; @@ -702,7 +715,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, sync_our_cib(request, true); } - mainloop_timer_stop(digest_timer); mainloop_timer_start(digest_timer); } else if (rc == pcmk_rc_schema_validation) { From 5956263d17d966785db6ff33e203eaeb41efd835 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 23:49:37 -0800 Subject: [PATCH 144/350] Refactor: libcib: Return standard code from cib_perform_op() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 -- lib/cib/cib_file.c | 6 ++--- lib/cib/cib_utils.c | 42 +++++++++++++++++---------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1524e0b0d27..be8405de695 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -633,7 +633,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, rc = cib_perform_op(NULL, op, call_options, op_function, true, section, request, input, false, &config_changed, &the_cib, &result_cib, NULL, &output); - rc = pcmk_legacy2rc(rc); CRM_CHECK(result_cib == NULL, pcmk__xml_free(result_cib)); goto done; @@ -663,7 +662,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, rc = cib_perform_op(NULL, op, call_options, op_function, false, section, request, input, manage_counters, &config_changed, &the_cib, &result_cib, &cib_diff, &output); - rc = pcmk_legacy2rc(rc); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 0ca37782a53..cd52936fb31 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -187,11 +187,11 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) goto done; } - if (rc == -pcmk_err_schema_validation) { + if (rc == pcmk_rc_schema_validation) { // Show validation errors to stderr pcmk__validate_xml(result_cib, NULL, NULL, NULL); - } else if ((rc == pcmk_ok) && !read_only) { + } else if ((rc == pcmk_rc_ok) && !read_only) { pcmk__log_xml_patchset(LOG_DEBUG, cib_diff); if (result_cib != private->cib_xml) { @@ -206,7 +206,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) pcmk__xml_free(result_cib); } pcmk__xml_free(cib_diff); - return rc; + return pcmk_rc2legacy(rc); } /*! diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 4ec2255cdef..5d0617085cb 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -204,7 +204,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, xmlNode **result_cib, xmlNode **diff, xmlNode **output) { const bool dry_run = pcmk__is_set(call_options, cib_dryrun); - int rc = pcmk_ok; + int rc = pcmk_rc_ok; bool check_schema = true; bool make_copy = true; xmlNode *top = NULL; @@ -219,10 +219,10 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__trace("Begin %s%s%s op", (dry_run? "dry run of " : ""), (is_query? "read-only " : ""), op); - CRM_CHECK(output != NULL, return -ENOMSG); - CRM_CHECK(current_cib != NULL, return -ENOMSG); - CRM_CHECK(result_cib != NULL, return -ENOMSG); - CRM_CHECK(config_changed != NULL, return -ENOMSG); + CRM_CHECK(output != NULL, return ENOMSG); + CRM_CHECK(current_cib != NULL, return ENOMSG); + CRM_CHECK(result_cib != NULL, return ENOMSG); + CRM_CHECK(config_changed != NULL, return ENOMSG); if(output) { *output = NULL; @@ -232,7 +232,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, *config_changed = false; if (fn == NULL) { - return -EINVAL; + return EINVAL; } if (is_query) { @@ -245,13 +245,14 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, if (cib_filtered == NULL) { pcmk__debug("Pre-filtered the entire cib"); - return -EACCES; + return EACCES; } cib_ro = cib_filtered; pcmk__log_xml_trace(cib_ro, "filtered"); } rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output); + rc = pcmk_legacy2rc(rc); if(output == NULL || *output == NULL) { /* nothing */ @@ -294,6 +295,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output); + rc = pcmk_legacy2rc(rc); /* If scratch points to a new object now (for example, after an erase * operation), then *current_cib should point to the same object. @@ -314,6 +316,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, rc = (*fn) (op, call_options, section, req, input, *current_cib, &scratch, output); + rc = pcmk_legacy2rc(rc); /* @TODO This appears to be a hack to determine whether scratch points * to a new object now, without saving the old pointer (which may be @@ -327,21 +330,21 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } pcmk__xml_mark_changes(*current_cib, scratch); } - CRM_CHECK(*current_cib != scratch, return -EINVAL); + CRM_CHECK(*current_cib != scratch, return EINVAL); } xml_acl_disable(scratch); /* Allow the system to make any additional changes */ - if (rc == pcmk_ok && scratch == NULL) { - rc = -EINVAL; + if ((rc == pcmk_rc_ok) && (scratch == NULL)) { + rc = EINVAL; goto done; - } else if(rc == pcmk_ok && xml_acl_denied(scratch)) { + } else if ((rc == pcmk_rc_ok) && xml_acl_denied(scratch)) { pcmk__trace("ACL rejected part or all of the proposed changes"); - rc = -EACCES; + rc = EACCES; goto done; - } else if (rc != pcmk_ok) { + } else if (rc != pcmk_rc_ok) { goto done; } @@ -358,7 +361,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__err("Discarding update with feature set '%s' greater than " "our own '%s'", new_version, CRM_FEATURE_SET); - rc = pcmk_rc2legacy(rc); goto done; } } @@ -375,7 +377,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, PCMK_XA_ADMIN_EPOCH, old, new, call_options); pcmk__log_xml_warn(req, "Bad Op"); pcmk__log_xml_warn(input, "Bad Data"); - rc = -pcmk_err_old_data; + rc = pcmk_rc_old_data; } else if (old == new) { pcmk__xe_get_int(scratch, PCMK_XA_EPOCH, &new); @@ -385,7 +387,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, PCMK_XA_EPOCH, old, new, call_options); pcmk__log_xml_warn(req, "Bad Op"); pcmk__log_xml_warn(input, "Bad Data"); - rc = -pcmk_err_old_data; + rc = pcmk_rc_old_data; } } } @@ -474,7 +476,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, const char *schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); if (schema == NULL) { - rc = -pcmk_err_cib_corrupt; + rc = pcmk_rc_cib_corrupt; } pcmk__xe_add_last_written(scratch); @@ -508,9 +510,9 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } pcmk__trace("Perform validation: %s", pcmk__btoa(check_schema)); - if ((rc == pcmk_ok) && check_schema + if ((rc == pcmk_rc_ok) && check_schema && !pcmk__configured_schema_validates(scratch)) { - rc = -pcmk_err_schema_validation; + rc = pcmk_rc_schema_validation; } done: @@ -520,7 +522,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, /* @TODO: This may not work correctly with !make_copy, since we don't * keep the original CIB. */ - if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user) + if ((rc != pcmk_rc_ok) && cib_acl_enabled(patchset_cib, user) && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) { if (*result_cib == NULL) { From a25c34bbdbd885f2e8832104349096b95e42f2dd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 00:47:00 -0800 Subject: [PATCH 145/350] Refactor: based, libcib: Drop cib_force_diff-related dead code It affects a couple of log messages but nothing else. That was also true at least as far back as Pacemaker 2.0.0. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 9 ++++----- daemons/based/based_messages.c | 3 --- lib/cib/cib_ops.c | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index be8405de695..e4d6d517f9b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -640,14 +640,13 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, /* @COMPAT: Handle a valid write action (legacy) * - * @TODO: Re-evaluate whether this is all truly legacy. The cib_force_diff - * portion is. However, PCMK__XA_CIB_UPDATE may be set by a sync operation - * even in non-legacy mode, and manage_counters tells xml_create_patchset() - * whether to update version/epoch info. + * @TODO: Re-evaluate whether this is all truly legacy. PCMK__XA_CIB_UPDATE + * may be set by a sync operation even in non-legacy mode, and + * manage_counters tells xml_create_patchset() whether to update + * version/epoch info. */ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { manage_counters = false; - cib__set_call_options(call_options, "call", cib_force_diff); pcmk__trace("Global update detected"); CRM_LOG_ASSERT(pcmk__str_any_of(op, diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 382e69c548a..18d4958bc6b 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -110,9 +110,6 @@ based_process_apply_patch(const char *op, int options, const char *section, } else if (rc == -pcmk_err_diff_resync) { rc = -pcmk_err_diff_failed; - if (options & cib_force_diff) { - pcmk__warn("Not requesting full refresh in R/W mode"); - } } return rc; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 18b9191c539..0edb315e64f 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -172,15 +172,13 @@ cib__process_apply_patch(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - const bool force = pcmk__is_set(options, cib_force_diff); const char *originator = NULL; if (req != NULL) { originator = pcmk__xe_get(req, PCMK__XA_SRC); } - pcmk__trace("Processing \"%s\" event from %s%s", op, originator, - (force? " (global update)" : "")); + pcmk__trace("Processing %s event from %s", op, originator); if (*result_cib != existing_cib) { pcmk__xml_free(*result_cib); From 4f9fe1b8c66b2f7189557fef0d95c8ca65882cc2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 00:48:07 -0800 Subject: [PATCH 146/350] API: libcib: Deprecate cib_force_diff Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index 83509ce04e7..efe528c11fc 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -119,7 +119,11 @@ enum cib_call_options { cib_no_mtime = (UINT32_C(1) << 13), cib_inhibit_notify = (UINT32_C(1) << 16), + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + //! \deprecated This value will be removed in a future release cib_force_diff = (UINT32_C(1) << 28), +#endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) }; typedef struct cib_s cib_t; From a5cb1575d5e82071ba780ffd9ed6c19742e231e1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 23 Dec 2025 23:52:18 -0800 Subject: [PATCH 147/350] Log: based: Drop unhelpful trace messages Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 31 ++++++------------------------- lib/cib/cib_utils.c | 1 - 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index e4d6d517f9b..c8d9eda8813 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -589,7 +589,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, uint32_t call_options = cib_none; - const char *op = NULL; + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *section = NULL; const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); @@ -613,7 +613,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, *reply = NULL; /* Start processing the request... */ - op = pcmk__xe_get(request, PCMK__XA_CIB_OP); rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { @@ -622,8 +621,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, if (!privileged && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + rc = EACCES; - pcmk__trace("Failed due to lack of privileges: %s", pcmk_rc_str(rc)); goto done; } @@ -647,12 +646,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, */ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { manage_counters = false; - pcmk__trace("Global update detected"); - - CRM_LOG_ASSERT(pcmk__str_any_of(op, - PCMK__CIB_REQUEST_APPLY_PATCH, - PCMK__CIB_REQUEST_REPLACE, - NULL)); + CRM_LOG_ASSERT(pcmk__str_any_of(op, PCMK__CIB_REQUEST_APPLY_PATCH, + PCMK__CIB_REQUEST_REPLACE, NULL)); } ping_modified_since = true; @@ -679,15 +674,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, config_changed = true; } - pcmk__trace("Activating %s->%s%s", - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - pcmk__xe_get(result_cib, PCMK_XA_NUM_UPDATES), - (config_changed? " changed" : "")); - rc = based_activate_cib(result_cib, config_changed, op); - if (rc != pcmk_rc_ok) { - pcmk__err("Failed to activate new CIB: %s", pcmk_rc_str(rc)); - } } if ((rc == pcmk_rc_ok) && contains_config_change(cib_diff)) { @@ -724,14 +711,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, output = result_cib; - } else { - pcmk__trace("Not activating %d %d %s", rc, - pcmk__is_set(call_options, cib_dryrun), - pcmk__xe_get(result_cib, PCMK_XA_NUM_UPDATES)); - - if (result_cib != the_cib) { - pcmk__xml_free(result_cib); - } + } else if (result_cib != the_cib) { + pcmk__xml_free(result_cib); } if (!pcmk__any_flags_set(call_options, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 5d0617085cb..f93981e822d 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -509,7 +509,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } } - pcmk__trace("Perform validation: %s", pcmk__btoa(check_schema)); if ((rc == pcmk_rc_ok) && check_schema && !pcmk__configured_schema_validates(scratch)) { rc = pcmk_rc_schema_validation; From 2dc7298340241f69e7c76cf2c640dc050aaee02d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 01:54:42 -0800 Subject: [PATCH 148/350] Feature: tools: Drop crm_attribute --attr-id option It has been hidden and thus informally deprecated since fe96d691 in 2009. It has been in the deprecated options section since conversion to GLib option parsing in 15f5c290 (2021). Signed-off-by: Reid Wahl --- tools/crm_attribute.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c index 4a2ce9cf72b..a8bd80041a8 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -320,10 +320,6 @@ static GOptionEntry addl_entries[] = { }; static GOptionEntry deprecated_entries[] = { - { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id, - NULL, NULL - }, - // NOTE: resource-agents <4.2.0 (2018-10-24) uses this option { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, attr_name_cb, NULL, NULL From f29d83ffbc1b4d3ef9df1d957c6ab2762f4be652 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 01:59:32 -0800 Subject: [PATCH 149/350] Feature: tools: Drop crm_attribute --attr-value/--delete-attr options They have been hidden and thus informally deprecated since 051c07cc in 2009. They have been in the deprecated options section since conversion to GLib option parsing in 15f5c290 (2021). Signed-off-by: Reid Wahl --- tools/crm_attribute.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c index a8bd80041a8..3ac4da9ce67 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -325,14 +325,6 @@ static GOptionEntry deprecated_entries[] = { NULL, NULL }, - { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb, - NULL, NULL - }, - - { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb, - NULL, NULL - }, - // NOTE: resource-agents <4.2.0 (2018-10-24) uses this option { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb, NULL, NULL From 97e8e78129a7fe5cbe690e009a7ddbe31b4fdd3b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 02:01:09 -0800 Subject: [PATCH 150/350] Feature: tools: Drop crm_attribute --inhibit-policy-engine/-i option It have been hidden and thus informally deprecated since 051c07cc in 2009. It was never formally deprecated. However, it also looks as if it was never documented (except that its existence was documented with no explanation prior to 051c07cc). It was added by commit 861fcc91 in 2005, and I haven't found anything that used it since then (although it's impossible to be certain). Since it's been hidden for over 16 years and I haven't found anything that has used it, I think it's safe to drop. Signed-off-by: Reid Wahl --- tools/crm_attribute.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c index 3ac4da9ce67..b79c2f9cd7c 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -66,7 +66,6 @@ struct { char *attr_value; char *dest_node; gchar *dest_uname; - gboolean inhibit; gchar *set_name; char *set_type; gchar *type; @@ -312,10 +311,6 @@ static GOptionEntry addl_entries[] = { NULL }, - { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit, - NULL, NULL - }, - { NULL } }; @@ -800,11 +795,6 @@ main(int argc, char **argv) goto done; } - if (options.inhibit) { - pcmk__warn("Inhibiting notifications for this update"); - cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify); - } - rc = cib__create_signon(&the_cib); if (rc != pcmk_rc_ok) { exit_code = pcmk_rc2exitc(rc); From ee6797901a46d187d36f67eeb6c5f6cb48593232 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 02:11:53 -0800 Subject: [PATCH 151/350] API: libcib: Deprecate cib_inhibit_notify At least as far back as Pacemaker 2.0.0, nothing internal has used this call option except for the crm_attribute --inhibit-policy-engine option, which itself had been hidden since 2009. Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index efe528c11fc..e1495423aed 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -118,6 +118,8 @@ enum cib_call_options { cib_sync_call = (UINT32_C(1) << 12), cib_no_mtime = (UINT32_C(1) << 13), + + //! \deprecated This value will be removed in a future release cib_inhibit_notify = (UINT32_C(1) << 16), #if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) From c66163fcb3548469138c5681e7ad761391780c43 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 02:23:04 -0800 Subject: [PATCH 152/350] Doc: libcrmcommon: Correct Doxygen for pcmk_find_cib_element() The cib argument is not modified. However, pcmk__xpath_find_one() takes a non-const xmlDoc * argument. Signed-off-by: Reid Wahl --- lib/common/cib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common/cib.c b/lib/common/cib.c index f9f187a88eb..94882caf831 100644 --- a/lib/common/cib.c +++ b/lib/common/cib.c @@ -161,8 +161,8 @@ pcmk_cib_parent_name_for(const char *element_name) /*! * \brief Find an element in the CIB * - * \param[in,out] cib Top-level CIB XML to search - * \param[in] element_name Name of CIB element to search for + * \param[in] cib Top-level CIB XML to search + * \param[in] element_name Name of CIB element to search for * * \return XML element in \p cib corresponding to \p element_name * (or \p cib itself if element is unknown or not found) From 944ee5d4121e185672513f8d3bfd63154c2b3204 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 02:32:47 -0800 Subject: [PATCH 153/350] Refactor: based: Drop config_hash Nothing uses the values in it. Previously, we were calling cib_read_config() every time the crm_config section changed. That updated the values in config_hash and validated them. Since nothing used it, the updates were pointless. The validation also isn't very useful, since the options will be validated (and defaults will be handled, etc.) when the scheduler unpacks them. So we can take this opportunity for a little bit of simplification. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 25 ------------------------- daemons/based/pacemaker-based.c | 11 ----------- daemons/based/pacemaker-based.h | 2 -- 3 files changed, 38 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c8d9eda8813..754e4237fe1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -21,7 +21,6 @@ #include // gboolean, gpointer, g_*, etc. #include // xmlNode -#include // xmlXPath* #include // QB_FALSE #include // qb_ipcs_connection_t #include // LOG_TRACE @@ -558,26 +557,6 @@ prepare_input(const xmlNode *request, enum cib__op_type type, return input; } -static bool -contains_config_change(xmlNode *diff) -{ - xmlXPathObject *xpath_obj = NULL; - bool changed = false; - - if (diff == NULL) { - return false; - } - - xpath_obj = pcmk__xpath_search(diff->doc, - "//" PCMK_XE_CHANGE - "[contains(@" PCMK_XA_PATH ", " - "'/" PCMK_XE_CRM_CONFIG "/')]"); - - changed = (pcmk__xpath_num_results(xpath_obj) > 0); - xmlXPathFreeObject(xpath_obj); - return changed; -} - static int cib_process_command(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **reply, bool privileged) @@ -677,10 +656,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, rc = based_activate_cib(result_cib, config_changed, op); } - if ((rc == pcmk_rc_ok) && contains_config_change(cib_diff)) { - cib_read_config(config_hash, result_cib); - } - /* @COMPAT Nodes older than feature set 3.19.0 don't support * transactions. In a mixed-version cluster with nodes <3.19.0, we must * sync the updated CIB, so that the older nodes receive the changes. diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 5013d581aad..de3bfaefee0 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -24,7 +24,6 @@ #include // xmlNode #include // CRM_CONFIG_DIR, CRM_DAEMON_USER -#include // cib_read_config #include // pcmk_cluster_* #include // pcmk__node_update, etc. #include // crm_ipc_* @@ -46,8 +45,6 @@ gchar *cib_root = NULL; gboolean stand_alone = FALSE; -GHashTable *config_hash = NULL; - static void cib_init(void); static crm_exit_t exit_code = CRM_EX_OK; @@ -286,10 +283,6 @@ main(int argc, char **argv) pcmk__free_arg_context(context); pcmk__cluster_destroy_node_caches(); - - if (config_hash != NULL) { - g_hash_table_destroy(config_hash); - } pcmk__client_cleanup(); pcmk_cluster_free(crm_cluster); g_free(cib_root); @@ -374,11 +367,7 @@ cib_init(void) crm_exit(CRM_EX_SOFTWARE); } - config_hash = pcmk__strkey_table(free, free); - cib_read_config(config_hash, the_cib); - based_remote_init(); - crm_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 1e8fe9303fd..9349bbb0145 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -26,8 +26,6 @@ #define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->priv->node_name) -extern GHashTable *config_hash; - extern GMainLoop *mainloop; extern pcmk_cluster_t *crm_cluster; extern gboolean stand_alone; From 0e2578bd03cde7db0c363e9b3ec16ac4106b1902 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 02:47:54 -0800 Subject: [PATCH 154/350] Refactor: libcib: cib_read_config() is now static No code changes other than renaming it to read_config() and moving it above its caller. Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 2 -- lib/cib/cib_utils.c | 62 +++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 8715785e44a..36b9665d7cd 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -95,8 +95,6 @@ enum cib__op_type { cib__op_upgrade, }; -void cib_read_config(GHashTable *options, xmlNode *current_cib); - typedef int (*cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index f93981e822d..0b5c465df14 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -129,6 +129,36 @@ createEmptyCib(int cib_epoch) return cib_root; } +static void +read_config(GHashTable *options, xmlNode *current_cib) +{ + xmlNode *config = NULL; + crm_time_t *now = NULL; + + if (options == NULL || current_cib == NULL) { + return; + } + + now = crm_time_new(NULL); + + g_hash_table_remove_all(options); + + config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG); + if (config) { + pcmk_rule_input_t rule_input = { + .now = now, + }; + + pcmk_unpack_nvpair_blocks(config, PCMK_XE_CLUSTER_PROPERTY_SET, + PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input, + options, NULL); + } + + pcmk__validate_cluster_options(options); + + crm_time_free(now); +} + static bool cib_acl_enabled(xmlNode *xml, const char *user) { @@ -138,7 +168,7 @@ cib_acl_enabled(xmlNode *xml, const char *user) const char *value = NULL; GHashTable *options = pcmk__strkey_table(free, free); - cib_read_config(options, xml); + read_config(options, xml); value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL); rc = pcmk__is_true(value); g_hash_table_destroy(options); @@ -731,36 +761,6 @@ cib_native_notify(gpointer data, gpointer user_data) pcmk__trace("Callback invoked..."); } -void -cib_read_config(GHashTable * options, xmlNode * current_cib) -{ - xmlNode *config = NULL; - crm_time_t *now = NULL; - - if (options == NULL || current_cib == NULL) { - return; - } - - now = crm_time_new(NULL); - - g_hash_table_remove_all(options); - - config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG); - if (config) { - pcmk_rule_input_t rule_input = { - .now = now, - }; - - pcmk_unpack_nvpair_blocks(config, PCMK_XE_CLUSTER_PROPERTY_SET, - PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input, - options, NULL); - } - - pcmk__validate_cluster_options(options); - - crm_time_free(now); -} - int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, From 93e2359657325b3d09a9e2ee7113d18658171d2a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 02:46:22 -0800 Subject: [PATCH 155/350] Refactor: libcib: Unindent cib_acl_enabled() And drop a trace message that doesn't seem especially helpful. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 0b5c465df14..ec411ae4274 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -162,19 +162,20 @@ read_config(GHashTable *options, xmlNode *current_cib) static bool cib_acl_enabled(xmlNode *xml, const char *user) { + const char *value = NULL; + GHashTable *options = NULL; bool rc = false; - if(pcmk_acl_required(user)) { - const char *value = NULL; - GHashTable *options = pcmk__strkey_table(free, free); - - read_config(options, xml); - value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL); - rc = pcmk__is_true(value); - g_hash_table_destroy(options); + if ((xml == NULL) || !pcmk_acl_required(user)) { + return false; } - pcmk__trace("CIB ACL is %s", (rc? "enabled" : "disabled")); + options = pcmk__strkey_table(free, free); + read_config(options, xml); + value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL); + + rc = pcmk__is_true(value); + g_hash_table_destroy(options); return rc; } From ad7225f77e99188793989c31546c340f46a0ffb2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 03:05:26 -0800 Subject: [PATCH 156/350] Refactor: libcib: Simplify read_config() options and current_cib are now guaranteed to be non-NULL, so we don't need to check them in a static function with one call. Also return early if config is NULL. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index ec411ae4274..b3c9440507f 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -132,30 +132,20 @@ createEmptyCib(int cib_epoch) static void read_config(GHashTable *options, xmlNode *current_cib) { - xmlNode *config = NULL; crm_time_t *now = NULL; + pcmk_rule_input_t rule_input = { 0, }; + xmlNode *config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG); - if (options == NULL || current_cib == NULL) { + if (config == NULL) { return; } now = crm_time_new(NULL); + rule_input.now = now; - g_hash_table_remove_all(options); - - config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG); - if (config) { - pcmk_rule_input_t rule_input = { - .now = now, - }; - - pcmk_unpack_nvpair_blocks(config, PCMK_XE_CLUSTER_PROPERTY_SET, - PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input, - options, NULL); - } - - pcmk__validate_cluster_options(options); - + pcmk_unpack_nvpair_blocks(config, PCMK_XE_CLUSTER_PROPERTY_SET, + PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input, + options, NULL); crm_time_free(now); } From d8213c761165fc939138074252c6abffff461911 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 03:42:53 -0800 Subject: [PATCH 157/350] Refactor: libcib: Split out is_query block of cib_perform_op() New cib__perform_query(). This may be renamed or reorganized later, but it makes things a little bit easier to look at. cib_perform_op() loses the is_query argument, and the new function has several fewer arguments. Also drop a trace message. There is plenty of tracing in the caller and (at least for now) in the various cib__op_fn_t functions. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 ++- include/crm/cib/internal.h | 13 ++-- lib/cib/cib_file.c | 11 ++- lib/cib/cib_utils.c | 118 +++++++++++++++++++------------- 4 files changed, 91 insertions(+), 62 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 754e4237fe1..ae12dfa45f6 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -608,9 +608,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, input = prepare_input(request, operation->type, §ion); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib_perform_op(NULL, op, call_options, op_function, true, section, - request, input, false, &config_changed, &the_cib, - &result_cib, NULL, &output); + rc = cib__perform_query(op, call_options, op_function, section, request, + input, &the_cib, &result_cib, &output); CRM_CHECK(result_cib == NULL, pcmk__xml_free(result_cib)); goto done; @@ -632,9 +631,9 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; // result_cib must not be modified after cib_perform_op() returns - rc = cib_perform_op(NULL, op, call_options, op_function, false, section, - request, input, manage_counters, &config_changed, - &the_cib, &result_cib, &cib_diff, &output); + rc = cib_perform_op(NULL, op, call_options, op_function, section, request, + input, manage_counters, &config_changed, &the_cib, + &result_cib, &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 36b9665d7cd..3084fa782c4 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -180,11 +180,16 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); +int cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, + const char *section, xmlNode *req, xmlNode *input, + xmlNode **current_cib, xmlNode **result_cib, + xmlNode **output); + int cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, - cib__op_fn_t fn, bool is_query, const char *section, - xmlNode *req, xmlNode *input, bool manage_counters, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output); + cib__op_fn_t fn, const char *section, xmlNode *req, + xmlNode *input, bool manage_counters, bool *config_changed, + xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index cd52936fb31..c6593a08461 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -176,9 +176,14 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) data = pcmk_find_cib_element(data, section); } - rc = cib_perform_op(cib, op, call_options, op_function, read_only, section, - request, data, true, &changed, &private->cib_xml, - &result_cib, &cib_diff, output); + if (read_only) { + rc = cib__perform_query(op, call_options, op_function, section, request, + data, &private->cib_xml, &result_cib, output); + } else { + rc = cib_perform_op(cib, op, call_options, op_function, section, + request, data, true, &changed, &private->cib_xml, + &result_cib, &cib_diff, output); + } if (pcmk__is_set(call_options, cib_transaction)) { /* The rest of the logic applies only to the transaction as a whole, not diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index b3c9440507f..57866b88050 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -169,6 +169,71 @@ cib_acl_enabled(xmlNode *xml, const char *user) return rc; } +int +cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, + const char *section, xmlNode *req, xmlNode *input, + xmlNode **current_cib, xmlNode **result_cib, + xmlNode **output) +{ + int rc = pcmk_rc_ok; + const char *user = pcmk__xe_get(req, PCMK__XA_CIB_USER); + + xmlNode *cib_ro = *current_cib; + xmlNode *cib_filtered = NULL; + + CRM_CHECK(output != NULL, return ENOMSG); + CRM_CHECK(current_cib != NULL, return ENOMSG); + CRM_CHECK(result_cib != NULL, return ENOMSG); + + if (output != NULL) { + *output = NULL; + } + + *result_cib = NULL; + + if (fn == NULL) { + return EINVAL; + } + + if (cib_acl_enabled(*current_cib, user) + && xml_acl_filtered_copy(user, *current_cib, *current_cib, + &cib_filtered)) { + + if (cib_filtered == NULL) { + pcmk__debug("Pre-filtered the entire cib"); + return EACCES; + } + cib_ro = cib_filtered; + pcmk__log_xml_trace(cib_ro, "filtered"); + } + + rc = fn(op, call_options, section, req, input, cib_ro, result_cib, output); + rc = pcmk_legacy2rc(rc); + + if ((output == NULL) || (*output == NULL)) { + // Do nothing + + } else if (cib_filtered == *output) { + // Let them have this copy + cib_filtered = NULL; + + } else if (*output == *current_cib) { + // They already know not to free it + + } else if ((cib_filtered != NULL) + && ((*output)->doc == cib_filtered->doc)) { + // We're about to free the document of which *output is a part + *output = pcmk__xml_copy(NULL, *output); + + } else if ((*output)->doc == (*current_cib)->doc) { + // Give them a copy they can free + *output = pcmk__xml_copy(NULL, *output); + } + + pcmk__xml_free(cib_filtered); + return rc; +} + /*! * \internal * \brief Determine whether to perform operations on a scratch copy of the CIB @@ -219,12 +284,11 @@ should_copy_cib(const char *op, const char *section, int call_options) int cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, - cib__op_fn_t fn, bool is_query, const char *section, - xmlNode *req, xmlNode *input, bool manage_counters, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output) + cib__op_fn_t fn, const char *section, xmlNode *req, + xmlNode *input, bool manage_counters, bool *config_changed, + xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, + xmlNode **output) { - const bool dry_run = pcmk__is_set(call_options, cib_dryrun); int rc = pcmk_rc_ok; bool check_schema = true; bool make_copy = true; @@ -237,9 +301,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, const bool enable_acl = cib_acl_enabled(*current_cib, user); bool with_digest = false; - pcmk__trace("Begin %s%s%s op", (dry_run? "dry run of " : ""), - (is_query? "read-only " : ""), op); - CRM_CHECK(output != NULL, return ENOMSG); CRM_CHECK(current_cib != NULL, return ENOMSG); CRM_CHECK(result_cib != NULL, return ENOMSG); @@ -256,47 +317,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, return EINVAL; } - if (is_query) { - xmlNode *cib_ro = *current_cib; - xmlNode *cib_filtered = NULL; - - if (enable_acl - && xml_acl_filtered_copy(user, *current_cib, *current_cib, - &cib_filtered)) { - - if (cib_filtered == NULL) { - pcmk__debug("Pre-filtered the entire cib"); - return EACCES; - } - cib_ro = cib_filtered; - pcmk__log_xml_trace(cib_ro, "filtered"); - } - - rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output); - rc = pcmk_legacy2rc(rc); - - if(output == NULL || *output == NULL) { - /* nothing */ - - } else if(cib_filtered == *output) { - cib_filtered = NULL; /* Let them have this copy */ - - } else if (*output == *current_cib) { - /* They already know not to free it */ - - } else if(cib_filtered && (*output)->doc == cib_filtered->doc) { - /* We're about to free the document of which *output is a part */ - *output = pcmk__xml_copy(NULL, *output); - - } else if ((*output)->doc == (*current_cib)->doc) { - /* Give them a copy they can free */ - *output = pcmk__xml_copy(NULL, *output); - } - - pcmk__xml_free(cib_filtered); - return rc; - } - make_copy = should_copy_cib(op, section, call_options); if (!make_copy) { From c96f212a37499283ddb376cdc2e20ca5a7d090ff Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 04:02:57 -0800 Subject: [PATCH 158/350] Refactor: libcib: Improve cib__perform_query/cib_perform_op assertions I'll feel better about changes to the rest of the function bodies with these more exhaustive assertions taken care of. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 49 +++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 57866b88050..5e0de7789e1 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -176,24 +176,18 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, xmlNode **output) { int rc = pcmk_rc_ok; - const char *user = pcmk__xe_get(req, PCMK__XA_CIB_USER); + const char *user = NULL; - xmlNode *cib_ro = *current_cib; + xmlNode *cib_ro = NULL; xmlNode *cib_filtered = NULL; - CRM_CHECK(output != NULL, return ENOMSG); - CRM_CHECK(current_cib != NULL, return ENOMSG); - CRM_CHECK(result_cib != NULL, return ENOMSG); + pcmk__assert((fn != NULL) && (req != NULL) + && (current_cib != NULL) && (*current_cib != NULL) + && (result_cib != NULL) && (*result_cib == NULL) + && (output != NULL) && (*output == NULL)); - if (output != NULL) { - *output = NULL; - } - - *result_cib = NULL; - - if (fn == NULL) { - return EINVAL; - } + user = pcmk__xe_get(req, PCMK__XA_CIB_USER); + cib_ro = *current_cib; if (cib_acl_enabled(*current_cib, user) && xml_acl_filtered_copy(user, *current_cib, *current_cib, @@ -210,7 +204,7 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, rc = fn(op, call_options, section, req, input, cib_ro, result_cib, output); rc = pcmk_legacy2rc(rc); - if ((output == NULL) || (*output == NULL)) { + if (*output == NULL) { // Do nothing } else if (cib_filtered == *output) { @@ -297,25 +291,18 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, xmlNode *patchset_cib = NULL; xmlNode *local_diff = NULL; - const char *user = pcmk__xe_get(req, PCMK__XA_CIB_USER); - const bool enable_acl = cib_acl_enabled(*current_cib, user); + const char *user = NULL; + bool enable_acl = false; bool with_digest = false; - CRM_CHECK(output != NULL, return ENOMSG); - CRM_CHECK(current_cib != NULL, return ENOMSG); - CRM_CHECK(result_cib != NULL, return ENOMSG); - CRM_CHECK(config_changed != NULL, return ENOMSG); + pcmk__assert((fn != NULL) && (req != NULL) + && (config_changed != NULL) && (!*config_changed) + && (current_cib != NULL) && (*current_cib != NULL) + && (result_cib != NULL) && (*result_cib == NULL) + && (output != NULL) && (*output == NULL)); - if(output) { - *output = NULL; - } - - *result_cib = NULL; - *config_changed = false; - - if (fn == NULL) { - return EINVAL; - } + user = pcmk__xe_get(req, PCMK__XA_CIB_USER); + enable_acl = cib_acl_enabled(*current_cib, user); make_copy = should_copy_cib(op, section, call_options); From 064a53255b3bab540e10c6677ae532541aae9c95 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 11:28:11 -0800 Subject: [PATCH 159/350] Refactor: libcib: Drop cib__perform_query() result_cib argument We expect it to be unmodified at the end. We were passing it because cib__op_fn_t requires it and because we recently split cib__perform_query() out from cib_perform_op(), which also requires it (for modifying operations). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 +--- include/crm/cib/internal.h | 3 +-- lib/cib/cib_file.c | 2 +- lib/cib/cib_utils.c | 9 +++++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ae12dfa45f6..0b076b1e7bd 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -609,9 +609,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { rc = cib__perform_query(op, call_options, op_function, section, request, - input, &the_cib, &result_cib, &output); - - CRM_CHECK(result_cib == NULL, pcmk__xml_free(result_cib)); + input, &the_cib, &output); goto done; } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 3084fa782c4..5b0359f5b18 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -182,8 +182,7 @@ int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); int cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, xmlNode *input, - xmlNode **current_cib, xmlNode **result_cib, - xmlNode **output); + xmlNode **current_cib, xmlNode **output); int cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index c6593a08461..69fa7168037 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -178,7 +178,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) if (read_only) { rc = cib__perform_query(op, call_options, op_function, section, request, - data, &private->cib_xml, &result_cib, output); + data, &private->cib_xml, output); } else { rc = cib_perform_op(cib, op, call_options, op_function, section, request, data, true, &changed, &private->cib_xml, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 5e0de7789e1..8591a8ccaf8 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -172,18 +172,17 @@ cib_acl_enabled(xmlNode *xml, const char *user) int cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, xmlNode *input, - xmlNode **current_cib, xmlNode **result_cib, - xmlNode **output) + xmlNode **current_cib, xmlNode **output) { int rc = pcmk_rc_ok; const char *user = NULL; xmlNode *cib_ro = NULL; xmlNode *cib_filtered = NULL; + xmlNode *result_cib = NULL; pcmk__assert((fn != NULL) && (req != NULL) && (current_cib != NULL) && (*current_cib != NULL) - && (result_cib != NULL) && (*result_cib == NULL) && (output != NULL) && (*output == NULL)); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); @@ -201,7 +200,7 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, pcmk__log_xml_trace(cib_ro, "filtered"); } - rc = fn(op, call_options, section, req, input, cib_ro, result_cib, output); + rc = fn(op, call_options, section, req, input, cib_ro, &result_cib, output); rc = pcmk_legacy2rc(rc); if (*output == NULL) { @@ -225,6 +224,8 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, } pcmk__xml_free(cib_filtered); + CRM_CHECK(result_cib == NULL, pcmk__xml_free(result_cib)); + return rc; } From 1f2132dd008f2bb609f1fb78724e5181060082d9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 16:06:56 -0800 Subject: [PATCH 160/350] Refactor: libcib: Move cib__op_schemas to alphabetical position in table Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 0edb315e64f..f94732b9b8b 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -107,6 +107,9 @@ static const cib__operation_t cib_ops[] = { |cib__op_attr_writes_through |cib__op_attr_transaction }, + { + 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 @@ -129,9 +132,6 @@ static const cib__operation_t cib_ops[] = { |cib__op_attr_writes_through |cib__op_attr_transaction }, - { - PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local - } }; /*! From e9bff6a0398620f5f6a6b3ae29a4eda9ea39e5ba Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 04:12:01 -0800 Subject: [PATCH 161/350] Doc: libcib: Clarify comment in cib_perform_op() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 8591a8ccaf8..342fb864432 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -489,16 +489,8 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, check_schema = false; } - /* === scratch must not be modified after this point === - * Exceptions, anything in: - - static filter_t filter[] = { - { 0, PCMK_XA_CRM_DEBUG_ORIGIN }, - { 0, PCMK_XA_CIB_LAST_WRITTEN }, - { 0, PCMK_XA_UPDATE_ORIGIN }, - { 0, PCMK_XA_UPDATE_CLIENT }, - { 0, PCMK_XA_UPDATE_USER }, - }; + /* scratch must not be modified after this point, except for the attributes + * for which pcmk__xa_filterable() returns true */ if (*config_changed && !pcmk__is_set(call_options, cib_no_mtime)) { From fa6c6060fcca52e259297c2f5ad6b3e87ab4f803 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 24 Dec 2025 04:17:04 -0800 Subject: [PATCH 162/350] Fix: libcib: Fix setting origin attributes in CIB updates This fixes a regression introduced by 53d4d347. The update-client attribute was being set to the user instead of the update client. This affects the outputs of the following public API functions and CLI tools (via the cluster-status message with pcmk_section_times set): * pcmk_simulate() * pcmk_status() * crm_mon * crm_simulate Also fix an indentation issue found while investigating what this affects. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 2 +- lib/pengine/pe_output.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 342fb864432..d32cc2a728c 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -517,7 +517,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } if (client != NULL) { - pcmk__xe_set(scratch, PCMK_XA_UPDATE_CLIENT, user); + pcmk__xe_set(scratch, PCMK_XA_UPDATE_CLIENT, client); } else { pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_CLIENT); } diff --git a/lib/pengine/pe_output.c b/lib/pengine/pe_output.c index c2398958f32..f6849e41e3d 100644 --- a/lib/pengine/pe_output.c +++ b/lib/pengine/pe_output.c @@ -455,7 +455,7 @@ cluster_summary(pcmk__output_t *out, va_list args) { const char *client = pcmk__xe_get(scheduler->input, PCMK_XA_UPDATE_CLIENT); const char *origin = pcmk__xe_get(scheduler->input, - PCMK_XA_UPDATE_ORIGIN); + PCMK_XA_UPDATE_ORIGIN); PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary"); out->message(out, "cluster-times", scheduler->priv->local_node_name, From bdea3ad0ae8189fd8833804574f02d8bed00e1b6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 28 Dec 2025 20:19:58 -0800 Subject: [PATCH 163/350] Refactor: libcib: Drop check_schema variable Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index d32cc2a728c..69d6c1e3d8e 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -285,7 +285,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, xmlNode **output) { int rc = pcmk_rc_ok; - bool check_schema = true; bool make_copy = true; xmlNode *top = NULL; xmlNode *scratch = NULL; @@ -481,14 +480,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, ); } - if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) { - /* Throttle the amount of costly validation we perform due to status updates - * a) we don't really care whats in the status section - * b) we don't validate any of its contents at the moment anyway - */ - check_schema = false; - } - /* scratch must not be modified after this point, except for the attributes * for which pcmk__xa_filterable() returns true */ @@ -530,8 +521,11 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } } - if ((rc == pcmk_rc_ok) && check_schema + // Skip validation for status-only updates, since we allow anything there + if ((rc == pcmk_rc_ok) + && !pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei) && !pcmk__configured_schema_validates(scratch)) { + rc = pcmk_rc_schema_validation; } From 7624f9cbc1840d0a8741fea65ecfc7f735f3251f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 29 Dec 2025 02:39:18 -0800 Subject: [PATCH 164/350] Refactor: libcib: Assume req != NULL in cib__process_apply_patch() As we do elsewhere. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index f94732b9b8b..045e957b005 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -172,11 +172,7 @@ cib__process_apply_patch(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - const char *originator = NULL; - - if (req != NULL) { - originator = pcmk__xe_get(req, PCMK__XA_SRC); - } + const char *originator = pcmk__xe_get(req, PCMK__XA_SRC); pcmk__trace("Processing %s event from %s", op, originator); From 07f86f4b1cae9a42905149309f92885e5dd84e45 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 29 Dec 2025 02:43:35 -0800 Subject: [PATCH 165/350] Refactor: libcib: Functionize replacing entire CIB Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 147 +++++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 045e957b005..8931edc3946 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -715,6 +715,84 @@ cib__process_query(const char *op, int options, const char *section, return result; } +static int +replace_cib(xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib) +{ + int result = pcmk_ok; + + int updates = 0; + int epoch = 0; + int admin_epoch = 0; + + int replace_updates = 0; + int replace_epoch = 0; + int replace_admin_epoch = 0; + + const char *reason = NULL; + const char *peer = pcmk__xe_get(req, PCMK__XA_SRC); + const char *digest = pcmk__xe_get(req, PCMK_XA_DIGEST); + + if (digest) { + char *digest_verify = pcmk__digest_xml(input, true); + + if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) { + pcmk__err("Digest mis-match on replace from %s: %s vs. %s " + "(expected)", + peer, digest_verify, digest); + reason = "digest mismatch"; + + } else { + pcmk__info("Digest matched on replace from %s: %s", peer, + digest); + } + free(digest_verify); + + } else { + pcmk__trace("No digest to verify"); + } + + cib_version_details(existing_cib, &admin_epoch, &epoch, &updates); + cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); + + if (replace_admin_epoch < admin_epoch) { + reason = PCMK_XA_ADMIN_EPOCH; + + } else if (replace_admin_epoch > admin_epoch) { + /* no more checks */ + + } else if (replace_epoch < epoch) { + reason = PCMK_XA_EPOCH; + + } else if (replace_epoch > epoch) { + /* no more checks */ + + } else if (replace_updates < updates) { + reason = PCMK_XA_NUM_UPDATES; + } + + if (reason != NULL) { + pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " + "current %s is greater than the replacement", + replace_admin_epoch, replace_epoch, + replace_updates, peer, admin_epoch, epoch, updates, + reason); + result = -pcmk_err_old_data; + } else { + pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", + admin_epoch, epoch, updates, + replace_admin_epoch, replace_epoch, replace_updates, + peer); + } + + if (*result_cib != existing_cib) { + pcmk__xml_free(*result_cib); + } + *result_cib = pcmk__xml_copy(NULL, input); + + return result; +} + int cib__process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -744,74 +822,7 @@ cib__process_replace(const char *op, int options, const char *section, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - int updates = 0; - int epoch = 0; - int admin_epoch = 0; - - int replace_updates = 0; - int replace_epoch = 0; - int replace_admin_epoch = 0; - - const char *reason = NULL; - const char *peer = pcmk__xe_get(req, PCMK__XA_SRC); - const char *digest = pcmk__xe_get(req, PCMK_XA_DIGEST); - - if (digest) { - char *digest_verify = pcmk__digest_xml(input, true); - - if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) { - pcmk__err("Digest mis-match on replace from %s: %s vs. %s " - "(expected)", - peer, digest_verify, digest); - reason = "digest mismatch"; - - } else { - pcmk__info("Digest matched on replace from %s: %s", peer, - digest); - } - free(digest_verify); - - } else { - pcmk__trace("No digest to verify"); - } - - cib_version_details(existing_cib, &admin_epoch, &epoch, &updates); - cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); - - if (replace_admin_epoch < admin_epoch) { - reason = PCMK_XA_ADMIN_EPOCH; - - } else if (replace_admin_epoch > admin_epoch) { - /* no more checks */ - - } else if (replace_epoch < epoch) { - reason = PCMK_XA_EPOCH; - - } else if (replace_epoch > epoch) { - /* no more checks */ - - } else if (replace_updates < updates) { - reason = PCMK_XA_NUM_UPDATES; - } - - if (reason != NULL) { - pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " - "current %s is greater than the replacement", - replace_admin_epoch, replace_epoch, - replace_updates, peer, admin_epoch, epoch, updates, - reason); - result = -pcmk_err_old_data; - } else { - pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", - admin_epoch, epoch, updates, - replace_admin_epoch, replace_epoch, replace_updates, - peer); - } - - if (*result_cib != existing_cib) { - pcmk__xml_free(*result_cib); - } - *result_cib = pcmk__xml_copy(NULL, input); + result = replace_cib(req, input, existing_cib, result_cib); } else { xmlNode *obj_root = NULL; From 0da32a3e6657f0ca3204ee4f6e8cd009c2816ba6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 29 Dec 2025 03:05:55 -0800 Subject: [PATCH 166/350] Refactor: libcib: Functionize checking CIB replacement digest Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 52 +++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 8931edc3946..7723108765f 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -715,8 +715,36 @@ cib__process_query(const char *op, int options, const char *section, return result; } +static bool +replace_cib_digest_matches(xmlNode *request, xmlNode *input) +{ + const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); + const char *expected = pcmk__xe_get(request, PCMK_XA_DIGEST); + char *calculated = NULL; + bool matches = false; + + if (expected == NULL) { + // Nothing to verify + return true; + } + + calculated = pcmk__digest_xml(input, true); + matches = pcmk__str_eq(calculated, expected, pcmk__str_none); + + if (matches) { + pcmk__info("Digest matched on replace from %s: %s", peer, expected); + + } else { + pcmk__err("Digest mismatch on replace from %s: %s vs. %s (expected)", + peer, calculated, expected); + } + + free(calculated); + return matches; +} + static int -replace_cib(xmlNode *req, xmlNode *input, xmlNode *existing_cib, +replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib) { int result = pcmk_ok; @@ -730,26 +758,10 @@ replace_cib(xmlNode *req, xmlNode *input, xmlNode *existing_cib, int replace_admin_epoch = 0; const char *reason = NULL; - const char *peer = pcmk__xe_get(req, PCMK__XA_SRC); - const char *digest = pcmk__xe_get(req, PCMK_XA_DIGEST); - - if (digest) { - char *digest_verify = pcmk__digest_xml(input, true); - - if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) { - pcmk__err("Digest mis-match on replace from %s: %s vs. %s " - "(expected)", - peer, digest_verify, digest); - reason = "digest mismatch"; + const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); - } else { - pcmk__info("Digest matched on replace from %s: %s", peer, - digest); - } - free(digest_verify); - - } else { - pcmk__trace("No digest to verify"); + if (!replace_cib_digest_matches(request, input)) { + reason = "digest mismatch"; } cib_version_details(existing_cib, &admin_epoch, &epoch, &updates); From df539bb3869cc45f4fefee915ea0153ad5fc1b7f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 03:31:29 -0800 Subject: [PATCH 167/350] Refactor: libcib: Use pcmk__is_set() in cib_ops.c Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 7723108765f..21edcf072d4 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -463,7 +463,7 @@ process_xpath(const char *op, int options, const char *section, } pcmk__xml_free(match); - if ((options & cib_multiple) == 0) { + if (!pcmk__is_set(options, cib_multiple)) { break; } @@ -476,7 +476,7 @@ process_xpath(const char *op, int options, const char *section, if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { rc = -ENXIO; - } else if ((options & cib_multiple) == 0) { + } else if (!pcmk__is_set(options, cib_multiple)) { break; } @@ -485,8 +485,7 @@ process_xpath(const char *op, int options, const char *section, break; } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { - - if (options & cib_no_children) { + if (pcmk__is_set(options, cib_no_children)) { xmlNode *shallow = pcmk__xe_create(*answer, (const char *) match->name); @@ -496,7 +495,7 @@ process_xpath(const char *op, int options, const char *section, *answer = shallow; } - } else if (options & cib_xpath_address) { + } else if (pcmk__is_set(options, cib_xpath_address)) { // @COMPAT cib_xpath_address is deprecated since 3.0.2 char *path = NULL; xmlNode *parent = match; @@ -541,7 +540,7 @@ process_xpath(const char *op, int options, const char *section, pcmk__xml_free(match); pcmk__xml_copy(parent, input); - if ((options & cib_multiple) == 0) { + if (!pcmk__is_set(options, cib_multiple)) { break; } } @@ -574,7 +573,7 @@ cib__process_delete(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); - if (options & cib_xpath) { + if (pcmk__is_set(options, cib_xpath)) { return process_xpath(op, options, section, req, input, existing_cib, result_cib, answer); } @@ -624,7 +623,7 @@ cib__process_modify(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); - if (options & cib_xpath) { + if (pcmk__is_set(options, cib_xpath)) { return process_xpath(op, options, section, req, input, existing_cib, result_cib, answer); } @@ -658,7 +657,7 @@ cib__process_modify(const char *op, int options, const char *section, } if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) { - if (options & cib_can_create) { + if (pcmk__is_set(options, cib_can_create)) { pcmk__xml_copy(obj_root, input); } else { return -ENXIO; @@ -679,7 +678,7 @@ cib__process_query(const char *op, int options, const char *section, pcmk__trace("Processing %s for %s section", op, pcmk__s(section, "unspecified")); - if (options & cib_xpath) { + if (pcmk__is_set(options, cib_xpath)) { return process_xpath(op, options, section, req, input, existing_cib, result_cib, answer); } @@ -696,7 +695,7 @@ cib__process_query(const char *op, int options, const char *section, if (obj_root == NULL) { result = -ENXIO; - } else if (options & cib_no_children) { + } else if (pcmk__is_set(options, cib_no_children)) { xmlNode *shallow = pcmk__xe_create(*answer, (const char *) obj_root->name); @@ -815,7 +814,7 @@ cib__process_replace(const char *op, int options, const char *section, pcmk__trace("Processing %s for %s section", op, pcmk__s(section, "unspecified")); - if (options & cib_xpath) { + if (pcmk__is_set(options, cib_xpath)) { return process_xpath(op, options, section, req, input, existing_cib, result_cib, answer); } From 68aba7704002ad99652a27ccad5a5ad7de5559d3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 03:43:59 -0800 Subject: [PATCH 168/350] Refactor: libcib: Drop unused process_xpath() req argument Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 21edcf072d4..23b3214ff1a 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -366,7 +366,6 @@ cib__process_create(const char *op, int options, const char *section, * \param[in] op PCMK__CIB_REQUEST_* operation to be performed * \param[in] options Flag set of \c cib_call_options * \param[in] section XPath to query or modify - * \param[in] req unused * \param[in] input Portion of CIB to modify (used with * PCMK__CIB_REQUEST_CREATE, * PCMK__CIB_REQUEST_MODIFY, and @@ -382,9 +381,8 @@ cib__process_create(const char *op, int options, const char *section, * \return Legacy Pacemaker return code */ static int -process_xpath(const char *op, int options, const char *section, - const xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) +process_xpath(const char *op, int options, const char *section, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { int num_results = 0; int rc = pcmk_ok; @@ -574,7 +572,7 @@ cib__process_delete(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, req, input, existing_cib, + return process_xpath(op, options, section, input, existing_cib, result_cib, answer); } @@ -624,7 +622,7 @@ cib__process_modify(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, req, input, existing_cib, + return process_xpath(op, options, section, input, existing_cib, result_cib, answer); } @@ -643,7 +641,7 @@ cib__process_modify(const char *op, int options, const char *section, } tmp_section = pcmk__xe_create(NULL, section); - process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section, + process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, tmp_section, NULL, result_cib, answer); pcmk__xml_free(tmp_section); @@ -679,7 +677,7 @@ cib__process_query(const char *op, int options, const char *section, pcmk__s(section, "unspecified")); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, req, input, existing_cib, + return process_xpath(op, options, section, input, existing_cib, result_cib, answer); } @@ -815,7 +813,7 @@ cib__process_replace(const char *op, int options, const char *section, pcmk__s(section, "unspecified")); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, req, input, existing_cib, + return process_xpath(op, options, section, input, existing_cib, result_cib, answer); } From 3e03ee34b57f65eb0748e7775463bd4f16c9ee8c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 03:49:59 -0800 Subject: [PATCH 169/350] Refactor: libcib: New process_query_xpath() I don't mind a bit of duplication for now, to avoid a monolithic function. This is mostly just splitting out the op-specific logic. We'll make further improvements soon. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 207 +++++++++++++++++++++++++--------------------- 1 file changed, 115 insertions(+), 92 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 23b3214ff1a..71a68b98f67 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -361,61 +361,44 @@ cib__process_create(const char *op, int options, const char *section, /*! * \internal - * \brief Query or modify a CIB + * \brief Modify a CIB using XPath as section * - * \param[in] op PCMK__CIB_REQUEST_* operation to be performed - * \param[in] options Flag set of \c cib_call_options - * \param[in] section XPath to query or modify - * \param[in] input Portion of CIB to modify (used with - * PCMK__CIB_REQUEST_CREATE, - * PCMK__CIB_REQUEST_MODIFY, and - * PCMK__CIB_REQUEST_REPLACE) - * \param[in,out] existing_cib Input CIB (used with PCMK__CIB_REQUEST_QUERY) - * \param[in,out] result_cib CIB copy to make changes in (used with - * PCMK__CIB_REQUEST_CREATE, - * PCMK__CIB_REQUEST_MODIFY, - * PCMK__CIB_REQUEST_DELETE, and - * PCMK__CIB_REQUEST_REPLACE) - * \param[out] answer Query result (used with PCMK__CIB_REQUEST_QUERY) + * \param[in] op \c PCMK__CIB_REQUEST_* operation to be performed + * \param[in] options Flag set of \c cib_call_options + * \param[in] xpath XPath to query or modify + * \param[in] input Portion of CIB to modify (used with + * \c PCMK__CIB_REQUEST_CREATE, + * \c PCMK__CIB_REQUEST_MODIFY, and + * \c PCMK__CIB_REQUEST_REPLACE) + * \param[in,out] result_cib CIB copy to make changes in * * \return Legacy Pacemaker return code */ static int -process_xpath(const char *op, int options, const char *section, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +process_xpath(const char *op, int options, const char *xpath, xmlNode *input, + xmlNode **result_cib) { int num_results = 0; int rc = pcmk_ok; - bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none); bool delete_multiple = pcmk__is_set(options, cib_multiple) && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none); - xmlXPathObject *xpathObj = NULL; + xmlXPathObject *xpathObj = pcmk__xpath_search((*result_cib)->doc, xpath); pcmk__trace("Processing \"%s\" event", op); - if (is_query) { - xpathObj = pcmk__xpath_search(existing_cib->doc, section); - } else { - xpathObj = pcmk__xpath_search((*result_cib)->doc, section); - } - num_results = pcmk__xpath_num_results(xpathObj); if (num_results == 0) { if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { - pcmk__debug("%s was already removed", section); + pcmk__debug("%s was already removed", xpath); } else { - pcmk__debug("%s: %s does not exist", op, section); + pcmk__debug("%s: %s does not exist", op, xpath); rc = -ENXIO; } goto done; } - if (is_query && (num_results > 1)) { - *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); - } - for (int i = 0; i < num_results; i++) { xmlNode *match = NULL; xmlChar *path = NULL; @@ -447,15 +430,14 @@ process_xpath(const char *op, int options, const char *section, xmlNode *input, } path = xmlGetNodePath(match); - pcmk__debug("Processing %s op for %s with %s", op, section, path); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { if (match == *result_cib) { /* Attempting to delete the whole "/cib" */ pcmk__warn("Cannot perform %s for %s: The xpath is addressing " - "the whole /cib", - op, section); + "the whole /cib", op, xpath); rc = -EINVAL; break; } @@ -482,55 +464,6 @@ process_xpath(const char *op, int options, const char *section, xmlNode *input, pcmk__xml_copy(match, input); break; - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { - if (pcmk__is_set(options, cib_no_children)) { - xmlNode *shallow = pcmk__xe_create(*answer, - (const char *) match->name); - - pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); - - if (*answer == NULL) { - *answer = shallow; - } - - } else if (pcmk__is_set(options, cib_xpath_address)) { - // @COMPAT cib_xpath_address is deprecated since 3.0.2 - char *path = NULL; - xmlNode *parent = match; - - while (parent && parent->type == XML_ELEMENT_NODE) { - const char *id = pcmk__xe_get(parent, PCMK_XA_ID); - char *new_path = NULL; - - if (id) { - new_path = - pcmk__assert_asprintf("/%s[@" PCMK_XA_ID "='%s']%s", - parent->name, id, - pcmk__s(path, "")); - } else { - new_path = pcmk__assert_asprintf("/%s%s", parent->name, - pcmk__s(path, "")); - } - free(path); - path = new_path; - parent = parent->parent; - } - pcmk__trace("Got: %s", path); - - if (*answer == NULL) { - *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); - } - parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH); - pcmk__xe_set(parent, PCMK_XA_ID, path); - free(path); - - } else if (*answer) { - pcmk__xml_copy(*answer, match); - - } else { - *answer = match; - } - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { xmlNode *parent = match->parent; @@ -572,8 +505,7 @@ cib__process_delete(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, existing_cib, - result_cib, answer); + return process_xpath(op, options, section, input, result_cib); } if (input == NULL) { @@ -622,8 +554,7 @@ cib__process_modify(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, existing_cib, - result_cib, answer); + return process_xpath(op, options, section, input, result_cib); } if (input == NULL) { @@ -642,7 +573,7 @@ cib__process_modify(const char *op, int options, const char *section, tmp_section = pcmk__xe_create(NULL, section); process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, tmp_section, - NULL, result_cib, answer); + result_cib); pcmk__xml_free(tmp_section); obj_root = pcmk_find_cib_element(*result_cib, section); @@ -665,6 +596,100 @@ cib__process_modify(const char *op, int options, const char *section, return pcmk_ok; } +static int +process_query_xpath(const char *op, int options, const char *xpath, + xmlNode *existing_cib, xmlNode **answer) +{ + int num_results = 0; + int rc = pcmk_ok; + xmlXPathObject *xpath_obj = pcmk__xpath_search(existing_cib->doc, xpath); + + pcmk__trace("Processing '%s' event", op); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s: %s does not exist", op, xpath); + rc = -ENXIO; + goto done; + } + + if (num_results > 1) { + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); + } + + for (int i = 0; i < num_results; i++) { + xmlChar *path = NULL; + xmlNode *match = pcmk__xpath_result(xpath_obj, i); + + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + if (pcmk__is_set(options, cib_no_children)) { + xmlNode *shallow = pcmk__xe_create(*answer, + (const char *) match->name); + + pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); + + if (*answer == NULL) { + *answer = shallow; + } + + continue; + } + + if (pcmk__is_set(options, cib_xpath_address)) { + // @COMPAT cib_xpath_address is deprecated since 3.0.2 + char *path = NULL; + xmlNode *parent = match; + + while ((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) { + const char *id = pcmk__xe_get(parent, PCMK_XA_ID); + char *new_path = NULL; + + if (id != NULL) { + new_path = pcmk__assert_asprintf("/%s[@" PCMK_XA_ID "='%s']" + "%s", parent->name, id, + pcmk__s(path, "")); + } else { + new_path = pcmk__assert_asprintf("/%s%s", parent->name, + pcmk__s(path, "")); + } + + free(path); + path = new_path; + parent = parent->parent; + } + + pcmk__trace("Got: %s", path); + + if (*answer == NULL) { + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); + } + + parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH); + pcmk__xe_set(parent, PCMK_XA_ID, path); + free(path); + continue; + } + + if (*answer != NULL) { + pcmk__xml_copy(*answer, match); + continue; + } + + *answer = match; + } + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + int cib__process_query(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -677,8 +702,7 @@ cib__process_query(const char *op, int options, const char *section, pcmk__s(section, "unspecified")); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, existing_cib, - result_cib, answer); + return process_query_xpath(op, options, section, existing_cib, answer); } CRM_CHECK(*answer == NULL, pcmk__xml_free(*answer)); @@ -813,8 +837,7 @@ cib__process_replace(const char *op, int options, const char *section, pcmk__s(section, "unspecified")); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, existing_cib, - result_cib, answer); + return process_xpath(op, options, section, input, result_cib); } *answer = NULL; From 0b03549c0b37481fe4c82ed8e5f80bfbbfd56272 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 04:03:17 -0800 Subject: [PATCH 170/350] Refactor: libcib: New process_delete_xpath() I don't mind a bit of duplication for now, to avoid a monolithic function. This is mostly just splitting out the op-specific logic. We'll make further improvements soon. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 127 +++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 71a68b98f67..f179c5b84c3 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -366,10 +366,7 @@ cib__process_create(const char *op, int options, const char *section, * \param[in] op \c PCMK__CIB_REQUEST_* operation to be performed * \param[in] options Flag set of \c cib_call_options * \param[in] xpath XPath to query or modify - * \param[in] input Portion of CIB to modify (used with - * \c PCMK__CIB_REQUEST_CREATE, - * \c PCMK__CIB_REQUEST_MODIFY, and - * \c PCMK__CIB_REQUEST_REPLACE) + * \param[in] input Portion of CIB to modify * \param[in,out] result_cib CIB copy to make changes in * * \return Legacy Pacemaker return code @@ -380,22 +377,14 @@ process_xpath(const char *op, int options, const char *xpath, xmlNode *input, { int num_results = 0; int rc = pcmk_ok; - bool delete_multiple = pcmk__is_set(options, cib_multiple) - && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, - pcmk__str_none); xmlXPathObject *xpathObj = pcmk__xpath_search((*result_cib)->doc, xpath); pcmk__trace("Processing \"%s\" event", op); num_results = pcmk__xpath_num_results(xpathObj); if (num_results == 0) { - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { - pcmk__debug("%s was already removed", xpath); - - } else { - pcmk__debug("%s: %s does not exist", op, xpath); - rc = -ENXIO; - } + pcmk__debug("%s: %s does not exist", op, xpath); + rc = -ENXIO; goto done; } @@ -403,28 +392,7 @@ process_xpath(const char *op, int options, const char *xpath, xmlNode *input, xmlNode *match = NULL; xmlChar *path = NULL; - /* If we're deleting multiple nodes, go in reverse document order. - * If we go in forward order and the node set contains both a parent and - * its descendant, then deleting the parent frees the descendant before - * the loop reaches the descendant. This is a use-after-free error. - * - * @COMPAT cib_multiple is only ever used with delete operations. The - * correct order to process multiple nodes for operations other than - * query (forward) and delete (reverse) is less clear but likely should - * be reverse. If we ever replace the CIB public API with libpacemaker - * functions, revisit this. For now, we keep forward order for other - * operations to preserve backward compatibility, even though external - * callers of other ops with cib_multiple might segfault. - * - * For more info, see comment in xpath2.c:update_xpath_nodes() in - * libxml2. - */ - if (delete_multiple) { - match = pcmk__xpath_result(xpathObj, num_results - 1 - i); - } else { - match = pcmk__xpath_result(xpathObj, i); - } - + match = pcmk__xpath_result(xpathObj, i); if (match == NULL) { continue; } @@ -433,21 +401,7 @@ process_xpath(const char *op, int options, const char *xpath, xmlNode *input, pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) { - if (match == *result_cib) { - /* Attempting to delete the whole "/cib" */ - pcmk__warn("Cannot perform %s for %s: The xpath is addressing " - "the whole /cib", op, xpath); - rc = -EINVAL; - break; - } - - pcmk__xml_free(match); - if (!pcmk__is_set(options, cib_multiple)) { - break; - } - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) { + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) { uint32_t flags = pcmk__xaf_none; if (pcmk__is_set(options, cib_score_update)) { @@ -482,6 +436,75 @@ process_xpath(const char *op, int options, const char *xpath, xmlNode *input, return rc; } +static int +process_delete_xpath(const char *op, int options, const char *xpath, + xmlNode **result_cib) +{ + int num_results = 0; + int rc = pcmk_ok; + + xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + + pcmk__trace("Processing '%s' event", op); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s was already removed", xpath); + goto done; + } + + for (int i = 0; i < num_results; i++) { + xmlNode *match = NULL; + xmlChar *path = NULL; + + /* If we're deleting multiple nodes, go in reverse document order. + * If we go in forward order and the node set contains both a parent and + * its descendant, then deleting the parent frees the descendant before + * the loop reaches the descendant. This is a use-after-free error. + * + * @COMPAT cib_multiple is only ever used with delete operations. The + * correct order to process multiple nodes for operations other than + * query (forward) and delete (reverse) is less clear but likely should + * be reverse. If we ever replace the CIB public API with libpacemaker + * functions, revisit this. For now, we keep forward order for other + * operations to preserve backward compatibility, even though external + * callers of other ops with cib_multiple might segfault. + * + * For more info, see comment in xpath2.c:update_xpath_nodes() in + * libxml2. + */ + if (pcmk__is_set(options, cib_multiple)) { + match = pcmk__xpath_result(xpath_obj, num_results - 1 - i); + } else { + match = pcmk__xpath_result(xpath_obj, i); + } + + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + if (match == *result_cib) { + pcmk__warn("Cannot perform %s for %s: the XPath is addressing the " + "whole /cib", op, xpath); + rc = -EINVAL; + break; + } + + pcmk__xml_free(match); + if (!pcmk__is_set(options, cib_multiple)) { + break; + } + } + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + static int delete_child(xmlNode *child, void *userdata) { @@ -505,7 +528,7 @@ cib__process_delete(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, result_cib); + return process_delete_xpath(op, options, section, result_cib); } if (input == NULL) { From d451b83b4660aa09d8267fa55ba582f6e9e0b24d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 12:07:55 -0800 Subject: [PATCH 171/350] Refactor: libcib: New process_modify_xpath() I don't mind a bit of duplication for now, to avoid a monolithic function. This is mostly just splitting out the op-specific logic. We'll make further improvements soon. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 62 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index f179c5b84c3..c280eb1dcad 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -401,20 +401,7 @@ process_xpath(const char *op, int options, const char *xpath, xmlNode *input, pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) { - uint32_t flags = pcmk__xaf_none; - - if (pcmk__is_set(options, cib_score_update)) { - flags |= pcmk__xaf_score_update; - } - - if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { - rc = -ENXIO; - } else if (!pcmk__is_set(options, cib_multiple)) { - break; - } - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) { + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) { pcmk__xml_copy(match, input); break; @@ -566,6 +553,51 @@ cib__process_erase(const char *op, int options, const char *section, return result; } +static int +process_modify_xpath(const char *op, int options, const char *xpath, + xmlNode *input, xmlNode **result_cib) +{ + int num_results = 0; + int rc = pcmk_ok; + xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + const bool score = pcmk__is_set(options, cib_score_update); + const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + + pcmk__trace("Processing \"%s\" event", op); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s: %s does not exist", op, xpath); + rc = -ENXIO; + goto done; + } + + for (int i = 0; i < num_results; i++) { + xmlNode *match = NULL; + xmlChar *path = NULL; + + match = pcmk__xpath_result(xpath_obj, i); + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { + rc = -ENXIO; + + } else if (!pcmk__is_set(options, cib_multiple)) { + break; + } + } + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + int cib__process_modify(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -577,7 +609,7 @@ cib__process_modify(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, result_cib); + return process_modify_xpath(op, options, section, input, result_cib); } if (input == NULL) { From 5238644db743ba009b62fb0c9e1857645dd04029 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 12:20:51 -0800 Subject: [PATCH 172/350] Refactor: libcib: New process_create_xpath() I don't mind a bit of duplication for now, to avoid a monolithic function. This is mostly just splitting out the op-specific logic. We'll make further improvements soon. We do go ahead and take advantage of the fact that process_xpath() always broke out of the for-loop after the first iteration for create ops. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 48 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index c280eb1dcad..d5c62ec61de 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -287,6 +287,41 @@ update_results(xmlNode *failed, xmlNode *target, const char *operation, int rc) pcmk__warn("Action %s failed: %s", operation, pcmk_rc_str(rc)); } +static int +process_create_xpath(const char *op, const char *xpath, xmlNode *input, + xmlNode **result_cib) +{ + int num_results = 0; + int rc = pcmk_ok; + xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + xmlNode *match = NULL; + xmlChar *path = NULL; + + pcmk__trace("Processing \"%s\" event", op); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s: %s does not exist", op, xpath); + rc = -ENXIO; + goto done; + } + + match = pcmk__xpath_result(xpath_obj, 0); + if (match == NULL) { + goto done; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + pcmk__xml_copy(match, input); + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + int cib__process_create(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -401,12 +436,7 @@ process_xpath(const char *op, int options, const char *xpath, xmlNode *input, pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) { - pcmk__xml_copy(match, input); - break; - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, - pcmk__str_none)) { + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { xmlNode *parent = match->parent; pcmk__xml_free(match); @@ -627,8 +657,10 @@ cib__process_modify(const char *op, int options, const char *section, } tmp_section = pcmk__xe_create(NULL, section); - process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, tmp_section, - result_cib); + + // @TODO This feels hacky and is the only call to process_create_xpath() + process_create_xpath(PCMK__CIB_REQUEST_CREATE, path, tmp_section, + result_cib); pcmk__xml_free(tmp_section); obj_root = pcmk_find_cib_element(*result_cib, section); From 78662453d1b2ad2976e079aa76738f070dd12b22 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 12:24:47 -0800 Subject: [PATCH 173/350] Refactor: libcib: New process_replace_xpath() I don't mind a bit of duplication for now, to avoid a monolithic function. This is mostly just splitting out the op-specific logic. We'll make further improvements soon. This completes splitting out all the pieces of process_xpath(), so we can drop it. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 107 ++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 60 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index d5c62ec61de..7d245eb5e04 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -394,65 +394,6 @@ cib__process_create(const char *op, int options, const char *section, return result; } -/*! - * \internal - * \brief Modify a CIB using XPath as section - * - * \param[in] op \c PCMK__CIB_REQUEST_* operation to be performed - * \param[in] options Flag set of \c cib_call_options - * \param[in] xpath XPath to query or modify - * \param[in] input Portion of CIB to modify - * \param[in,out] result_cib CIB copy to make changes in - * - * \return Legacy Pacemaker return code - */ -static int -process_xpath(const char *op, int options, const char *xpath, xmlNode *input, - xmlNode **result_cib) -{ - int num_results = 0; - int rc = pcmk_ok; - xmlXPathObject *xpathObj = pcmk__xpath_search((*result_cib)->doc, xpath); - - pcmk__trace("Processing \"%s\" event", op); - - num_results = pcmk__xpath_num_results(xpathObj); - if (num_results == 0) { - pcmk__debug("%s: %s does not exist", op, xpath); - rc = -ENXIO; - goto done; - } - - for (int i = 0; i < num_results; i++) { - xmlNode *match = NULL; - xmlChar *path = NULL; - - match = pcmk__xpath_result(xpathObj, i); - if (match == NULL) { - continue; - } - - path = xmlGetNodePath(match); - pcmk__debug("Processing %s op for %s with %s", op, xpath, path); - free(path); - - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { - xmlNode *parent = match->parent; - - pcmk__xml_free(match); - pcmk__xml_copy(parent, input); - - if (!pcmk__is_set(options, cib_multiple)) { - break; - } - } - } - -done: - xmlXPathFreeObject(xpathObj); - return rc; -} - static int process_delete_xpath(const char *op, int options, const char *xpath, xmlNode **result_cib) @@ -913,6 +854,52 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, return result; } +static int +process_replace_xpath(const char *op, int options, const char *xpath, + xmlNode *input, xmlNode **result_cib) +{ + int num_results = 0; + int rc = pcmk_ok; + xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + + pcmk__trace("Processing \"%s\" event", op); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s: %s does not exist", op, xpath); + rc = -ENXIO; + goto done; + } + + for (int i = 0; i < num_results; i++) { + xmlNode *match = NULL; + xmlNode *parent = NULL; + xmlChar *path = NULL; + + match = pcmk__xpath_result(xpath_obj, i); + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + parent = match->parent; + + pcmk__xml_free(match); + pcmk__xml_copy(parent, input); + + if (!pcmk__is_set(options, cib_multiple)) { + break; + } + } + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + int cib__process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, @@ -924,7 +911,7 @@ cib__process_replace(const char *op, int options, const char *section, pcmk__s(section, "unspecified")); if (pcmk__is_set(options, cib_xpath)) { - return process_xpath(op, options, section, input, result_cib); + return process_replace_xpath(op, options, section, input, result_cib); } *answer = NULL; From b0da5c9c542aec058367c126b9d607b0b4e12b88 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 13:31:55 -0800 Subject: [PATCH 174/350] Refactor: libcib: Return standard RC from cib__op_fn_t Public API return codes and backward compatibility in mixed-version clusters *should* be unaffected. This commit calls pcmk_rc2legacy() when setting PCMK__XA_CIB_UPGRADE_RC and when returning from cib_apply_patch_event(). The only intentional changes here besides using standard return codes internally are as follows: * Rename result variables to rc if the line is already changing. * Unindent a block of cib__process_replace(). Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 75 ++++++----- lib/cib/cib_ops.c | 125 ++++++++---------- lib/cib/cib_utils.c | 4 +- .../pcmk_resource/pcmk_resource_delete_test.c | 8 +- 4 files changed, 100 insertions(+), 112 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 18d4958bc6b..cfa0220a2ad 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -54,7 +54,7 @@ static int sync_in_progress = 0; * \param[in] result_cib Ignored * \param[in] answer Ignored * - * \return \c -EINVAL + * \return \c EINVAL * * \note This is unimplemented and simply returns an error. */ @@ -66,7 +66,7 @@ based_process_abs_delete(const char *op, int options, const char *section, /* @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; + return EINVAL; } int @@ -74,7 +74,7 @@ based_process_apply_patch(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; if (sync_in_progress > MAX_DIFF_RETRY) { /* Don't ignore diffs forever; the last request may have been lost. @@ -95,21 +95,21 @@ based_process_apply_patch(const char *op, int options, const char *section, "progress)", source[0], source[1], source[2], target[0], target[1], target[2]); - return -pcmk_err_diff_resync; + return pcmk_rc_diff_resync; } rc = cib__process_apply_patch(op, options, section, req, input, existing_cib, result_cib, answer); - pcmk__trace("result: %s (%d), %s", pcmk_strerror(rc), rc, + pcmk__trace("result: %s (%d), %s", pcmk_rc_str(rc), rc, (based_is_primary? "primary": "secondary")); - if ((rc == -pcmk_err_diff_resync) && !based_is_primary) { + if ((rc == pcmk_rc_diff_resync) && !based_is_primary) { pcmk__xml_free(*result_cib); *result_cib = NULL; send_sync_request(); - } else if (rc == -pcmk_err_diff_resync) { - rc = -pcmk_err_diff_failed; + } else if (rc == pcmk_rc_diff_resync) { + rc = pcmk_rc_diff_failed; } return rc; @@ -131,7 +131,6 @@ based_process_commit_transact(const char *op, int options, const char *section, pcmk__client_t *client = pcmk__find_client_by_id(client_id); rc = based_commit_transaction(input, client, origin, result_cib); - if (rc != pcmk_rc_ok) { char *source = based_transaction_source_str(client, origin); @@ -139,7 +138,8 @@ based_process_commit_transact(const char *op, int options, const char *section, pcmk_rc_str(rc)); free(source); } - return pcmk_rc2legacy(rc); + + return rc; } int @@ -150,7 +150,7 @@ based_process_is_primary(const char *op, int options, const char *section, pcmk__trace("Processing \"%s\" event", op); // @COMPAT Pacemaker Remote clients <3.0.0 may send this - return (based_is_primary? pcmk_ok : -EPERM); + return (based_is_primary? pcmk_rc_ok : EPERM); } // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed @@ -161,7 +161,7 @@ based_process_noop(const char *op, int options, const char *section, { pcmk__trace("Processing \"%s\" event", op); *answer = NULL; - return pcmk_ok; + return pcmk_rc_ok; } int @@ -210,7 +210,7 @@ based_process_ping(const char *op, int options, const char *section, free(digest); - return pcmk_ok; + return pcmk_rc_ok; } int @@ -228,7 +228,7 @@ based_process_primary(const char *op, int options, const char *section, pcmk__debug("We are still in R/W mode"); } - return pcmk_ok; + return pcmk_rc_ok; } int @@ -239,7 +239,7 @@ based_process_replace(const char *op, int options, const char *section, int rc = cib__process_replace(op, options, section, req, input, existing_cib, result_cib, answer); - if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { + if ((rc == pcmk_rc_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { sync_in_progress = 0; } return rc; @@ -263,13 +263,13 @@ based_process_schemas(const char *op, int options, const char *section, data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); if (data == NULL) { pcmk__warn("No data specified in request"); - return -EPROTO; + return EPROTO; } after_ver = pcmk__xe_get(data, PCMK_XA_VERSION); if (after_ver == NULL) { pcmk__warn("No version specified in request"); - return -EPROTO; + return EPROTO; } /* The client requested all schemas after the latest one we know about, which @@ -277,7 +277,7 @@ based_process_schemas(const char *op, int options, const char *section, * with no schemas. */ if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) { - return pcmk_ok; + return pcmk_rc_ok; } schemas = pcmk__schema_files_later_than(after_ver); @@ -288,7 +288,7 @@ based_process_schemas(const char *op, int options, const char *section, g_list_free_full(schemas, free); g_list_free_full(already_included, free); - return pcmk_ok; + return pcmk_rc_ok; } int @@ -306,7 +306,7 @@ based_process_secondary(const char *op, int options, const char *section, pcmk__debug("We are still in R/O mode"); } - return pcmk_ok; + return pcmk_rc_ok; } int @@ -320,17 +320,17 @@ based_process_shutdown(const char *op, int options, const char *section, if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { pcmk__info("Peer %s is requesting to shut down", host); - return pcmk_ok; + return pcmk_rc_ok; } if (!cib_shutdown_flag) { pcmk__err("Peer %s mistakenly thinks we wanted to shut down", host); - return -EINVAL; + return EINVAL; } pcmk__info("Exiting after %s acknowledged our shutdown request", host); based_terminate(CRM_EX_OK); - return pcmk_ok; + return pcmk_rc_ok; } int @@ -354,7 +354,7 @@ based_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; *answer = NULL; @@ -382,17 +382,16 @@ based_process_upgrade(const char *op, int options, const char *section, pcmk__info("Rejecting upgrade request from %s: No " PCMK_XA_VALIDATE_WITH, host); - return -pcmk_err_cib_corrupt; + return pcmk_rc_cib_corrupt; } rc = pcmk__update_schema(&scratch, NULL, true, true); - rc = pcmk_rc2legacy(rc); 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_ok; + rc = pcmk_rc_ok; pcmk__notice("Upgrade request from %s verified", host); pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); @@ -407,11 +406,11 @@ based_process_upgrade(const char *op, int options, const char *section, pcmk__xml_free(up); - } else if(rc == pcmk_ok) { - rc = -pcmk_err_schema_unchanged; + } else if (rc == pcmk_rc_ok) { + rc = pcmk_rc_schema_unchanged; } - if (rc != pcmk_ok) { + if (rc != pcmk_rc_ok) { // Notify originating peer so it can notify its local clients pcmk__node_status_t *origin = NULL; @@ -419,8 +418,7 @@ based_process_upgrade(const char *op, int options, const char *section, pcmk__node_search_cluster_member); pcmk__info("Rejecting upgrade request from %s: %s " - QB_XS " rc=%d peer=%s", - host, pcmk_strerror(rc), rc, + QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, ((origin != NULL)? origin->name : "lost")); if (origin) { @@ -433,7 +431,8 @@ based_process_upgrade(const char *op, int options, const char *section, 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, rc); + 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); } @@ -498,7 +497,7 @@ cib_msg_copy(xmlNode *msg) int sync_our_cib(xmlNode *request, bool all) { - int result = pcmk_ok; + int rc = pcmk_rc_ok; char *digest = NULL; const char *host = pcmk__xe_get(request, PCMK__XA_SRC); const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); @@ -506,8 +505,8 @@ sync_our_cib(xmlNode *request, bool all) xmlNode *replace_request = NULL; xmlNode *wrapper = NULL; - CRM_CHECK(the_cib != NULL, return -EINVAL); - CRM_CHECK(all || (host != NULL), return -EINVAL); + CRM_CHECK(the_cib != NULL, return EINVAL); + CRM_CHECK(all || (host != NULL), return EINVAL); pcmk__debug("Syncing CIB to %s", (all? "all peers" : host)); @@ -538,9 +537,9 @@ sync_our_cib(xmlNode *request, bool all) peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); } if (!pcmk__cluster_send_message(peer, pcmk_ipc_based, replace_request)) { - result = -ENOTCONN; + rc = ENOTCONN; } pcmk__xml_free(replace_request); free(digest); - return result; + return rc; } diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 7d245eb5e04..6bd0109a784 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -181,10 +181,10 @@ cib__process_apply_patch(const char *op, int options, const char *section, } *result_cib = pcmk__xml_copy(NULL, existing_cib); - return xml_apply_patchset(*result_cib, input, TRUE); + return pcmk_legacy2rc(xml_apply_patchset(*result_cib, input, true)); } -static int +static void update_counter(xmlNode *xml_obj, const char *field, bool reset) { char *new_value = NULL; @@ -207,8 +207,6 @@ update_counter(xmlNode *xml_obj, const char *field, bool reset) free(new_value); free(old_value); - - return pcmk_ok; } int @@ -216,15 +214,13 @@ cib__process_bump(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int result = pcmk_ok; - pcmk__trace("Processing %s for epoch='%s'", op, pcmk__s(pcmk__xe_get(existing_cib, PCMK_XA_EPOCH), "")); *answer = NULL; update_counter(*result_cib, PCMK_XA_EPOCH, false); - return result; + return pcmk_rc_ok; } static int @@ -234,19 +230,19 @@ add_cib_object(xmlNode *parent, xmlNode *new_obj) const char *object_id = NULL; if ((parent == NULL) || (new_obj == NULL)) { - return -EINVAL; + return EINVAL; } object_name = (const char *) new_obj->name; if (object_name == NULL) { - return -EINVAL; + return EINVAL; } object_id = pcmk__xe_id(new_obj); if (pcmk__xe_first_child(parent, object_name, ((object_id != NULL)? PCMK_XA_ID : NULL), object_id)) { - return -EEXIST; + return EEXIST; } if (object_id != NULL) { @@ -268,7 +264,7 @@ add_cib_object(xmlNode *parent, xmlNode *new_obj) (void *) PCMK__XA_REPLACE); pcmk__xml_copy(parent, new_obj); - return pcmk_ok; + return pcmk_rc_ok; } static void @@ -292,7 +288,7 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, xmlNode **result_cib) { int num_results = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); xmlNode *match = NULL; xmlChar *path = NULL; @@ -302,7 +298,7 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); - rc = -ENXIO; + rc = ENXIO; goto done; } @@ -328,7 +324,7 @@ cib__process_create(const char *op, int options, const char *section, xmlNode **result_cib, xmlNode **answer) { xmlNode *failed = NULL; - int result = pcmk_ok; + int rc = pcmk_rc_ok; xmlNode *update_section = NULL; pcmk__trace("Processing %s for %s section", op, @@ -347,7 +343,7 @@ cib__process_create(const char *op, int options, const char *section, if (input == NULL) { pcmk__err("Cannot perform modification with no data"); - return -EINVAL; + return EINVAL; } if (section == NULL) { @@ -365,25 +361,25 @@ cib__process_create(const char *op, int options, const char *section, for (a_child = pcmk__xml_first_child(input); a_child != NULL; a_child = pcmk__xml_next(a_child)) { - result = add_cib_object(update_section, a_child); - if (result != pcmk_ok) { - update_results(failed, a_child, op, pcmk_legacy2rc(result)); + rc = add_cib_object(update_section, a_child); + if (rc != pcmk_rc_ok) { + update_results(failed, a_child, op, rc); break; } } } else { - result = add_cib_object(update_section, input); - if (result != pcmk_ok) { - update_results(failed, input, op, pcmk_legacy2rc(result)); + rc = add_cib_object(update_section, input); + if (rc != pcmk_rc_ok) { + update_results(failed, input, op, rc); } } - if ((result == pcmk_ok) && (failed->children != NULL)) { - result = -EINVAL; + if ((rc == pcmk_rc_ok) && (failed->children != NULL)) { + rc = EINVAL; } - if (result != pcmk_ok) { + if (rc != pcmk_rc_ok) { pcmk__log_xml_err(failed, "CIB Update failures"); *answer = failed; @@ -391,7 +387,7 @@ cib__process_create(const char *op, int options, const char *section, pcmk__xml_free(failed); } - return result; + return rc; } static int @@ -399,7 +395,7 @@ process_delete_xpath(const char *op, int options, const char *xpath, xmlNode **result_cib) { int num_results = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); @@ -448,7 +444,7 @@ process_delete_xpath(const char *op, int options, const char *xpath, if (match == *result_cib) { pcmk__warn("Cannot perform %s for %s: the XPath is addressing the " "whole /cib", op, xpath); - rc = -EINVAL; + rc = EINVAL; break; } @@ -491,7 +487,7 @@ cib__process_delete(const char *op, int options, const char *section, if (input == NULL) { pcmk__err("Cannot perform modification with no data"); - return -EINVAL; + return EINVAL; } obj_root = pcmk_find_cib_element(*result_cib, section); @@ -501,7 +497,7 @@ cib__process_delete(const char *op, int options, const char *section, delete_child(input, obj_root); } - return pcmk_ok; + return pcmk_rc_ok; } int @@ -509,8 +505,6 @@ cib__process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int result = pcmk_ok; - pcmk__trace("Processing \"%s\" event", op); if (*result_cib != existing_cib) { @@ -521,7 +515,7 @@ cib__process_erase(const char *op, int options, const char *section, update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); *answer = NULL; - return result; + return pcmk_rc_ok; } static int @@ -529,7 +523,7 @@ process_modify_xpath(const char *op, int options, const char *xpath, xmlNode *input, xmlNode **result_cib) { int num_results = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); const bool score = pcmk__is_set(options, cib_score_update); const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); @@ -539,7 +533,7 @@ process_modify_xpath(const char *op, int options, const char *xpath, num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); - rc = -ENXIO; + rc = ENXIO; goto done; } @@ -557,7 +551,7 @@ process_modify_xpath(const char *op, int options, const char *xpath, free(path); if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) { - rc = -ENXIO; + rc = ENXIO; } else if (!pcmk__is_set(options, cib_multiple)) { break; @@ -585,7 +579,7 @@ cib__process_modify(const char *op, int options, const char *section, if (input == NULL) { pcmk__err("Cannot perform modification with no data"); - return -EINVAL; + return EINVAL; } obj_root = pcmk_find_cib_element(*result_cib, section); @@ -594,7 +588,7 @@ cib__process_modify(const char *op, int options, const char *section, const char *path = pcmk_cib_parent_name_for(section); if (path == NULL) { - return -EINVAL; + return EINVAL; } tmp_section = pcmk__xe_create(NULL, section); @@ -607,7 +601,7 @@ cib__process_modify(const char *op, int options, const char *section, obj_root = pcmk_find_cib_element(*result_cib, section); } - CRM_CHECK(obj_root != NULL, return -EINVAL); + CRM_CHECK(obj_root != NULL, return EINVAL); if (pcmk__is_set(options, cib_score_update)) { flags |= pcmk__xaf_score_update; @@ -617,11 +611,11 @@ cib__process_modify(const char *op, int options, const char *section, if (pcmk__is_set(options, cib_can_create)) { pcmk__xml_copy(obj_root, input); } else { - return -ENXIO; + return ENXIO; } } - return pcmk_ok; + return pcmk_rc_ok; } static int @@ -629,7 +623,7 @@ process_query_xpath(const char *op, int options, const char *xpath, xmlNode *existing_cib, xmlNode **answer) { int num_results = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(existing_cib->doc, xpath); pcmk__trace("Processing '%s' event", op); @@ -637,7 +631,7 @@ process_query_xpath(const char *op, int options, const char *xpath, num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); - rc = -ENXIO; + rc = ENXIO; goto done; } @@ -724,7 +718,7 @@ cib__process_query(const char *op, int options, const char *section, xmlNode **result_cib, xmlNode **answer) { xmlNode *obj_root = NULL; - int result = pcmk_ok; + int rc = pcmk_rc_ok; pcmk__trace("Processing %s for %s section", op, pcmk__s(section, "unspecified")); @@ -743,7 +737,7 @@ cib__process_query(const char *op, int options, const char *section, obj_root = pcmk_find_cib_element(existing_cib, section); if (obj_root == NULL) { - result = -ENXIO; + rc = ENXIO; } else if (pcmk__is_set(options, cib_no_children)) { xmlNode *shallow = pcmk__xe_create(*answer, @@ -756,12 +750,12 @@ cib__process_query(const char *op, int options, const char *section, *answer = obj_root; } - if (result == pcmk_ok && *answer == NULL) { + if ((rc == pcmk_rc_ok) && (*answer == NULL)) { pcmk__err("Error creating query response"); - result = -ENOMSG; + rc = ENOMSG; } - return result; + return rc; } static bool @@ -796,7 +790,7 @@ static int replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib) { - int result = pcmk_ok; + int rc = pcmk_rc_ok; int updates = 0; int epoch = 0; @@ -838,7 +832,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, replace_admin_epoch, replace_epoch, replace_updates, peer, admin_epoch, epoch, updates, reason); - result = -pcmk_err_old_data; + rc = pcmk_rc_old_data; } else { pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", admin_epoch, epoch, updates, @@ -851,7 +845,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, } *result_cib = pcmk__xml_copy(NULL, input); - return result; + return rc; } static int @@ -859,7 +853,7 @@ process_replace_xpath(const char *op, int options, const char *xpath, xmlNode *input, xmlNode **result_cib) { int num_results = 0; - int rc = pcmk_ok; + int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); pcmk__trace("Processing \"%s\" event", op); @@ -867,7 +861,7 @@ process_replace_xpath(const char *op, int options, const char *xpath, num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); - rc = -ENXIO; + rc = ENXIO; goto done; } @@ -905,7 +899,8 @@ cib__process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int result = pcmk_ok; + int rc = pcmk_rc_ok; + xmlNode *obj_root = NULL; pcmk__trace("Processing %s for %s section", op, pcmk__s(section, "unspecified")); @@ -917,7 +912,7 @@ cib__process_replace(const char *op, int options, const char *section, *answer = NULL; if (input == NULL) { - return -EINVAL; + return EINVAL; } if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { @@ -928,20 +923,17 @@ cib__process_replace(const char *op, int options, const char *section, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - result = replace_cib(req, input, existing_cib, result_cib); + return replace_cib(req, input, existing_cib, result_cib); + } - } else { - xmlNode *obj_root = NULL; + obj_root = pcmk_find_cib_element(*result_cib, section); - obj_root = pcmk_find_cib_element(*result_cib, section); - result = pcmk__xe_replace_match(obj_root, input); - result = pcmk_rc2legacy(result); - if (result != pcmk_ok) { - pcmk__trace("No matching object to replace"); - } + rc = pcmk__xe_replace_match(obj_root, input); + if (rc != pcmk_rc_ok) { + pcmk__trace("No matching object to replace"); } - return result; + return rc; } int @@ -949,7 +941,7 @@ cib__process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int rc = 0; + int rc = pcmk_rc_ok; const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); const char *original_schema = NULL; const char *new_schema = NULL; @@ -960,14 +952,13 @@ cib__process_upgrade(const char *op, int options, const char *section, original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); rc = pcmk__update_schema(result_cib, max_schema, true, !pcmk__is_set(options, cib_verbose)); - rc = pcmk_rc2legacy(rc); new_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); update_counter(*result_cib, PCMK_XA_EPOCH, true); update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true); - return pcmk_ok; + return pcmk_rc_ok; } return rc; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 69d6c1e3d8e..1419aacf600 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -201,7 +201,6 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, } rc = fn(op, call_options, section, req, input, cib_ro, &result_cib, output); - rc = pcmk_legacy2rc(rc); if (*output == NULL) { // Do nothing @@ -323,7 +322,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output); - rc = pcmk_legacy2rc(rc); /* If scratch points to a new object now (for example, after an erase * operation), then *current_cib should point to the same object. @@ -344,7 +342,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, rc = (*fn) (op, call_options, section, req, input, *current_cib, &scratch, output); - rc = pcmk_legacy2rc(rc); /* @TODO This appears to be a hack to determine whether scratch points * to a new object now, without saving the old pointer (which may be @@ -810,6 +807,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, if (input != NULL) { rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, output, NULL); + rc = pcmk_rc2legacy(rc); if (rc != pcmk_ok) { pcmk__debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), diff --git a/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c b/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c index 2492a5a411d..29b22d2e6cf 100644 --- a/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c +++ b/lib/pacemaker/tests/pcmk_resource/pcmk_resource_delete_test.c @@ -95,8 +95,8 @@ incorrect_type(void **state) xmlNode *xml = NULL; xmlNode *result = NULL; - /* cib__process_delete() returns pcmk_ok even if given the wrong type, so we - * have to do an xpath query of the CIB to make sure it's still there. + /* cib__process_delete() returns pcmk_rc_ok even if given the wrong type, so + * we have to do an XPath query of the CIB to make sure it's still there */ assert_int_equal(pcmk_resource_delete(&xml, "Fencing", "clone"), pcmk_rc_ok); pcmk__assert_validates(xml); @@ -129,8 +129,8 @@ unknown_resource(void **state) { xmlNode *xml = NULL; - /* cib__process_delete() returns pcmk_ok even if asked to delete something - * that doesn't exist. + /* cib__process_delete() returns pcmk_rc_ok even if asked to delete + * something that doesn't exist */ assert_int_equal(pcmk_resource_delete(&xml, "no_such_resource", "primitive"), pcmk_rc_ok); pcmk__assert_validates(xml); From 64e2dfdae9b236000a846ed50b0d125dae8640c8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 13:52:41 -0800 Subject: [PATCH 175/350] Refactor: libcib: New process_delete_section() Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 6bd0109a784..d9c31633e9a 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -392,12 +392,12 @@ cib__process_create(const char *op, int options, const char *section, static int process_delete_xpath(const char *op, int options, const char *xpath, - xmlNode **result_cib) + xmlNode *result_cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); pcmk__trace("Processing '%s' event", op); @@ -441,7 +441,7 @@ process_delete_xpath(const char *op, int options, const char *xpath, pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); - if (match == *result_cib) { + if (match == result_cib) { pcmk__warn("Cannot perform %s for %s: the XPath is addressing the " "whole /cib", op, xpath); rc = EINVAL; @@ -472,27 +472,21 @@ delete_child(xmlNode *child, void *userdata) return pcmk_rc_ok; } -int -cib__process_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) +static int +process_delete_section(const char *section, xmlNode *input, xmlNode *result_cib) { xmlNode *obj_root = NULL; - pcmk__trace("Processing \"%s\" event", op); - - if (pcmk__is_set(options, cib_xpath)) { - return process_delete_xpath(op, options, section, result_cib); - } - if (input == NULL) { - pcmk__err("Cannot perform modification with no data"); + pcmk__err("Cannot find matching section to delete with no input data"); return EINVAL; } - obj_root = pcmk_find_cib_element(*result_cib, section); + obj_root = pcmk_find_cib_element(result_cib, section); + if (pcmk__xe_is(input, section)) { pcmk__xe_foreach_child(input, NULL, delete_child, obj_root); + } else { delete_child(input, obj_root); } @@ -500,6 +494,20 @@ cib__process_delete(const char *op, int options, const char *section, return pcmk_rc_ok; } +int +cib__process_delete(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + pcmk__trace("Processing \"%s\" event", op); + + if (pcmk__is_set(options, cib_xpath)) { + return process_delete_xpath(op, options, section, *result_cib); + } + + return process_delete_section(section, input, *result_cib); +} + int cib__process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, From 2534f72bbb1cc8e80a9fd8e6a93466026c585392 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 14:06:43 -0800 Subject: [PATCH 176/350] Refactor: libcib: New process_modify_section() Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 58 ++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index d9c31633e9a..b5794d854a6 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -285,11 +285,11 @@ update_results(xmlNode *failed, xmlNode *target, const char *operation, int rc) static int process_create_xpath(const char *op, const char *xpath, xmlNode *input, - xmlNode **result_cib) + xmlNode *result_cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); xmlNode *match = NULL; xmlChar *path = NULL; @@ -528,11 +528,11 @@ cib__process_erase(const char *op, int options, const char *section, static int process_modify_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode **result_cib) + xmlNode *input, xmlNode *result_cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); const bool score = pcmk__is_set(options, cib_score_update); const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); @@ -571,26 +571,20 @@ process_modify_xpath(const char *op, int options, const char *xpath, return rc; } -int -cib__process_modify(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) +static int +process_modify_section(int options, const char *section, xmlNode *input, + xmlNode *result_cib) { + const bool score = pcmk__is_set(options, cib_score_update); + const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); xmlNode *obj_root = NULL; - uint32_t flags = pcmk__xaf_none; - - pcmk__trace("Processing \"%s\" event", op); - - if (pcmk__is_set(options, cib_xpath)) { - return process_modify_xpath(op, options, section, input, result_cib); - } if (input == NULL) { - pcmk__err("Cannot perform modification with no data"); + pcmk__err("Cannot complete CIB modify request with no input data"); return EINVAL; } - obj_root = pcmk_find_cib_element(*result_cib, section); + obj_root = pcmk_find_cib_element(result_cib, section); if (obj_root == NULL) { xmlNode *tmp_section = NULL; const char *path = pcmk_cib_parent_name_for(section); @@ -606,26 +600,38 @@ cib__process_modify(const char *op, int options, const char *section, result_cib); pcmk__xml_free(tmp_section); - obj_root = pcmk_find_cib_element(*result_cib, section); + obj_root = pcmk_find_cib_element(result_cib, section); } + // Should be impossible, as we just created this section if it didn't exist CRM_CHECK(obj_root != NULL, return EINVAL); - if (pcmk__is_set(options, cib_score_update)) { - flags |= pcmk__xaf_score_update; + if (pcmk__xe_update_match(obj_root, input, flags) == pcmk_rc_ok) { + return pcmk_rc_ok; } - if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) { - if (pcmk__is_set(options, cib_can_create)) { - pcmk__xml_copy(obj_root, input); - } else { - return ENXIO; - } + if (!pcmk__is_set(options, cib_can_create)) { + return ENXIO; } + pcmk__xml_copy(obj_root, input); return pcmk_rc_ok; } +int +cib__process_modify(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + pcmk__trace("Processing \"%s\" event", op); + + if (pcmk__is_set(options, cib_xpath)) { + return process_modify_xpath(op, options, section, input, *result_cib); + } + + return process_modify_section(options, section, input, *result_cib); +} + static int process_query_xpath(const char *op, int options, const char *xpath, xmlNode *existing_cib, xmlNode **answer) From acf1a330b8d16dcf38198e4ad6ccce1af73b80d6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 14:11:43 -0800 Subject: [PATCH 177/350] Refactor: libcib: New process_query_section() Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index b5794d854a6..89f378ad7e2 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -726,23 +726,14 @@ process_query_xpath(const char *op, int options, const char *xpath, return rc; } -int -cib__process_query(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) +static int +process_query_section(int options, const char *section, xmlNode *existing_cib, + xmlNode **answer) { xmlNode *obj_root = NULL; int rc = pcmk_rc_ok; - pcmk__trace("Processing %s for %s section", op, - pcmk__s(section, "unspecified")); - - if (pcmk__is_set(options, cib_xpath)) { - return process_query_xpath(op, options, section, existing_cib, answer); - } - - CRM_CHECK(*answer == NULL, pcmk__xml_free(*answer)); - *answer = NULL; + CRM_CHECK(*answer == NULL, g_clear_pointer(answer, pcmk__xml_free)); if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; @@ -772,6 +763,21 @@ cib__process_query(const char *op, int options, const char *section, return rc; } +int +cib__process_query(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode **answer) +{ + pcmk__trace("Processing %s for %s section", op, + pcmk__s(section, "unspecified")); + + if (pcmk__is_set(options, cib_xpath)) { + return process_query_xpath(op, options, section, existing_cib, answer); + } + + return process_query_section(options, section, existing_cib, answer); +} + static bool replace_cib_digest_matches(xmlNode *request, xmlNode *input) { From de6d6c3aecc4c518f3427690e74c5bcb560147bc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 14:14:35 -0800 Subject: [PATCH 178/350] Refactor: libcib: Drop shallow variable from process_query_section() *answer must be NULL at this point, so we can call pcmk__xe_create(NULL, ...) and assign the result to *answer. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 89f378ad7e2..ddd516be296 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -745,11 +745,8 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, rc = ENXIO; } else if (pcmk__is_set(options, cib_no_children)) { - xmlNode *shallow = pcmk__xe_create(*answer, - (const char *) obj_root->name); - - pcmk__xe_copy_attrs(shallow, obj_root, pcmk__xaf_none); - *answer = shallow; + *answer = pcmk__xe_create(NULL, (const char *) obj_root->name); + pcmk__xe_copy_attrs(*answer, obj_root, pcmk__xaf_none); } else { *answer = obj_root; From 80bbf6819f1cf4d54da9e0079ec05f378839fc37 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 14:22:02 -0800 Subject: [PATCH 179/350] Refactor: libcib: Drop dead code from process_query_section() If rc is pcmk_rc_ok, then *answer must be non-NULL by the time we reach the dropped block. And if obj_root is NULL, then rc is not pcmk_rc_ok, so we would never have entered that block; we would have just returned. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index ddd516be296..764b2a51430 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -731,7 +731,6 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, xmlNode **answer) { xmlNode *obj_root = NULL; - int rc = pcmk_rc_ok; CRM_CHECK(*answer == NULL, g_clear_pointer(answer, pcmk__xml_free)); @@ -740,11 +739,15 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, } obj_root = pcmk_find_cib_element(existing_cib, section); - if (obj_root == NULL) { - rc = ENXIO; + return ENXIO; + } - } else if (pcmk__is_set(options, cib_no_children)) { + /* We make a copy in the cib_no_children case but not in the other. We may + * be able to simplify the callers if we're able to do the same thing (copy + * or don't copy) for both. + */ + if (pcmk__is_set(options, cib_no_children)) { *answer = pcmk__xe_create(NULL, (const char *) obj_root->name); pcmk__xe_copy_attrs(*answer, obj_root, pcmk__xaf_none); @@ -752,12 +755,7 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, *answer = obj_root; } - if ((rc == pcmk_rc_ok) && (*answer == NULL)) { - pcmk__err("Error creating query response"); - rc = ENOMSG; - } - - return rc; + return pcmk_rc_ok; } int From 3c92bf3eb227e23f6452ecf7c788554cc3761a67 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 18:15:40 -0800 Subject: [PATCH 180/350] Refactor: libcib: New process_replace_section() Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 131 +++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 60 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 764b2a51430..edcf7098ab8 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -773,6 +773,52 @@ cib__process_query(const char *op, int options, const char *section, return process_query_section(options, section, existing_cib, answer); } +static int +process_replace_xpath(const char *op, int options, const char *xpath, + xmlNode *input, xmlNode *result_cib) +{ + int num_results = 0; + int rc = pcmk_rc_ok; + xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); + + pcmk__trace("Processing \"%s\" event", op); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s: %s does not exist", op, xpath); + rc = ENXIO; + goto done; + } + + for (int i = 0; i < num_results; i++) { + xmlNode *match = NULL; + xmlNode *parent = NULL; + xmlChar *path = NULL; + + match = pcmk__xpath_result(xpath_obj, i); + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + parent = match->parent; + + pcmk__xml_free(match); + pcmk__xml_copy(parent, input); + + if (!pcmk__is_set(options, cib_multiple)) { + break; + } + } + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + static bool replace_cib_digest_matches(xmlNode *request, xmlNode *input) { @@ -848,6 +894,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, replace_updates, peer, admin_epoch, epoch, updates, reason); rc = pcmk_rc_old_data; + } else { pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", admin_epoch, epoch, updates, @@ -858,54 +905,44 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, if (*result_cib != existing_cib) { pcmk__xml_free(*result_cib); } - *result_cib = pcmk__xml_copy(NULL, input); + *result_cib = pcmk__xml_copy(NULL, input); return rc; } static int -process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode **result_cib) +process_replace_section(const char *section, xmlNode *request, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, + xmlNode **answer) { - int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search((*result_cib)->doc, xpath); + xmlNode *obj_root = NULL; - pcmk__trace("Processing \"%s\" event", op); + // @TODO Drop this? + *answer = NULL; - num_results = pcmk__xpath_num_results(xpath_obj); - if (num_results == 0) { - pcmk__debug("%s: %s does not exist", op, xpath); - rc = ENXIO; - goto done; + if (input == NULL) { + pcmk__err("Cannot find matching section to replace with no input data"); + return EINVAL; } - for (int i = 0; i < num_results; i++) { - xmlNode *match = NULL; - xmlNode *parent = NULL; - xmlChar *path = NULL; + if (pcmk__xe_is(input, PCMK_XE_CIB)) { + return replace_cib(request, input, existing_cib, result_cib); + } - match = pcmk__xpath_result(xpath_obj, i); - if (match == NULL) { - continue; - } + if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei) + || pcmk__xe_is(input, section)) { - path = xmlGetNodePath(match); - pcmk__debug("Processing %s op for %s with %s", op, xpath, path); - free(path); - - parent = match->parent; + section = NULL; + } - pcmk__xml_free(match); - pcmk__xml_copy(parent, input); + obj_root = pcmk_find_cib_element(*result_cib, section); - if (!pcmk__is_set(options, cib_multiple)) { - break; - } + rc = pcmk__xe_replace_match(obj_root, input); + if (rc != pcmk_rc_ok) { + pcmk__trace("No matching object to replace"); } -done: - xmlXPathFreeObject(xpath_obj); return rc; } @@ -914,41 +951,15 @@ cib__process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int rc = pcmk_rc_ok; - xmlNode *obj_root = NULL; - pcmk__trace("Processing %s for %s section", op, pcmk__s(section, "unspecified")); if (pcmk__is_set(options, cib_xpath)) { - return process_replace_xpath(op, options, section, input, result_cib); - } - - *answer = NULL; - - if (input == NULL) { - return EINVAL; - } - - if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { - section = NULL; - - } else if (pcmk__xe_is(input, section)) { - section = NULL; - } - - if (pcmk__xe_is(input, PCMK_XE_CIB)) { - return replace_cib(req, input, existing_cib, result_cib); - } - - obj_root = pcmk_find_cib_element(*result_cib, section); - - rc = pcmk__xe_replace_match(obj_root, input); - if (rc != pcmk_rc_ok) { - pcmk__trace("No matching object to replace"); + return process_replace_xpath(op, options, section, input, *result_cib); } - return rc; + return process_replace_section(section, req, input, existing_cib, + result_cib, answer); } int From 9cb83a36de99dd3aed8230303b61ff5b6bbf72c8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 18:27:31 -0800 Subject: [PATCH 181/350] Log: libcib, based: Consolidate "Processing" trace logs ...into cib_perform_op()/cib__perform_query(). We lose some detail on a few of these unless we enable tracing for cib_perform_op()/cib__perform_query() (which will be very verbose), but there are several places where we log XML when processing a CIB operation. We probably have enough. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 9 --------- lib/cib/cib_ops.c | 32 -------------------------------- lib/cib/cib_utils.c | 16 ++++++++++++++-- 3 files changed, 14 insertions(+), 43 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index cfa0220a2ad..122e53f6c6c 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -147,8 +147,6 @@ based_process_is_primary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing \"%s\" event", op); - // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); } @@ -159,7 +157,6 @@ based_process_noop(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing \"%s\" event", op); *answer = NULL; return pcmk_rc_ok; } @@ -175,7 +172,6 @@ based_process_ping(const char *op, int options, const char *section, xmlNode *wrapper = NULL; - pcmk__trace("Processing \"%s\" event %s from %s", op, seq, host); *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); pcmk__xe_set(*answer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); @@ -218,8 +214,6 @@ based_process_primary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode ** answer) { - pcmk__trace("Processing \"%s\" event", op); - if (!based_is_primary) { pcmk__info("We are now in R/W mode"); based_is_primary = true; @@ -296,8 +290,6 @@ based_process_secondary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing \"%s\" event", op); - if (based_is_primary) { pcmk__info("We are now in R/O mode"); based_is_primary = false; @@ -376,7 +368,6 @@ based_process_upgrade(const char *op, int options, const char *section, const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); - pcmk__trace("Processing \"%s\" event", op); original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); if (original_schema == NULL) { pcmk__info("Rejecting upgrade request from %s: No " diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index edcf7098ab8..73cdda23f49 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -172,10 +172,6 @@ cib__process_apply_patch(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - const char *originator = pcmk__xe_get(req, PCMK__XA_SRC); - - pcmk__trace("Processing %s event from %s", op, originator); - if (*result_cib != existing_cib) { pcmk__xml_free(*result_cib); } @@ -214,9 +210,6 @@ cib__process_bump(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing %s for epoch='%s'", op, - pcmk__s(pcmk__xe_get(existing_cib, PCMK_XA_EPOCH), "")); - *answer = NULL; update_counter(*result_cib, PCMK_XA_EPOCH, false); @@ -293,8 +286,6 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, xmlNode *match = NULL; xmlChar *path = NULL; - pcmk__trace("Processing \"%s\" event", op); - num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -327,8 +318,6 @@ cib__process_create(const char *op, int options, const char *section, int rc = pcmk_rc_ok; xmlNode *update_section = NULL; - pcmk__trace("Processing %s for %s section", op, - pcmk__s(section, "unspecified")); if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; @@ -399,8 +388,6 @@ process_delete_xpath(const char *op, int options, const char *xpath, xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); - pcmk__trace("Processing '%s' event", op); - num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s was already removed", xpath); @@ -499,8 +486,6 @@ cib__process_delete(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing \"%s\" event", op); - if (pcmk__is_set(options, cib_xpath)) { return process_delete_xpath(op, options, section, *result_cib); } @@ -513,8 +498,6 @@ cib__process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing \"%s\" event", op); - if (*result_cib != existing_cib) { pcmk__xml_free(*result_cib); } @@ -536,8 +519,6 @@ process_modify_xpath(const char *op, int options, const char *xpath, const bool score = pcmk__is_set(options, cib_score_update); const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); - pcmk__trace("Processing \"%s\" event", op); - num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -623,8 +604,6 @@ cib__process_modify(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing \"%s\" event", op); - if (pcmk__is_set(options, cib_xpath)) { return process_modify_xpath(op, options, section, input, *result_cib); } @@ -640,8 +619,6 @@ process_query_xpath(const char *op, int options, const char *xpath, int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(existing_cib->doc, xpath); - pcmk__trace("Processing '%s' event", op); - num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -763,9 +740,6 @@ cib__process_query(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing %s for %s section", op, - pcmk__s(section, "unspecified")); - if (pcmk__is_set(options, cib_xpath)) { return process_query_xpath(op, options, section, existing_cib, answer); } @@ -781,8 +755,6 @@ process_replace_xpath(const char *op, int options, const char *xpath, int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); - pcmk__trace("Processing \"%s\" event", op); - num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -951,9 +923,6 @@ cib__process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - pcmk__trace("Processing %s for %s section", op, - pcmk__s(section, "unspecified")); - if (pcmk__is_set(options, cib_xpath)) { return process_replace_xpath(op, options, section, input, *result_cib); } @@ -973,7 +942,6 @@ cib__process_upgrade(const char *op, int options, const char *section, const char *new_schema = NULL; *answer = NULL; - pcmk__trace("Processing \"%s\" event with max=%s", op, max_schema); original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); rc = pcmk__update_schema(result_cib, max_schema, true, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 1419aacf600..423bb6d2196 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -181,7 +181,7 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, xmlNode *cib_filtered = NULL; xmlNode *result_cib = NULL; - pcmk__assert((fn != NULL) && (req != NULL) + pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) && (current_cib != NULL) && (*current_cib != NULL) && (output != NULL) && (*output == NULL)); @@ -200,6 +200,10 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, pcmk__log_xml_trace(cib_ro, "filtered"); } + pcmk__trace("Processing %s for section '%s', user '%s'", op, + pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); + pcmk__log_xml_trace(req, "request"); + rc = fn(op, call_options, section, req, input, cib_ro, &result_cib, output); if (*output == NULL) { @@ -294,7 +298,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, bool enable_acl = false; bool with_digest = false; - pcmk__assert((fn != NULL) && (req != NULL) + pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) && (current_cib != NULL) && (*current_cib != NULL) && (result_cib != NULL) && (*result_cib == NULL) @@ -321,6 +325,10 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__enable_acl(*current_cib, scratch, user); } + pcmk__trace("Processing %s for section '%s', user '%s'", op, + pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); + pcmk__log_xml_trace(req, "request"); + rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output); /* If scratch points to a new object now (for example, after an erase @@ -340,6 +348,10 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__enable_acl(*current_cib, scratch, user); } + pcmk__trace("Processing %s for section '%s', user '%s'", op, + pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); + pcmk__log_xml_trace(req, "request"); + rc = (*fn) (op, call_options, section, req, input, *current_cib, &scratch, output); From 0de7ad66941a42486379556976a9a5e4490fbbd2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 18:43:34 -0800 Subject: [PATCH 182/350] Refactor: libcib: Simplify update_counter() Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 73cdda23f49..d35acb3f12d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -181,28 +181,20 @@ cib__process_apply_patch(const char *op, int options, const char *section, } static void -update_counter(xmlNode *xml_obj, const char *field, bool reset) +update_counter(xmlNode *xml, const char *field, bool reset) { - char *new_value = NULL; - char *old_value = NULL; - int int_value = -1; + int old_value = 0; + bool was_set = (pcmk__xe_get_int(xml, field, &old_value) == pcmk_rc_ok); + int new_value = (reset? 1 : (old_value + 1)); + + if (was_set) { + pcmk__trace("Updating %s from %d to %d", field, old_value, new_value); - if (!reset && pcmk__xe_get(xml_obj, field) != NULL) { - old_value = pcmk__xe_get_copy(xml_obj, field); - } - if (old_value != NULL) { - int_value = atoi(old_value); - new_value = pcmk__itoa(++int_value); } else { - new_value = pcmk__str_copy("1"); + pcmk__trace("Updating %s from unset to %d", field, new_value); } - pcmk__trace("Update %s from %s to %s", field, pcmk__s(old_value, "unset"), - new_value); - pcmk__xe_set(xml_obj, field, new_value); - - free(new_value); - free(old_value); + pcmk__xe_set_int(xml, field, new_value); } int From bc41fdbf0a40c4d6fa12dc8e631332413230d05d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 18:54:09 -0800 Subject: [PATCH 183/350] Refactor: libcib: Drop redundant setting of *answer to NULL It's always NULL when these functions are called. Additionally, the only callers are cib_perform_op() and cib__perform_query(), with one exception: cib_apply_patch_event() calls cib__process_apply_patch(). cib_apply_patch_event() is unused internally but is used by sbd. It passes NULL to cib__process_apply_patch(). Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index d35acb3f12d..4cc60ccff96 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -202,9 +202,7 @@ cib__process_bump(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - *answer = NULL; update_counter(*result_cib, PCMK_XA_EPOCH, false); - return pcmk_rc_ok; } @@ -496,7 +494,6 @@ cib__process_erase(const char *op, int options, const char *section, *result_cib = createEmptyCib(0); pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none); update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); - *answer = NULL; return pcmk_rc_ok; } @@ -701,8 +698,6 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, { xmlNode *obj_root = NULL; - CRM_CHECK(*answer == NULL, g_clear_pointer(answer, pcmk__xml_free)); - if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; } @@ -876,15 +871,11 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, static int process_replace_section(const char *section, xmlNode *request, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, - xmlNode **answer) + xmlNode *existing_cib, xmlNode **result_cib) { int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; - // @TODO Drop this? - *answer = NULL; - if (input == NULL) { pcmk__err("Cannot find matching section to replace with no input data"); return EINVAL; @@ -920,7 +911,7 @@ cib__process_replace(const char *op, int options, const char *section, } return process_replace_section(section, req, input, existing_cib, - result_cib, answer); + result_cib); } int @@ -933,8 +924,6 @@ cib__process_upgrade(const char *op, int options, const char *section, const char *original_schema = NULL; const char *new_schema = NULL; - *answer = NULL; - original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); rc = pcmk__update_schema(result_cib, max_schema, true, !pcmk__is_set(options, cib_verbose)); From f039f0d6ef1fe459800e6d152782b042e524fb57 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 19:34:47 -0800 Subject: [PATCH 184/350] Refactor: libcib: Unindent cib_apply_patch_event() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 423bb6d2196..cee840e9310 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -808,7 +808,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, NULL); diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - if (rc < pcmk_ok || diff == NULL) { + if ((rc < pcmk_ok) || (diff == NULL)) { return rc; } @@ -816,26 +816,29 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, pcmk__log_xml_patchset(level, diff); } - if (input != NULL) { - rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, - output, NULL); - rc = pcmk_rc2legacy(rc); + if (input == NULL) { + return rc; + } - if (rc != pcmk_ok) { - pcmk__debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), - rc, *output); + rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, + output, NULL); + rc = pcmk_rc2legacy(rc); - if (rc == -pcmk_err_old_data) { - pcmk__trace("Masking error, we already have the supplied " - "update"); - return pcmk_ok; - } - pcmk__xml_free(*output); - *output = NULL; - return rc; + if (rc != pcmk_ok) { + pcmk__debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), rc, + *output); + + if (rc == -pcmk_err_old_data) { + pcmk__trace("Masking error, we already have the supplied " + "update"); + return pcmk_ok; } + + g_clear_pointer(output, pcmk__xml_free); + return rc; } - return rc; + + return pcmk_ok; } #define log_signon_query_err(out, fmt, args...) do { \ From f45e9f1dbb93f7f173cf1b7e12b5a7672bc2d956 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 19:37:32 -0800 Subject: [PATCH 185/350] Refactor: libcib: Unindent more of cib_apply_patch_event() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index cee840e9310..3fd5bd2affb 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -823,22 +823,20 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, output, NULL); rc = pcmk_rc2legacy(rc); + if (rc == pcmk_ok) { + return pcmk_ok; + } - if (rc != pcmk_ok) { - pcmk__debug("Update didn't apply: %s (%d) %p", pcmk_strerror(rc), rc, - *output); - - if (rc == -pcmk_err_old_data) { - pcmk__trace("Masking error, we already have the supplied " - "update"); - return pcmk_ok; - } + pcmk__debug("Update didn't apply: %s (%d)", pcmk_strerror(rc), rc); - g_clear_pointer(output, pcmk__xml_free); - return rc; + if (rc == -pcmk_err_old_data) { + // Mask this error, since it means we already have the supplied update + return pcmk_ok; } - return pcmk_ok; + // Some other error + g_clear_pointer(output, pcmk__xml_free); + return rc; } #define log_signon_query_err(out, fmt, args...) do { \ From 24fffd0473061586e22820621ae690345023bcbe Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 19:56:46 -0800 Subject: [PATCH 186/350] Refactor: libcib: Allow cib__process_apply_patch() to use existing_cib Previously, we made a copy no matter what. There are only two callers: cib_perform_op() and cib_apply_patch_event(). When called by cib_perform_op(), it is always the case that either *result_cib == existing_cib or *result_cib is a copy of existing_cib. In either case, we don't need to make another copy. cib_apply_patch_event(), on the other hand, can pass an arbitrary value for result_cib, since it's a public API function. In order to keep sane behavior, we free the result CIB and set it to a copy of the existing CIB if it doesn't point to the existing CIB initially. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 7 ++----- lib/cib/cib_utils.c | 5 +++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 4cc60ccff96..a4dfbcb726d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -172,12 +172,9 @@ cib__process_apply_patch(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - if (*result_cib != existing_cib) { - pcmk__xml_free(*result_cib); - } - *result_cib = pcmk__xml_copy(NULL, existing_cib); + int rc = xml_apply_patchset(*result_cib, input, true); - return pcmk_legacy2rc(xml_apply_patchset(*result_cib, input, true)); + return pcmk_legacy2rc(rc); } static void diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 3fd5bd2affb..b6a0d9d29fe 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -820,6 +820,11 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, return rc; } + if (*output != input) { + pcmk__xml_free(*output); + *output = pcmk__xml_copy(NULL, input); + } + rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, output, NULL); rc = pcmk_rc2legacy(rc); From 3b01a429d3c7956c2cab441560be7b440c62d6b7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 20:13:06 -0800 Subject: [PATCH 187/350] Refactor: based: Drop unused based_diff_notify() argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_notify.c | 2 +- daemons/based/based_notify.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0b076b1e7bd..35c09e3aa0c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -692,7 +692,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__trace("Sending notifications %d", pcmk__is_set(call_options, cib_dryrun)); based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, - client_name, originator, input, cib_diff); + client_name, originator, cib_diff); } pcmk__log_xml_patchset(LOG_TRACE, cib_diff); diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 8620bab953a..16e31e909b5 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -201,7 +201,7 @@ cib_notify_send(const xmlNode *xml) void based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, - const char *origin, xmlNode *update, xmlNode *diff) + const char *origin, xmlNode *diff) { xmlNode *update_msg = NULL; xmlNode *wrapper = NULL; diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index 1735cac54d9..79886f79aa1 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -18,6 +18,6 @@ int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); void based_diff_notify(const char *op, int result, const char *call_id, const char *client_id, const char *client_name, - const char *origin, xmlNode *update, xmlNode *diff); + const char *origin, xmlNode *diff); #endif // BASED_NOTIFY__H From 19c7de1d023860ec616b561c16fc5d6e0c7d95ea Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 20:25:17 -0800 Subject: [PATCH 188/350] Refactor: libcib: Get attributes from result CIB instead of existing CIB ...in replace_cib() and cib__process_upgrade(). This is to isolate the uses of the existing_cib variable in the cib__op_fn_t functions. *result_cib is always either existing_cib or a copy of it. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index a4dfbcb726d..73f788d95df 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -824,7 +824,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, reason = "digest mismatch"; } - cib_version_details(existing_cib, &admin_epoch, &epoch, &updates); + cib_version_details(*result_cib, &admin_epoch, &epoch, &updates); cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); if (replace_admin_epoch < admin_epoch) { @@ -921,7 +921,7 @@ cib__process_upgrade(const char *op, int options, const char *section, const char *original_schema = NULL; const char *new_schema = NULL; - original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); + original_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); rc = pcmk__update_schema(result_cib, max_schema, true, !pcmk__is_set(options, cib_verbose)); new_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); From a4ca85e9f7e8b3ff067f07fa5ad6936be7670e28 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 20:52:21 -0800 Subject: [PATCH 189/350] Refactor: libcib: Log correct error message in replace_cib() ...for digest mismatch. Previously, upon digest mismatch, we would log that the current digest mismatch is greater than the replacement. Also return pcmk_rc_digest_mismatch in that case. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 73f788d95df..52409ac6462 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -820,13 +820,17 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, const char *reason = NULL; const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); - if (!replace_cib_digest_matches(request, input)) { - reason = "digest mismatch"; - } - cib_version_details(*result_cib, &admin_epoch, &epoch, &updates); cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); + if (!replace_cib_digest_matches(request, input)) { + pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " + "digest mismatch", replace_admin_epoch, replace_epoch, + replace_updates, peer, admin_epoch, epoch, updates); + rc = pcmk_rc_digest_mismatch; + goto done; + } + if (replace_admin_epoch < admin_epoch) { reason = PCMK_XA_ADMIN_EPOCH; @@ -846,18 +850,17 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, if (reason != NULL) { pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " "current %s is greater than the replacement", - replace_admin_epoch, replace_epoch, - replace_updates, peer, admin_epoch, epoch, updates, - reason); + replace_admin_epoch, replace_epoch, replace_updates, peer, + admin_epoch, epoch, updates, reason); rc = pcmk_rc_old_data; - - } else { - pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", - admin_epoch, epoch, updates, - replace_admin_epoch, replace_epoch, replace_updates, - peer); + goto done; } + pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", admin_epoch, epoch, + updates, replace_admin_epoch, replace_epoch, replace_updates, + peer); + +done: if (*result_cib != existing_cib) { pcmk__xml_free(*result_cib); } From 7546b3927ed9c3ad5cfb301f2823bd45464607d5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 20:55:55 -0800 Subject: [PATCH 190/350] Refactor: libcib: Free *result_cib only on success in replace_cib() Free and replace *result_cib if and only if the operation succeeds, regardless of whether it's equal to the existing CIB or is a copy of the existing CIB. In fact, it's always a copy of the existing CIB unless we're processing a request within a transaction. But if it were equal to the existing CIB: * On success, we'd want to replace the existing CIB. That's the point of running these operations without making a copy, when we do so. * On failure, it doesn't seem like we'd want to free it and replace it with the input that we just rejected. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 52409ac6462..4d7f96c3031 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -804,11 +804,8 @@ replace_cib_digest_matches(xmlNode *request, xmlNode *input) } static int -replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib) +replace_cib(xmlNode *request, xmlNode *input, xmlNode **result_cib) { - int rc = pcmk_rc_ok; - int updates = 0; int epoch = 0; int admin_epoch = 0; @@ -821,14 +818,14 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); cib_version_details(*result_cib, &admin_epoch, &epoch, &updates); - cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); + cib_version_details(input, &replace_admin_epoch, &replace_epoch, + &replace_updates); if (!replace_cib_digest_matches(request, input)) { pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " "digest mismatch", replace_admin_epoch, replace_epoch, replace_updates, peer, admin_epoch, epoch, updates); - rc = pcmk_rc_digest_mismatch; - goto done; + return pcmk_rc_digest_mismatch; } if (replace_admin_epoch < admin_epoch) { @@ -852,26 +849,22 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode *existing_cib, "current %s is greater than the replacement", replace_admin_epoch, replace_epoch, replace_updates, peer, admin_epoch, epoch, updates, reason); - rc = pcmk_rc_old_data; - goto done; + return pcmk_rc_old_data; } pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", admin_epoch, epoch, updates, replace_admin_epoch, replace_epoch, replace_updates, peer); -done: - if (*result_cib != existing_cib) { - pcmk__xml_free(*result_cib); - } - + pcmk__xml_free(*result_cib); *result_cib = pcmk__xml_copy(NULL, input); - return rc; + + return pcmk_rc_ok; } static int process_replace_section(const char *section, xmlNode *request, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib) + xmlNode **result_cib) { int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; @@ -882,7 +875,7 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - return replace_cib(request, input, existing_cib, result_cib); + return replace_cib(request, input, result_cib); } if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei) @@ -910,8 +903,7 @@ cib__process_replace(const char *op, int options, const char *section, return process_replace_xpath(op, options, section, input, *result_cib); } - return process_replace_section(section, req, input, existing_cib, - result_cib); + return process_replace_section(section, req, input, result_cib); } int From 793ae0e930626f8c89a634232a09812f5b8e6c2b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 21:04:26 -0800 Subject: [PATCH 191/350] Refactor: libcib: Free *result_cib unconditionally in cib__process_erase() *result_cib is always either existing_cib itself or a copy of existing_cib. When we run these operations on existing_cib (without making a copy), the whole point is that we want existing_cib to reflect the changes when we're finished. So in this case, we always want *result_cib to be replaced by an empty CIB, starting with the original version numbers and then bumping admin epoch. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 4d7f96c3031..e5666a07c90 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -485,13 +485,20 @@ cib__process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - if (*result_cib != existing_cib) { - pcmk__xml_free(*result_cib); - } + xmlNode *tmp = pcmk__xe_create(NULL, (const char *) (*result_cib)->name); + + // Save version details + pcmk__xe_copy_attrs(tmp, *result_cib, pcmk__xaf_none); + + // Replace with an empty CIB + pcmk__xml_free(*result_cib); *result_cib = createEmptyCib(0); - pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none); + + // Restore version details and bump PCMK_XA_ADMIN_EPOCH + pcmk__xe_copy_attrs(*result_cib, tmp, pcmk__xaf_none); update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); + pcmk__xml_free(tmp); return pcmk_rc_ok; } From 7d65f93d7d2c904386d4e5e0228b3f2d7c3aebcd Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 21:16:14 -0800 Subject: [PATCH 192/350] Refactor: based: Get versions from *result_cib in based_process_ping() It's either equal to the current CIB or a copy of the current CIB. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 122e53f6c6c..91c6a5849f5 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -200,9 +200,9 @@ based_process_ping(const char *op, int options, const char *section, pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", host, digest, - pcmk__xe_get(existing_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(existing_cib, PCMK_XA_EPOCH), - pcmk__xe_get(existing_cib, PCMK_XA_NUM_UPDATES)); + pcmk__xe_get(*result_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(*result_cib, PCMK_XA_EPOCH), + pcmk__xe_get(*result_cib, PCMK_XA_NUM_UPDATES)); free(digest); From 73604e86da8117f8dfb6403a44bc65e6da2384b2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 21:18:39 -0800 Subject: [PATCH 193/350] Refactor: based: Copy based on *result_cib in based_process_upgrade() It's either equal to the current CIB or a copy of the current CIB. 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 91c6a5849f5..d9367cbddc5 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -360,7 +360,7 @@ based_process_upgrade(const char *op, int options, const char *section, existing_cib, result_cib, answer); } else { - xmlNode *scratch = pcmk__xml_copy(NULL, existing_cib); + xmlNode *scratch = pcmk__xml_copy(NULL, *result_cib); const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *original_schema = NULL; const char *new_schema = NULL; @@ -368,7 +368,7 @@ based_process_upgrade(const char *op, int options, const char *section, const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); - original_schema = pcmk__xe_get(existing_cib, PCMK_XA_VALIDATE_WITH); + original_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); if (original_schema == NULL) { pcmk__info("Rejecting upgrade request from %s: No " PCMK_XA_VALIDATE_WITH, From 5d9f63552cc0b6569cec9b87278d15fad28ef5b4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 02:54:06 -0800 Subject: [PATCH 194/350] Refactor: libcib: cib_process_erase() doesn't free its cib argument This way, we preserve all the document private data. We were already preserving the top-level attributes, but we were doing so by saving and restoring them. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index e5666a07c90..05cc14a9b1c 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -485,20 +485,29 @@ cib__process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - xmlNode *tmp = pcmk__xe_create(NULL, (const char *) (*result_cib)->name); - - // Save version details - pcmk__xe_copy_attrs(tmp, *result_cib, pcmk__xaf_none); - - // Replace with an empty CIB - pcmk__xml_free(*result_cib); - *result_cib = createEmptyCib(0); + xmlNode *empty = createEmptyCib(0); + xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, + NULL, NULL); + xmlNode *empty_status = pcmk__xe_first_child(empty, PCMK_XE_STATUS, NULL, + NULL); + + // Free all existing children, regardless of node type + while ((*result_cib)->children != NULL) { + pcmk__xml_free((*result_cib)->children); + } + + /* Copying is a wasteful here, but calling pcmk__xml_copy() adds the copy as + * a child of the existing *result_cib within the same document. This + * reduces the number of opportunities to make mistakes related to XML + * documents, change tracking, etc., compared to calling xmlUnlinkChild(), + * xmlAddChild(), etc. + */ + pcmk__xml_copy(*result_cib, empty_config); + pcmk__xml_copy(*result_cib, empty_status); - // Restore version details and bump PCMK_XA_ADMIN_EPOCH - pcmk__xe_copy_attrs(*result_cib, tmp, pcmk__xaf_none); update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); - pcmk__xml_free(tmp); + pcmk__xml_free(empty); return pcmk_rc_ok; } From eba895a8d3a1722c5832d002ad2a262375597ee5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 18:47:15 -0800 Subject: [PATCH 195/350] Test: cts-cli: Use word boundaries for CIB version attributes In the sed call, match a space character instead of a word boundary, because word boundary support is version-dependent (for example, wasn't part of POSIX sed for a long time). Re-order the shadow version replacements as admin_epoch -> epoch -> num_updates, since that's the order of precedence. Signed-off-by: Reid Wahl --- cts/cts-cli.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cts/cts-cli.in b/cts/cts-cli.in index 6f2422b3587..eadb6a69ba1 100644 --- a/cts/cts-cli.in +++ b/cts/cts-cli.in @@ -193,9 +193,9 @@ def reset_shadow_cib_version(): """Set various version numbers in a shadow CIB file back to 0.""" with fileinput.input(files=[shadow_path()], inplace=True) as f: for line in f: - line = re.sub('epoch="[0-9]*"', 'epoch="1"', line) - line = re.sub('num_updates="[0-9]*"', 'num_updates="0"', line) - line = re.sub('admin_epoch="[0-9]*"', 'admin_epoch="0"', line) + line = re.sub(r'\badmin_epoch="[0-9]*"', 'admin_epoch="0"', line) + line = re.sub(r'\bepoch="[0-9]*"', 'epoch="1"', line) + line = re.sub(r'\bnum_updates="[0-9]*"', 'num_updates="0"', line) print(line, end='') @@ -1417,7 +1417,7 @@ class CrmAttributeRegressionTest(RegressionTest): "crm_attribute --query -n cpu -N node1 -z"), # This update will fail because it has version numbers Test("Replace operation should fail", - """cibadmin -Q | sed -e 's/epoch="[^"]*"/epoch="1"/' | cibadmin -R -p""", + """cibadmin -Q | sed -e 's/ epoch="[^"]*"/ epoch="1"/' | cibadmin -R -p""", expected_rc=ExitStatus.OLD), ] @@ -2346,7 +2346,7 @@ class CrmSimulateRegressionTest(RegressionTest): no_version_cib = good_cib.replace('validate-with="pacemaker-1.2" ', "") - no_version_bad_cib = bad_version_cib.replace('epoch="3"', 'epoch="30"').replace("start", "break") + no_version_bad_cib = re.sub(r'\bepoch="3"', 'epoch="30"', bad_version_cib).replace("start", "break") basic_tests = [ Test("Show allocation scores with crm_simulate", From 01d3df30925854e55f31e8062f423329f160fbb7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 19:00:47 -0800 Subject: [PATCH 196/350] Feature: libcib, tools: Empty CIB contains admin_epoch before epoch The order of precedence of version numbers when determining which of two CIBs is newer, is admin_epoch -> epoch -> num_updates. This change affects cibadmin, crm_shadow, and createEmptyCib(). However, since this only affects newly created empty CIBs, the attribute order shouldn't matter to users unless they're diffing the empty CIB against some other CIB. Signed-off-by: Reid Wahl --- cts/cli/regression.access_render.exp | 10 +-- cts/cli/regression.acls.exp | 74 +++++++++--------- cts/cli/regression.cibadmin.exp | 24 +++--- cts/cli/regression.crm_attribute.exp | 70 ++++++++--------- cts/cli/regression.crm_resource.exp | 110 +++++++++++++-------------- cts/cli/regression.crm_shadow.exp | 11 +-- cts/cli/regression.crm_standby.exp | 4 +- cts/cli/regression.crm_ticket.exp | 20 ++--- cts/cli/regression.upgrade.exp | 8 +- cts/cli/regression.validity.exp | 6 +- lib/cib/cib_utils.c | 2 +- 11 files changed, 170 insertions(+), 169 deletions(-) diff --git a/cts/cli/regression.access_render.exp b/cts/cli/regression.access_render.exp index f0ec2675178..ed2f704bcfd 100644 --- a/cts/cli/regression.access_render.exp +++ b/cts/cli/regression.access_render.exp @@ -1,6 +1,6 @@ =#=#=#= Begin test: Configure some ACLs =#=#=#= =#=#=#= Current cib after: Configure some ACLs =#=#=#= - + @@ -23,7 +23,7 @@ * Passed: cibadmin - Configure some ACLs =#=#=#= Begin test: Enable ACLs =#=#=#= =#=#=#= Current cib after: Enable ACLs =#=#=#= - + @@ -50,7 +50,7 @@ * Passed: crm_attribute - Enable ACLs =#=#=#= Begin test: An instance of ACLs render (into color) =#=#=#= - +    @@ -77,7 +77,7 @@ * Passed: cibadmin - An instance of ACLs render (into color) =#=#=#= Begin test: An instance of ACLs render (into namespacing) =#=#=#= - + @@ -105,7 +105,7 @@ =#=#=#= Begin test: An instance of ACLs render (into text) =#=#=#= vvv---[ READABLE ]---vvv - + diff --git a/cts/cli/regression.acls.exp b/cts/cli/regression.acls.exp index a6aeb155e23..353edeb53c3 100644 --- a/cts/cli/regression.acls.exp +++ b/cts/cli/regression.acls.exp @@ -1,6 +1,6 @@ =#=#=#= Begin test: Configure some ACLs =#=#=#= =#=#=#= Current cib after: Configure some ACLs =#=#=#= - + @@ -56,7 +56,7 @@ * Passed: cibadmin - Configure some ACLs =#=#=#= Begin test: Enable ACLs =#=#=#= =#=#=#= Current cib after: Enable ACLs =#=#=#= - + @@ -116,7 +116,7 @@ * Passed: crm_attribute - Enable ACLs =#=#=#= Begin test: Set cluster option =#=#=#= =#=#=#= Current cib after: Set cluster option =#=#=#= - + @@ -177,7 +177,7 @@ * Passed: crm_attribute - Set cluster option =#=#=#= Begin test: New ACL role =#=#=#= =#=#=#= Current cib after: New ACL role =#=#=#= - + @@ -241,7 +241,7 @@ * Passed: cibadmin - New ACL role =#=#=#= Begin test: New ACL target =#=#=#= =#=#=#= Current cib after: New ACL target =#=#=#= - + @@ -308,7 +308,7 @@ * Passed: cibadmin - New ACL target =#=#=#= Begin test: Another ACL role =#=#=#= =#=#=#= Current cib after: Another ACL role =#=#=#= - + @@ -378,7 +378,7 @@ * Passed: cibadmin - Another ACL role =#=#=#= Begin test: Another ACL target =#=#=#= =#=#=#= Current cib after: Another ACL target =#=#=#= - + @@ -451,7 +451,7 @@ * Passed: cibadmin - Another ACL target =#=#=#= Begin test: Updated ACL =#=#=#= =#=#=#= Current cib after: Updated ACL =#=#=#= - + @@ -560,7 +560,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= End test: l33t-haxor: Create a resource - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - l33t-haxor: Create a resource =#=#=#= Begin test: niceguy: Query configuration =#=#=#= - + @@ -641,7 +641,7 @@ crm_attribute: Error performing operation: Permission denied =#=#=#= Begin test: niceguy: Set fencing-enabled =#=#=#= pcmk__apply_creation_acl trace: ACLs allow creation of with id="cib-bootstrap-options-fencing-enabled" =#=#=#= Current cib after: niceguy: Set fencing-enabled =#=#=#= - + @@ -721,7 +721,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= End test: niceguy: Create a resource - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - niceguy: Create a resource =#=#=#= Begin test: root: Query configuration =#=#=#= - + @@ -796,7 +796,7 @@ cibadmin: CIB API call failed: Permission denied * Passed: cibadmin - root: Query configuration =#=#=#= Begin test: root: Set fencing-enabled =#=#=#= =#=#=#= Current cib after: root: Set fencing-enabled =#=#=#= - + @@ -871,7 +871,7 @@ cibadmin: CIB API call failed: Permission denied * Passed: crm_attribute - root: Set fencing-enabled =#=#=#= Begin test: root: Create a resource =#=#=#= =#=#=#= Current cib after: root: Create a resource =#=#=#= - + @@ -948,7 +948,7 @@ cibadmin: CIB API call failed: Permission denied * Passed: cibadmin - root: Create a resource =#=#=#= Begin test: root: Create another resource (with description) =#=#=#= =#=#=#= Current cib after: root: Create another resource (with description) =#=#=#= - + @@ -1045,7 +1045,7 @@ pcmk__apply_creation_acl trace: Creation of scaffolding with pcmk__apply_creation_acl trace: ACLs allow creation of with id="dummy-meta_attributes-target-role" Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attributes name=target-role value=Stopped =#=#=#= Current cib after: niceguy: Create a resource meta attribute =#=#=#= - + @@ -1129,7 +1129,7 @@ Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attribut unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Stopped =#=#=#= Current cib after: niceguy: Query a resource meta attribute =#=#=#= - + @@ -1213,7 +1213,7 @@ Stopped unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Deleted 'dummy' option: id=dummy-meta_attributes-target-role name=target-role =#=#=#= Current cib after: niceguy: Remove a resource meta attribute =#=#=#= - + @@ -1296,7 +1296,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h pcmk__apply_creation_acl trace: ACLs allow creation of with id="dummy-meta_attributes-target-role" Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attributes name=target-role value=Started =#=#=#= Current cib after: niceguy: Create a resource meta attribute =#=#=#= - + @@ -1404,7 +1404,7 @@ Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attribut =#=#=#= End test: betteridea: Query configuration - explicit deny - OK (0) =#=#=#= * Passed: cibadmin - betteridea: Query configuration - explicit deny - + @@ -1432,7 +1432,7 @@ pcmk__check_acl trace: Default ACL denies user 'niceguy' read/write access to / cibadmin: CIB API call failed: Permission denied =#=#=#= End test: niceguy: Replace - remove acls - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - niceguy: Replace - remove acls - + @@ -1518,7 +1518,7 @@ pcmk__apply_creation_acl trace: ACLs disallow creation of with id=" cibadmin: CIB API call failed: Permission denied =#=#=#= End test: niceguy: Replace - create resource - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - niceguy: Replace - create resource - + @@ -1602,7 +1602,7 @@ pcmk__check_acl trace: Default ACL denies user 'niceguy' read/write access to / cibadmin: CIB API call failed: Permission denied =#=#=#= End test: niceguy: Replace - modify attribute (deny) - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - niceguy: Replace - modify attribute (deny) - + @@ -1686,7 +1686,7 @@ pcmk__check_acl trace: Default ACL denies user 'niceguy' read/write access to / cibadmin: CIB API call failed: Permission denied =#=#=#= End test: niceguy: Replace - delete attribute (deny) - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - niceguy: Replace - delete attribute (deny) - + @@ -1770,7 +1770,7 @@ pcmk__check_acl trace: Default ACL denies user 'niceguy' read/write access to / cibadmin: CIB API call failed: Permission denied =#=#=#= End test: niceguy: Replace - create attribute (deny) - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - niceguy: Replace - create attribute (deny) - + @@ -1851,7 +1851,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: bob: Replace - create attribute (direct allow) =#=#=#= =#=#=#= End test: bob: Replace - create attribute (direct allow) - OK (0) =#=#=#= * Passed: cibadmin - bob: Replace - create attribute (direct allow) - + @@ -1932,7 +1932,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: bob: Replace - modify attribute (direct allow) =#=#=#= =#=#=#= End test: bob: Replace - modify attribute (direct allow) - OK (0) =#=#=#= * Passed: cibadmin - bob: Replace - modify attribute (direct allow) - + @@ -2009,7 +2009,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: bob: Replace - delete attribute (direct allow) =#=#=#= =#=#=#= End test: bob: Replace - delete attribute (direct allow) - OK (0) =#=#=#= * Passed: cibadmin - bob: Replace - delete attribute (direct allow) - + @@ -2086,7 +2086,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: joe: Replace - create attribute (inherited allow) =#=#=#= =#=#=#= End test: joe: Replace - create attribute (inherited allow) - OK (0) =#=#=#= * Passed: cibadmin - joe: Replace - create attribute (inherited allow) - + @@ -2163,7 +2163,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: joe: Replace - modify attribute (inherited allow) =#=#=#= =#=#=#= End test: joe: Replace - modify attribute (inherited allow) - OK (0) =#=#=#= * Passed: cibadmin - joe: Replace - modify attribute (inherited allow) - + @@ -2240,7 +2240,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: joe: Replace - delete attribute (inherited allow) =#=#=#= =#=#=#= End test: joe: Replace - delete attribute (inherited allow) - OK (0) =#=#=#= * Passed: cibadmin - joe: Replace - delete attribute (inherited allow) - + @@ -2317,7 +2317,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: mike: Replace - create attribute (allow overrides deny) =#=#=#= =#=#=#= End test: mike: Replace - create attribute (allow overrides deny) - OK (0) =#=#=#= * Passed: cibadmin - mike: Replace - create attribute (allow overrides deny) - + @@ -2394,7 +2394,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: mike: Replace - modify attribute (allow overrides deny) =#=#=#= =#=#=#= End test: mike: Replace - modify attribute (allow overrides deny) - OK (0) =#=#=#= * Passed: cibadmin - mike: Replace - modify attribute (allow overrides deny) - + @@ -2471,7 +2471,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: mike: Replace - delete attribute (allow overrides deny) =#=#=#= =#=#=#= End test: mike: Replace - delete attribute (allow overrides deny) - OK (0) =#=#=#= * Passed: cibadmin - mike: Replace - delete attribute (allow overrides deny) - + @@ -2548,7 +2548,7 @@ cibadmin: CIB API call failed: Permission denied =#=#=#= Begin test: mike: Create another resource =#=#=#= pcmk__apply_creation_acl trace: ACLs allow creation of with id="dummy2" =#=#=#= Current cib after: mike: Create another resource =#=#=#= - + @@ -2625,7 +2625,7 @@ pcmk__apply_creation_acl trace: ACLs allow creation of with id="dum =#=#=#= End test: mike: Create another resource - OK (0) =#=#=#= * Passed: cibadmin - mike: Create another resource - + @@ -2705,7 +2705,7 @@ pcmk__check_acl trace: Parent ACL denies user 'chris' read/write access to /cib cibadmin: CIB API call failed: Permission denied =#=#=#= End test: chris: Replace - create attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - chris: Replace - create attribute (deny overrides allow) - + @@ -2785,7 +2785,7 @@ pcmk__check_acl trace: Parent ACL denies user 'chris' read/write access to /cib cibadmin: CIB API call failed: Permission denied =#=#=#= End test: chris: Replace - modify attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#= * Passed: cibadmin - chris: Replace - modify attribute (deny overrides allow) - + diff --git a/cts/cli/regression.cibadmin.exp b/cts/cli/regression.cibadmin.exp index 94d919f082e..0b009a88815 100644 --- a/cts/cli/regression.cibadmin.exp +++ b/cts/cli/regression.cibadmin.exp @@ -1,5 +1,5 @@ =#=#=#= Begin test: Validate CIB =#=#=#= - + @@ -17,7 +17,7 @@ =#=#=#= Current cib after: Validate CIB =#=#=#= - + @@ -38,7 +38,7 @@ * Passed: cibadmin - Validate CIB =#=#=#= Begin test: Validate CIB (XML) =#=#=#= - + @@ -59,7 +59,7 @@ =#=#=#= Current cib after: Validate CIB (XML) =#=#=#= - + @@ -79,12 +79,12 @@ =#=#=#= End test: Validate CIB (XML) - OK (0) =#=#=#= * Passed: cibadmin - Validate CIB (XML) =#=#=#= Begin test: Digest calculation =#=#=#= -9130d6f39c2b83bd032a00e76621508c +1f3492630cde57d6b7ee45de4d390469 =#=#=#= End test: Digest calculation - OK (0) =#=#=#= * Passed: cibadmin - Digest calculation =#=#=#= Begin test: Digest calculation (XML) =#=#=#= - + =#=#=#= End test: Digest calculation (XML) - OK (0) =#=#=#= @@ -92,7 +92,7 @@ =#=#=#= Begin test: Require --force for CIB erasure =#=#=#= cibadmin: The supplied command is considered dangerous. To prevent accidental destruction of the cluster, the --force flag is required in order to proceed. =#=#=#= Current cib after: Require --force for CIB erasure =#=#=#= - + @@ -120,7 +120,7 @@ cibadmin: The supplied command is considered dangerous. To prevent accidental de =#=#=#= Current cib after: Require --force for CIB erasure (XML) =#=#=#= - + @@ -149,7 +149,7 @@ cibadmin: The supplied command is considered dangerous. To prevent accidental de =#=#=#= End test: Allow CIB erasure with --force (XML) - OK (0) =#=#=#= * Passed: cibadmin - Allow CIB erasure with --force (XML) =#=#=#= Begin test: Query CIB =#=#=#= - + @@ -159,7 +159,7 @@ cibadmin: The supplied command is considered dangerous. To prevent accidental de =#=#=#= Current cib after: Query CIB =#=#=#= - + @@ -172,7 +172,7 @@ cibadmin: The supplied command is considered dangerous. To prevent accidental de * Passed: cibadmin - Query CIB =#=#=#= Begin test: Query CIB (XML) =#=#=#= - + @@ -185,7 +185,7 @@ cibadmin: The supplied command is considered dangerous. To prevent accidental de =#=#=#= Current cib after: Query CIB (XML) =#=#=#= - + diff --git a/cts/cli/regression.crm_attribute.exp b/cts/cli/regression.crm_attribute.exp index 882f5da2113..a9e69cec54c 100644 --- a/cts/cli/regression.crm_attribute.exp +++ b/cts/cli/regression.crm_attribute.exp @@ -1067,7 +1067,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Query the value of an attribute that does not exist =#=#=#= Begin test: Configure something before erasing =#=#=#= =#=#=#= Current cib after: Configure something before erasing =#=#=#= - + @@ -1084,7 +1084,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Configure something before erasing =#=#=#= Begin test: Test '++' XML attribute update syntax =#=#=#= =#=#=#= Current cib after: Test '++' XML attribute update syntax =#=#=#= - + @@ -1101,7 +1101,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: cibadmin - Test '++' XML attribute update syntax =#=#=#= Begin test: Test '+=' XML attribute update syntax =#=#=#= =#=#=#= Current cib after: Test '+=' XML attribute update syntax =#=#=#= - + @@ -1118,7 +1118,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: cibadmin - Test '+=' XML attribute update syntax =#=#=#= Begin test: Test '++' nvpair value update syntax =#=#=#= =#=#=#= Current cib after: Test '++' nvpair value update syntax =#=#=#= - + @@ -1138,7 +1138,7 @@ crm_attribute: Error performing operation: No such device or address =#=#=#= Current cib after: Test '++' nvpair value update syntax (XML) =#=#=#= - + @@ -1155,7 +1155,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Test '++' nvpair value update syntax (XML) =#=#=#= Begin test: Test '+=' nvpair value update syntax =#=#=#= =#=#=#= Current cib after: Test '+=' nvpair value update syntax =#=#=#= - + @@ -1175,7 +1175,7 @@ crm_attribute: Error performing operation: No such device or address =#=#=#= Current cib after: Test '+=' nvpair value update syntax (XML) =#=#=#= - + @@ -1192,7 +1192,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Test '+=' nvpair value update syntax (XML) =#=#=#= Begin test: Test '++' XML attribute update syntax (--score not set) =#=#=#= =#=#=#= Current cib after: Test '++' XML attribute update syntax (--score not set) =#=#=#= - + @@ -1209,7 +1209,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: cibadmin - Test '++' XML attribute update syntax (--score not set) =#=#=#= Begin test: Test '+=' XML attribute update syntax (--score not set) =#=#=#= =#=#=#= Current cib after: Test '+=' XML attribute update syntax (--score not set) =#=#=#= - + @@ -1226,7 +1226,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: cibadmin - Test '+=' XML attribute update syntax (--score not set) =#=#=#= Begin test: Test '++' nvpair value update syntax (--score not set) =#=#=#= =#=#=#= Current cib after: Test '++' nvpair value update syntax (--score not set) =#=#=#= - + @@ -1246,7 +1246,7 @@ crm_attribute: Error performing operation: No such device or address =#=#=#= Current cib after: Test '++' nvpair value update syntax (--score not set) (XML) =#=#=#= - + @@ -1263,7 +1263,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Test '++' nvpair value update syntax (--score not set) (XML) =#=#=#= Begin test: Test '+=' nvpair value update syntax (--score not set) =#=#=#= =#=#=#= Current cib after: Test '+=' nvpair value update syntax (--score not set) =#=#=#= - + @@ -1283,7 +1283,7 @@ crm_attribute: Error performing operation: No such device or address =#=#=#= Current cib after: Test '+=' nvpair value update syntax (--score not set) (XML) =#=#=#= - + @@ -1300,7 +1300,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Test '+=' nvpair value update syntax (--score not set) (XML) =#=#=#= Begin test: Set cluster option =#=#=#= =#=#=#= Current cib after: Set cluster option =#=#=#= - + @@ -1321,7 +1321,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: cibadmin - Query new cluster option =#=#=#= Begin test: Set no-quorum policy =#=#=#= =#=#=#= Current cib after: Set no-quorum policy =#=#=#= - + @@ -1339,7 +1339,7 @@ crm_attribute: Error performing operation: No such device or address * Passed: crm_attribute - Set no-quorum policy =#=#=#= Begin test: Delete nvpair =#=#=#= =#=#=#= Current cib after: Delete nvpair =#=#=#= - + @@ -1364,7 +1364,7 @@ cibadmin: CIB API call failed: File exists =#=#=#= Current cib after: Create operation should fail =#=#=#= - + @@ -1381,7 +1381,7 @@ cibadmin: CIB API call failed: File exists * Passed: cibadmin - Create operation should fail =#=#=#= Begin test: Modify cluster options section =#=#=#= =#=#=#= Current cib after: Modify cluster options section =#=#=#= - + @@ -1400,7 +1400,7 @@ cibadmin: CIB API call failed: File exists =#=#=#= Begin test: Query updated cluster option =#=#=#= =#=#=#= Current cib after: Query updated cluster option =#=#=#= - + @@ -1418,7 +1418,7 @@ cibadmin: CIB API call failed: File exists * Passed: cibadmin - Query updated cluster option =#=#=#= Begin test: Set duplicate cluster option =#=#=#= =#=#=#= Current cib after: Set duplicate cluster option =#=#=#= - + @@ -1443,7 +1443,7 @@ Multiple attributes match name=cluster-delay Value: 60s (id=cib-bootstrap-options-cluster-delay) Value: 40s (id=duplicate-cluster-delay) =#=#=#= Current cib after: Setting multiply defined cluster option should fail =#=#=#= - + @@ -1464,7 +1464,7 @@ Multiple attributes match name=cluster-delay * Passed: crm_attribute - Setting multiply defined cluster option should fail =#=#=#= Begin test: Set cluster option with -s =#=#=#= =#=#=#= Current cib after: Set cluster option with -s =#=#=#= - + @@ -1486,7 +1486,7 @@ Multiple attributes match name=cluster-delay =#=#=#= Begin test: Delete cluster option with -i =#=#=#= Deleted crm_config option: id=(null) name=cluster-delay =#=#=#= Current cib after: Delete cluster option with -i =#=#=#= - + @@ -1526,7 +1526,7 @@ Revised Cluster Status: * Full List of Resources: * No resources =#=#=#= Current cib after: Create node1 and bring it online =#=#=#= - + @@ -1550,7 +1550,7 @@ Revised Cluster Status: * Passed: crm_simulate - Create node1 and bring it online =#=#=#= Begin test: Create node attribute =#=#=#= =#=#=#= Current cib after: Create node attribute =#=#=#= - + @@ -1579,7 +1579,7 @@ Revised Cluster Status: =#=#=#= Begin test: Query new node attribute =#=#=#= =#=#=#= Current cib after: Query new node attribute =#=#=#= - + @@ -1607,7 +1607,7 @@ Revised Cluster Status: * Passed: cibadmin - Query new node attribute =#=#=#= Begin test: Create second node attribute =#=#=#= =#=#=#= Current cib after: Create second node attribute =#=#=#= - + @@ -1641,7 +1641,7 @@ scope=nodes name=rattr value=XYZ * Passed: crm_attribute - Query node attributes by pattern =#=#=#= Begin test: Update node attributes by pattern =#=#=#= =#=#=#= Current cib after: Update node attributes by pattern =#=#=#= - + @@ -1671,7 +1671,7 @@ scope=nodes name=rattr value=XYZ =#=#=#= Begin test: Delete node attributes by pattern =#=#=#= Deleted nodes attribute: id=nodes-node1-rattr name=rattr =#=#=#= Current cib after: Delete node attributes by pattern =#=#=#= - + @@ -1699,7 +1699,7 @@ Deleted nodes attribute: id=nodes-node1-rattr name=rattr * Passed: crm_attribute - Delete node attributes by pattern =#=#=#= Begin test: Set a transient (fail-count) node attribute =#=#=#= =#=#=#= Current cib after: Set a transient (fail-count) node attribute =#=#=#= - + @@ -1734,7 +1734,7 @@ Deleted nodes attribute: id=nodes-node1-rattr name=rattr =#=#=#= Begin test: Query a fail count =#=#=#= scope=status name=fail-count-foo value=3 =#=#=#= Current cib after: Query a fail count =#=#=#= - + @@ -1782,7 +1782,7 @@ Current cluster status: * Passed: crm_simulate - Show node attributes with crm_simulate =#=#=#= Begin test: Set a second transient node attribute =#=#=#= =#=#=#= Current cib after: Set a second transient node attribute =#=#=#= - + @@ -1822,7 +1822,7 @@ scope=status name=fail-count-bar value=5 * Passed: crm_attribute - Query transient node attributes by pattern =#=#=#= Begin test: Update transient node attributes by pattern =#=#=#= =#=#=#= Current cib after: Update transient node attributes by pattern =#=#=#= - + @@ -1859,7 +1859,7 @@ scope=status name=fail-count-bar value=5 Deleted status attribute: id=status-node1-fail-count-foo name=fail-count-foo Deleted status attribute: id=status-node1-fail-count-bar name=fail-count-bar =#=#=#= Current cib after: Delete transient node attributes by pattern =#=#=#= - + @@ -1895,7 +1895,7 @@ crm_attribute: Error: must specify attribute name or pattern to delete * Passed: crm_attribute - crm_attribute given invalid delete usage =#=#=#= Begin test: Set a utilization node attribute =#=#=#= =#=#=#= Current cib after: Set a utilization node attribute =#=#=#= - + diff --git a/cts/cli/regression.crm_resource.exp b/cts/cli/regression.crm_resource.exp index 394d5b933a8..1b8f728f653 100644 --- a/cts/cli/regression.crm_resource.exp +++ b/cts/cli/regression.crm_resource.exp @@ -824,7 +824,7 @@ Special parameters that are available for all fencing resources, regardless of t * Passed: crm_resource - List all available fencing parameters (XML) =#=#=#= Begin test: Create a resource =#=#=#= =#=#=#= Current cib after: Create a resource =#=#=#= - + @@ -857,7 +857,7 @@ crm_resource: --class, --agent, and --provider can only be used with --validate unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Set 'dummy' option: id=dummy-meta_attributes-is-managed set=dummy-meta_attributes name=is-managed value=false =#=#=#= Current cib after: Create a resource meta attribute =#=#=#= - + @@ -886,7 +886,7 @@ Set 'dummy' option: id=dummy-meta_attributes-is-managed set=dummy-meta_attribute unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. false =#=#=#= Current cib after: Query a resource meta attribute =#=#=#= - + @@ -915,7 +915,7 @@ false unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Deleted 'dummy' option: id=dummy-meta_attributes-is-managed name=is-managed =#=#=#= Current cib after: Remove a resource meta attribute =#=#=#= - + @@ -984,7 +984,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Attribute 'nonexistent' not found for 'dummy' =#=#=#= Current cib after: Get a non-existent attribute from a resource element =#=#=#= - + @@ -1017,7 +1017,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h =#=#=#= Current cib after: Get a non-existent attribute from a resource element (XML) =#=#=#= - + @@ -1044,7 +1044,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. ocf =#=#=#= Current cib after: Get an existent attribute from a resource element =#=#=#= - + @@ -1073,7 +1073,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h =#=#=#= Current cib after: Set a non-existent attribute for a resource element (XML) =#=#=#= - + @@ -1102,7 +1102,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h =#=#=#= Current cib after: Set an existent attribute for a resource element (XML) =#=#=#= - + @@ -1131,7 +1131,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h =#=#=#= Current cib after: Delete an existent attribute for a resource element (XML) =#=#=#= - + @@ -1160,7 +1160,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h =#=#=#= Current cib after: Delete a non-existent attribute for a resource element (XML) =#=#=#= - + @@ -1187,7 +1187,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Set attribute: name=description value=test_description =#=#=#= Current cib after: Set a non-existent attribute for a resource element =#=#=#= - + @@ -1214,7 +1214,7 @@ Set attribute: name=description value=test_description unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Set attribute: name=description value=test_description =#=#=#= Current cib after: Set an existent attribute for a resource element =#=#=#= - + @@ -1241,7 +1241,7 @@ Set attribute: name=description value=test_description unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Deleted attribute: description =#=#=#= Current cib after: Delete an existent attribute for a resource element =#=#=#= - + @@ -1268,7 +1268,7 @@ Deleted attribute: description unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Deleted attribute: description =#=#=#= Current cib after: Delete a non-existent attribute for a resource element =#=#=#= - + @@ -1295,7 +1295,7 @@ Deleted attribute: description unpack_resources error: Resource start-up disabled since no fencing resources have been defined. Either configure some or disable fencing with the fencing-enabled option. NOTE: Clusters with shared data need fencing to ensure data integrity. Set 'dummy' option: id=dummy-instance_attributes-delay set=dummy-instance_attributes name=delay value=10s =#=#=#= Current cib after: Create a resource attribute =#=#=#= - + @@ -1326,7 +1326,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h Full List of Resources: * dummy (ocf:pacemaker:Dummy): Stopped =#=#=#= Current cib after: List the configured resources =#=#=#= - + @@ -1361,7 +1361,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h =#=#=#= Current cib after: List the configured resources (XML) =#=#=#= - + @@ -1432,7 +1432,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h crm_resource: Resource 'dummy' not moved: active in 0 locations. To prevent 'dummy' from running on a specific location, specify a node. =#=#=#= Current cib after: Require a destination when migrating a resource that is stopped =#=#=#= - + @@ -1463,7 +1463,7 @@ unpack_resources error: Resource start-up disabled since no fencing resources h crm_resource: Node 'i.do.not.exist' not found Error performing operation: No such object =#=#=#= Current cib after: Don't support migration to non-existent locations =#=#=#= - + @@ -1491,7 +1491,7 @@ Error performing operation: No such object * Passed: crm_resource - Don't support migration to non-existent locations =#=#=#= Begin test: Create a fencing resource =#=#=#= =#=#=#= Current cib after: Create a fencing resource =#=#=#= - + @@ -1545,7 +1545,7 @@ Revised Cluster Status: * dummy (ocf:pacemaker:Dummy): Started node1 * Fence (stonith:fence_true): Started node1 =#=#=#= Current cib after: Bring resources online =#=#=#= - + @@ -1586,7 +1586,7 @@ Revised Cluster Status: =#=#=#= Begin test: Try to move a resource to its existing location =#=#=#= crm_resource: Error performing operation: Requested item already exists =#=#=#= Current cib after: Try to move a resource to its existing location =#=#=#= - + @@ -1634,7 +1634,7 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool. This will be the case even if node1 is the last node in the cluster =#=#=#= Current cib after: Move a resource from its existing location =#=#=#= - + @@ -1677,7 +1677,7 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score =#=#=#= Begin test: Clear out constraints generated by --move =#=#=#= Removing constraint: cli-ban-dummy-on-node1 =#=#=#= Current cib after: Clear out constraints generated by --move =#=#=#= - + @@ -1752,7 +1752,7 @@ Revised Cluster Status: * dummy (ocf:pacemaker:Dummy): Started node1 * Fence (stonith:fence_true): Started node2 =#=#=#= Current cib after: Create two more nodes and bring them online =#=#=#= - + @@ -1821,7 +1821,7 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool. This will be the case even if node1 is the last node in the cluster =#=#=#= Current cib after: Ban dummy from node1 =#=#=#= - + @@ -1901,7 +1901,7 @@ Locations: =#=#=#= Current cib after: Ban dummy from node2 (XML) =#=#=#= - + @@ -1992,7 +1992,7 @@ Revised Cluster Status: * dummy (ocf:pacemaker:Dummy): Started node3 * Fence (stonith:fence_true): Started node2 =#=#=#= Current cib after: Relocate resources due to ban =#=#=#= - + @@ -2064,7 +2064,7 @@ Revised Cluster Status: =#=#=#= Current cib after: Move dummy to node1 (XML) =#=#=#= - + @@ -2134,7 +2134,7 @@ Revised Cluster Status: =#=#=#= Begin test: Clear implicit constraints for dummy on node2 =#=#=#= Removing constraint: cli-ban-dummy-on-node2 =#=#=#= Current cib after: Clear implicit constraints for dummy on node2 =#=#=#= - + @@ -2210,7 +2210,7 @@ Removing constraint: cli-ban-dummy-on-node2 Performing update of 'is-managed' on 'test-clone', the parent of 'test-primitive' Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone-meta_attributes name=is-managed value=false =#=#=#= Current cib after: Create a resource meta attribute =#=#=#= - + @@ -2248,7 +2248,7 @@ Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone =#=#=#= Begin test: Create a resource meta attribute in the primitive =#=#=#= Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed set=test-primitive-meta_attributes name=is-managed value=false =#=#=#= Current cib after: Create a resource meta attribute in the primitive =#=#=#= - + @@ -2295,7 +2295,7 @@ Multiple attributes match name=is-managed A value for 'is-managed' already exists in child 'test-primitive', performing update on that instead of 'test-clone' Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed value=true =#=#=#= Current cib after: Update resource meta attribute with duplicates =#=#=#= - + @@ -2337,7 +2337,7 @@ Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=i =#=#=#= Begin test: Update resource meta attribute with duplicates (force clone) =#=#=#= Set 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-managed value=true =#=#=#= Current cib after: Update resource meta attribute with duplicates (force clone) =#=#=#= - + @@ -2383,7 +2383,7 @@ Multiple attributes match name=is-managed Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed value=false =#=#=#= Current cib after: Update child resource meta attribute with duplicates =#=#=#= - + @@ -2430,7 +2430,7 @@ Multiple attributes match name=is-managed A value for 'is-managed' already exists in child 'test-primitive', performing delete on that instead of 'test-clone' Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed =#=#=#= Current cib after: Delete resource meta attribute with duplicates =#=#=#= - + @@ -2471,7 +2471,7 @@ Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed na Performing delete of 'is-managed' on 'test-clone', the parent of 'test-primitive' Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-managed =#=#=#= Current cib after: Delete resource meta attribute in parent =#=#=#= - + @@ -2509,7 +2509,7 @@ Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-ma =#=#=#= Begin test: Create a resource meta attribute in the primitive =#=#=#= Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed set=test-primitive-meta_attributes name=is-managed value=false =#=#=#= Current cib after: Create a resource meta attribute in the primitive =#=#=#= - + @@ -2550,7 +2550,7 @@ Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed set=te A value for 'is-managed' already exists in child 'test-primitive', performing update on that instead of 'test-clone' Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed value=true =#=#=#= Current cib after: Update existing resource meta attribute =#=#=#= - + @@ -2590,7 +2590,7 @@ Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=i =#=#=#= Begin test: Create a resource meta attribute in the parent =#=#=#= Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone-meta_attributes name=is-managed value=true =#=#=#= Current cib after: Create a resource meta attribute in the parent =#=#=#= - + @@ -2632,7 +2632,7 @@ Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone =#=#=#= Begin test: Delete resource parent meta attribute (force) =#=#=#= Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-managed =#=#=#= Current cib after: Delete resource parent meta attribute (force) =#=#=#= - + @@ -2676,7 +2676,7 @@ Multiple attributes match name=is-managed Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed =#=#=#= Current cib after: Delete resource child meta attribute =#=#=#= - + @@ -2715,7 +2715,7 @@ Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed na * Passed: crm_resource - Delete resource child meta attribute =#=#=#= Begin test: Create the dummy-group resource group =#=#=#= =#=#=#= Current cib after: Create the dummy-group resource group =#=#=#= - + @@ -2759,7 +2759,7 @@ Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed na =#=#=#= Begin test: Create a resource meta attribute in dummy1 =#=#=#= Set 'dummy1' option: id=dummy1-meta_attributes-is-managed set=dummy1-meta_attributes name=is-managed value=true =#=#=#= Current cib after: Create a resource meta attribute in dummy1 =#=#=#= - + @@ -2808,7 +2808,7 @@ Set 'dummy1' option: id=dummy1-meta_attributes-is-managed set=dummy1-meta_attrib Set 'dummy1' option: id=dummy1-meta_attributes-is-managed name=is-managed value=false Set 'dummy-group' option: id=dummy-group-meta_attributes-is-managed set=dummy-group-meta_attributes name=is-managed value=false =#=#=#= Current cib after: Create a resource meta attribute in dummy-group =#=#=#= - + @@ -2858,7 +2858,7 @@ Set 'dummy-group' option: id=dummy-group-meta_attributes-is-managed set=dummy-gr * Passed: crm_resource - Create a resource meta attribute in dummy-group =#=#=#= Begin test: Delete the dummy-group resource group =#=#=#= =#=#=#= Current cib after: Delete the dummy-group resource group =#=#=#= - + @@ -2898,7 +2898,7 @@ Set 'dummy-group' option: id=dummy-group-meta_attributes-is-managed set=dummy-gr =#=#=#= Begin test: Specify a lifetime when moving a resource =#=#=#= Migration will take effect until: =#=#=#= Current cib after: Specify a lifetime when moving a resource =#=#=#= - + @@ -2942,7 +2942,7 @@ Migration will take effect until: * Passed: crm_resource - Specify a lifetime when moving a resource =#=#=#= Begin test: Try to move a resource previously moved with a lifetime =#=#=#= =#=#=#= Current cib after: Try to move a resource previously moved with a lifetime =#=#=#= - + @@ -2985,7 +2985,7 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool. This will be the case even if node1 is the last node in the cluster =#=#=#= Current cib after: Ban dummy from node1 for a short time =#=#=#= - + @@ -3031,7 +3031,7 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score =#=#=#= Begin test: Remove expired constraints =#=#=#= Removing constraint: cli-ban-dummy-on-node1 =#=#=#= Current cib after: Remove expired constraints =#=#=#= - + @@ -3071,7 +3071,7 @@ Removing constraint: cli-ban-dummy-on-node1 =#=#=#= Begin test: Clear all implicit constraints for dummy =#=#=#= Removing constraint: cli-prefer-dummy =#=#=#= Current cib after: Clear all implicit constraints for dummy =#=#=#= - + @@ -3108,7 +3108,7 @@ Removing constraint: cli-prefer-dummy * Passed: crm_resource - Clear all implicit constraints for dummy =#=#=#= Begin test: Set a node health strategy =#=#=#= =#=#=#= Current cib after: Set a node health strategy =#=#=#= - + @@ -3146,7 +3146,7 @@ Removing constraint: cli-prefer-dummy * Passed: crm_attribute - Set a node health strategy =#=#=#= Begin test: Set a node health attribute =#=#=#= =#=#=#= Current cib after: Set a node health attribute =#=#=#= - + @@ -3197,7 +3197,7 @@ Removing constraint: cli-prefer-dummy * Passed: crm_resource - Show why a resource is not running on an unhealthy node (XML) =#=#=#= Begin test: Delete a resource =#=#=#= =#=#=#= Current cib after: Delete a resource =#=#=#= - + diff --git a/cts/cli/regression.crm_shadow.exp b/cts/cli/regression.crm_shadow.exp index f8a733743db..bccf28bdb13 100644 --- a/cts/cli/regression.crm_shadow.exp +++ b/cts/cli/regression.crm_shadow.exp @@ -1322,7 +1322,7 @@ A new shadow instance was created. To begin using it, enter the following into y =#=#=#= End test: Create empty shadow instance (file already exists) (force) (XML) - OK (0) =#=#=#= * Passed: crm_shadow - Create empty shadow instance (file already exists) (force) (XML) =#=#=#= Begin test: Get active shadow instance's contents (empty CIB) =#=#=#= - + @@ -1336,7 +1336,7 @@ A new shadow instance was created. To begin using it, enter the following into y =#=#=#= Begin test: Get active shadow instance's contents (empty CIB) (XML) =#=#=#= - + @@ -1374,7 +1374,7 @@ Diff: +++ 0.1.0 (null) -- /cib/status/node_state[@id='1'] -- /cib/status/node_state[@id='httpd-bundle-0'] -- /cib/status/node_state[@id='httpd-bundle-1'] -+ /cib: @validate-with=pacemaker-X, @num_updates=0, @admin_epoch=0 ++ /cib: @validate-with=pacemaker-X, @admin_epoch=0, @epoch=1, @num_updates=0 -- /cib: @cib-last-written, @update-origin, @update-client, @update-user, @have-quorum, @dc-uuid =#=#=#= End test: Get active shadow instance's diff (empty CIB) - Error occurred (1) =#=#=#= * Passed: crm_shadow - Get active shadow instance's diff (empty CIB) @@ -1410,8 +1410,9 @@ Diff: +++ 0.1.0 (null) - + + @@ -1420,7 +1421,7 @@ Diff: +++ 0.1.0 (null) - + diff --git a/cts/cli/regression.crm_standby.exp b/cts/cli/regression.crm_standby.exp index ef406bb72be..5dce954c75d 100644 --- a/cts/cli/regression.crm_standby.exp +++ b/cts/cli/regression.crm_standby.exp @@ -4,7 +4,7 @@ scope=status name=standby value=off * Passed: crm_standby - Default standby value =#=#=#= Begin test: Set standby status =#=#=#= =#=#=#= Current cib after: Set standby status =#=#=#= - + @@ -28,7 +28,7 @@ scope=nodes name=standby value=true =#=#=#= Begin test: Delete standby value =#=#=#= Deleted nodes attribute: id=nodes-node1-standby name=standby =#=#=#= Current cib after: Delete standby value =#=#=#= - + diff --git a/cts/cli/regression.crm_ticket.exp b/cts/cli/regression.crm_ticket.exp index afb8be30794..1a30cb97190 100644 --- a/cts/cli/regression.crm_ticket.exp +++ b/cts/cli/regression.crm_ticket.exp @@ -4,7 +4,7 @@ false * Passed: crm_ticket - Default ticket granted state =#=#=#= Begin test: Set ticket granted state =#=#=#= =#=#=#= Current cib after: Set ticket granted state =#=#=#= - + @@ -70,7 +70,7 @@ false * Passed: crm_ticket - Query ticket granted state (XML) =#=#=#= Begin test: Delete ticket granted state =#=#=#= =#=#=#= Current cib after: Delete ticket granted state =#=#=#= - + @@ -93,7 +93,7 @@ false * Passed: crm_ticket - Delete ticket granted state =#=#=#= Begin test: Make a ticket standby =#=#=#= =#=#=#= Current cib after: Make a ticket standby =#=#=#= - + @@ -120,7 +120,7 @@ true * Passed: crm_ticket - Query ticket standby state =#=#=#= Begin test: Activate a ticket =#=#=#= =#=#=#= Current cib after: Activate a ticket =#=#=#= - + @@ -157,7 +157,7 @@ ticketA revoked (standby=false) =#=#=#= Begin test: Add a second ticket =#=#=#= false =#=#=#= Current cib after: Add a second ticket =#=#=#= - + @@ -180,7 +180,7 @@ false * Passed: crm_ticket - Add a second ticket =#=#=#= Begin test: Set second ticket granted state =#=#=#= =#=#=#= Current cib after: Set second ticket granted state =#=#=#= - + @@ -219,7 +219,7 @@ ticketB revoked * Passed: crm_ticket - List tickets (XML) =#=#=#= Begin test: Delete second ticket =#=#=#= =#=#=#= Current cib after: Delete second ticket =#=#=#= - + @@ -242,7 +242,7 @@ ticketB revoked * Passed: cibadmin - Delete second ticket =#=#=#= Begin test: Delete ticket standby state =#=#=#= =#=#=#= Current cib after: Delete ticket standby state =#=#=#= - + @@ -265,7 +265,7 @@ ticketB revoked * Passed: crm_ticket - Delete ticket standby state =#=#=#= Begin test: Add a constraint to a ticket =#=#=#= =#=#=#= Current cib after: Add a constraint to a ticket =#=#=#= - + @@ -312,7 +312,7 @@ Constraints XML: * Passed: crm_ticket - Query ticket constraints (XML) =#=#=#= Begin test: Delete ticket constraint =#=#=#= =#=#=#= Current cib after: Delete ticket constraint =#=#=#= - + diff --git a/cts/cli/regression.upgrade.exp b/cts/cli/regression.upgrade.exp index b4e69f6195a..3f3455a8e82 100644 --- a/cts/cli/regression.upgrade.exp +++ b/cts/cli/regression.upgrade.exp @@ -1,6 +1,6 @@ =#=#=#= Begin test: Set fencing-enabled=false =#=#=#= =#=#=#= Current cib after: Set fencing-enabled=false =#=#=#= - + @@ -17,7 +17,7 @@ * Passed: crm_attribute - Set fencing-enabled=false =#=#=#= Begin test: Configure the initial resource =#=#=#= =#=#=#= Current cib after: Configure the initial resource =#=#=#= - + @@ -78,7 +78,7 @@ pcmk__update_schema debug: Schema pacemaker-3.10 validates pcmk__update_schema debug: Schema pacemaker-4.0 validates pcmk__update_schema info: Transformed the configuration schema to pacemaker-4.0 =#=#=#= Current cib after: Upgrade to latest CIB schema (trigger 2.10.xsl + the wrapping) =#=#=#= - + @@ -111,7 +111,7 @@ pcmk__update_schema info: Transformed the configuration schema to pacemaker-4.0 =#=#=#= Begin test: Query a resource instance attribute (shall survive) =#=#=#= outputpower =#=#=#= Current cib after: Query a resource instance attribute (shall survive) =#=#=#= - + diff --git a/cts/cli/regression.validity.exp b/cts/cli/regression.validity.exp index 03b61cee5b3..fd566608b3f 100644 --- a/cts/cli/regression.validity.exp +++ b/cts/cli/regression.validity.exp @@ -9,7 +9,7 @@ cibadmin: CIB API call failed: Update does not conform to the configured schema =#=#=#= Begin test: Try to use rsc_order first-action value disallowed by schema =#=#=#= cibadmin: CIB API call failed: Update does not conform to the configured schema =#=#=#= Current cib after: Try to use rsc_order first-action value disallowed by schema =#=#=#= - + @@ -28,7 +28,7 @@ cibadmin: CIB API call failed: Update does not conform to the configured schema =#=#=#= Begin test: Try to use configuration legal only with schema after configured one =#=#=#= cibadmin: CIB API call failed: Update does not conform to the configured schema =#=#=#= Current cib after: Try to use configuration legal only with schema after configured one =#=#=#= - + @@ -49,7 +49,7 @@ cibadmin: CIB API call failed: Update does not conform to the configured schema * Passed: cibadmin - Disable schema validation =#=#=#= Begin test: Set invalid rsc_order first-action value (schema validation disabled) =#=#=#= =#=#=#= Current cib after: Set invalid rsc_order first-action value (schema validation disabled) =#=#=#= - + diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index b6a0d9d29fe..57639030f21 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -100,9 +100,9 @@ createEmptyCib(int cib_epoch) pcmk__xe_set(cib_root, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); pcmk__xe_set(cib_root, PCMK_XA_VALIDATE_WITH, pcmk__highest_schema_name()); + pcmk__xe_set_int(cib_root, PCMK_XA_ADMIN_EPOCH, 0); pcmk__xe_set_int(cib_root, PCMK_XA_EPOCH, cib_epoch); pcmk__xe_set_int(cib_root, PCMK_XA_NUM_UPDATES, 0); - pcmk__xe_set_int(cib_root, PCMK_XA_ADMIN_EPOCH, 0); config = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION); pcmk__xe_create(cib_root, PCMK_XE_STATUS); From 13dba90022205c1f0ba178496385552ac831cc5a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 02:34:47 -0800 Subject: [PATCH 197/350] Refactor: libcrmcommon: New pcmk__xml_replace_with_copy() This exposes a previously static function. Nothing else calls it yet. Signed-off-by: Reid Wahl --- include/crm/common/xml_internal.h | 1 + lib/common/xml.c | 45 +++++++++++++++++++++++++++++++ lib/common/xml_element.c | 34 +---------------------- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/include/crm/common/xml_internal.h b/include/crm/common/xml_internal.h index eed9c6fc837..8fd2148354d 100644 --- a/include/crm/common/xml_internal.h +++ b/include/crm/common/xml_internal.h @@ -326,6 +326,7 @@ pcmk__xml_next(const xmlNode *child) void pcmk__xml_free(xmlNode *xml); void pcmk__xml_free_doc(xmlDoc *doc); xmlNode *pcmk__xml_copy(xmlNode *parent, xmlNode *src); +xmlNode *pcmk__xml_replace_with_copy(xmlNode *old, xmlNode *new); /*! * \internal diff --git a/lib/common/xml.c b/lib/common/xml.c index 772ef3d4f2e..14e7ab88e18 100644 --- a/lib/common/xml.c +++ b/lib/common/xml.c @@ -860,6 +860,51 @@ pcmk__xml_copy(xmlNode *parent, xmlNode *src) return copy; } +/*! + * \internal + * \brief Replace one XML node with a copy of another XML node + * + * This function handles change tracking and applies ACLs. + * + * \param[in,out] old XML node to replace + * \param[in] new XML node to copy as replacement for \p old + * + * \return Copy of \p new that replaced \p old + * + * \note This frees \p old. + * \note The caller is responsible for freeing the return value using + * \c pcmk__xml_free() (but note that it may be part of a larger XML + * tree). + */ +xmlNode * +pcmk__xml_replace_with_copy(xmlNode *old, xmlNode *new) +{ + xmlNode *new_copy = NULL; + + pcmk__assert((old != NULL) && (new != NULL)); + + /* Pass old to pcmk__xml_copy() so that new_copy gets created within the + * same doc. But old won't remain its parent. + */ + new_copy = pcmk__xml_copy(old, new); + old = xmlReplaceNode(old, new_copy); + + // old == NULL means memory allocation error + pcmk__assert(old != NULL); + + // May be unnecessary but avoids slight changes to some test outputs + pcmk__xml_tree_foreach(new_copy, pcmk__xml_reset_node_flags, NULL); + + if (pcmk__xml_doc_all_flags_set(new_copy->doc, pcmk__xf_tracking)) { + // Replaced sections may have included relevant ACLs + pcmk__apply_acl(new_copy); + } + pcmk__xml_mark_changes(old, new_copy); + pcmk__xml_free_node(old); + + return new_copy; +} + /*! * \internal * \brief Remove XML text nodes from specified XML and all its children diff --git a/lib/common/xml_element.c b/lib/common/xml_element.c index 7bd3f70d477..24c9819f0eb 100644 --- a/lib/common/xml_element.c +++ b/lib/common/xml_element.c @@ -732,38 +732,6 @@ pcmk__xe_delete_match(xmlNode *xml, xmlNode *search) return ENXIO; } -/*! - * \internal - * \brief Replace one XML node with a copy of another XML node - * - * This function handles change tracking and applies ACLs. - * - * \param[in,out] old XML node to replace - * \param[in] new XML node to copy as replacement for \p old - * - * \note This frees \p old. - */ -static void -replace_node(xmlNode *old, xmlNode *new) -{ - // Pass old for its doc; it won't remain the parent of new - new = pcmk__xml_copy(old, new); - old = xmlReplaceNode(old, new); - - // old == NULL means memory allocation error - pcmk__assert(old != NULL); - - // May be unnecessary but avoids slight changes to some test outputs - pcmk__xml_tree_foreach(new, pcmk__xml_reset_node_flags, NULL); - - if (pcmk__xml_doc_all_flags_set(new->doc, pcmk__xf_tracking)) { - // Replaced sections may have included relevant ACLs - pcmk__apply_acl(new); - } - pcmk__xml_mark_changes(old, new); - pcmk__xml_free_node(old); -} - /*! * \internal * \brief Replace one XML subtree with a copy of another if the two match @@ -805,7 +773,7 @@ replace_xe_if_matching(xmlNode *xml, void *user_data) pcmk__log_xml_trace(xml, "replace-match"); pcmk__log_xml_trace(replace, "replace-with"); - replace_node(xml, replace); + pcmk__xml_replace_with_copy(xml, replace); // Found a match and replaced it; stop traversing tree return false; From 8b4373e2556e7f184554a8a27df1f235234f2a73 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 02:27:57 -0800 Subject: [PATCH 198/350] Refactor: libcib: Replace CIB with a copy of input in same doc This avoids freeing the XML document to which *cib belonged, thus preserving document private data. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 05cc14a9b1c..f52b604d885 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -868,13 +868,11 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **result_cib) return pcmk_rc_old_data; } + *result_cib = pcmk__xml_replace_with_copy(*result_cib, input); + pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", admin_epoch, epoch, updates, replace_admin_epoch, replace_epoch, replace_updates, peer); - - pcmk__xml_free(*result_cib); - *result_cib = pcmk__xml_copy(NULL, input); - return pcmk_rc_ok; } From 0ddd677e8ed97cfddc11ec9204fec3de55232d1a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 21:37:41 -0800 Subject: [PATCH 199/350] Refactor: libcib: Merge existing_cib and result_cib args of cib__op_fn_t Immediately prior to this commit: - *result_cib was always either the existing CIB or a copy of it. - cib__process_query() was the only cib__op_fn_t that still used its existing_cib argument. It did not use its result_cib argument. If *result_cib was a copy of the existing CIB, then we didn't want changes to *result_cib to affect the existing CIB until the operation succeeded. If *result_cib was the existing CIB itself, then we didn't have a copy, and we wanted the changes to apply directly to the existing CIB. We maintain that behavior in this commit. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 104 ++++++++++++------------ daemons/based/based_messages.h | 51 ++++++------ include/crm/cib/internal.h | 38 ++++----- lib/cib/cib_file.c | 6 +- lib/cib/cib_ops.c | 140 ++++++++++++++++----------------- lib/cib/cib_utils.c | 48 ++++++----- 6 files changed, 185 insertions(+), 202 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d9367cbddc5..9e6de7cdbc1 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -45,14 +45,13 @@ static int sync_in_progress = 0; * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * - * \param[in] op Ignored - * \param[in] options Ignored - * \param[in] section Ignored - * \param[in] req Ignored - * \param[in] input Ignored - * \param[in] existing_cib Ignored - * \param[in] result_cib Ignored - * \param[in] answer Ignored + * \param[in] op Ignored + * \param[in] options Ignored + * \param[in] section Ignored + * \param[in] req Ignored + * \param[in] input Ignored + * \param[in] cib Ignored + * \param[in] answer Ignored * * \return \c EINVAL * @@ -60,8 +59,8 @@ static int sync_in_progress = 0; */ int based_process_abs_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, 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. @@ -71,8 +70,8 @@ based_process_abs_delete(const char *op, int options, const char *section, int based_process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { int rc = pcmk_rc_ok; @@ -98,14 +97,13 @@ based_process_apply_patch(const char *op, int options, const char *section, return pcmk_rc_diff_resync; } - rc = cib__process_apply_patch(op, options, section, req, input, - existing_cib, result_cib, answer); + rc = cib__process_apply_patch(op, options, section, req, input, cib, + answer); pcmk__trace("result: %s (%d), %s", pcmk_rc_str(rc), rc, (based_is_primary? "primary": "secondary")); if ((rc == pcmk_rc_diff_resync) && !based_is_primary) { - pcmk__xml_free(*result_cib); - *result_cib = NULL; + g_clear_pointer(cib, pcmk__xml_free); send_sync_request(); } else if (rc == pcmk_rc_diff_resync) { @@ -117,20 +115,19 @@ based_process_apply_patch(const char *op, int options, const char *section, int based_process_commit_transact(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { - /* On success, our caller will activate *result_cib locally, trigger a - * replace notification if appropriate, and sync *result_cib to all nodes. - * On failure, our caller will free *result_cib. + /* On success, our caller will activate *cib locally, trigger a replace + * notification if appropriate, and sync *cib to all nodes. On failure, our + * caller will free *cib. */ int rc = pcmk_rc_ok; const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); pcmk__client_t *client = pcmk__find_client_by_id(client_id); - rc = based_commit_transaction(input, client, origin, result_cib); + rc = based_commit_transaction(input, client, origin, cib); if (rc != pcmk_rc_ok) { char *source = based_transaction_source_str(client, origin); @@ -144,8 +141,8 @@ based_process_commit_transact(const char *op, int options, const char *section, int based_process_is_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -154,8 +151,8 @@ based_process_is_primary(const char *op, int options, const char *section, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int based_process_noop(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { *answer = NULL; return pcmk_rc_ok; @@ -163,8 +160,8 @@ based_process_noop(const char *op, int options, const char *section, int based_process_ping(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); @@ -199,10 +196,9 @@ based_process_ping(const char *op, int options, const char *section, } pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", - host, digest, - pcmk__xe_get(*result_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(*result_cib, PCMK_XA_EPOCH), - pcmk__xe_get(*result_cib, PCMK_XA_NUM_UPDATES)); + host, digest, pcmk__xe_get(*cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(*cib, PCMK_XA_EPOCH), + pcmk__xe_get(*cib, PCMK_XA_NUM_UPDATES)); free(digest); @@ -211,8 +207,8 @@ based_process_ping(const char *op, int options, const char *section, int based_process_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode ** answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -227,11 +223,11 @@ based_process_primary(const char *op, int options, const char *section, int based_process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { - int rc = cib__process_replace(op, options, section, req, input, - existing_cib, result_cib, answer); + int rc = cib__process_replace(op, options, section, req, input, cib, + answer); if ((rc == pcmk_rc_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { sync_in_progress = 0; @@ -241,8 +237,8 @@ based_process_replace(const char *op, int options, const char *section, int based_process_schemas(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { xmlNode *wrapper = NULL; xmlNode *data = NULL; @@ -287,8 +283,8 @@ based_process_schemas(const char *op, int options, const char *section, int based_process_secondary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -303,8 +299,8 @@ based_process_secondary(const char *op, int options, const char *section, int based_process_shutdown(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -327,24 +323,24 @@ based_process_shutdown(const char *op, int options, const char *section, int based_process_sync_to_all(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { return sync_our_cib(req, true); } int based_process_sync_to_one(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { return sync_our_cib(req, false); } int based_process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { int rc = pcmk_rc_ok; @@ -356,11 +352,11 @@ based_process_upgrade(const char *op, int options, const char *section, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(op, options, section, req, input, - existing_cib, result_cib, answer); + return cib__process_upgrade(op, options, section, req, input, cib, + answer); } else { - xmlNode *scratch = pcmk__xml_copy(NULL, *result_cib); + xmlNode *scratch = pcmk__xml_copy(NULL, *cib); const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *original_schema = NULL; const char *new_schema = NULL; @@ -368,7 +364,7 @@ based_process_upgrade(const char *op, int options, const char *section, const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); - original_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); + original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); if (original_schema == NULL) { pcmk__info("Rejecting upgrade request from %s: No " PCMK_XA_VALIDATE_WITH, diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index fee6e089cb8..2cae380e8e6 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -18,66 +18,61 @@ extern bool based_is_primary; extern xmlNode *the_cib; int based_process_abs_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); int based_process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); int based_process_commit_transact(const char *op, int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_is_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); int based_process_noop(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_ping(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_schemas(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_secondary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_shutdown(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_sync_to_all(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); int based_process_sync_to_one(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); int based_process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); void send_sync_request(void); int sync_our_cib(xmlNode *request, bool all); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 5b0359f5b18..c2418f00f4c 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -96,7 +96,7 @@ enum cib__op_type { }; typedef int (*cib__op_fn_t)(const char *, int, const char *, xmlNode *, - xmlNode *, xmlNode *, xmlNode **, xmlNode **); + xmlNode *, xmlNode **, xmlNode **); typedef struct cib__operation_s { const char *name; @@ -203,40 +203,40 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); int cib__process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_bump(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_create(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_erase(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_modify(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_query(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib__process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer); + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 69fa7168037..923aea96efc 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -322,8 +322,8 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) static int process_commit_transact(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib_xml, + xmlNode **answer) { int rc = pcmk_rc_ok; const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); @@ -334,7 +334,7 @@ process_commit_transact(const char *op, int options, const char *section, cib = get_client(client_id); CRM_CHECK(cib != NULL, return -EINVAL); - rc = commit_transaction(cib, input, result_cib); + rc = commit_transaction(cib, input, cib_xml); if (rc != pcmk_rc_ok) { file_opaque_t *private = cib->variant_opaque; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index f52b604d885..72bb93efad4 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -169,10 +169,10 @@ cib__get_operation(const char *op, const cib__operation_t **operation) int cib__process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { - int rc = xml_apply_patchset(*result_cib, input, true); + int rc = xml_apply_patchset(*cib, input, true); return pcmk_legacy2rc(rc); } @@ -196,10 +196,9 @@ update_counter(xmlNode *xml, const char *field, bool reset) int cib__process_bump(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { - update_counter(*result_cib, PCMK_XA_EPOCH, false); + update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; } @@ -265,11 +264,11 @@ update_results(xmlNode *failed, xmlNode *target, const char *operation, int rc) static int process_create_xpath(const char *op, const char *xpath, xmlNode *input, - xmlNode *result_cib) + xmlNode *cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); xmlNode *match = NULL; xmlChar *path = NULL; @@ -298,8 +297,8 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, int cib__process_create(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { xmlNode *failed = NULL; int rc = pcmk_rc_ok; @@ -323,14 +322,14 @@ cib__process_create(const char *op, int options, const char *section, } if (section == NULL) { - return cib__process_modify(op, options, section, req, input, - existing_cib, result_cib, answer); + return cib__process_modify(op, options, section, req, input, cib, + answer); } // @COMPAT Deprecated since 2.1.8 failed = pcmk__xe_create(NULL, PCMK__XE_FAILED); - update_section = pcmk_find_cib_element(*result_cib, section); + update_section = pcmk_find_cib_element(*cib, section); if (pcmk__xe_is(input, section)) { xmlNode *a_child = NULL; @@ -368,12 +367,12 @@ cib__process_create(const char *op, int options, const char *section, static int process_delete_xpath(const char *op, int options, const char *xpath, - xmlNode *result_cib) + xmlNode *cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { @@ -415,7 +414,7 @@ process_delete_xpath(const char *op, int options, const char *xpath, pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); - if (match == result_cib) { + if (match == cib) { pcmk__warn("Cannot perform %s for %s: the XPath is addressing the " "whole /cib", op, xpath); rc = EINVAL; @@ -447,7 +446,7 @@ delete_child(xmlNode *child, void *userdata) } static int -process_delete_section(const char *section, xmlNode *input, xmlNode *result_cib) +process_delete_section(const char *section, xmlNode *input, xmlNode *cib) { xmlNode *obj_root = NULL; @@ -456,7 +455,7 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *result_cib) return EINVAL; } - obj_root = pcmk_find_cib_element(result_cib, section); + obj_root = pcmk_find_cib_element(cib, section); if (pcmk__xe_is(input, section)) { pcmk__xe_foreach_child(input, NULL, delete_child, obj_root); @@ -470,20 +469,20 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *result_cib) int cib__process_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { - return process_delete_xpath(op, options, section, *result_cib); + return process_delete_xpath(op, options, section, *cib); } - return process_delete_section(section, input, *result_cib); + return process_delete_section(section, input, *cib); } int cib__process_erase(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -492,20 +491,19 @@ cib__process_erase(const char *op, int options, const char *section, NULL); // Free all existing children, regardless of node type - while ((*result_cib)->children != NULL) { - pcmk__xml_free((*result_cib)->children); + while ((*cib)->children != NULL) { + pcmk__xml_free((*cib)->children); } /* Copying is a wasteful here, but calling pcmk__xml_copy() adds the copy as - * a child of the existing *result_cib within the same document. This - * reduces the number of opportunities to make mistakes related to XML - * documents, change tracking, etc., compared to calling xmlUnlinkChild(), - * xmlAddChild(), etc. + * a child of the existing *cib within the same document. This reduces the + * number of opportunities to make mistakes related to XML documents, change + * tracking, etc., compared to calling xmlUnlinkChild(), xmlAddChild(), etc. */ - pcmk__xml_copy(*result_cib, empty_config); - pcmk__xml_copy(*result_cib, empty_status); + pcmk__xml_copy(*cib, empty_config); + pcmk__xml_copy(*cib, empty_status); - update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); + update_counter(*cib, PCMK_XA_ADMIN_EPOCH, false); pcmk__xml_free(empty); return pcmk_rc_ok; @@ -513,11 +511,11 @@ cib__process_erase(const char *op, int options, const char *section, static int process_modify_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *result_cib) + xmlNode *input, xmlNode *cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); const bool score = pcmk__is_set(options, cib_score_update); const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); @@ -556,7 +554,7 @@ process_modify_xpath(const char *op, int options, const char *xpath, static int process_modify_section(int options, const char *section, xmlNode *input, - xmlNode *result_cib) + xmlNode *cib) { const bool score = pcmk__is_set(options, cib_score_update); const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); @@ -567,7 +565,7 @@ process_modify_section(int options, const char *section, xmlNode *input, return EINVAL; } - obj_root = pcmk_find_cib_element(result_cib, section); + obj_root = pcmk_find_cib_element(cib, section); if (obj_root == NULL) { xmlNode *tmp_section = NULL; const char *path = pcmk_cib_parent_name_for(section); @@ -579,11 +577,10 @@ process_modify_section(int options, const char *section, xmlNode *input, tmp_section = pcmk__xe_create(NULL, section); // @TODO This feels hacky and is the only call to process_create_xpath() - process_create_xpath(PCMK__CIB_REQUEST_CREATE, path, tmp_section, - result_cib); + process_create_xpath(PCMK__CIB_REQUEST_CREATE, path, tmp_section, cib); pcmk__xml_free(tmp_section); - obj_root = pcmk_find_cib_element(result_cib, section); + obj_root = pcmk_find_cib_element(cib, section); } // Should be impossible, as we just created this section if it didn't exist @@ -603,23 +600,23 @@ process_modify_section(int options, const char *section, xmlNode *input, int cib__process_modify(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { - return process_modify_xpath(op, options, section, input, *result_cib); + return process_modify_xpath(op, options, section, input, *cib); } - return process_modify_section(options, section, input, *result_cib); + return process_modify_section(options, section, input, *cib); } static int process_query_xpath(const char *op, int options, const char *xpath, - xmlNode *existing_cib, xmlNode **answer) + xmlNode *cib, xmlNode **answer) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(existing_cib->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { @@ -706,7 +703,7 @@ process_query_xpath(const char *op, int options, const char *xpath, } static int -process_query_section(int options, const char *section, xmlNode *existing_cib, +process_query_section(int options, const char *section, xmlNode *cib, xmlNode **answer) { xmlNode *obj_root = NULL; @@ -715,7 +712,7 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, section = NULL; } - obj_root = pcmk_find_cib_element(existing_cib, section); + obj_root = pcmk_find_cib_element(cib, section); if (obj_root == NULL) { return ENXIO; } @@ -737,23 +734,22 @@ process_query_section(int options, const char *section, xmlNode *existing_cib, int cib__process_query(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { - return process_query_xpath(op, options, section, existing_cib, answer); + return process_query_xpath(op, options, section, *cib, answer); } - return process_query_section(options, section, existing_cib, answer); + return process_query_section(options, section, *cib, answer); } static int process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *result_cib) + xmlNode *input, xmlNode *cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(result_cib->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { @@ -820,7 +816,7 @@ replace_cib_digest_matches(xmlNode *request, xmlNode *input) } static int -replace_cib(xmlNode *request, xmlNode *input, xmlNode **result_cib) +replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) { int updates = 0; int epoch = 0; @@ -833,7 +829,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **result_cib) const char *reason = NULL; const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); - cib_version_details(*result_cib, &admin_epoch, &epoch, &updates); + cib_version_details(*cib, &admin_epoch, &epoch, &updates); cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); @@ -868,7 +864,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **result_cib) return pcmk_rc_old_data; } - *result_cib = pcmk__xml_replace_with_copy(*result_cib, input); + *cib = pcmk__xml_replace_with_copy(*cib, input); pcmk__info("Replaced %d.%d.%d with %d.%d.%d from %s", admin_epoch, epoch, updates, replace_admin_epoch, replace_epoch, replace_updates, @@ -878,7 +874,7 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **result_cib) static int process_replace_section(const char *section, xmlNode *request, xmlNode *input, - xmlNode **result_cib) + xmlNode **cib) { int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; @@ -889,7 +885,7 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - return replace_cib(request, input, result_cib); + return replace_cib(request, input, cib); } if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei) @@ -898,7 +894,7 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, section = NULL; } - obj_root = pcmk_find_cib_element(*result_cib, section); + obj_root = pcmk_find_cib_element(*cib, section); rc = pcmk__xe_replace_match(obj_root, input); if (rc != pcmk_rc_ok) { @@ -910,35 +906,35 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, int cib__process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { - return process_replace_xpath(op, options, section, input, *result_cib); + return process_replace_xpath(op, options, section, input, *cib); } - return process_replace_section(section, req, input, result_cib); + return process_replace_section(section, req, input, cib); } int cib__process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode *existing_cib, - xmlNode **result_cib, xmlNode **answer) + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { int rc = pcmk_rc_ok; const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); const char *original_schema = NULL; const char *new_schema = NULL; - original_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); - rc = pcmk__update_schema(result_cib, max_schema, true, + original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); + rc = pcmk__update_schema(cib, max_schema, true, !pcmk__is_set(options, cib_verbose)); - new_schema = pcmk__xe_get(*result_cib, PCMK_XA_VALIDATE_WITH); + new_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false); - update_counter(*result_cib, PCMK_XA_EPOCH, true); - update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true); + update_counter(*cib, PCMK_XA_ADMIN_EPOCH, false); + update_counter(*cib, PCMK_XA_EPOCH, true); + update_counter(*cib, PCMK_XA_NUM_UPDATES, true); return pcmk_rc_ok; } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 57639030f21..dbecb7ab27e 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -177,16 +177,15 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, int rc = pcmk_rc_ok; const char *user = NULL; - xmlNode *cib_ro = NULL; + xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; - xmlNode *result_cib = NULL; pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) && (current_cib != NULL) && (*current_cib != NULL) && (output != NULL) && (*output == NULL)); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); - cib_ro = *current_cib; + cib = *current_cib; if (cib_acl_enabled(*current_cib, user) && xml_acl_filtered_copy(user, *current_cib, *current_cib, @@ -196,15 +195,15 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, pcmk__debug("Pre-filtered the entire cib"); return EACCES; } - cib_ro = cib_filtered; - pcmk__log_xml_trace(cib_ro, "filtered"); + cib = cib_filtered; + pcmk__log_xml_trace(cib, "filtered"); } pcmk__trace("Processing %s for section '%s', user '%s'", op, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = fn(op, call_options, section, req, input, cib_ro, &result_cib, output); + rc = fn(op, call_options, section, req, input, &cib, output); if (*output == NULL) { // Do nothing @@ -227,8 +226,6 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, } pcmk__xml_free(cib_filtered); - CRM_CHECK(result_cib == NULL, pcmk__xml_free(result_cib)); - return rc; } @@ -312,32 +309,32 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, if (!make_copy) { /* Conditional on v2 patch style */ - scratch = *current_cib; - // Make a copy of the top-level element to store version details - top = pcmk__xe_create(NULL, (const char *) scratch->name); - pcmk__xe_copy_attrs(top, scratch, pcmk__xaf_none); + top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name); + pcmk__xe_copy_attrs(top, *current_cib, pcmk__xaf_none); patchset_cib = top; - pcmk__xml_commit_changes(scratch->doc); - pcmk__xml_doc_set_flags(scratch->doc, pcmk__xf_tracking); + pcmk__xml_commit_changes((*current_cib)->doc); + pcmk__xml_doc_set_flags((*current_cib)->doc, pcmk__xf_tracking); if (enable_acl) { - pcmk__enable_acl(*current_cib, scratch, user); + pcmk__enable_acl(*current_cib, *current_cib, user); } pcmk__trace("Processing %s for section '%s', user '%s'", op, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output); + rc = (*fn) (op, call_options, section, req, input, current_cib, output); - /* If scratch points to a new object now (for example, after an erase - * operation), then *current_cib should point to the same object. - * - * @TODO Enable tracking and ACLs and calculate changes? Change tracking - * and unpacked ACLs didn't carry over to new object. + /* Set scratch to *current_cib after fn(), in case *current_cib points + * somewhere else now (for example, erase or full-CIB replace op). + */ + scratch = *current_cib; + + /* @TODO Enable tracking and ACLs and calculate changes? If + * scratch and *current_cib point to a new object, then change tracking + * and unpacked ACLs didn't carry over to it. */ - *current_cib = scratch; } else { scratch = pcmk__xml_copy(NULL, *current_cib); @@ -352,8 +349,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = (*fn) (op, call_options, section, req, input, *current_cib, - &scratch, output); + rc = (*fn) (op, call_options, section, req, input, &scratch, output); /* @TODO This appears to be a hack to determine whether scratch points * to a new object now, without saving the old pointer (which may be @@ -825,8 +821,8 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, input, - output, NULL); + rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, output, + NULL); rc = pcmk_rc2legacy(rc); if (rc == pcmk_ok) { return pcmk_ok; From 026dcba0a8ef9a189c182de7b7fd36e7c03f246a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 22:55:25 -0800 Subject: [PATCH 200/350] Refactor: libcib: Return standard code from cib_file.c:process_request() Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 923aea96efc..4cf3bc5dd63 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -138,7 +138,7 @@ get_client(const char *client_id) static int process_request(cib_t *cib, xmlNode *request, xmlNode **output) { - int rc = pcmk_ok; + int rc = pcmk_rc_ok; const cib__operation_t *operation = NULL; cib__op_fn_t op_function = NULL; @@ -211,7 +211,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) pcmk__xml_free(result_cib); } pcmk__xml_free(cib_diff); - return pcmk_rc2legacy(rc); + return rc; } /*! @@ -241,7 +241,6 @@ process_transaction_requests(cib_t *cib, xmlNode *transaction) int rc = process_request(cib, request, &output); - rc = pcmk_legacy2rc(rc); if (rc != pcmk_rc_ok) { pcmk__err("Aborting transaction for CIB file client (%s) on file " "'%s' due to failed %s request: %s", @@ -465,6 +464,7 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, } rc = process_request(cib, request, &output); + rc = pcmk_rc2legacy(rc); if ((output_data != NULL) && (output != NULL)) { if (output->doc == private->cib_xml->doc) { From 381e4d6aa42a2100b843e8ca069cf10e1d2394aa Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 23:05:19 -0800 Subject: [PATCH 201/350] Refactor: libcib: Return standard code from cib__extend_transaction() Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 1 + lib/cib/cib_native.c | 1 + lib/cib/cib_remote.c | 2 +- lib/cib/cib_utils.c | 22 +++++++++++----------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 4cf3bc5dd63..4340aea0431 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -460,6 +460,7 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (pcmk__is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, request); + rc = pcmk_rc2legacy(rc); goto done; } diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 2c54b4a7bab..9949e7fcb2b 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -74,6 +74,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (pcmk__is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, op_msg); + rc = pcmk_rc2legacy(rc); goto done; } diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index d25e8008231..4d54c6c93a1 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -85,7 +85,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, if (pcmk__is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, op_msg); pcmk__xml_free(op_msg); - return rc; + return pcmk_rc2legacy(rc); } pcmk__trace("Sending %s message to the CIB manager", op); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index dbecb7ab27e..537e270f25b 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -639,11 +639,13 @@ validate_transaction_request(const xmlNode *request) * \param[in,out] cib CIB client whose transaction to extend * \param[in,out] request Request to add to transaction * - * \return Legacy Pacemaker return code + * \return Standard Pacemaker return code */ int cib__extend_transaction(cib_t *cib, xmlNode *request) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *client_id = NULL; int rc = pcmk_rc_ok; pcmk__assert((cib != NULL) && (request != NULL)); @@ -656,18 +658,16 @@ cib__extend_transaction(cib_t *cib, xmlNode *request) if (rc == pcmk_rc_ok) { pcmk__xml_copy(cib->transaction, request); + return pcmk_rc_ok; + } - } else { - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *client_id = NULL; + cib->cmds->client_id(cib, NULL, &client_id); - cib->cmds->client_id(cib, NULL, &client_id); - pcmk__err("Failed to add '%s' operation to transaction for client %s: " - "%s", - op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc)); - pcmk__log_xml_info(request, "failed"); - } - return pcmk_rc2legacy(rc); + pcmk__err("Failed to add '%s' operation to transaction for client %s: %s", + op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc)); + pcmk__log_xml_info(request, "failed"); + + return rc; } void From e48bee10dafaca009b24eeffe70c0312b1de5356 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 23:08:15 -0800 Subject: [PATCH 202/350] Refactor: libcib: Return standard code from cib__create_op() Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 2 ++ lib/cib/cib_native.c | 2 ++ lib/cib/cib_remote.c | 1 + lib/cib/cib_utils.c | 4 ++-- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 4340aea0431..a9bbf9246e8 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -452,9 +452,11 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = cib__create_op(cib, op, host, section, data, call_options, user_name, NULL, &request); + rc = pcmk_rc2legacy(rc); if (rc != pcmk_ok) { return rc; } + pcmk__xe_set(request, PCMK__XA_ACL_TARGET, user_name); pcmk__xe_set(request, PCMK__XA_CIB_CLIENTID, private->id); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 9949e7fcb2b..ed3b221f6c5 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -68,6 +68,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = cib__create_op(cib, op, host, section, data, call_options, user_name, NULL, &op_msg); + rc = pcmk_rc2legacy(rc); if (rc != pcmk_ok) { return rc; } @@ -312,6 +313,7 @@ cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type) 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); } if (rc == pcmk_ok) { diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 4d54c6c93a1..4dcfe4a50cc 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -78,6 +78,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, rc = cib__create_op(cib, op, host, section, data, call_options, user_name, NULL, &op_msg); + rc = pcmk_rc2legacy(rc); if (rc != pcmk_ok) { return rc; } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 537e270f25b..c2a7d3062d4 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -567,7 +567,7 @@ cib__create_op(cib_t *cib, const char *op, const char *host, const char *user_name, const char *client_name, xmlNode **op_msg) { - CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO); + CRM_CHECK((cib != NULL) && (op_msg != NULL), return EPROTO); *op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND); @@ -594,7 +594,7 @@ cib__create_op(cib_t *cib, const char *op, const char *host, pcmk__xml_copy(wrapper, data); } - return pcmk_ok; + return pcmk_rc_ok; } /*! From 96fb1f09cb8be99633a97bcbb89105b5bb5645f2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 23:24:51 -0800 Subject: [PATCH 203/350] Refactor: libcib: Assert *current_cib != scratch when make_copy true We might as well assert fatally here, as that would mean a major programming error. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index c2a7d3062d4..8e79107ba9c 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -363,7 +363,8 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } pcmk__xml_mark_changes(*current_cib, scratch); } - CRM_CHECK(*current_cib != scratch, return EINVAL); + + pcmk__assert(*current_cib != scratch); } xml_acl_disable(scratch); /* Allow the system to make any additional changes */ From 7934e4e4015ed1ca208c1f34e170ee3de613be10 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 23:30:38 -0800 Subject: [PATCH 204/350] Refactor: libcib: Rename scratch to working_cib in cib_perform_op() It may not be a copy, the changes to it may be accepted as permanent, and the name "scratch" didn't make it obvious that it was CIB XML. I think we need all the clarity we can get, as complicated as cib_perform_op() is. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 107 +++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 8e79107ba9c..4592c0a56de 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -287,7 +287,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, int rc = pcmk_rc_ok; bool make_copy = true; xmlNode *top = NULL; - xmlNode *scratch = NULL; + xmlNode *working_cib = NULL; xmlNode *patchset_cib = NULL; xmlNode *local_diff = NULL; @@ -324,56 +324,58 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = (*fn) (op, call_options, section, req, input, current_cib, output); + rc = fn(op, call_options, section, req, input, current_cib, output); - /* Set scratch to *current_cib after fn(), in case *current_cib points - * somewhere else now (for example, erase or full-CIB replace op). + /* Set working_cib to *current_cib after fn(), in case *current_cib + * points somewhere else now (for example, after a erase or full-CIB + * replace op). */ - scratch = *current_cib; + working_cib = *current_cib; - /* @TODO Enable tracking and ACLs and calculate changes? If - * scratch and *current_cib point to a new object, then change tracking - * and unpacked ACLs didn't carry over to it. + /* @TODO Enable tracking and ACLs and calculate changes? If working_cib + * and *current_cib point to a new object, then change tracking and + * unpacked ACLs didn't carry over to it. */ } else { - scratch = pcmk__xml_copy(NULL, *current_cib); + working_cib = pcmk__xml_copy(NULL, *current_cib); patchset_cib = *current_cib; - pcmk__xml_doc_set_flags(scratch->doc, pcmk__xf_tracking); + pcmk__xml_doc_set_flags(working_cib->doc, pcmk__xf_tracking); if (enable_acl) { - pcmk__enable_acl(*current_cib, scratch, user); + pcmk__enable_acl(*current_cib, working_cib, user); } pcmk__trace("Processing %s for section '%s', user '%s'", op, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = (*fn) (op, call_options, section, req, input, &scratch, output); + rc = fn(op, call_options, section, req, input, &working_cib, output); - /* @TODO This appears to be a hack to determine whether scratch points - * to a new object now, without saving the old pointer (which may be - * invalid now) for comparison. Confirm this, and check more clearly. + /* @TODO This appears to be a hack to determine whether working_cib + * points to a new object now, without saving the old pointer (which may + * be invalid now) for comparison. Confirm this, and check more clearly. */ - if (!pcmk__xml_doc_all_flags_set(scratch->doc, pcmk__xf_tracking)) { + if (!pcmk__xml_doc_all_flags_set(working_cib->doc, pcmk__xf_tracking)) { pcmk__trace("Inferring changes after %s op", op); - pcmk__xml_commit_changes(scratch->doc); + pcmk__xml_commit_changes(working_cib->doc); if (enable_acl) { - pcmk__enable_acl(*current_cib, scratch, user); + pcmk__enable_acl(*current_cib, working_cib, user); } - pcmk__xml_mark_changes(*current_cib, scratch); + pcmk__xml_mark_changes(*current_cib, working_cib); } - pcmk__assert(*current_cib != scratch); + pcmk__assert(*current_cib != working_cib); } - xml_acl_disable(scratch); /* Allow the system to make any additional changes */ + // Allow ourselves to make any additional necessary changes + xml_acl_disable(working_cib); - if ((rc == pcmk_rc_ok) && (scratch == NULL)) { + if ((rc == pcmk_rc_ok) && (working_cib == NULL)) { rc = EINVAL; goto done; - } else if ((rc == pcmk_rc_ok) && xml_acl_denied(scratch)) { + } else if ((rc == pcmk_rc_ok) && xml_acl_denied(working_cib)) { pcmk__trace("ACL rejected part or all of the proposed changes"); rc = EACCES; goto done; @@ -386,8 +388,10 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, * supported. All we care about in that case is the schema version, which * is checked elsewhere. */ - if (scratch && (cib == NULL || cib->variant != cib_file)) { - const char *new_version = pcmk__xe_get(scratch, + if ((working_cib != NULL) + && ((cib == NULL) || (cib->variant != cib_file))) { + + const char *new_version = pcmk__xe_get(working_cib, PCMK_XA_CRM_FEATURE_SET); rc = pcmk__check_feature_set(new_version); @@ -403,7 +407,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, int old = 0; int new = 0; - pcmk__xe_get_int(scratch, PCMK_XA_ADMIN_EPOCH, &new); + pcmk__xe_get_int(working_cib, PCMK_XA_ADMIN_EPOCH, &new); pcmk__xe_get_int(patchset_cib, PCMK_XA_ADMIN_EPOCH, &old); if (old > new) { @@ -414,7 +418,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, rc = pcmk_rc_old_data; } else if (old == new) { - pcmk__xe_get_int(scratch, PCMK_XA_EPOCH, &new); + pcmk__xe_get_int(working_cib, PCMK_XA_EPOCH, &new); pcmk__xe_get_int(patchset_cib, PCMK_XA_EPOCH, &old); if (old > new) { pcmk__err("%s went backwards: %d -> %d (Opts: %#x)", @@ -427,7 +431,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } pcmk__trace("Massaging CIB contents"); - pcmk__strip_xml_text(scratch); + pcmk__strip_xml_text(working_cib); if (make_copy) { static time_t expires = 0; @@ -439,15 +443,15 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } } - local_diff = xml_create_patchset(0, patchset_cib, scratch, + local_diff = xml_create_patchset(0, patchset_cib, working_cib, config_changed, manage_counters); - pcmk__log_xml_changes(LOG_TRACE, scratch); - pcmk__xml_commit_changes(scratch->doc); + pcmk__log_xml_changes(LOG_TRACE, working_cib); + pcmk__xml_commit_changes(working_cib->doc); - if(local_diff) { + if (local_diff != NULL) { if (with_digest) { - pcmk__xml_patchset_add_digest(local_diff, scratch); + pcmk__xml_patchset_add_digest(local_diff, working_cib); } pcmk__log_xml_patchset(LOG_INFO, local_diff); pcmk__log_xml_trace(local_diff, "raw patch"); @@ -471,7 +475,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, NULL); pcmk__xml_write_temp_file(patchset_cib, "PatchApply:input", NULL); - pcmk__xml_write_temp_file(scratch, "PatchApply:actual", + pcmk__xml_write_temp_file(working_cib, "PatchApply:actual", NULL); pcmk__xml_write_temp_file(local_diff, "PatchApply:diff", NULL); @@ -486,43 +490,43 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, ); } - /* scratch must not be modified after this point, except for the attributes - * for which pcmk__xa_filterable() returns true + /* working_cib must not be modified after this point, except for the + * attributes for which pcmk__xa_filterable() returns true */ if (*config_changed && !pcmk__is_set(call_options, cib_no_mtime)) { - const char *schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); + const char *schema = pcmk__xe_get(working_cib, PCMK_XA_VALIDATE_WITH); if (schema == NULL) { rc = pcmk_rc_cib_corrupt; } - pcmk__xe_add_last_written(scratch); + pcmk__xe_add_last_written(working_cib); pcmk__warn_if_schema_deprecated(schema); - /* Make values of origin, client, and user in scratch match - * the ones in req (if the schema allows the attributes) + /* Make values of origin, client, and user in working_cib match the ones + * in req (if the schema allows the attributes) */ if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") >= 0) { const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); const char *client = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTNAME); if (origin != NULL) { - pcmk__xe_set(scratch, PCMK_XA_UPDATE_ORIGIN, origin); + pcmk__xe_set(working_cib, PCMK_XA_UPDATE_ORIGIN, origin); } else { - pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_ORIGIN); + pcmk__xe_remove_attr(working_cib, PCMK_XA_UPDATE_ORIGIN); } if (client != NULL) { - pcmk__xe_set(scratch, PCMK_XA_UPDATE_CLIENT, client); + pcmk__xe_set(working_cib, PCMK_XA_UPDATE_CLIENT, client); } else { - pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_CLIENT); + pcmk__xe_remove_attr(working_cib, PCMK_XA_UPDATE_CLIENT); } if (user != NULL) { - pcmk__xe_set(scratch, PCMK_XA_UPDATE_USER, user); + pcmk__xe_set(working_cib, PCMK_XA_UPDATE_USER, user); } else { - pcmk__xe_remove_attr(scratch, PCMK_XA_UPDATE_USER); + pcmk__xe_remove_attr(working_cib, PCMK_XA_UPDATE_USER); } } } @@ -530,25 +534,24 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, // Skip validation for status-only updates, since we allow anything there if ((rc == pcmk_rc_ok) && !pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei) - && !pcmk__configured_schema_validates(scratch)) { + && !pcmk__configured_schema_validates(working_cib)) { rc = pcmk_rc_schema_validation; } - done: - - *result_cib = scratch; +done: + *result_cib = working_cib; /* @TODO: This may not work correctly with !make_copy, since we don't * keep the original CIB. */ if ((rc != pcmk_rc_ok) && cib_acl_enabled(patchset_cib, user) - && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) { + && xml_acl_filtered_copy(user, patchset_cib, working_cib, result_cib)) { if (*result_cib == NULL) { pcmk__debug("Pre-filtered the entire cib result"); } - pcmk__xml_free(scratch); + pcmk__xml_free(working_cib); } if(diff) { From d157c81b713ac6149e3c2226edecc99ac63eede1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 23:40:31 -0800 Subject: [PATCH 205/350] Refactor: libcib: New set_update_origin() for cib_perform_op() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 86 ++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 4592c0a56de..2cae10ecf83 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -277,6 +277,56 @@ should_copy_cib(const char *op, const char *section, int call_options) return true; } +/*! + * \internal + * \brief Set values for update origin host, client, and user in new CIB + * + * \param[in,out] new_cib Result CIB after performing operation + * \param[in] request CIB request (source of origin info) + * + * \return Standard Pacemaker return code + */ +static int +set_update_origin(xmlNode *new_cib, const xmlNode *request) +{ + const char *origin = pcmk__xe_get(request, PCMK__XA_SRC); + const char *client = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); + const char *user = pcmk__xe_get(request, PCMK__XA_CIB_USER); + const char *schema = pcmk__xe_get(new_cib, PCMK_XA_VALIDATE_WITH); + + if (schema == NULL) { + return pcmk_rc_cib_corrupt; + } + + pcmk__xe_add_last_written(new_cib); + pcmk__warn_if_schema_deprecated(schema); + + // pacemaker-1.2 is the earliest schema version that allow these attributes + if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") < 0) { + return pcmk_rc_ok; + } + + if (origin != NULL) { + pcmk__xe_set(new_cib, PCMK_XA_UPDATE_ORIGIN, origin); + } else { + pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_ORIGIN); + } + + if (client != NULL) { + pcmk__xe_set(new_cib, PCMK_XA_UPDATE_CLIENT, client); + } else { + pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_CLIENT); + } + + if (user != NULL) { + pcmk__xe_set(new_cib, PCMK_XA_UPDATE_USER, user); + } else { + pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_USER); + } + + return pcmk_rc_ok; +} + int cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, @@ -495,39 +545,9 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, */ if (*config_changed && !pcmk__is_set(call_options, cib_no_mtime)) { - const char *schema = pcmk__xe_get(working_cib, PCMK_XA_VALIDATE_WITH); - - if (schema == NULL) { - rc = pcmk_rc_cib_corrupt; - } - - pcmk__xe_add_last_written(working_cib); - pcmk__warn_if_schema_deprecated(schema); - - /* Make values of origin, client, and user in working_cib match the ones - * in req (if the schema allows the attributes) - */ - if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") >= 0) { - const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); - const char *client = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTNAME); - - if (origin != NULL) { - pcmk__xe_set(working_cib, PCMK_XA_UPDATE_ORIGIN, origin); - } else { - pcmk__xe_remove_attr(working_cib, PCMK_XA_UPDATE_ORIGIN); - } - - if (client != NULL) { - pcmk__xe_set(working_cib, PCMK_XA_UPDATE_CLIENT, client); - } else { - pcmk__xe_remove_attr(working_cib, PCMK_XA_UPDATE_CLIENT); - } - - if (user != NULL) { - pcmk__xe_set(working_cib, PCMK_XA_UPDATE_USER, user); - } else { - pcmk__xe_remove_attr(working_cib, PCMK_XA_UPDATE_USER); - } + rc = set_update_origin(working_cib, req); + if (rc != pcmk_rc_ok) { + goto done; } } From 7ea03b803e088e182d5f4a96dc70b85c752fc63c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 30 Dec 2025 23:43:59 -0800 Subject: [PATCH 206/350] Log: libcib: Drop unhelpful trace message Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 2cae10ecf83..0a088271f65 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -480,7 +480,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } } - pcmk__trace("Massaging CIB contents"); pcmk__strip_xml_text(working_cib); if (make_copy) { From 12c0068892c99ea6d35c3031e51922fafa311f9b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 01:25:39 -0800 Subject: [PATCH 207/350] Low: libcrmcommon: Handle NULL and PCMK_XA_FORMAT and PCMK_XA_DIGEST A patchset doesn't necessarily contain a digest. A patchset from a Pacemaker version recent enough to be supported should contain a format, but let's check just in case. Signed-off-by: Reid Wahl --- lib/common/patchset_display.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/common/patchset_display.c b/lib/common/patchset_display.c index 9cc1663eaf0..110e56e3a98 100644 --- a/lib/common/patchset_display.c +++ b/lib/common/patchset_display.c @@ -50,9 +50,10 @@ xml_show_patchset_header(pcmk__output_t *out, const xmlNode *patchset) const char *fmt = pcmk__xe_get(patchset, PCMK_XA_FORMAT); const char *digest = pcmk__xe_get(patchset, PCMK_XA_DIGEST); - out->info(out, "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt); + out->info(out, "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], + pcmk__s(fmt, "(no format)")); rc = out->info(out, "Diff: +++ %d.%d.%d %s", - add[0], add[1], add[2], digest); + add[0], add[1], add[2], pcmk__s(digest, "(no digest)")); } else if ((add[0] != 0) || (add[1] != 0) || (add[2] != 0)) { rc = out->info(out, "Local-only Change: %d.%d.%d", From 0e70b657f58857373082255914dfdf709e913fb2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 02:55:45 -0800 Subject: [PATCH 208/350] Test: cts-cli: Update outputs for "(no digest)" Signed-off-by: Reid Wahl --- cts/cli/regression.crm_shadow.exp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cts/cli/regression.crm_shadow.exp b/cts/cli/regression.crm_shadow.exp index bccf28bdb13..84fae0fdf17 100644 --- a/cts/cli/regression.crm_shadow.exp +++ b/cts/cli/regression.crm_shadow.exp @@ -743,7 +743,7 @@ cts-cli * Passed: crm_shadow - Get active shadow instance's diff (copied) (XML) =#=#=#= Begin test: Get active shadow instance's diff (after changes) =#=#=#= Diff: --- 1.1.173 2 -Diff: +++ 1.4.1 (null) +Diff: +++ 1.4.1 (no digest) -- /cib/configuration/op_defaults + /cib: @epoch=4, @num_updates=1 + /cib/configuration/resources/primitive[@id='dummy']: @description=desc @@ -800,7 +800,7 @@ To prevent accidental destruction of the cluster, the --force flag is required i * Passed: crm_shadow - Commit shadow instance (force) =#=#=#= Begin test: Get active shadow instance's diff (after commit) =#=#=#= Diff: --- 1.2.0 2 -Diff: +++ 1.4.1 (null) +Diff: +++ 1.4.1 (no digest) + /cib: @epoch=4, @num_updates=1 ++ /cib/status: =#=#=#= End test: Get active shadow instance's diff (after commit) - Error occurred (1) =#=#=#= @@ -810,7 +810,7 @@ Diff: +++ 1.4.1 (null) * Passed: crm_shadow - Commit shadow instance (force) (all) =#=#=#= Begin test: Get active shadow instance's diff (after commit all) =#=#=#= Diff: --- 1.4.2 2 -Diff: +++ 1.4.1 (null) +Diff: +++ 1.4.1 (no digest) + /cib: @num_updates=1 =#=#=#= End test: Get active shadow instance's diff (after commit all) - Error occurred (1) =#=#=#= * Passed: crm_shadow - Get active shadow instance's diff (after commit all) @@ -1353,7 +1353,7 @@ A new shadow instance was created. To begin using it, enter the following into y * Passed: crm_shadow - Get active shadow instance's contents (empty CIB) (XML) =#=#=#= Begin test: Get active shadow instance's diff (empty CIB) =#=#=#= Diff: --- 1.1.173 2 -Diff: +++ 0.1.0 (null) +Diff: +++ 0.1.0 (no digest) -- /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options'] -- /cib/configuration/nodes/node[@id='1'] -- /cib/configuration/nodes/node[@id='2'] @@ -1446,7 +1446,7 @@ A new shadow instance was created. To begin using it, enter the following into y * Passed: crm_shadow - Active shadow instance no different from active CIB after reset =#=#=#= Begin test: Active shadow instance differs from active CIB after change =#=#=#= Diff: --- 1.1.173 2 -Diff: +++ 1.2.0 (null) +Diff: +++ 1.2.0 (no digest) + /cib: @epoch=2, @num_updates=0 ++ /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']: =#=#=#= End test: Active shadow instance differs from active CIB after change - Error occurred (1) =#=#=#= From bb53c7833b24f4b175d1c7dced4a0f87bb3299e9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 01:38:25 -0800 Subject: [PATCH 209/350] Refactor: libcib: Add digest in cib_perform_op if and only if tracing Checking the digest once per minute is an apparently arbitrary interval for sanity-checking. However, we don't actually USE the digest and do the check unless we're tracing (although we would log it at info level prior to this commit). Since we're almost never tracing, only add the digest if we're tracing. This lets us drop the expires/tm_now code. This functionality was added by commit 6a2ffe20 with no explanation beyond what's in the code comment. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 0a088271f65..fffa845f191 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -343,7 +343,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, const char *user = NULL; bool enable_acl = false; - bool with_digest = false; pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) @@ -482,16 +481,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__strip_xml_text(working_cib); - if (make_copy) { - static time_t expires = 0; - time_t tm_now = time(NULL); - - if (expires < tm_now) { - expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */ - with_digest = true; - } - } - local_diff = xml_create_patchset(0, patchset_cib, working_cib, config_changed, manage_counters); @@ -499,9 +488,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__xml_commit_changes(working_cib->doc); if (local_diff != NULL) { - if (with_digest) { - pcmk__xml_patchset_add_digest(local_diff, working_cib); - } pcmk__log_xml_patchset(LOG_INFO, local_diff); pcmk__log_xml_trace(local_diff, "raw patch"); } @@ -515,6 +501,8 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, int format = 1; xmlNode *cib_copy = pcmk__xml_copy(NULL, patchset_cib); + pcmk__xml_patchset_add_digest(local_diff, working_cib); + pcmk__xe_get_int(local_diff, PCMK_XA_FORMAT, &format); test_rc = xml_apply_patchset(cib_copy, local_diff, manage_counters); From ff94011c15d9f83bf2cf69658e38a10e8fb472d4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 01:47:46 -0800 Subject: [PATCH 210/350] Refactor: libcib: Drop test apply-patchset when tracing cib_perform_op() In theory, this could be a nice sanity-check, to catch a mistake if we mess up xml_apply_patchset() or something related. In practice, we never enable tracing for cib_perform_op(), so we would never actually SEE an error here. By the time we've enabled tracing, we already know there's a problem. At that point, we can add tracing and recompile, or run under gdb, or whatever other debugging strategy we prefer. We don't need to leave all this tracing lying around just in case, when we're unlikely to ever use it. This tracing was introduced over the course of a few commits. c96c99ae and c55115d5 look like the main relevant ones. This may have been more relevant or useful when we v2 patchsets were being developed and we may have been uncertain that they would work. Also drop a comment about v2 patchsets that probably hasn't been relevant since we dropped support for rolling upgrades from Pacemaker versions that create v1 patchsets. We only support v2. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index fffa845f191..601d9a52766 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -356,8 +356,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, make_copy = should_copy_cib(op, section, call_options); if (!make_copy) { - /* Conditional on v2 patch style */ - // Make a copy of the top-level element to store version details top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name); pcmk__xe_copy_attrs(top, *current_cib, pcmk__xaf_none); @@ -492,41 +490,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, pcmk__log_xml_trace(local_diff, "raw patch"); } - if (make_copy && (local_diff != NULL)) { - // Original to compare against doesn't exist - pcmk__if_tracing( - { - // Validate the calculated patch set - int test_rc = pcmk_ok; - int format = 1; - xmlNode *cib_copy = pcmk__xml_copy(NULL, patchset_cib); - - pcmk__xml_patchset_add_digest(local_diff, working_cib); - - pcmk__xe_get_int(local_diff, PCMK_XA_FORMAT, &format); - test_rc = xml_apply_patchset(cib_copy, local_diff, - manage_counters); - - if (test_rc != pcmk_ok) { - pcmk__xml_write_temp_file(cib_copy, "PatchApply:calculated", - NULL); - pcmk__xml_write_temp_file(patchset_cib, "PatchApply:input", - NULL); - pcmk__xml_write_temp_file(working_cib, "PatchApply:actual", - NULL); - pcmk__xml_write_temp_file(local_diff, "PatchApply:diff", - NULL); - pcmk__err("v%d patchset error, patch failed to apply: %s " - "(%d)", - format, pcmk_rc_str(pcmk_legacy2rc(test_rc)), - test_rc); - } - pcmk__xml_free(cib_copy); - }, - {} - ); - } - /* working_cib must not be modified after this point, except for the * attributes for which pcmk__xa_filterable() returns true */ From 289d4067a5bfd21fc0394f6d33a732eb4e8c558d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 01:58:57 -0800 Subject: [PATCH 211/350] Refactor: libcib: Drop make_copy variable After dropping some code, we now use the variable in only one place after setting it. Let's get rid of some more clutter. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 601d9a52766..10f8101e6c3 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -335,7 +335,6 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, xmlNode **output) { int rc = pcmk_rc_ok; - bool make_copy = true; xmlNode *top = NULL; xmlNode *working_cib = NULL; xmlNode *patchset_cib = NULL; @@ -353,9 +352,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); enable_acl = cib_acl_enabled(*current_cib, user); - make_copy = should_copy_cib(op, section, call_options); - - if (!make_copy) { + if (!should_copy_cib(op, section, call_options)) { // Make a copy of the top-level element to store version details top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name); pcmk__xe_copy_attrs(top, *current_cib, pcmk__xaf_none); @@ -512,7 +509,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, done: *result_cib = working_cib; - /* @TODO: This may not work correctly with !make_copy, since we don't + /* @TODO This may not work correctly when !should_copy_cib(), since we don't * keep the original CIB. */ if ((rc != pcmk_rc_ok) && cib_acl_enabled(patchset_cib, user) From 180c60c350b2742ecdf2249b664435def8c00aa7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 02:50:24 -0800 Subject: [PATCH 212/350] Refactor: libcib: Functionize checking versions in cib_perform_op() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 113 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 26 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 10f8101e6c3..35eb477e451 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -277,6 +277,92 @@ should_copy_cib(const char *op, const char *section, int call_options) return true; } +/*! + * \internal + * \brief Validate that a new CIB has a newer version attribute than an old CIB + * + * Return an error if the value of the given attribute is higher in the old CIB + * than in the new CIB. + * + * \param[in] attr Name of version attribute to check + * \param[in] old_cib \c PCMK_XE_CIB element before performing operation + * \param[in] new_cib \c PCMK_XE_CIB element from result of operation + * \param[in] request CIB request + * \param[in] input Input data for CIB request + * + * \return Standard Pacemaker return code + * + * \note \p old_cib only has to contain the top-level \c PCMK_XE_CIB element. It + * might not be a full CIB. + */ +static int +check_cib_version_attr(const char *attr, const xmlNode *old_cib, + const xmlNode *new_cib, const xmlNode *request, + const xmlNode *input) +{ + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + int old_version = 0; + int new_version = 0; + + pcmk__xe_get_int(old_cib, attr, &old_version); + pcmk__xe_get_int(new_cib, attr, &new_version); + + if (old_version < new_version) { + return pcmk_rc_ok; + } + + if (old_version == new_version) { + return pcmk_rc_undetermined; + } + + pcmk__err("%s went backwards in %s request: %d -> %d", attr, op, + old_version, new_version); + pcmk__log_xml_warn(request, "bad-request"); + pcmk__log_xml_warn(input, "bad-input"); + + return pcmk_rc_old_data; +} + +/*! + * \internal + * \brief Validate that a new CIB has newer versions than an old CIB + * + * Return an error if: + * - \c PCMK_XA_ADMIN_EPOCH is newer in the old CIB than in the new CIB; or + * - The \c PCMK_XA_ADMIN_EPOCH attributes are equal and \c PCMK_XA_EPOCH is + * newer in the old CIB than in the new CIB. + * + * \param[in] old_cib \c PCMK_XE_CIB element before performing operation + * \param[in] new_cib \c PCMK_XE_CIB element from result of operation + * \param[in] request CIB request + * \param[in] input Input data for CIB request + * + * \return Standard Pacemaker return code + * + * \note \p old_cib only has to contain the top-level \c PCMK_XE_CIB element. It + * might not be a full CIB. + */ +static int +check_cib_versions(const xmlNode *old_cib, const xmlNode *new_cib, + const xmlNode *request, const xmlNode *input) +{ + int rc = check_cib_version_attr(PCMK_XA_ADMIN_EPOCH, old_cib, new_cib, + request, input); + + if (rc != pcmk_rc_undetermined) { + return rc; + } + + // @TODO Why aren't we checking PCMK_XA_NUM_UPDATES if epochs are equal? + rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request, + input); + if (rc == pcmk_rc_undetermined) { + rc = pcmk_rc_ok; + } + + return rc; +} + /*! * \internal * \brief Set values for update origin host, client, and user in new CIB @@ -447,32 +533,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, } } - if (patchset_cib != NULL) { - int old = 0; - int new = 0; - - pcmk__xe_get_int(working_cib, PCMK_XA_ADMIN_EPOCH, &new); - pcmk__xe_get_int(patchset_cib, PCMK_XA_ADMIN_EPOCH, &old); - - if (old > new) { - pcmk__err("%s went backwards: %d -> %d (Opts: %#x)", - PCMK_XA_ADMIN_EPOCH, old, new, call_options); - pcmk__log_xml_warn(req, "Bad Op"); - pcmk__log_xml_warn(input, "Bad Data"); - rc = pcmk_rc_old_data; - - } else if (old == new) { - pcmk__xe_get_int(working_cib, PCMK_XA_EPOCH, &new); - pcmk__xe_get_int(patchset_cib, PCMK_XA_EPOCH, &old); - if (old > new) { - pcmk__err("%s went backwards: %d -> %d (Opts: %#x)", - PCMK_XA_EPOCH, old, new, call_options); - pcmk__log_xml_warn(req, "Bad Op"); - pcmk__log_xml_warn(input, "Bad Data"); - rc = pcmk_rc_old_data; - } - } - } + rc = check_cib_versions(patchset_cib, working_cib, req, input); pcmk__strip_xml_text(working_cib); From 4bde810501f4ea5d39720a1672f63b403ad00e40 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 03:35:40 -0800 Subject: [PATCH 213/350] Refactor: libcib: Simplify some return-code handling in cib_perform_op() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 35eb477e451..00d585122bb 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -501,17 +501,19 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, // Allow ourselves to make any additional necessary changes xml_acl_disable(working_cib); - if ((rc == pcmk_rc_ok) && (working_cib == NULL)) { + if (rc != pcmk_rc_ok) { + goto done; + } + + if (working_cib == NULL) { rc = EINVAL; goto done; + } - } else if ((rc == pcmk_rc_ok) && xml_acl_denied(working_cib)) { + if (xml_acl_denied(working_cib)) { pcmk__trace("ACL rejected part or all of the proposed changes"); rc = EACCES; goto done; - - } else if (rc != pcmk_rc_ok) { - goto done; } /* If the CIB is from a file, we don't need to check that the feature set is From d229fc28ec33819bf349dad037ebc2c8cfc0b219 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 03:37:40 -0800 Subject: [PATCH 214/350] Refactor: libcib: Drop redundant NULL check in cib_perform_op() We've already ensured that working_cib is not NULL. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 00d585122bb..13bf8fae262 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -520,9 +520,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, * supported. All we care about in that case is the schema version, which * is checked elsewhere. */ - if ((working_cib != NULL) - && ((cib == NULL) || (cib->variant != cib_file))) { - + if ((cib == NULL) || (cib->variant != cib_file)) { const char *new_version = pcmk__xe_get(working_cib, PCMK_XA_CRM_FEATURE_SET); From 3590d61dce8eaa9f3c8b9dc23f94cbd20fef102e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 03:44:24 -0800 Subject: [PATCH 215/350] Refactor: libcib: cib_perform_op() takes enum cib_variant arg It wasn't using the cib_t * argument for anything except checking the variant. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 12 ++++++++---- include/crm/cib/internal.h | 10 +++++----- lib/cib/cib_file.c | 2 +- lib/cib/cib_utils.c | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 35c09e3aa0c..7f83121e4f9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -628,10 +628,14 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; - // result_cib must not be modified after cib_perform_op() returns - rc = cib_perform_op(NULL, op, call_options, op_function, section, request, - input, manage_counters, &config_changed, &the_cib, - &result_cib, &cib_diff, &output); + /* result_cib must not be modified after cib_perform_op() returns. + * + * It's not important whether the client variant is cib_native or + * cib_remote. + */ + rc = cib_perform_op(cib_undefined, op, call_options, op_function, section, + request, input, manage_counters, &config_changed, + &the_cib, &result_cib, &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index c2418f00f4c..7b529d6e93a 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -184,11 +184,11 @@ int cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, xmlNode *input, xmlNode **current_cib, xmlNode **output); -int cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, - cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode *input, bool manage_counters, bool *config_changed, - xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, - xmlNode **output); +int cib_perform_op(enum cib_variant variant, const char *op, + uint32_t call_options, cib__op_fn_t fn, const char *section, + xmlNode *req, xmlNode *input, bool manage_counters, + bool *config_changed, xmlNode **current_cib, + xmlNode **result_cib, xmlNode **diff, xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index a9bbf9246e8..b397ae70736 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -180,7 +180,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) rc = cib__perform_query(op, call_options, op_function, section, request, data, &private->cib_xml, output); } else { - rc = cib_perform_op(cib, op, call_options, op_function, section, + rc = cib_perform_op(cib_file, op, call_options, op_function, section, request, data, true, &changed, &private->cib_xml, &result_cib, &cib_diff, output); } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 13bf8fae262..91a75e8ef6e 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -414,7 +414,7 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, +cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, xmlNode *input, bool manage_counters, bool *config_changed, xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, @@ -520,7 +520,7 @@ cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, * supported. All we care about in that case is the schema version, which * is checked elsewhere. */ - if ((cib == NULL) || (cib->variant != cib_file)) { + if (variant != cib_file) { const char *new_version = pcmk__xe_get(working_cib, PCMK_XA_CRM_FEATURE_SET); From a9d301fd57a2686464d27364b04abd695266b1ec Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 03:53:16 -0800 Subject: [PATCH 216/350] Refactor: libcib: Functionize checking new feature set in cib_perform_op Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 91a75e8ef6e..426e3b7d0b4 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -277,6 +277,31 @@ should_copy_cib(const char *op, const char *section, int call_options) return true; } +/*! + * \internal + * \brief Validate that a new CIB's feature set is not newer than ours + * + * Return an error if the new CIB's feature set is newer than ours. + * + * \param[in] new_cib Result CIB after performing operation + * + * \return Standard Pacemaker return code + */ +static int +check_new_feature_set(const xmlNode *new_cib) +{ + const char *new_version = pcmk__xe_get(new_cib, PCMK_XA_CRM_FEATURE_SET); + int rc = pcmk__check_feature_set(new_version); + + if (rc == pcmk_rc_ok) { + return pcmk_rc_ok; + } + + pcmk__err("Discarding update with feature set %s greater than our own (%s)", + new_version, CRM_FEATURE_SET); + return rc; +} + /*! * \internal * \brief Validate that a new CIB has a newer version attribute than an old CIB @@ -521,14 +546,8 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, * is checked elsewhere. */ if (variant != cib_file) { - const char *new_version = pcmk__xe_get(working_cib, - PCMK_XA_CRM_FEATURE_SET); - - rc = pcmk__check_feature_set(new_version); + rc = check_new_feature_set(working_cib); if (rc != pcmk_rc_ok) { - pcmk__err("Discarding update with feature set '%s' greater than " - "our own '%s'", - new_version, CRM_FEATURE_SET); goto done; } } From b1b48275404c50a3401d598c964ba8a1779f3086 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 04:00:00 -0800 Subject: [PATCH 217/350] API: libcrmcommon: xml_create_patchset() source argument is now const Signed-off-by: Reid Wahl --- include/crm/common/xml.h | 4 ++-- lib/common/patchset.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h index b5a5b08f0e5..ceb6cddf390 100644 --- a/include/crm/common/xml.h +++ b/include/crm/common/xml.h @@ -33,8 +33,8 @@ extern "C" { * undeprecated until we create replacements */ -xmlNode *xml_create_patchset( - int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version); +xmlNode *xml_create_patchset(int format, const xmlNode *source, xmlNode *target, + bool *config, bool manage_version); int xml_apply_patchset(xmlNode *xml, const xmlNode *patchset, bool check_version); diff --git a/lib/common/patchset.c b/lib/common/patchset.c index 3650cf5f286..625efee0aab 100644 --- a/lib/common/patchset.c +++ b/lib/common/patchset.c @@ -181,7 +181,7 @@ is_config_change(xmlNode *xml) } static xmlNode * -xml_create_patchset_v2(xmlNode *source, xmlNode *target) +xml_create_patchset_v2(const xmlNode *source, xmlNode *target) { int lpc = 0; GList *gIter = NULL; @@ -241,7 +241,7 @@ xml_create_patchset_v2(xmlNode *source, xmlNode *target) } xmlNode * -xml_create_patchset(int format, xmlNode *source, xmlNode *target, +xml_create_patchset(int format, const xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version) { bool local_config_changed = false; From 55ce3ab5838c09edb1aea557da0eb57391c5568e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 04:06:36 -0800 Subject: [PATCH 218/350] Refactor: libcib: Clarify patchset_cib variable And rename it to old_versions. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 426e3b7d0b4..501c4c462ef 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -446,9 +446,15 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, xmlNode **output) { int rc = pcmk_rc_ok; + + /* PCMK_XE_CIB element containing version numbers from before the operation. + * This may or may not point to a full CIB XML tree. Do not free, as this + * will be used as an alias for another pointer. + */ + xmlNode *old_versions = NULL; + xmlNode *top = NULL; xmlNode *working_cib = NULL; - xmlNode *patchset_cib = NULL; xmlNode *local_diff = NULL; const char *user = NULL; @@ -467,7 +473,7 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, // Make a copy of the top-level element to store version details top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name); pcmk__xe_copy_attrs(top, *current_cib, pcmk__xaf_none); - patchset_cib = top; + old_versions = top; pcmk__xml_commit_changes((*current_cib)->doc); pcmk__xml_doc_set_flags((*current_cib)->doc, pcmk__xf_tracking); @@ -494,7 +500,7 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, } else { working_cib = pcmk__xml_copy(NULL, *current_cib); - patchset_cib = *current_cib; + old_versions = *current_cib; pcmk__xml_doc_set_flags(working_cib->doc, pcmk__xf_tracking); if (enable_acl) { @@ -552,11 +558,14 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, } } - rc = check_cib_versions(patchset_cib, working_cib, req, input); + rc = check_cib_versions(old_versions, working_cib, req, input); pcmk__strip_xml_text(working_cib); - local_diff = xml_create_patchset(0, patchset_cib, working_cib, + /* If we didn't make a copy, the diff will only be accurate for the + * top-level PCMK_XE_CIB element + */ + local_diff = xml_create_patchset(0, old_versions, working_cib, config_changed, manage_counters); pcmk__log_xml_changes(LOG_TRACE, working_cib); @@ -592,8 +601,8 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, /* @TODO This may not work correctly when !should_copy_cib(), since we don't * keep the original CIB. */ - if ((rc != pcmk_rc_ok) && cib_acl_enabled(patchset_cib, user) - && xml_acl_filtered_copy(user, patchset_cib, working_cib, result_cib)) { + if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user) + && xml_acl_filtered_copy(user, old_versions, working_cib, result_cib)) { if (*result_cib == NULL) { pcmk__debug("Pre-filtered the entire cib result"); From 71cca19360001a836fc0452087bbf583c412be4c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 23:27:41 -0800 Subject: [PATCH 219/350] Refactor: libcib: Assert on NULL diff in cib_perform_op() Both callers currently always pass a non-NULL diff argument. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 501c4c462ef..d683b083942 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -455,7 +455,6 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, xmlNode *top = NULL; xmlNode *working_cib = NULL; - xmlNode *local_diff = NULL; const char *user = NULL; bool enable_acl = false; @@ -464,6 +463,7 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, && (config_changed != NULL) && (!*config_changed) && (current_cib != NULL) && (*current_cib != NULL) && (result_cib != NULL) && (*result_cib == NULL) + && (diff != NULL) && (*diff == NULL) && (output != NULL) && (*output == NULL)); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); @@ -565,15 +565,15 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, /* If we didn't make a copy, the diff will only be accurate for the * top-level PCMK_XE_CIB element */ - local_diff = xml_create_patchset(0, old_versions, working_cib, - config_changed, manage_counters); + *diff = xml_create_patchset(0, old_versions, working_cib, config_changed, + manage_counters); pcmk__log_xml_changes(LOG_TRACE, working_cib); pcmk__xml_commit_changes(working_cib->doc); - if (local_diff != NULL) { - pcmk__log_xml_patchset(LOG_INFO, local_diff); - pcmk__log_xml_trace(local_diff, "raw patch"); + if (*diff != NULL) { + pcmk__log_xml_patchset(LOG_INFO, *diff); + pcmk__log_xml_trace(*diff, "raw patch"); } /* working_cib must not be modified after this point, except for the @@ -610,12 +610,6 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, pcmk__xml_free(working_cib); } - if(diff) { - *diff = local_diff; - } else { - pcmk__xml_free(local_diff); - } - pcmk__xml_free(top); pcmk__trace("Done"); return rc; From 05b6b60dcceca34e9446b711ef0d17120b7c88a8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Wed, 31 Dec 2025 23:50:20 -0800 Subject: [PATCH 220/350] Refactor: libcrmcommon: Drop redundant check in xml_create_patchset_v2() This is a static function with one caller (xml_create_patchset()), which already checks whether the doc has the dirty flag set. This lets us clearly make two guarantees: * xml_create_patchset_v2() returns non-NULL. * xml_create_patchset() doesn't change *config_changed if its return value is NULL. Signed-off-by: Reid Wahl --- lib/common/patchset.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/common/patchset.c b/lib/common/patchset.c index 625efee0aab..e4876c2bba3 100644 --- a/lib/common/patchset.c +++ b/lib/common/patchset.c @@ -180,6 +180,7 @@ is_config_change(xmlNode *xml) return FALSE; } +// Guaranteed to return non-NULL static xmlNode * xml_create_patchset_v2(const xmlNode *source, xmlNode *target) { @@ -192,11 +193,6 @@ xml_create_patchset_v2(const xmlNode *source, xmlNode *target) xmlNode *patchset = NULL; pcmk__assert(target != NULL); - - if (!pcmk__xml_doc_all_flags_set(target->doc, pcmk__xf_dirty)) { - return NULL; - } - pcmk__assert(target->doc != NULL); docpriv = target->doc->_private; @@ -240,6 +236,7 @@ xml_create_patchset_v2(const xmlNode *source, xmlNode *target) return patchset; } +// *config_changed is unchanged if the return value is NULL xmlNode * xml_create_patchset(int format, const xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version) From e79337009e30078f21f2b17a774cfbfe7ed54b93 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 00:03:53 -0800 Subject: [PATCH 221/350] Refactor: libcib: goto done if *diff is NULL in cib_perform_op() If *diff is NULL, then nothing changed. There's nothing to log, *config_changed will be false, and there's no reason to validate against the schema. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index d683b083942..4f551e887fe 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -1,6 +1,6 @@ /* * Original copyright 2004 International Business Machines - * Later changes copyright 2008-2025 the Pacemaker project contributors + * Later changes copyright 2008-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -568,13 +568,20 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, *diff = xml_create_patchset(0, old_versions, working_cib, config_changed, manage_counters); + if (*diff == NULL) { + // pcmk__xml_commit_changes() resets document private data + pcmk__xml_commit_changes(working_cib->doc); + + // If nothing changed, nothing else to do + goto done; + } + + // Committing changes removes attrs marked as deleted, so log first pcmk__log_xml_changes(LOG_TRACE, working_cib); pcmk__xml_commit_changes(working_cib->doc); - if (*diff != NULL) { - pcmk__log_xml_patchset(LOG_INFO, *diff); - pcmk__log_xml_trace(*diff, "raw patch"); - } + pcmk__log_xml_patchset(LOG_INFO, *diff); + pcmk__log_xml_trace(*diff, "raw patch"); /* working_cib must not be modified after this point, except for the * attributes for which pcmk__xa_filterable() returns true From d05cf938326898ff24fe7cdfefeea27687e2b3f8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 00:17:15 -0800 Subject: [PATCH 222/350] Log: libcib: Drop logging XML changes and raw patchset in cib_perform_op I don't see any reason to log these. We already log the patchset at info level using pcmk__log_xml_patchset(). Unless there's something wrong with patchset creation or logging, the (pretty) patchset logs make the change logs and raw patchset logs redundant. They contain the same info. We're not going to have tracing enabled unless we're trying to troubleshoot something. Those particular trace logs aren't going to be helpful unless we're specifically trying to troubleshoot patchset creation or logging. If that's the case, then we can add logging lines and recompile as needed. Whatever the issue is with patchset creation/logging, it ought to be reproducible for developers. Also drop patchset logging in the callers. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 -- lib/cib/cib_file.c | 4 +--- lib/cib/cib_utils.c | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 7f83121e4f9..a72a0598196 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -699,8 +699,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, client_name, originator, cib_diff); } - pcmk__log_xml_patchset(LOG_TRACE, cib_diff); - done: if (!pcmk__is_set(call_options, cib_discard_reply)) { *reply = create_cib_reply(op, call_id, client_id, call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index b397ae70736..310ffdd0cca 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -1,6 +1,6 @@ /* * Original copyright 2004 International Business Machines - * Later changes copyright 2008-2025 the Pacemaker project contributors + * Later changes copyright 2008-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -197,8 +197,6 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) pcmk__validate_xml(result_cib, NULL, NULL, NULL); } else if ((rc == pcmk_rc_ok) && !read_only) { - pcmk__log_xml_patchset(LOG_DEBUG, cib_diff); - if (result_cib != private->cib_xml) { pcmk__xml_free(private->cib_xml); private->cib_xml = result_cib; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 4f551e887fe..acbeb7089fc 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -576,12 +576,8 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, goto done; } - // Committing changes removes attrs marked as deleted, so log first - pcmk__log_xml_changes(LOG_TRACE, working_cib); pcmk__xml_commit_changes(working_cib->doc); - pcmk__log_xml_patchset(LOG_INFO, *diff); - pcmk__log_xml_trace(*diff, "raw patch"); /* working_cib must not be modified after this point, except for the * attributes for which pcmk__xa_filterable() returns true From bc401223136710ede934f81603611c9540559b9a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 00:07:59 -0800 Subject: [PATCH 223/350] Refactor: libcib: Move pcmk__xml_commit_changes() call Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index acbeb7089fc..821e16ada7e 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -568,15 +568,15 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, *diff = xml_create_patchset(0, old_versions, working_cib, config_changed, manage_counters); - if (*diff == NULL) { - // pcmk__xml_commit_changes() resets document private data - pcmk__xml_commit_changes(working_cib->doc); + /* pcmk__xml_commit_changes() resets document private data, so call it even + * if there were no changes. + */ + pcmk__xml_commit_changes(working_cib->doc); - // If nothing changed, nothing else to do + if (*diff == NULL) { goto done; } - pcmk__xml_commit_changes(working_cib->doc); pcmk__log_xml_patchset(LOG_INFO, *diff); /* working_cib must not be modified after this point, except for the From 559ac67bb653824fd43337a61c38ed8bf9d4b154 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 20:21:33 -0800 Subject: [PATCH 224/350] Refactor: based: Drop dead code for PCMK__XA_CIB_UPDATE If we reached the dropped "if" block, then op is not PCMK__CIB_REQUEST_REPLACE. That was tested by the first "if" block. But as of Pacemaker 2.0.0, PCMK__XA_CIB_UPDATE can be true only if the op is PCMK__CIB_REQUEST_REPLACE (it's set by sync_our_cib()), unless there's an even older node in the cluster that would cause the CIB manager to run in legacy mode. (See broadcast argument of send_peer_reply() in 2.0.0.) However, rolling upgrades from versions earlier than 2.0.0 are no longer supported, so that's not a concern. Similarly, we can drop PCMK__CIB_REQUEST_APPLY_PATCH from the CRM_LOG_ASSERT() call. 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 a72a0598196..a0d83835db7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -416,11 +416,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return false; } - } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { - pcmk__info("Detected legacy %s global update from %s", op, originator); - send_sync_request(); - return false; - } else if (is_reply && pcmk__is_set(operation->flags, cib__op_attr_modifies)) { @@ -615,15 +610,14 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, /* @COMPAT: Handle a valid write action (legacy) * - * @TODO: Re-evaluate whether this is all truly legacy. PCMK__XA_CIB_UPDATE - * may be set by a sync operation even in non-legacy mode, and - * manage_counters tells xml_create_patchset() whether to update - * version/epoch info. + * @TODO: Re-evaluate whether this is truly legacy. PCMK__XA_CIB_UPDATE may + * be set by a sync operation even in non-legacy mode, and manage_counters + * tells xml_create_patchset() whether to update version/epoch info. */ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { manage_counters = false; - CRM_LOG_ASSERT(pcmk__str_any_of(op, PCMK__CIB_REQUEST_APPLY_PATCH, - PCMK__CIB_REQUEST_REPLACE, NULL)); + CRM_LOG_ASSERT(pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, + pcmk__str_none)); } ping_modified_since = true; From f91d6fbd1e9cf4331719e2910b61dea5b72e0317 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 21:04:12 -0800 Subject: [PATCH 225/350] Refactor: based: Drop based_process_apply_patch, send_sync_request, etc. All of this is legacy code based on the legacy mode (pre-Pacemaker-1.1.12) sync process. We maintained support for that until Pacemaker 3.0.0, enabling it if any node in the cluster was running a Pacemaker earlier than 1.1.12, because we supported rolling upgrades from 1.1.11 to 2.y.z. We removed that support in 3.0.0, however. You can see a call to send_peer_reply() with broadcast=TRUE guarded by "if (cib_legacy_mode() ...)" from 1.1.13 through the latest 2.y.z. That's the PCMK__CIB_REQUEST_APPLY_PATCH/CIB_OP_APPLY_DIFF request that this dropped code was dealing with. Back then, a CIB manager itself would send an apply-patch request. pcmk_rc_diff_resync means that the source version (not the target version) in the patchset is newer than our current CIB version. So the idea was apparently that we received a sync request from a node with a newer CIB, and we didn't want to apply it before first getting the newer CIB ourselves. However, outside of legacy mode (which we dropped in 3.0.0), cibadmin is the only thing that sends an apply-patch request. cib__process_apply_diff() simply calls xml_apply_patchset() with check_version=true and returns the result. That's all we need in the CIB manager, now that we know that the request is coming from a client rather than from a peer. Failure to process a user-submitted patchset doesn't affect our ability to keep correct cluster state. We don't need to request a resync (via send_sync_request()); we can just fail, and the user can submit a new patchset with compatible versions. Since send_sync_request() is the only thing that sets sync_in_progress from 0 to a nonzero value, all of the other supporting code is dead and can be removed if we're dropping send_sync_request(). Note: this enables more changes, which will follow in upcoming commits. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 78 +-------------------------------- daemons/based/based_messages.h | 3 +- daemons/based/based_operation.c | 2 +- 3 files changed, 3 insertions(+), 80 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 9e6de7cdbc1..a04eb54f7ab 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -29,18 +29,10 @@ #include -/* Maximum number of diffs to ignore while waiting for a resync */ -#define MAX_DIFF_RETRY 5 - bool based_is_primary = false; xmlNode *the_cib = NULL; -/* Set to 1 when a sync is requested, incremented when a diff is ignored, - * reset to 0 when a sync is received - */ -static int sync_in_progress = 0; - /*! * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE @@ -68,51 +60,6 @@ based_process_abs_delete(const char *op, int options, const char *section, return EINVAL; } -int -based_process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) -{ - int rc = pcmk_rc_ok; - - if (sync_in_progress > MAX_DIFF_RETRY) { - /* Don't ignore diffs forever; the last request may have been lost. - * If the diff fails, we'll ask for another full resync. - */ - sync_in_progress = 0; - } - - // The primary instance should never ignore a diff - if (sync_in_progress && !based_is_primary) { - int source[] = { 0, 0, 0 }; - int target[] = { 0, 0, 0 }; - - pcmk__xml_patchset_versions(input, source, target); - - sync_in_progress++; - pcmk__notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in " - "progress)", - source[0], source[1], source[2], - target[0], target[1], target[2]); - return pcmk_rc_diff_resync; - } - - rc = cib__process_apply_patch(op, options, section, req, input, cib, - answer); - pcmk__trace("result: %s (%d), %s", pcmk_rc_str(rc), rc, - (based_is_primary? "primary": "secondary")); - - if ((rc == pcmk_rc_diff_resync) && !based_is_primary) { - g_clear_pointer(cib, pcmk__xml_free); - send_sync_request(); - - } else if (rc == pcmk_rc_diff_resync) { - rc = pcmk_rc_diff_failed; - } - - return rc; -} - int based_process_commit_transact(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, @@ -226,13 +173,7 @@ based_process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { - int rc = cib__process_replace(op, options, section, req, input, cib, - answer); - - if ((rc == pcmk_rc_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) { - sync_in_progress = 0; - } - return rc; + return cib__process_replace(op, options, section, req, input, cib, answer); } int @@ -431,23 +372,6 @@ based_process_upgrade(const char *op, int options, const char *section, return rc; } -void -send_sync_request(void) -{ - xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me"); - pcmk__node_status_t *peer = NULL; - - pcmk__info("Requesting re-sync from all peers"); - sync_in_progress = 1; - - pcmk__xe_set(sync_me, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE); - pcmk__xe_set(sync_me, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME); - - pcmk__cluster_send_message(peer, pcmk_ipc_based, sync_me); - pcmk__xml_free(sync_me); -} - static xmlNode * cib_msg_copy(xmlNode *msg) { diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 2cae380e8e6..cb5100f9e37 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.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. * @@ -74,7 +74,6 @@ int based_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); -void send_sync_request(void); int sync_our_cib(xmlNode *request, bool all); #endif // BASED_MESSAGES__H diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index f7e58a5ea11..7e7362a7717 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -18,7 +18,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_abs_delete] = based_process_abs_delete, - [cib__op_apply_patch] = based_process_apply_patch, + [cib__op_apply_patch] = cib__process_apply_patch, [cib__op_bump] = cib__process_bump, [cib__op_commit_transact] = based_process_commit_transact, [cib__op_create] = cib__process_create, From 8c255320735fd99cfc5ada84b7b7e73614b91ceb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 21:28:58 -0800 Subject: [PATCH 226/350] Refactor: based: Drop based_process_replace() All it did immediately prior to this commit was to call cib__process_replace(). Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 8 -------- daemons/based/based_messages.h | 4 ---- daemons/based/based_operation.c | 2 +- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index a04eb54f7ab..18baa35acb9 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -168,14 +168,6 @@ based_process_primary(const char *op, int options, const char *section, return pcmk_rc_ok; } -int -based_process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) -{ - return cib__process_replace(op, options, section, req, input, cib, answer); -} - int based_process_schemas(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index cb5100f9e37..087723e88df 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -46,10 +46,6 @@ int based_process_primary(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - int based_process_schemas(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 7e7362a7717..cbd55ae08a2 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -30,7 +30,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_ping] = based_process_ping, [cib__op_primary] = based_process_primary, [cib__op_query] = cib__process_query, - [cib__op_replace] = based_process_replace, + [cib__op_replace] = cib__process_replace, [cib__op_schemas] = based_process_schemas, [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, From 0398bbe45b5cd3ad7b278139e66affe20c25efa7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 21:32:55 -0800 Subject: [PATCH 227/350] Refactor: libcib, based: Drop cib__op_sync_to_one and related things Nothing makes a "sync to one" request anymore since we dropped send_sync_request(). Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 8 -------- daemons/based/based_messages.h | 4 ---- daemons/based/based_operation.c | 1 - include/crm/cib/internal.h | 4 +--- lib/cib/cib_ops.c | 6 +----- 5 files changed, 2 insertions(+), 21 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 18baa35acb9..f5fd578191b 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -262,14 +262,6 @@ based_process_sync_to_all(const char *op, int options, const char *section, return sync_our_cib(req, true); } -int -based_process_sync_to_one(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) -{ - return sync_our_cib(req, false); -} - int based_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 087723e88df..41d91dd3a3e 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -62,10 +62,6 @@ int based_process_sync_to_all(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_sync_to_one(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - int based_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index cbd55ae08a2..1e563148b47 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -35,7 +35,6 @@ static const cib__op_fn_t op_functions[] = { [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, [cib__op_sync_to_all] = based_process_sync_to_all, - [cib__op_sync_to_one] = based_process_sync_to_one, [cib__op_upgrade] = based_process_upgrade, }; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 7b529d6e93a..023841f7b90 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.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. * @@ -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_TO_ALL "cib_sync" -#define PCMK__CIB_REQUEST_SYNC_TO_ONE "cib_sync_one" #define PCMK__CIB_REQUEST_IS_PRIMARY "cib_ismaster" #define PCMK__CIB_REQUEST_BUMP "cib_bump" #define PCMK__CIB_REQUEST_QUERY "cib_query" @@ -91,7 +90,6 @@ enum cib__op_type { cib__op_secondary, cib__op_shutdown, cib__op_sync_to_all, - cib__op_sync_to_one, cib__op_upgrade, }; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 72bb93efad4..739848b0ded 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.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. * @@ -121,10 +121,6 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_SYNC_TO_ALL, cib__op_sync_to_all, cib__op_attr_privileged }, - { - PCMK__CIB_REQUEST_SYNC_TO_ONE, cib__op_sync_to_one, - cib__op_attr_privileged - }, { PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, cib__op_attr_modifies From f46a4942eb8b252625ef4feb6f4048df8e810947 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 21:37:17 -0800 Subject: [PATCH 228/350] Refactor: libcib: Rename *_sync_to_all to *_sync We removed *_sync_to_one in a previous commit, so there is no longer any ambiguity. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- daemons/based/based_messages.c | 6 +++--- daemons/based/based_messages.h | 6 +++--- daemons/based/based_operation.c | 2 +- include/crm/cib/internal.h | 4 ++-- lib/cib/cib_client.c | 6 +++--- lib/cib/cib_ops.c | 3 +-- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a0d83835db7..08925b51fac 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -371,9 +371,9 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, delegated = reply_to; } goto skip_is_reply; + } - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC_TO_ALL, - pcmk__str_none)) { + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { // Nothing to do } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) { diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index f5fd578191b..3eb0b6cff30 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -255,9 +255,9 @@ based_process_shutdown(const char *op, int options, const char *section, } int -based_process_sync_to_all(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_sync(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { return sync_our_cib(req, true); } diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 41d91dd3a3e..a3e986536d2 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -58,9 +58,9 @@ int based_process_shutdown(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_sync_to_all(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_sync(const char *op, int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int based_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c index 1e563148b47..97d1679e3b1 100644 --- a/daemons/based/based_operation.c +++ b/daemons/based/based_operation.c @@ -34,7 +34,7 @@ static const cib__op_fn_t op_functions[] = { [cib__op_schemas] = based_process_schemas, [cib__op_secondary] = based_process_secondary, [cib__op_shutdown] = based_process_shutdown, - [cib__op_sync_to_all] = based_process_sync_to_all, + [cib__op_sync] = based_process_sync, [cib__op_upgrade] = based_process_upgrade, }; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 023841f7b90..b7c57136a86 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -23,7 +23,7 @@ extern "C" { // 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_TO_ALL "cib_sync" +#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" @@ -89,7 +89,7 @@ enum cib__op_type { cib__op_schemas, cib__op_secondary, cib__op_shutdown, - cib__op_sync_to_all, + cib__op_sync, cib__op_upgrade, }; diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 38fae89be7c..2009c5d4739 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.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. * @@ -290,8 +290,8 @@ cib_client_sync(cib_t * cib, const char *section, int call_options) static int cib_client_sync_from(cib_t * cib, const char *host, const char *section, int call_options) { - return cib_internal_op(cib, PCMK__CIB_REQUEST_SYNC_TO_ALL, host, section, - NULL, NULL, call_options, cib->user); + return cib_internal_op(cib, PCMK__CIB_REQUEST_SYNC, host, section, NULL, + NULL, call_options, cib->user); } static int diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 739848b0ded..187144db19c 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -118,8 +118,7 @@ static const cib__operation_t cib_ops[] = { PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_privileged }, { - PCMK__CIB_REQUEST_SYNC_TO_ALL, cib__op_sync_to_all, - cib__op_attr_privileged + PCMK__CIB_REQUEST_SYNC, cib__op_sync, cib__op_attr_privileged }, { PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade, From e659a06099e92b77c3621f3fa21e679bfeab4aad Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:12:46 -0800 Subject: [PATCH 229/350] Fix: libcib: Don't convert -pcmk_err_diff_resync to pcmk_ok The note that "internal value that clients do not and should not care " about" is false or at best outdated. When a cluster had at least one node running Pacemaker 1.1.11 or earlier (see other recent commit messages), certain changes were "broadcast" using an apply-patch request. This request may fail with -pcmk_err_diff_resync if it came from a node with a newer CIB than ours. This error means the source version of the patchset is newer than our CIB version. Now, however, an apply-patch request can only come from cibadmin. If applying a patch fails because the patchset source version is too new, then a client should report that as an error. Signed-off-by: Reid Wahl --- lib/cib/cib_native.c | 7 +------ lib/cib/cib_remote.c | 7 +------ lib/cib/cib_utils.c | 5 ----- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index ed3b221f6c5..b4ecf4d919c 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -1,6 +1,6 @@ /* * Copyright 2004 International Business Machines - * Later changes copyright 2004-2025 the Pacemaker project contributors + * Later changes copyright 2004-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -143,11 +143,6 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, case -EPERM: break; - /* This is an internal value that clients do not and should not care about */ - case -pcmk_err_diff_resync: - rc = pcmk_ok; - break; - /* These indicate internal problems */ case -EPROTO: case -ENOMSG: diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 4dcfe4a50cc..752a326425c 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2025 the Pacemaker project contributors + * Copyright 2008-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -163,11 +163,6 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, rc = -EPROTO; } - if (rc == -pcmk_err_diff_resync) { - /* This is an internal value that clients do not and should not care about */ - rc = pcmk_ok; - } - if (rc == pcmk_ok || rc == -EPERM) { pcmk__log_xml_debug(op_reply, "passed"); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 821e16ada7e..9d4ae50ef63 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -752,11 +752,6 @@ cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc) pcmk__debug("No cib object supplied"); } - if (rc == -pcmk_err_diff_resync) { - /* This is an internal value that clients do not and should not care about */ - rc = pcmk_ok; - } - if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) { pcmk__trace("Invoking callback %s for call %d", pcmk__s(blob->id, "without ID"), call_id); From ac477649e091d32485bce13a4d56dc8361f93cb8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:17:48 -0800 Subject: [PATCH 230/350] Feature: libcrmcommon: xml_apply_patchset doesn't return diff_resync err This shouldn't affect cibadmin even though it can call xml_apply_patchset(), since pcmk_rc_diff_resync and pcmk_rc_diff_failed map to the same exit code. Signed-off-by: Reid Wahl --- daemons/fenced/fenced_cib.c | 3 +-- lib/common/patchset.c | 4 ++-- tools/crm_mon.c | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/daemons/fenced/fenced_cib.c b/daemons/fenced/fenced_cib.c index f030c1b2b06..9ecf635f371 100644 --- a/daemons/fenced/fenced_cib.c +++ b/daemons/fenced/fenced_cib.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2025 the Pacemaker project contributors + * Copyright 2009-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -516,7 +516,6 @@ update_cib_cache_cb(const char *event, xmlNode * msg) * old diff. */ break; - case -pcmk_err_diff_resync: case -pcmk_err_diff_failed: pcmk__notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc); diff --git a/lib/common/patchset.c b/lib/common/patchset.c index e4876c2bba3..4149f3e757e 100644 --- a/lib/common/patchset.c +++ b/lib/common/patchset.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. * @@ -464,7 +464,7 @@ check_patchset_versions(const xmlNode *cib_root, const xmlNode *patchset) vfields[i], current[0], current[1], current[2], source[0], source[1], source[2], target[0], target[1], target[2]); - return pcmk_rc_diff_resync; + return pcmk_rc_diff_failed; } if (current[i] > source[i]) { pcmk__info("Current %s is too high " diff --git a/tools/crm_mon.c b/tools/crm_mon.c index ab42c3bac96..8853e6ba52e 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.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. * @@ -1948,7 +1948,6 @@ crm_diff_update(const char *event, xmlNode * msg) rc = xml_apply_patchset(current_cib, diff, TRUE); switch (rc) { - case -pcmk_err_diff_resync: case -pcmk_err_diff_failed: pcmk__notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc); @@ -1961,6 +1960,7 @@ crm_diff_update(const char *event, xmlNode * msg) pcmk__notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc); pcmk__xml_free(current_cib); current_cib = NULL; + break; } } From 069a0105d7499e56b2e7f98df07ee90a9ad1262e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:37:52 -0800 Subject: [PATCH 231/350] Log: controller: Don't treat patchset-apply errors as OK It is in fact an error if we get -pcmk_err_diff_failed or -pcmk_err_diff_resync. This "ignore" goes back to commit a0cecc0c. We probably treated diff-related errors as OK because of how diffs were used in legacy mode (for compatibility with Pacemaker versions 1.1.11 and earlier) -- see recent commit messages. However, this shouldn't make any difference. * Nothing returns -pcmk_err_diff_resync/pcmk_rc_diff_resync anymore. * The return code we're checking is from a CIB modify operation, which can't return a diff_failed error because it doesn't apply a patchset. Signed-off-by: Reid Wahl --- daemons/controld/controld_cib.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/daemons/controld/controld_cib.c b/daemons/controld/controld_cib.c index bdfd8dd8d87..93008948c6d 100644 --- a/daemons/controld/controld_cib.c +++ b/daemons/controld/controld_cib.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. * @@ -731,22 +731,16 @@ controld_record_pending_op(const char *node_name, const lrmd_rsc_info_t *rsc, static void cib_rsc_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data) { - switch (rc) { - case pcmk_ok: - case -pcmk_err_diff_failed: - case -pcmk_err_diff_resync: - pcmk__trace("Resource history update completed (call=%d rc=%d)", - call_id, rc); - break; - default: - if (call_id > 0) { - pcmk__warn("Resource history update %d failed: %s " - QB_XS " rc=%d", - call_id, pcmk_strerror(rc), rc); - } else { - pcmk__warn("Resource history update failed: %s " QB_XS " rc=%d", - pcmk_strerror(rc), rc); - } + if (rc == pcmk_ok) { + pcmk__trace("Resource history update completed (call=%d rc=%d)", + call_id, rc); + + } else if (call_id > 0) { + pcmk__warn("Resource history update %d failed: %s " QB_XS " rc=%d", + call_id, pcmk_strerror(rc), rc); + } else { + pcmk__warn("Resource history update failed: %s " QB_XS " rc=%d", + pcmk_strerror(rc), rc); } if (call_id == pending_rsc_update) { From 3d8f468e78e7ffcac3125de06aed85a5703e1605 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:42:45 -0800 Subject: [PATCH 232/350] Refactor: based: Drop pcmk_rc_diff_resync from switch Nothing returns that code anymore. 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 08925b51fac..547ce4698ea 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -743,7 +743,6 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, break; case pcmk_rc_old_data: - case pcmk_rc_diff_resync: case pcmk_rc_diff_failed: level = LOG_TRACE; break; From 5be34d3f135e40b46d67d02cdca7893f37ab5ee0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:44:11 -0800 Subject: [PATCH 233/350] Log: based: Don't log specially for PCMK__XA_CIB_UPDATE As discussed in several recent commit messages, PCMK__XA_CIB_UPDATE used to get set when broadcasting changes while in legacy mode. We haven't supported that since 3.0.0, so PCMK__XA_CIB_UPDATE is set only for sync requests (in sync_our_cib()). I don't see any reason to treat the return code differently for sync operations than for other modifying operations. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 547ce4698ea..8b87e08d50c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -736,21 +736,6 @@ log_op_result(const xmlNode *request, const cib__operation_t *operation, int rc, if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { level = LOG_TRACE; - } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { - switch (rc) { - case pcmk_rc_ok: - level = LOG_INFO; - break; - - case pcmk_rc_old_data: - case pcmk_rc_diff_failed: - level = LOG_TRACE; - break; - - default: - level = LOG_ERR; - } - } else if (rc != pcmk_rc_ok) { level = LOG_WARNING; } From e0b0ccdb9a85a3fe2431a93908f4b1c35d584340 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:52:15 -0800 Subject: [PATCH 234/350] Low: fencer: Refresh CIB in case of old diff In light of several recent commits (and my suspicion when I wrote the TODO comment that this commit addresses)... it feels like the most correct and consistent course of action is to refresh if we get an old_data error when applying the patchset. Signed-off-by: Reid Wahl --- daemons/fenced/fenced_cib.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/daemons/fenced/fenced_cib.c b/daemons/fenced/fenced_cib.c index 9ecf635f371..7ed15e3896a 100644 --- a/daemons/fenced/fenced_cib.c +++ b/daemons/fenced/fenced_cib.c @@ -507,26 +507,17 @@ update_cib_cache_cb(const char *event, xmlNode * msg) patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); rc = xml_apply_patchset(local_cib, patchset, TRUE); - switch (rc) { - case pcmk_ok: - case -pcmk_err_old_data: - /* @TODO Full refresh (with or without query) in case of - * -pcmk_err_old_data? It seems wrong to call - * stonith_device_remove() based on primitive deletion in an - * old diff. - */ - break; - case -pcmk_err_diff_failed: + + if (rc != pcmk_ok) { + if ((rc == -pcmk_err_old_data) || (rc == -pcmk_err_diff_failed)) { pcmk__notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc); - pcmk__xml_free(local_cib); - local_cib = NULL; - break; - default: + } else { pcmk__warn("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc); - pcmk__xml_free(local_cib); - local_cib = NULL; + } + + g_clear_pointer(&local_cib, pcmk__xml_free); } } From 7a67bbc47d75b3e3f56d1175d8c54986151992b8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 22:58:12 -0800 Subject: [PATCH 235/350] API: libcrmcommon: Deprecate pcmk_rc_diff_resync, pcmk_err_diff_resync Signed-off-by: Reid Wahl --- include/crm/common/results.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/crm/common/results.h b/include/crm/common/results.h index 7c7126fd0b6..d452805a645 100644 --- a/include/crm/common/results.h +++ b/include/crm/common/results.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2025 the Pacemaker project contributors + * Copyright 2012-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -76,6 +76,7 @@ extern "C" { #define pcmk_err_diff_failed 206 // NOTE: sbd (as of at least 1.5.2) uses this +//! \deprecated Do not use #define pcmk_err_diff_resync 207 #define pcmk_err_cib_modified 208 @@ -142,7 +143,10 @@ enum pcmk_rc_e { pcmk_rc_transform_failed = -1014, pcmk_rc_old_data = -1013, pcmk_rc_diff_failed = -1012, + + //! \deprecated Do not use pcmk_rc_diff_resync = -1011, + pcmk_rc_cib_modified = -1010, pcmk_rc_cib_backup = -1009, pcmk_rc_cib_save = -1008, From 520e743e69298c4baf9c8b86c6c47de3b266e451 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Thu, 1 Jan 2026 23:06:47 -0800 Subject: [PATCH 236/350] Log: based: Don't send entire CIB in ping reply for digest mismatch When we send a ping reply, we send the entire CIB if tracing is enabled. Then if the digest doesn't match the digest on the receiver, the receiver logs the changes between the sender's CIB and its own CIB at info level. To make this happen, we'd have to enable tracing on the sender in order to log the changes on the receiver. It's possible this could be useful, but I struggle to imagine us ever reaching that point and doing so. I'd like to get rid of what I perceive as bloat. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 29 ++++++++++++----------------- daemons/based/based_messages.c | 19 ++++--------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8b87e08d50c..3cd98016053 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -248,37 +248,32 @@ process_ping_reply(xmlNode *reply) if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { xmlNode *wrapper = pcmk__xe_first_child(pong, PCMK__XE_CIB_CALLDATA, NULL, NULL); - xmlNode *remote_cib = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *remote_versions = pcmk__xe_first_child(wrapper, NULL, NULL, + NULL); const char *admin_epoch_s = NULL; const char *epoch_s = NULL; const char *num_updates_s = NULL; - if (remote_cib != NULL) { - admin_epoch_s = pcmk__xe_get(remote_cib, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_cib, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_cib, PCMK_XA_NUM_UPDATES); + if (remote_versions != NULL) { + admin_epoch_s = pcmk__xe_get(remote_versions, + PCMK_XA_ADMIN_EPOCH); + epoch_s = pcmk__xe_get(remote_versions, + PCMK_XA_EPOCH); + num_updates_s = pcmk__xe_get(remote_versions, + PCMK_XA_NUM_UPDATES); } - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s " - "%p", + pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), pcmk__xe_get(the_cib, PCMK_XA_EPOCH), pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), ping_digest, host, pcmk__s(admin_epoch_s, "_"), pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), - digest, remote_cib); - - if(remote_cib && remote_cib->children) { - // Additional debug - pcmk__xml_mark_changes(the_cib, remote_cib); - pcmk__log_xml_changes(LOG_INFO, remote_cib); - pcmk__trace("End of differences"); - } + pcmk__s(num_updates_s, "_"), digest); - pcmk__xml_free(remote_cib); + pcmk__xml_free(remote_versions); sync_our_cib(reply, false); } } diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 3eb0b6cff30..47cfb37b132 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -125,21 +125,10 @@ based_process_ping(const char *op, int options, const char *section, wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA); if (the_cib != NULL) { - pcmk__if_tracing( - { - /* Append additional detail so the receiver can log the - * differences - */ - pcmk__xml_copy(wrapper, the_cib); - }, - { - // Always include at least the version details - const char *name = (const char *) the_cib->name; - xmlNode *shallow = pcmk__xe_create(wrapper, name); - - pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none); - } - ); + xmlNode *shallow = pcmk__xe_create(wrapper, + (const char *) the_cib->name); + + pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none); } pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", From fc2792f2a7f1ff0bc4681943877e5a1fd546b48a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 00:39:22 -0800 Subject: [PATCH 237/350] Doc: libcib: Address a comment about possible legacy code Beekhof added the original "legacy code" comment via commit b9f13af4 in 2014. However, as far as I can tell, this is not legacy code. We don't want to bump versions after we receive a CIB in response to a sync request. We want to use whatever versions are in the received CIB. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3cd98016053..19849dc73a0 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -603,13 +603,10 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - /* @COMPAT: Handle a valid write action (legacy) - * - * @TODO: Re-evaluate whether this is truly legacy. PCMK__XA_CIB_UPDATE may - * be set by a sync operation even in non-legacy mode, and manage_counters - * tells xml_create_patchset() whether to update version/epoch info. - */ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { + /* This is a replace operation as a reply to a sync request. Keep + * whatever versions are in the received CIB. + */ manage_counters = false; CRM_LOG_ASSERT(pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)); From 397ab69d15468be5c036be5284edda1aea023fde Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 01:15:16 -0800 Subject: [PATCH 238/350] Refactor: libcib: Drop cib_perform_op() manage_counters argument I hate to bloat the function body even more, but I also hate how many arguments it has. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 ++------------ include/crm/cib/internal.h | 6 +++--- lib/cib/cib_file.c | 2 +- lib/cib/cib_utils.c | 15 +++++++++++---- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 19849dc73a0..4b6de061789 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -568,7 +568,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, int rc = pcmk_rc_ok; bool config_changed = false; - bool manage_counters = true; static mainloop_timer_t *digest_timer = NULL; @@ -603,15 +602,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) { - /* This is a replace operation as a reply to a sync request. Keep - * whatever versions are in the received CIB. - */ - manage_counters = false; - CRM_LOG_ASSERT(pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, - pcmk__str_none)); - } - ping_modified_since = true; /* result_cib must not be modified after cib_perform_op() returns. @@ -620,8 +610,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * cib_remote. */ rc = cib_perform_op(cib_undefined, op, call_options, op_function, section, - request, input, manage_counters, &config_changed, - &the_cib, &result_cib, &cib_diff, &output); + request, input, &config_changed, &the_cib, &result_cib, + &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index b7c57136a86..06d244568fb 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -184,9 +184,9 @@ int cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, int cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, - xmlNode *req, xmlNode *input, bool manage_counters, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output); + xmlNode *req, xmlNode *input, bool *config_changed, + xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 310ffdd0cca..8b34a1c5ae4 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -181,7 +181,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) data, &private->cib_xml, output); } else { rc = cib_perform_op(cib_file, op, call_options, op_function, section, - request, data, true, &changed, &private->cib_xml, + request, data, &changed, &private->cib_xml, &result_cib, &cib_diff, output); } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 9d4ae50ef63..f487a84d712 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -441,9 +441,8 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) int cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode *input, bool manage_counters, bool *config_changed, - xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, - xmlNode **output) + xmlNode *input, bool *config_changed, xmlNode **current_cib, + xmlNode **result_cib, xmlNode **diff, xmlNode **output) { int rc = pcmk_rc_ok; @@ -458,6 +457,7 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, const char *user = NULL; bool enable_acl = false; + bool manage_version = true; pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) @@ -562,11 +562,18 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, pcmk__strip_xml_text(working_cib); + if (pcmk__xe_attr_is_true(req, PCMK__XA_CIB_UPDATE)) { + /* This is a replace operation as a reply to a sync request. Keep + * whatever versions are in the received CIB. + */ + manage_version = false; + } + /* If we didn't make a copy, the diff will only be accurate for the * top-level PCMK_XE_CIB element */ *diff = xml_create_patchset(0, old_versions, working_cib, config_changed, - manage_counters); + manage_version); /* pcmk__xml_commit_changes() resets document private data, so call it even * if there were no changes. From 4d9b01dab734a5c4bd67749588fc2f7660b60616 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 01:20:07 -0800 Subject: [PATCH 239/350] Refactor: libcib: Drop op arg from cib__perform_query()/cib_perform_op() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- include/crm/cib/internal.h | 11 +++++------ lib/cib/cib_file.c | 4 ++-- lib/cib/cib_utils.c | 20 ++++++++++++-------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4b6de061789..dac751f63b7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -597,7 +597,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, input = prepare_input(request, operation->type, §ion); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(op, call_options, op_function, section, request, + rc = cib__perform_query(call_options, op_function, section, request, input, &the_cib, &output); goto done; } @@ -609,7 +609,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * It's not important whether the client variant is cib_native or * cib_remote. */ - rc = cib_perform_op(cib_undefined, op, call_options, op_function, section, + rc = cib_perform_op(cib_undefined, call_options, op_function, section, request, input, &config_changed, &the_cib, &result_cib, &cib_diff, &output); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 06d244568fb..956d0f3ae37 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -178,15 +178,14 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); -int cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, +int cib__perform_query(uint32_t call_options, cib__op_fn_t fn, const char *section, xmlNode *req, xmlNode *input, xmlNode **current_cib, xmlNode **output); -int cib_perform_op(enum cib_variant variant, const char *op, - uint32_t call_options, cib__op_fn_t fn, const char *section, - xmlNode *req, xmlNode *input, bool *config_changed, - xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, - xmlNode **output); +int cib_perform_op(enum cib_variant variant, uint32_t call_options, + cib__op_fn_t fn, const char *section, xmlNode *req, + xmlNode *input, bool *config_changed, xmlNode **current_cib, + xmlNode **result_cib, xmlNode **diff, xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 8b34a1c5ae4..55b31c9d2ea 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -177,10 +177,10 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) } if (read_only) { - rc = cib__perform_query(op, call_options, op_function, section, request, + rc = cib__perform_query(call_options, op_function, section, request, data, &private->cib_xml, output); } else { - rc = cib_perform_op(cib_file, op, call_options, op_function, section, + rc = cib_perform_op(cib_file, call_options, op_function, section, request, data, &changed, &private->cib_xml, &result_cib, &cib_diff, output); } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index f487a84d712..62229d5e13b 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -170,20 +170,22 @@ cib_acl_enabled(xmlNode *xml, const char *user) } int -cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, - const char *section, xmlNode *req, xmlNode *input, - xmlNode **current_cib, xmlNode **output) +cib__perform_query(uint32_t call_options, cib__op_fn_t fn, const char *section, + xmlNode *req, xmlNode *input, xmlNode **current_cib, + xmlNode **output) { int rc = pcmk_rc_ok; + const char *op = NULL; const char *user = NULL; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; - pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) + pcmk__assert((fn != NULL) && (req != NULL) && (current_cib != NULL) && (*current_cib != NULL) && (output != NULL) && (*output == NULL)); + op = pcmk__xe_get(req, PCMK__XA_CIB_OP); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); cib = *current_cib; @@ -439,9 +441,9 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, - cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode *input, bool *config_changed, xmlNode **current_cib, +cib_perform_op(enum cib_variant variant, uint32_t call_options, cib__op_fn_t fn, + const char *section, xmlNode *req, xmlNode *input, + bool *config_changed, xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, xmlNode **output) { int rc = pcmk_rc_ok; @@ -455,17 +457,19 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, xmlNode *top = NULL; xmlNode *working_cib = NULL; + const char *op = NULL; const char *user = NULL; bool enable_acl = false; bool manage_version = true; - pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL) + pcmk__assert((fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) && (current_cib != NULL) && (*current_cib != NULL) && (result_cib != NULL) && (*result_cib == NULL) && (diff != NULL) && (*diff == NULL) && (output != NULL) && (*output == NULL)); + op = pcmk__xe_get(req, PCMK__XA_CIB_OP); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); enable_acl = cib_acl_enabled(*current_cib, user); From a378cffc91072c40e41fbaa1eaee2df50a5b69d4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 01:26:46 -0800 Subject: [PATCH 240/350] Refactor: libcib: Drop call_options arg from cib_perform_op()... and cib__perform_query(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 +++++----- include/crm/cib/internal.h | 11 +++++------ lib/cib/cib_file.c | 10 +++++----- lib/cib/cib_utils.c | 18 +++++++++++------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index dac751f63b7..b2e6738c923 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -597,8 +597,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, input = prepare_input(request, operation->type, §ion); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(call_options, op_function, section, request, - input, &the_cib, &output); + rc = cib__perform_query(op_function, section, request, input, &the_cib, + &output); goto done; } @@ -609,9 +609,9 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * It's not important whether the client variant is cib_native or * cib_remote. */ - rc = cib_perform_op(cib_undefined, call_options, op_function, section, - request, input, &config_changed, &the_cib, &result_cib, - &cib_diff, &output); + rc = cib_perform_op(cib_undefined, op_function, section, request, input, + &config_changed, &the_cib, &result_cib, &cib_diff, + &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 956d0f3ae37..2737086ed0d 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -178,13 +178,12 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); -int cib__perform_query(uint32_t call_options, cib__op_fn_t fn, - const char *section, xmlNode *req, xmlNode *input, - xmlNode **current_cib, xmlNode **output); +int cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, + xmlNode *input, xmlNode **current_cib, xmlNode **output); -int cib_perform_op(enum cib_variant variant, uint32_t call_options, - cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode *input, bool *config_changed, xmlNode **current_cib, +int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, + const char *section, xmlNode *req, xmlNode *input, + bool *config_changed, xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 55b31c9d2ea..5fbbd376813 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -177,12 +177,12 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) } if (read_only) { - rc = cib__perform_query(call_options, op_function, section, request, - data, &private->cib_xml, output); + rc = cib__perform_query(op_function, section, request, data, + &private->cib_xml, output); } else { - rc = cib_perform_op(cib_file, call_options, op_function, section, - request, data, &changed, &private->cib_xml, - &result_cib, &cib_diff, output); + rc = cib_perform_op(cib_file, op_function, section, request, data, + &changed, &private->cib_xml, &result_cib, &cib_diff, + output); } if (pcmk__is_set(call_options, cib_transaction)) { diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 62229d5e13b..5bfb03ebdfc 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -170,13 +170,13 @@ cib_acl_enabled(xmlNode *xml, const char *user) } int -cib__perform_query(uint32_t call_options, cib__op_fn_t fn, const char *section, - xmlNode *req, xmlNode *input, xmlNode **current_cib, - xmlNode **output) +cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, + xmlNode *input, xmlNode **current_cib, xmlNode **output) { int rc = pcmk_rc_ok; const char *op = NULL; const char *user = NULL; + uint32_t call_options = cib_none; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -187,6 +187,7 @@ cib__perform_query(uint32_t call_options, cib__op_fn_t fn, const char *section, op = pcmk__xe_get(req, PCMK__XA_CIB_OP); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -441,10 +442,10 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(enum cib_variant variant, uint32_t call_options, cib__op_fn_t fn, - const char *section, xmlNode *req, xmlNode *input, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output) +cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, const char *section, + xmlNode *req, xmlNode *input, bool *config_changed, + xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, + xmlNode **output) { int rc = pcmk_rc_ok; @@ -459,6 +460,7 @@ cib_perform_op(enum cib_variant variant, uint32_t call_options, cib__op_fn_t fn, const char *op = NULL; const char *user = NULL; + uint32_t call_options = cib_none; bool enable_acl = false; bool manage_version = true; @@ -471,6 +473,8 @@ cib_perform_op(enum cib_variant variant, uint32_t call_options, cib__op_fn_t fn, op = pcmk__xe_get(req, PCMK__XA_CIB_OP); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); + enable_acl = cib_acl_enabled(*current_cib, user); if (!should_copy_cib(op, section, call_options)) { From d476afb6738b915d502a25992861607b935528ac Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 01:52:55 -0800 Subject: [PATCH 241/350] Refactor: based: Don't pass type to prepare_input() cib__process_apply_patchset() ignores the section argument, and the input data shouldn't start with a PCMK_XE_CIB element anyway (although it turns out nothing checks that currently). This is another step toward simplifying things. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b2e6738c923..fd5876828bc 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -517,7 +517,6 @@ forward_request(xmlNode *request) * \brief Get a CIB operation's input from the request XML * * \param[in] request CIB request XML - * \param[in] type CIB operation type * \param[out] section Where to store CIB section name * * \return Input XML for CIB operation @@ -526,18 +525,13 @@ forward_request(xmlNode *request) * \p request. The caller should not free it directly. */ static xmlNode * -prepare_input(const xmlNode *request, enum cib__op_type type, - const char **section) +prepare_input(const xmlNode *request, const char **section) { xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL, NULL); xmlNode *input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - if (type == cib__op_apply_patch) { - *section = NULL; - } else { - *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); - } + *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); // Grab the specified section if ((*section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { @@ -594,7 +588,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - input = prepare_input(request, operation->type, §ion); + input = prepare_input(request, §ion); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { rc = cib__perform_query(op_function, section, request, input, &the_cib, From 123c9fd58f8e4c73a2b59484038b2915511771eb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 02:02:07 -0800 Subject: [PATCH 242/350] Refactor: libcib: Drop input arg from cib__perform_query/cib_perform_op Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 39 +++------------------------- include/crm/cib/internal.h | 8 +++--- lib/cib/cib_file.c | 16 +++--------- lib/cib/cib_utils.c | 45 +++++++++++++++++++++++++-------- 4 files changed, 45 insertions(+), 63 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index fd5876828bc..c4c800a3cd3 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -29,7 +29,6 @@ #include // cib__* #include // pcmk_cluster_disconnect #include // pcmk__cluster_send_message -#include // pcmk_find_cib_element #include // pcmk__s, pcmk__str_eq #include // crm_ipc_*, pcmk_ipc_* #include // CRM_LOG_ASSERT, CRM_CHECK @@ -512,48 +511,18 @@ forward_request(xmlNode *request) pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM); } -/*! - * \internal - * \brief Get a CIB operation's input from the request XML - * - * \param[in] request CIB request XML - * \param[out] section Where to store CIB section name - * - * \return Input XML for CIB operation - * - * \note If not \c NULL, the return value is a non-const pointer to part of - * \p request. The caller should not free it directly. - */ -static xmlNode * -prepare_input(const xmlNode *request, const char **section) -{ - xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - - *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); - - // Grab the specified section - if ((*section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { - input = pcmk_find_cib_element(input, *section); - } - - return input; -} - static int cib_process_command(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **reply, bool privileged) { xmlNode *cib_diff = NULL; - xmlNode *input = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; uint32_t call_options = cib_none; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *section = NULL; + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); @@ -588,10 +557,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - input = prepare_input(request, §ion); - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(op_function, section, request, input, &the_cib, + rc = cib__perform_query(op_function, section, request, &the_cib, &output); goto done; } @@ -603,7 +570,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * It's not important whether the client variant is cib_native or * cib_remote. */ - rc = cib_perform_op(cib_undefined, op_function, section, request, input, + rc = cib_perform_op(cib_undefined, op_function, section, request, &config_changed, &the_cib, &result_cib, &cib_diff, &output); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 2737086ed0d..0623efcbe68 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -179,12 +179,12 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); int cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode *input, xmlNode **current_cib, xmlNode **output); + xmlNode **current_cib, xmlNode **output); int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, - const char *section, xmlNode *req, xmlNode *input, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output); + const char *section, xmlNode *req, bool *config_changed, + xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 5fbbd376813..94600437fd7 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -146,9 +146,6 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) uint32_t call_options = cib_none; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); - xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); bool changed = false; bool read_only = false; @@ -170,19 +167,12 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); - // Mirror the logic in prepare_input() in the CIB manager - if ((section != NULL) && pcmk__xe_is(data, PCMK_XE_CIB)) { - - data = pcmk_find_cib_element(data, section); - } - if (read_only) { - rc = cib__perform_query(op_function, section, request, data, + rc = cib__perform_query(op_function, section, request, &private->cib_xml, output); } else { - rc = cib_perform_op(cib_file, op_function, section, request, data, - &changed, &private->cib_xml, &result_cib, &cib_diff, - output); + rc = cib_perform_op(cib_file, op_function, section, request, &changed, + &private->cib_xml, &result_cib, &cib_diff, output); } if (pcmk__is_set(call_options, cib_transaction)) { diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 5bfb03ebdfc..3069da5c742 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -169,14 +169,36 @@ cib_acl_enabled(xmlNode *xml, const char *user) return rc; } +/*! + * \internal + * \brief Get input data from a CIB request, based on section and call data + * + * \param[in] request CIB request XML + */ +static xmlNode * +get_op_input(const xmlNode *request) +{ + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, + NULL, NULL); + xmlNode *input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + + if ((section == NULL) || !pcmk__xe_is(input, PCMK_XE_CIB)) { + return input; + } + + return pcmk_find_cib_element(input, section); +} + int cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode *input, xmlNode **current_cib, xmlNode **output) + xmlNode **current_cib, xmlNode **output) { int rc = pcmk_rc_ok; const char *op = NULL; const char *user = NULL; uint32_t call_options = cib_none; + xmlNode *input = NULL; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -188,6 +210,8 @@ cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, op = pcmk__xe_get(req, PCMK__XA_CIB_OP); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); + + input = get_op_input(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -443,12 +467,18 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, const char *section, - xmlNode *req, xmlNode *input, bool *config_changed, - xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, - xmlNode **output) + xmlNode *req, bool *config_changed, xmlNode **current_cib, + xmlNode **result_cib, xmlNode **diff, xmlNode **output) { int rc = pcmk_rc_ok; + const char *op = NULL; + const char *user = NULL; + uint32_t call_options = cib_none; + xmlNode *input = NULL; + bool enable_acl = false; + bool manage_version = true; + /* PCMK_XE_CIB element containing version numbers from before the operation. * This may or may not point to a full CIB XML tree. Do not free, as this * will be used as an alias for another pointer. @@ -458,12 +488,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, const char *section, xmlNode *top = NULL; xmlNode *working_cib = NULL; - const char *op = NULL; - const char *user = NULL; - uint32_t call_options = cib_none; - bool enable_acl = false; - bool manage_version = true; - pcmk__assert((fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) && (current_cib != NULL) && (*current_cib != NULL) @@ -475,6 +499,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, const char *section, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); + input = get_op_input(req); enable_acl = cib_acl_enabled(*current_cib, user); if (!should_copy_cib(op, section, call_options)) { From c6a11b64506cfb929dca26143e0a2057fe318378 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 04:29:17 -0800 Subject: [PATCH 243/350] Low: libcib: Move CIB op input transformation to ops that need it I've been testing full-CIB replacements and encountered some problems due to the early input transformation (currently in get_op_input()), particularly with XPath. When we're treating the section string as an XPath expression, it doesn't make sense to try to find it as an element of input. (That is, unless we want to do an XPath search, which we can think about if necessary.) This moves the input transformation to the operations that need it, and performs it only when NOT treating the section as XPath. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 30 ++++++++++++++++++------------ lib/cib/cib_utils.c | 10 ++-------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 187144db19c..8b9f011ab00 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -299,25 +299,19 @@ cib__process_create(const char *op, int options, const char *section, int rc = pcmk_rc_ok; xmlNode *update_section = NULL; - if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { - section = NULL; - - } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) { - section = NULL; - - } else if (pcmk__xe_is(input, PCMK_XE_CIB)) { - section = NULL; + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { + input = pcmk_find_cib_element(input, section); } - CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL); - if (input == NULL) { pcmk__err("Cannot perform modification with no data"); return EINVAL; } - if (section == NULL) { - return cib__process_modify(op, options, section, req, input, cib, + if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) + || pcmk__xe_is(input, PCMK_XE_CIB)) { + + return cib__process_modify(op, options, NULL, req, input, cib, answer); } @@ -445,6 +439,10 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) { xmlNode *obj_root = NULL; + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { + input = pcmk_find_cib_element(input, section); + } + if (input == NULL) { pcmk__err("Cannot find matching section to delete with no input data"); return EINVAL; @@ -555,6 +553,10 @@ process_modify_section(int options, const char *section, xmlNode *input, const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); xmlNode *obj_root = NULL; + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { + input = pcmk_find_cib_element(input, section); + } + if (input == NULL) { pcmk__err("Cannot complete CIB modify request with no input data"); return EINVAL; @@ -874,6 +876,10 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { + input = pcmk_find_cib_element(input, section); + } + if (input == NULL) { pcmk__err("Cannot find matching section to replace with no input data"); return EINVAL; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 3069da5c742..0d21b85586c 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -171,23 +171,17 @@ cib_acl_enabled(xmlNode *xml, const char *user) /*! * \internal - * \brief Get input data from a CIB request, based on section and call data + * \brief Get input data from a CIB request * * \param[in] request CIB request XML */ static xmlNode * get_op_input(const xmlNode *request) { - const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL, NULL); - xmlNode *input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - if ((section == NULL) || !pcmk__xe_is(input, PCMK_XE_CIB)) { - return input; - } - - return pcmk_find_cib_element(input, section); + return pcmk__xe_first_child(wrapper, NULL, NULL, NULL); } int From 5fa5eee4a40ce7ea9a0c34c60d3dd82e7b5e3d0e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 02:08:04 -0800 Subject: [PATCH 244/350] Refactor: libcib: Drop section arg of cib__perform_query/cib_perform_op Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 9 +++------ include/crm/cib/internal.h | 11 +++++------ lib/cib/cib_file.c | 9 +++------ lib/cib/cib_utils.c | 12 ++++++++---- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c4c800a3cd3..f91d6b8e5df 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -522,7 +522,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, uint32_t call_options = cib_none; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); @@ -558,8 +557,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, } if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(op_function, section, request, &the_cib, - &output); + rc = cib__perform_query(op_function, request, &the_cib, &output); goto done; } @@ -570,9 +568,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * It's not important whether the client variant is cib_native or * cib_remote. */ - rc = cib_perform_op(cib_undefined, op_function, section, request, - &config_changed, &the_cib, &result_cib, &cib_diff, - &output); + rc = cib_perform_op(cib_undefined, op_function, request, &config_changed, + &the_cib, &result_cib, &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 0623efcbe68..8c4b332f107 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -178,13 +178,12 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); -int cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode **current_cib, xmlNode **output); +int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, + xmlNode **output); -int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, - const char *section, xmlNode *req, bool *config_changed, - xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, - xmlNode **output); +int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **current_cib, + xmlNode **result_cib, xmlNode **diff, xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 94600437fd7..c688299d3b3 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -142,10 +142,8 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) const cib__operation_t *operation = NULL; cib__op_fn_t op_function = NULL; - int call_id = 0; uint32_t call_options = cib_none; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); bool changed = false; bool read_only = false; @@ -158,7 +156,6 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) cib__get_operation(op, &operation); op_function = get_op_function(operation); - pcmk__xe_get_int(request, PCMK__XA_CIB_CALLID, &call_id); rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { @@ -168,10 +165,10 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); if (read_only) { - rc = cib__perform_query(op_function, section, request, - &private->cib_xml, output); + rc = cib__perform_query(op_function, request, &private->cib_xml, + output); } else { - rc = cib_perform_op(cib_file, op_function, section, request, &changed, + rc = cib_perform_op(cib_file, op_function, request, &changed, &private->cib_xml, &result_cib, &cib_diff, output); } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 0d21b85586c..2ff16820b67 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -185,11 +185,12 @@ get_op_input(const xmlNode *request) } int -cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, - xmlNode **current_cib, xmlNode **output) +cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, + xmlNode **output) { int rc = pcmk_rc_ok; const char *op = NULL; + const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; xmlNode *input = NULL; @@ -202,6 +203,7 @@ cib__perform_query(cib__op_fn_t fn, const char *section, xmlNode *req, && (output != NULL) && (*output == NULL)); op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); @@ -460,13 +462,14 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, const char *section, - xmlNode *req, bool *config_changed, xmlNode **current_cib, +cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, xmlNode **output) { int rc = pcmk_rc_ok; const char *op = NULL; + const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; xmlNode *input = NULL; @@ -490,6 +493,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, const char *section, && (output != NULL) && (*output == NULL)); op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); From 1d77f15270d324a2de4d0978bc0b494e07fa2eb6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 03:14:47 -0800 Subject: [PATCH 245/350] Refactor: libcib: Move process_replace_xpath() downward This will make the next commit easier to review. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 88 +++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 8b9f011ab00..6381ae30f4d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -740,50 +740,6 @@ cib__process_query(const char *op, int options, const char *section, return process_query_section(options, section, *cib, answer); } -static int -process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *cib) -{ - int num_results = 0; - int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); - - num_results = pcmk__xpath_num_results(xpath_obj); - if (num_results == 0) { - pcmk__debug("%s: %s does not exist", op, xpath); - rc = ENXIO; - goto done; - } - - for (int i = 0; i < num_results; i++) { - xmlNode *match = NULL; - xmlNode *parent = NULL; - xmlChar *path = NULL; - - match = pcmk__xpath_result(xpath_obj, i); - if (match == NULL) { - continue; - } - - path = xmlGetNodePath(match); - pcmk__debug("Processing %s op for %s with %s", op, xpath, path); - free(path); - - parent = match->parent; - - pcmk__xml_free(match); - pcmk__xml_copy(parent, input); - - if (!pcmk__is_set(options, cib_multiple)) { - break; - } - } - -done: - xmlXPathFreeObject(xpath_obj); - return rc; -} - static bool replace_cib_digest_matches(xmlNode *request, xmlNode *input) { @@ -869,6 +825,50 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) return pcmk_rc_ok; } +static int +process_replace_xpath(const char *op, int options, const char *xpath, + xmlNode *input, xmlNode *cib) +{ + int num_results = 0; + int rc = pcmk_rc_ok; + xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + + num_results = pcmk__xpath_num_results(xpath_obj); + if (num_results == 0) { + pcmk__debug("%s: %s does not exist", op, xpath); + rc = ENXIO; + goto done; + } + + for (int i = 0; i < num_results; i++) { + xmlNode *match = NULL; + xmlNode *parent = NULL; + xmlChar *path = NULL; + + match = pcmk__xpath_result(xpath_obj, i); + if (match == NULL) { + continue; + } + + path = xmlGetNodePath(match); + pcmk__debug("Processing %s op for %s with %s", op, xpath, path); + free(path); + + parent = match->parent; + + pcmk__xml_free(match); + pcmk__xml_copy(parent, input); + + if (!pcmk__is_set(options, cib_multiple)) { + break; + } + } + +done: + xmlXPathFreeObject(xpath_obj); + return rc; +} + static int process_replace_section(const char *section, xmlNode *request, xmlNode *input, xmlNode **cib) From 92cec7f30fe18baea089d9b01646f35c375cb9d0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 04:36:44 -0800 Subject: [PATCH 246/350] Fix: libcib: Full-CIB replace op no longer segfaults with cib_xpath Previously, a CIB replace operation targeting the full CIB would fail with a segmentation fault if the cib_xpath flag were set (or if --xpath were used with cibadmin). Presumably this is because process_replace_xpath() was freeing the CIB, leaving no reference to it, and possibly dereferencing more nodes in the document as it continued processing any remaining XPath search results. Now, a full CIB replacement with XPath succeeds if the replacement CIB is well-formed and is of a newer version, and it appears to fail sanely otherwise. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 6381ae30f4d..a6d3b60ef92 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -827,11 +827,11 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) static int process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *cib) + xmlNode *request, xmlNode *input, xmlNode **cib) { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + xmlXPathObject *xpath_obj = pcmk__xpath_search((*cib)->doc, xpath); num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { @@ -854,6 +854,11 @@ process_replace_xpath(const char *op, int options, const char *xpath, pcmk__debug("Processing %s op for %s with %s", op, xpath, path); free(path); + if (match == *cib) { + rc = replace_cib(request, input, cib); + break; + } + parent = match->parent; pcmk__xml_free(match); @@ -911,7 +916,7 @@ cib__process_replace(const char *op, int options, const char *section, xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { - return process_replace_xpath(op, options, section, input, *cib); + return process_replace_xpath(op, options, section, req, input, cib); } return process_replace_section(section, req, input, cib); From 4508f0c957e004accf3d47f5df83d522dece4ec7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 14:02:28 -0800 Subject: [PATCH 247/350] Refactor: libcrmcommon: apply_transformation takes xmlDoc * argument Previously, it took a const xmlNode * argument. However, the function only used the node's document. Also, the node might get modified via its document pointer -- xsltApplyStylesheet() takes a non-const doc and makes no guarantees about whether it will be changed or not. Also use bool instead of gboolean. Signed-off-by: Reid Wahl --- lib/common/schemas.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/common/schemas.c b/lib/common/schemas.c index 87d87f95910..23bc0dced13 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.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. * @@ -968,23 +968,21 @@ cib_upgrade_err(void *ctx, const char *fmt, ...) /*! * \internal - * \brief Apply a single XSL transformation to given XML + * \brief Apply a single XSL transformation to the given XML document * - * \param[in] xml XML to transform + * \param[in] doc XML document * \param[in] transform XSL name * \param[in] to_logs If false, certain validation errors will be sent to * stderr rather than logged * * \return Transformed XML on success, otherwise NULL */ -static xmlNode * -apply_transformation(const xmlNode *xml, const char *transform, - gboolean to_logs) +static xmlDoc * +apply_transformation(xmlDoc *doc, const char *transform, bool to_logs) { char *xform = NULL; - xmlNode *out = NULL; - xmlDocPtr res = NULL; xsltStylesheet *xslt = NULL; + xmlDoc *result_doc = NULL; xform = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt, transform); @@ -1002,13 +1000,11 @@ apply_transformation(const xmlNode *xml, const char *transform, /* Caller allocates private data for final result document. Intermediate * result documents are temporary and don't need private data. */ - res = xsltApplyStylesheet(xslt, xml->doc, NULL); - CRM_CHECK(res != NULL, goto cleanup); + result_doc = xsltApplyStylesheet(xslt, doc, NULL); + CRM_CHECK(result_doc != NULL, goto cleanup); xsltSetGenericErrorFunc(NULL, NULL); /* restore default one */ - out = xmlDocGetRootElement(res); - cleanup: if (xslt) { xsltFreeStylesheet(xslt); @@ -1016,7 +1012,7 @@ apply_transformation(const xmlNode *xml, const char *transform, free(xform); - return out; + return result_doc; } /*! @@ -1039,6 +1035,7 @@ apply_upgrade(const xmlNode *input_xml, int schema_index, gboolean to_logs) schema_index + 1); xmlNode *old_xml = NULL; + xmlDoc *new_doc = NULL; xmlNode *new_xml = NULL; xmlRelaxNGValidityErrorFunc error_handler = NULL; @@ -1055,13 +1052,15 @@ apply_upgrade(const xmlNode *input_xml, int schema_index, gboolean to_logs) pcmk__debug("Upgrading schema from %s to %s: applying XSL transform %s", schema->name, upgraded_schema->name, transform); - new_xml = apply_transformation(input_xml, transform, to_logs); + new_doc = apply_transformation(input_xml->doc, transform, to_logs); pcmk__xml_free(old_xml); - if (new_xml == NULL) { + if (new_doc == NULL) { pcmk__err("XSL transform %s failed, aborting upgrade", transform); return NULL; } + + new_xml = xmlDocGetRootElement(new_doc); input_xml = new_xml; old_xml = new_xml; } From 4114fc391a844c25a76d3b16f0610047adb6e1e7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 14:31:38 -0800 Subject: [PATCH 248/350] Refactor: libcrmcommon: apply_upgrade() takes xmlDoc * argument Previously, it took a const xmlNode * argument. However, the function only used the node's document. Also, the node might get modified via its document pointer -- xsltApplyStylesheet() takes a non-const doc and makes no guarantees about whether it will be changed or not. Also use bool instead of gboolean. Signed-off-by: Reid Wahl --- lib/common/schemas.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/common/schemas.c b/lib/common/schemas.c index 23bc0dced13..da5c972c84e 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -1019,7 +1019,7 @@ apply_transformation(xmlDoc *doc, const char *transform, bool to_logs) * \internal * \brief Perform all transformations needed to upgrade XML to next schema * - * \param[in] input_xml XML to transform + * \param[in] input_doc XML document to transform * \param[in] schema_index Index of schema that successfully validates * \p original_xml * \param[in] to_logs If false, certain validation errors will be sent to @@ -1027,14 +1027,14 @@ apply_transformation(xmlDoc *doc, const char *transform, bool to_logs) * * \return XML result of schema transforms if successful, otherwise NULL */ -static xmlNode * -apply_upgrade(const xmlNode *input_xml, int schema_index, gboolean to_logs) +static xmlDoc * +apply_upgrade(xmlDoc *input_doc, int schema_index, bool to_logs) { pcmk__schema_t *schema = g_list_nth_data(known_schemas, schema_index); pcmk__schema_t *upgraded_schema = g_list_nth_data(known_schemas, schema_index + 1); - xmlNode *old_xml = NULL; + xmlDoc *old_doc = NULL; xmlDoc *new_doc = NULL; xmlNode *new_xml = NULL; xmlRelaxNGValidityErrorFunc error_handler = NULL; @@ -1052,21 +1052,22 @@ apply_upgrade(const xmlNode *input_xml, int schema_index, gboolean to_logs) pcmk__debug("Upgrading schema from %s to %s: applying XSL transform %s", schema->name, upgraded_schema->name, transform); - new_doc = apply_transformation(input_xml->doc, transform, to_logs); - pcmk__xml_free(old_xml); + new_doc = apply_transformation(input_doc, transform, to_logs); + pcmk__xml_free_doc(old_doc); if (new_doc == NULL) { pcmk__err("XSL transform %s failed, aborting upgrade", transform); return NULL; } - new_xml = xmlDocGetRootElement(new_doc); - input_xml = new_xml; - old_xml = new_xml; + input_doc = new_doc; + old_doc = new_doc; } // Final result document from upgrade pipeline needs private data - pcmk__xml_new_private_data((xmlNode *) new_xml->doc); + pcmk__xml_new_private_data((xmlNode *) new_doc); + + new_xml = xmlDocGetRootElement(new_doc); // Ensure result validates with its new schema if (!validate_with(new_xml, upgraded_schema, error_handler, @@ -1081,7 +1082,7 @@ apply_upgrade(const xmlNode *input_xml, int schema_index, gboolean to_logs) pcmk__info("Schema upgrade from %s to %s succeeded", schema->name, upgraded_schema->name); - return new_xml; + return new_doc; } /*! @@ -1125,12 +1126,15 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, GList *entry = NULL; pcmk__schema_t *best_schema = NULL; pcmk__schema_t *original_schema = NULL; - xmlRelaxNGValidityErrorFunc error_handler = - to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL; + xmlRelaxNGValidityErrorFunc error_handler = NULL; CRM_CHECK((xml != NULL) && (*xml != NULL) && ((*xml)->doc != NULL), return EINVAL); + if (to_logs) { + error_handler = (xmlRelaxNGValidityErrorFunc) xml_log; + } + if (max_schema_name != NULL) { GList *max_entry = pcmk__get_schema(max_schema_name); @@ -1155,7 +1159,7 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, for (; entry != NULL; entry = entry->next) { pcmk__schema_t *current_schema = entry->data; - xmlNode *upgrade = NULL; + xmlDoc *upgrade = NULL; if (current_schema->schema_index > max_schema_index) { break; @@ -1189,17 +1193,19 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, continue; } - upgrade = apply_upgrade(*xml, current_schema->schema_index, to_logs); + upgrade = apply_upgrade((*xml)->doc, current_schema->schema_index, + to_logs); if (upgrade == NULL) { /* The transform failed, so this schema can't be used. Later * schemas are unlikely to validate, but try anyway until we * run out of options. */ rc = pcmk_rc_transform_failed; + } else { best_schema = current_schema; pcmk__xml_free(*xml); - *xml = upgrade; + *xml = xmlDocGetRootElement(upgrade); } } From 79febbf2183a38cf3809620f0cfd660d1d70d90d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 14:39:31 -0800 Subject: [PATCH 249/350] Refactor: libcrmcommon: validate_with() takes xmlDoc * argument Previously, it took an xmlNode * argument. However, the function only used the node's document. Signed-off-by: Reid Wahl --- lib/common/schemas.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/common/schemas.c b/lib/common/schemas.c index da5c972c84e..f99e6c3d5f1 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -749,7 +749,7 @@ pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name) } static bool -validate_with(xmlNode *xml, pcmk__schema_t *schema, +validate_with(xmlDoc *doc, pcmk__schema_t *schema, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context) { @@ -773,7 +773,8 @@ validate_with(xmlNode *xml, pcmk__schema_t *schema, switch (schema->validator) { case pcmk__schema_validator_rng: cache = (relaxng_ctx_cache_t **) &(schema->cache); - valid = validate_with_relaxng(xml->doc, error_handler, error_handler_context, file, cache); + valid = validate_with_relaxng(doc, error_handler, + error_handler_context, file, cache); break; default: pcmk__err("Unknown validator type: %d", schema->validator); @@ -789,7 +790,8 @@ validate_with_silent(xmlNode *xml, pcmk__schema_t *schema) { bool rc, sl_backup = silent_logging; silent_logging = TRUE; - rc = validate_with(xml, schema, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR)); + rc = validate_with(xml->doc, schema, (xmlRelaxNGValidityErrorFunc) xml_log, + GUINT_TO_POINTER(LOG_ERR)); silent_logging = sl_backup; return rc; } @@ -818,7 +820,7 @@ pcmk__validate_xml(xmlNode *xml_blob, const char *validation, } schema = entry->data; - return validate_with(xml_blob, schema, error_handler, + return validate_with(xml_blob->doc, schema, error_handler, error_handler_context); } @@ -1036,7 +1038,6 @@ apply_upgrade(xmlDoc *input_doc, int schema_index, bool to_logs) xmlDoc *old_doc = NULL; xmlDoc *new_doc = NULL; - xmlNode *new_xml = NULL; xmlRelaxNGValidityErrorFunc error_handler = NULL; pcmk__assert((schema != NULL) && (upgraded_schema != NULL)); @@ -1067,16 +1068,15 @@ apply_upgrade(xmlDoc *input_doc, int schema_index, bool to_logs) // Final result document from upgrade pipeline needs private data pcmk__xml_new_private_data((xmlNode *) new_doc); - new_xml = xmlDocGetRootElement(new_doc); - // Ensure result validates with its new schema - if (!validate_with(new_xml, upgraded_schema, error_handler, + if (!validate_with(new_doc, upgraded_schema, error_handler, GUINT_TO_POINTER(LOG_ERR))) { pcmk__err("Schema upgrade from %s to %s failed: XSL transform pipeline " "produced an invalid configuration", schema->name, upgraded_schema->name); - pcmk__log_xml_debug(new_xml, "bad-transform-result"); - pcmk__xml_free(new_xml); + pcmk__log_xml_debug(xmlDocGetRootElement(new_doc), + "bad-transform-result"); + pcmk__xml_free_doc(new_doc); return NULL; } @@ -1165,7 +1165,7 @@ pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, break; } - if (!validate_with(*xml, current_schema, error_handler, + if (!validate_with((*xml)->doc, current_schema, error_handler, GUINT_TO_POINTER(LOG_ERR))) { pcmk__debug("Schema %s does not validate", current_schema->name); if (best_schema != NULL) { From 8e89fe6160c72752afaea89da226ed7a787255bc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 14:41:09 -0800 Subject: [PATCH 250/350] Refactor: libcrmcommon: validate_with_silent() takes xmlDoc * argument Previously, it took an xmlNode * argument. However, the function only used the node's document. Signed-off-by: Reid Wahl --- lib/common/schemas.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/common/schemas.c b/lib/common/schemas.c index f99e6c3d5f1..b737f0aa8f3 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -786,11 +786,13 @@ validate_with(xmlDoc *doc, pcmk__schema_t *schema, } static bool -validate_with_silent(xmlNode *xml, pcmk__schema_t *schema) +validate_with_silent(xmlDoc *doc, pcmk__schema_t *schema) { - bool rc, sl_backup = silent_logging; + bool rc = false; + bool sl_backup = silent_logging; + silent_logging = TRUE; - rc = validate_with(xml->doc, schema, (xmlRelaxNGValidityErrorFunc) xml_log, + rc = validate_with(doc, schema, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR)); silent_logging = sl_backup; return rc; @@ -1185,7 +1187,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) - || validate_with_silent(*xml, entry->next->data)) { + || 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 * try the next schema with the same XML. From e3cd9ae1c3aaa784c194191deaaccb3b0be311fc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 14:44:26 -0800 Subject: [PATCH 251/350] Refactor: libcrmcommon: Drop pcmk__validate_xml() validation argument It was always NULL. Signed-off-by: Reid Wahl --- include/crm/common/schemas_internal.h | 3 +-- lib/cib/cib_file.c | 2 +- lib/common/schemas.c | 15 ++++++--------- lib/pacemaker/pcmk_simulate.c | 4 ++-- lib/pacemaker/pcmk_verify.c | 6 +++--- tools/cibadmin.c | 4 ++-- tools/crm_simulate.c | 4 ++-- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/include/crm/common/schemas_internal.h b/include/crm/common/schemas_internal.h index f685118673c..fa4c1039099 100644 --- a/include/crm/common/schemas_internal.h +++ b/include/crm/common/schemas_internal.h @@ -36,8 +36,7 @@ GList *pcmk__get_schema(const char *name); const char *pcmk__highest_schema_name(void); int pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name); -bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, - xmlRelaxNGValidityErrorFunc error_handler, +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, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index c688299d3b3..8fc5de65a9a 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -181,7 +181,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) if (rc == pcmk_rc_schema_validation) { // Show validation errors to stderr - pcmk__validate_xml(result_cib, NULL, NULL, NULL); + pcmk__validate_xml(result_cib, NULL, NULL); } else if ((rc == pcmk_rc_ok) && !read_only) { if (result_cib != private->cib_xml) { diff --git a/lib/common/schemas.c b/lib/common/schemas.c index b737f0aa8f3..c006c34d012 100644 --- a/lib/common/schemas.c +++ b/lib/common/schemas.c @@ -799,18 +799,16 @@ validate_with_silent(xmlDoc *doc, pcmk__schema_t *schema) } bool -pcmk__validate_xml(xmlNode *xml_blob, const char *validation, - xmlRelaxNGValidityErrorFunc error_handler, +pcmk__validate_xml(xmlNode *xml, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context) { + const char *validation = NULL; GList *entry = NULL; pcmk__schema_t *schema = NULL; - CRM_CHECK((xml_blob != NULL) && (xml_blob->doc != NULL), return false); + CRM_CHECK((xml != NULL) && (xml->doc != NULL), return false); - if (validation == NULL) { - validation = pcmk__xe_get(xml_blob, PCMK_XA_VALIDATE_WITH); - } + validation = pcmk__xe_get(xml, PCMK_XA_VALIDATE_WITH); pcmk__warn_if_schema_deprecated(validation); entry = pcmk__get_schema(validation); @@ -822,7 +820,7 @@ pcmk__validate_xml(xmlNode *xml_blob, const char *validation, } schema = entry->data; - return validate_with(xml_blob->doc, schema, error_handler, + return validate_with(xml->doc, schema, error_handler, error_handler_context); } @@ -837,8 +835,7 @@ pcmk__validate_xml(xmlNode *xml_blob, const char *validation, bool pcmk__configured_schema_validates(xmlNode *xml) { - return pcmk__validate_xml(xml, NULL, - (xmlRelaxNGValidityErrorFunc) xml_log, + return pcmk__validate_xml(xml, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR)); } diff --git a/lib/pacemaker/pcmk_simulate.c b/lib/pacemaker/pcmk_simulate.c index 54393a968e2..647c2342257 100644 --- a/lib/pacemaker/pcmk_simulate.c +++ b/lib/pacemaker/pcmk_simulate.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. * @@ -404,7 +404,7 @@ profile_file(const char *xml_file, unsigned int repeat, goto done; } - if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) { + if (!pcmk__validate_xml(cib_object, NULL, NULL)) { goto done; } diff --git a/lib/pacemaker/pcmk_verify.c b/lib/pacemaker/pcmk_verify.c index 0ae1d1fb0c6..b8653094d56 100644 --- a/lib/pacemaker/pcmk_verify.c +++ b/lib/pacemaker/pcmk_verify.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. * @@ -83,8 +83,8 @@ pcmk__verify(pcmk_scheduler_t *scheduler, pcmk__output_t *out, pcmk__xe_create(*cib_object, PCMK_XE_STATUS); } - if (!pcmk__validate_xml(*cib_object, NULL, - (xmlRelaxNGValidityErrorFunc) out->err, out)) { + if (!pcmk__validate_xml(*cib_object, (xmlRelaxNGValidityErrorFunc) out->err, + out)) { pcmk__config_has_error = true; rc = pcmk_rc_schema_validation; goto verify_done; diff --git a/tools/cibadmin.c b/tools/cibadmin.c index fc20bdea950..0c769aa91ed 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.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. * @@ -349,7 +349,7 @@ cibadmin_post_default(pcmk__output_t *out, cib_t *cib_conn, int call_options, && pcmk__xe_is(output, PCMK_XE_CIB)) { // Show validation errors to stderr - pcmk__validate_xml(output, NULL, NULL, NULL); + pcmk__validate_xml(output, NULL, NULL); } return pcmk_rc2exitc(cib_rc); } diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c index 6f08a917f44..dc186c38608 100644 --- a/tools/crm_simulate.c +++ b/tools/crm_simulate.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2025 the Pacemaker project contributors + * Copyright 2009-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -381,7 +381,7 @@ setup_input(pcmk__output_t *out, const char *input, const char *output, return rc; } - if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) { + if (!pcmk__validate_xml(cib_object, NULL, NULL)) { pcmk__xml_free(cib_object); return pcmk_rc_schema_validation; } From 88449c5762a61149d1fc6ef3269da9ba8ed794ed Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 15:21:13 -0800 Subject: [PATCH 252/350] Refactor: libcib: cib__process_upgrade() keeps same doc The XSL transformation pipeline frees its XML argument and replaces it with a new XML tree. libxslt and libxml2 documentation are missing a lot of info, such as whether xsltApplyStylesheet returns a new document or modifies the existing one in place. We can avoid guesswork as follows: * Make a copy of the CIB (name the variable "upgraded"). * Perform the upgrade on "upgraded". * Replace the CIB root element with a copy of "upgraded" (which unlinks and frees the original CIB root element). * Free "upgraded". In this way, our resulting CIB is an upgraded CIB, but it's part of the same document as the pre-upgrade CIB, which has been freed. Specifically, it's the new root of that same document. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index a6d3b60ef92..66281a2df94 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -929,12 +929,15 @@ cib__process_upgrade(const char *op, int options, const char *section, { int rc = pcmk_rc_ok; const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); - const char *original_schema = NULL; + const char *original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); const char *new_schema = NULL; + xmlNode *updated = pcmk__xml_copy(NULL, *cib); - original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); - rc = pcmk__update_schema(cib, max_schema, true, + rc = pcmk__update_schema(&updated, max_schema, true, !pcmk__is_set(options, cib_verbose)); + *cib = pcmk__xml_replace_with_copy(*cib, updated); + pcmk__xml_free(updated); + new_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { From 62fc075c84f060556e6c87fc93705ede54a5a72b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 15:33:30 -0800 Subject: [PATCH 253/350] Doc: libcib: Require that cib__op_fn_t does not alter doc private data I don't know of any way to enforce this, but if we adhere to this requirement, we can make cib_perform_op() simpler. Signed-off-by: Reid Wahl --- include/crm/cib/internal.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 8c4b332f107..8b7f801101f 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -93,8 +93,19 @@ enum cib__op_type { cib__op_upgrade, }; -typedef int (*cib__op_fn_t)(const char *, int, const char *, xmlNode *, - xmlNode *, xmlNode **, xmlNode **); +/* A cib__op_fn_t must not alter the document private data except for adding to + * the deleted_objs list, and (*cib)->doc must point to the same value before + * and after the function call. This allows us to make the useful assumptions + * that change tracking and ACLs remain enabled if they were enabled initially, + * and that any ACLs are still unpacked in the xml_doc_private_t:acls list. + * + * *cib should be the root element of its document. A cib__op_fn_t may free and + * replace *cib, but the replacement must become the root of the original + * document. + */ +typedef int (*cib__op_fn_t)(const char *op, int options, const char *section, + xmlNode *request, xmlNode *input, xmlNode **cib, + xmlNode **output); typedef struct cib__operation_s { const char *name; From 9f569a5b949df40078f66e00f4e5b8582f71ba2c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 15:38:34 -0800 Subject: [PATCH 254/350] Refactor: libcib: cib_perform_op no longer checks tracking flag post-fn Now that we have updated all cib__op_fn_t functions so that they don't change what document the resulting *cib is part of (or unset its tracking flag), this is unnecessary. Signed-off-by: Reid Wahl --- daemons/based/based_transaction.c | 6 ++---- lib/cib/cib_file.c | 6 ++---- lib/cib/cib_utils.c | 20 -------------------- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index d6d7a2b3678..d8edadb5d9c 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -137,10 +137,8 @@ based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, /* *result_cib should be a copy of the_cib (created by cib_perform_op()). If * not, make a copy now. Change tracking isn't strictly required here - * because: - * * Each request in the transaction will have changes tracked and ACLs - * checked if appropriate. - * * cib_perform_op() will infer changes for the commit request at the end. + * because each request in the transaction will have changes tracked and + * ACLs checked if appropriate. */ CRM_CHECK((*result_cib != NULL) && (*result_cib != the_cib), *result_cib = pcmk__xml_copy(NULL, the_cib)); diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 8fc5de65a9a..1f21e12e164 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -269,10 +269,8 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) /* *result_cib should be a copy of private->cib_xml (created by * cib_perform_op()). If not, make a copy now. Change tracking isn't - * strictly required here because: - * * Each request in the transaction will have changes tracked and ACLs - * checked if appropriate. - * * cib_perform_op() will infer changes for the commit request at the end. + * strictly required here because each request in the transaction will have + * changes tracked and ACLs checked if appropriate. */ CRM_CHECK((*result_cib != NULL) && (*result_cib != private->cib_xml), *result_cib = pcmk__xml_copy(NULL, private->cib_xml)); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 2ff16820b67..aaea3dd27ab 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -524,11 +524,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, */ working_cib = *current_cib; - /* @TODO Enable tracking and ACLs and calculate changes? If working_cib - * and *current_cib point to a new object, then change tracking and - * unpacked ACLs didn't carry over to it. - */ - } else { working_cib = pcmk__xml_copy(NULL, *current_cib); old_versions = *current_cib; @@ -543,21 +538,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__log_xml_trace(req, "request"); rc = fn(op, call_options, section, req, input, &working_cib, output); - - /* @TODO This appears to be a hack to determine whether working_cib - * points to a new object now, without saving the old pointer (which may - * be invalid now) for comparison. Confirm this, and check more clearly. - */ - if (!pcmk__xml_doc_all_flags_set(working_cib->doc, pcmk__xf_tracking)) { - pcmk__trace("Inferring changes after %s op", op); - pcmk__xml_commit_changes(working_cib->doc); - if (enable_acl) { - pcmk__enable_acl(*current_cib, working_cib, user); - } - pcmk__xml_mark_changes(*current_cib, working_cib); - } - - pcmk__assert(*current_cib != working_cib); } // Allow ourselves to make any additional necessary changes From 206200c3d0a06865b9626aff44140044edcecced Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 15:52:16 -0800 Subject: [PATCH 255/350] Refactor: libcib: Deduplicate tracing in cib_perform_op() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index aaea3dd27ab..cf2df67bd5c 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -500,6 +500,10 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, input = get_op_input(req); enable_acl = cib_acl_enabled(*current_cib, user); + pcmk__trace("Processing %s for section '%s', user '%s'", op, + pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); + pcmk__log_xml_trace(req, "request"); + if (!should_copy_cib(op, section, call_options)) { // Make a copy of the top-level element to store version details top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name); @@ -512,10 +516,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__enable_acl(*current_cib, *current_cib, user); } - pcmk__trace("Processing %s for section '%s', user '%s'", op, - pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); - pcmk__log_xml_trace(req, "request"); - rc = fn(op, call_options, section, req, input, current_cib, output); /* Set working_cib to *current_cib after fn(), in case *current_cib @@ -533,10 +533,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__enable_acl(*current_cib, working_cib, user); } - pcmk__trace("Processing %s for section '%s', user '%s'", op, - pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); - pcmk__log_xml_trace(req, "request"); - rc = fn(op, call_options, section, req, input, &working_cib, output); } From d51dd1a62140a4e7d9ad3760765e907d5ef69b1f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 15:58:19 -0800 Subject: [PATCH 256/350] Refactor: libcib: cib_perform_op() takes only one CIB argument The new approach is to set the caller's result_cib pointer to the current CIB instead of to NULL. Then pass &result_cib to cib_perform_op() as its "cib" argument. * If it doesn't make a copy, then it operates on the original CIB directly and then assign the original CIB to working_cib -- just as we were already doing. * If it does make a copy, then it's copying the original CIB, just as we were already doing. * If it makes *cib point somewhere else at the end (in the "done" section), then that changes the value of the caller's result_cib, but it DOES NOT change the value of caller's current CIB pointer (the_cib in pacemaker-based or private->cib_xml in cib_file.c). So the caller still has a reference to the original CIB. The logic is arguably a bit more complicated, but previously we had five CIB pointers (current_cib, result_cib, working_cib, old_versions, top) in cib_perform_op(). Now we only have to deal with four. That feels more manageable and thus worth it IMO. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 3 ++- include/crm/cib/internal.h | 4 ++-- lib/cib/cib_file.c | 3 ++- lib/cib/cib_utils.c | 40 ++++++++++++++++----------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f91d6b8e5df..e222840195e 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -568,8 +568,9 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * It's not important whether the client variant is cib_native or * cib_remote. */ + result_cib = the_cib; rc = cib_perform_op(cib_undefined, op_function, request, &config_changed, - &the_cib, &result_cib, &cib_diff, &output); + &result_cib, &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 8b7f801101f..88333e2a195 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -193,8 +193,8 @@ int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output); + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 1f21e12e164..d26ac433d9b 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -168,8 +168,9 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) rc = cib__perform_query(op_function, request, &private->cib_xml, output); } else { + result_cib = private->cib_xml; rc = cib_perform_op(cib_file, op_function, request, &changed, - &private->cib_xml, &result_cib, &cib_diff, output); + &result_cib, &cib_diff, output); } if (pcmk__is_set(call_options, cib_transaction)) { diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index cf2df67bd5c..0e5bc3d6e04 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -463,8 +463,8 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **current_cib, - xmlNode **result_cib, xmlNode **diff, xmlNode **output) + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output) { int rc = pcmk_rc_ok; @@ -487,8 +487,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__assert((fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) - && (current_cib != NULL) && (*current_cib != NULL) - && (result_cib != NULL) && (*result_cib == NULL) + && (cib != NULL) && (*cib != NULL) && (diff != NULL) && (*diff == NULL) && (output != NULL) && (*output == NULL)); @@ -498,7 +497,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); input = get_op_input(req); - enable_acl = cib_acl_enabled(*current_cib, user); + enable_acl = cib_acl_enabled(*cib, user); pcmk__trace("Processing %s for section '%s', user '%s'", op, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); @@ -506,31 +505,30 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, if (!should_copy_cib(op, section, call_options)) { // Make a copy of the top-level element to store version details - top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name); - pcmk__xe_copy_attrs(top, *current_cib, pcmk__xaf_none); + top = pcmk__xe_create(NULL, (const char *) (*cib)->name); + pcmk__xe_copy_attrs(top, *cib, pcmk__xaf_none); old_versions = top; - pcmk__xml_commit_changes((*current_cib)->doc); - pcmk__xml_doc_set_flags((*current_cib)->doc, pcmk__xf_tracking); + pcmk__xml_commit_changes((*cib)->doc); + pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking); if (enable_acl) { - pcmk__enable_acl(*current_cib, *current_cib, user); + pcmk__enable_acl(*cib, *cib, user); } - rc = fn(op, call_options, section, req, input, current_cib, output); + rc = fn(op, call_options, section, req, input, cib, output); - /* Set working_cib to *current_cib after fn(), in case *current_cib - * points somewhere else now (for example, after a erase or full-CIB - * replace op). + /* Set working_cib to *cib after fn(), in case *cib points somewhere + * else now (for example, after a erase or full-CIB replace op). */ - working_cib = *current_cib; + working_cib = *cib; } else { - working_cib = pcmk__xml_copy(NULL, *current_cib); - old_versions = *current_cib; + working_cib = pcmk__xml_copy(NULL, *cib); + old_versions = *cib; pcmk__xml_doc_set_flags(working_cib->doc, pcmk__xf_tracking); if (enable_acl) { - pcmk__enable_acl(*current_cib, working_cib, user); + pcmk__enable_acl(*cib, working_cib, user); } rc = fn(op, call_options, section, req, input, &working_cib, output); @@ -613,15 +611,15 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, } done: - *result_cib = working_cib; + *cib = working_cib; /* @TODO This may not work correctly when !should_copy_cib(), since we don't * keep the original CIB. */ if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user) - && xml_acl_filtered_copy(user, old_versions, working_cib, result_cib)) { + && xml_acl_filtered_copy(user, old_versions, working_cib, cib)) { - if (*result_cib == NULL) { + if (*cib == NULL) { pcmk__debug("Pre-filtered the entire cib result"); } pcmk__xml_free(working_cib); From 0ca5d6ca720eb6ac3574bf911c0e8f85dca157dc Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 16:30:56 -0800 Subject: [PATCH 257/350] Refactor: libcib: Drop working_cib variable from cib_perform_op() We were setting *cib = working_cib in the "done" section. There are no early returns, so that always happens before we return. The only real change required here, is that we assign *cib to old_versions before setting *cib to a copy of itself. (Otherwise, old_versions would point to the copy.) We now have only three CIB pointers to deal with in cib_perform_op, not including saved_cib, which is used only in a small scope in order to free an object. We also have some easy opportunities to reduce duplication in an upcoming commit. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 54 ++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 0e5bc3d6e04..5d70b6b1b86 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -483,7 +483,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, xmlNode *old_versions = NULL; xmlNode *top = NULL; - xmlNode *working_cib = NULL; pcmk__assert((fn != NULL) && (req != NULL) && (config_changed != NULL) && (!*config_changed) @@ -517,36 +516,31 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, rc = fn(op, call_options, section, req, input, cib, output); - /* Set working_cib to *cib after fn(), in case *cib points somewhere - * else now (for example, after a erase or full-CIB replace op). - */ - working_cib = *cib; - } else { - working_cib = pcmk__xml_copy(NULL, *cib); old_versions = *cib; + *cib = pcmk__xml_copy(NULL, *cib); - pcmk__xml_doc_set_flags(working_cib->doc, pcmk__xf_tracking); + pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking); if (enable_acl) { - pcmk__enable_acl(*cib, working_cib, user); + pcmk__enable_acl(*cib, *cib, user); } - rc = fn(op, call_options, section, req, input, &working_cib, output); + rc = fn(op, call_options, section, req, input, cib, output); } // Allow ourselves to make any additional necessary changes - xml_acl_disable(working_cib); + xml_acl_disable(*cib); if (rc != pcmk_rc_ok) { goto done; } - if (working_cib == NULL) { + if (*cib == NULL) { rc = EINVAL; goto done; } - if (xml_acl_denied(working_cib)) { + if (xml_acl_denied(*cib)) { pcmk__trace("ACL rejected part or all of the proposed changes"); rc = EACCES; goto done; @@ -557,15 +551,15 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, * is checked elsewhere. */ if (variant != cib_file) { - rc = check_new_feature_set(working_cib); + rc = check_new_feature_set(*cib); if (rc != pcmk_rc_ok) { goto done; } } - rc = check_cib_versions(old_versions, working_cib, req, input); + rc = check_cib_versions(old_versions, *cib, req, input); - pcmk__strip_xml_text(working_cib); + pcmk__strip_xml_text(*cib); if (pcmk__xe_attr_is_true(req, PCMK__XA_CIB_UPDATE)) { /* This is a replace operation as a reply to a sync request. Keep @@ -577,13 +571,13 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, /* If we didn't make a copy, the diff will only be accurate for the * top-level PCMK_XE_CIB element */ - *diff = xml_create_patchset(0, old_versions, working_cib, config_changed, + *diff = xml_create_patchset(0, old_versions, *cib, config_changed, manage_version); /* pcmk__xml_commit_changes() resets document private data, so call it even * if there were no changes. */ - pcmk__xml_commit_changes(working_cib->doc); + pcmk__xml_commit_changes((*cib)->doc); if (*diff == NULL) { goto done; @@ -591,12 +585,12 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__log_xml_patchset(LOG_INFO, *diff); - /* working_cib must not be modified after this point, except for the - * attributes for which pcmk__xa_filterable() returns true + /* *cib must not be modified after this point, except for the attributes for + * which pcmk__xa_filterable() returns true */ if (*config_changed && !pcmk__is_set(call_options, cib_no_mtime)) { - rc = set_update_origin(working_cib, req); + rc = set_update_origin(*cib, req); if (rc != pcmk_rc_ok) { goto done; } @@ -605,24 +599,24 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, // Skip validation for status-only updates, since we allow anything there if ((rc == pcmk_rc_ok) && !pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei) - && !pcmk__configured_schema_validates(working_cib)) { + && !pcmk__configured_schema_validates(*cib)) { rc = pcmk_rc_schema_validation; } done: - *cib = working_cib; - /* @TODO This may not work correctly when !should_copy_cib(), since we don't * keep the original CIB. */ - if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user) - && xml_acl_filtered_copy(user, old_versions, working_cib, cib)) { - - if (*cib == NULL) { - pcmk__debug("Pre-filtered the entire cib result"); + if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user)) { + xmlNode *saved_cib = *cib; + + if (xml_acl_filtered_copy(user, old_versions, *cib, cib)) { + if (*cib == NULL) { + pcmk__debug("Pre-filtered the entire cib result"); + } + pcmk__xml_free(saved_cib); } - pcmk__xml_free(working_cib); } pcmk__xml_free(top); From ec599d87f72ca46239c9db466f0ab8a19dfb800b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 16:41:09 -0800 Subject: [PATCH 258/350] Refactor: libcib: Deduplicate enable-ACL and fn call in cib_perform_op() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 5d70b6b1b86..ebcad196610 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -508,26 +508,19 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__xe_copy_attrs(top, *cib, pcmk__xaf_none); old_versions = top; - pcmk__xml_commit_changes((*cib)->doc); - pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking); - if (enable_acl) { - pcmk__enable_acl(*cib, *cib, user); - } - - rc = fn(op, call_options, section, req, input, cib, output); - } else { old_versions = *cib; *cib = pcmk__xml_copy(NULL, *cib); + } - pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking); - if (enable_acl) { - pcmk__enable_acl(*cib, *cib, user); - } - - rc = fn(op, call_options, section, req, input, cib, output); + pcmk__xml_commit_changes((*cib)->doc); + pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking); + if (enable_acl) { + pcmk__enable_acl(*cib, *cib, user); } + rc = fn(op, call_options, section, req, input, cib, output); + // Allow ourselves to make any additional necessary changes xml_acl_disable(*cib); From b5035abf566f2016870265548d7053b0dfef8491 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 17:28:33 -0800 Subject: [PATCH 259/350] Refactor: libcib: Drop op arg from cib__op_fn_t The request argument provides the op value for functions that need it. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 59 +++++++++++++-------------------- daemons/based/based_messages.h | 60 ++++++++++++++-------------------- include/crm/cib/internal.h | 50 ++++++++++++---------------- lib/cib/cib_file.c | 5 ++- lib/cib/cib_ops.c | 52 +++++++++++++++-------------- lib/cib/cib_utils.c | 7 ++-- 6 files changed, 99 insertions(+), 134 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 47cfb37b132..d4a19795757 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -37,7 +37,6 @@ xmlNode *the_cib = NULL; * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * - * \param[in] op Ignored * \param[in] options Ignored * \param[in] section Ignored * \param[in] req Ignored @@ -50,9 +49,8 @@ xmlNode *the_cib = NULL; * \note This is unimplemented and simply returns an error. */ int -based_process_abs_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_abs_delete(int options, const char *section, xmlNode *req, + xmlNode *input, 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. @@ -61,9 +59,8 @@ based_process_abs_delete(const char *op, int options, const char *section, } int -based_process_commit_transact(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_commit_transact(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace * notification if appropriate, and sync *cib to all nodes. On failure, our @@ -87,9 +84,8 @@ based_process_commit_transact(const char *op, int options, const char *section, } int -based_process_is_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_is_primary(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -97,18 +93,16 @@ based_process_is_primary(const char *op, int options, const char *section, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -based_process_noop(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_noop(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { *answer = NULL; return pcmk_rc_ok; } int -based_process_ping(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_ping(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); @@ -142,9 +136,8 @@ based_process_ping(const char *op, int options, const char *section, } int -based_process_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_primary(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -158,9 +151,8 @@ based_process_primary(const char *op, int options, const char *section, } int -based_process_schemas(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_schemas(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { xmlNode *wrapper = NULL; xmlNode *data = NULL; @@ -204,9 +196,8 @@ based_process_schemas(const char *op, int options, const char *section, } int -based_process_secondary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_secondary(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -220,9 +211,8 @@ based_process_secondary(const char *op, int options, const char *section, } int -based_process_shutdown(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_shutdown(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -244,17 +234,15 @@ based_process_shutdown(const char *op, int options, const char *section, } int -based_process_sync(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_sync(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { return sync_our_cib(req, true); } int -based_process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_upgrade(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; @@ -266,8 +254,7 @@ based_process_upgrade(const char *op, int options, const char *section, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(op, options, section, req, input, cib, - answer); + return cib__process_upgrade(options, section, req, input, cib, answer); } else { xmlNode *scratch = pcmk__xml_copy(NULL, *cib); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index a3e986536d2..34911a3299d 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,54 +17,42 @@ extern bool based_is_primary; extern xmlNode *the_cib; -int based_process_abs_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_abs_delete(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_apply_patch(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_commit_transact(const char *op, int options, - const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, +int based_process_commit_transact(int options, const char *section, + xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_is_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_is_primary(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_noop(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_noop(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_ping(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_ping(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_primary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_primary(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_schemas(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_schemas(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_secondary(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_secondary(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_shutdown(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_shutdown(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_sync(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_sync(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_upgrade(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); int sync_our_cib(xmlNode *request, bool all); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 88333e2a195..43371591f45 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -103,9 +103,8 @@ enum cib__op_type { * replace *cib, but the replacement must become the root of the original * document. */ -typedef int (*cib__op_fn_t)(const char *op, int options, const char *section, - xmlNode *request, xmlNode *input, xmlNode **cib, - xmlNode **output); +typedef int (*cib__op_fn_t)(int options, const char *section, xmlNode *request, + xmlNode *input, xmlNode **cib, xmlNode **output); typedef struct cib__operation_s { const char *name; @@ -208,41 +207,32 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); -int cib__process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_apply_patch(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_bump(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_bump(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_create(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_create(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_delete(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_erase(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_erase(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_modify(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_modify(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_query(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_query(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_replace(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int cib__process_upgrade(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_upgrade(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index d26ac433d9b..8e632f6f04d 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -304,9 +304,8 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) } static int -process_commit_transact(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib_xml, - xmlNode **answer) +process_commit_transact(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib_xml, xmlNode **answer) { int rc = pcmk_rc_ok; const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 66281a2df94..aee2e60c27a 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -163,9 +163,8 @@ cib__get_operation(const char *op, const cib__operation_t **operation) } int -cib__process_apply_patch(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_apply_patch(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { int rc = xml_apply_patchset(*cib, input, true); @@ -190,8 +189,8 @@ update_counter(xmlNode *xml, const char *field, bool reset) } int -cib__process_bump(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_bump(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; @@ -291,10 +290,10 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, } int -cib__process_create(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_create(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); xmlNode *failed = NULL; int rc = pcmk_rc_ok; xmlNode *update_section = NULL; @@ -311,8 +310,7 @@ cib__process_create(const char *op, int options, const char *section, if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) || pcmk__xe_is(input, PCMK_XE_CIB)) { - return cib__process_modify(op, options, NULL, req, input, cib, - answer); + return cib__process_modify(options, NULL, req, input, cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -461,11 +459,12 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) } int -cib__process_delete(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_delete(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + return process_delete_xpath(op, options, section, *cib); } @@ -473,9 +472,8 @@ cib__process_delete(const char *op, int options, const char *section, } int -cib__process_erase(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_erase(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -596,11 +594,12 @@ process_modify_section(int options, const char *section, xmlNode *input, } int -cib__process_modify(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_modify(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + return process_modify_xpath(op, options, section, input, *cib); } @@ -730,10 +729,12 @@ process_query_section(int options, const char *section, xmlNode *cib, } int -cib__process_query(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_query(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + return process_query_xpath(op, options, section, *cib, answer); } @@ -911,11 +912,12 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } int -cib__process_replace(const char *op, int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_replace(int options, const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer) { if (pcmk__is_set(options, cib_xpath)) { + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + return process_replace_xpath(op, options, section, req, input, cib); } @@ -923,7 +925,7 @@ cib__process_replace(const char *op, int options, const char *section, } int -cib__process_upgrade(const char *op, int options, const char *section, +cib__process_upgrade(int options, const char *section, xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index ebcad196610..0ce97e1b1c2 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -226,7 +226,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = fn(op, call_options, section, req, input, &cib, output); + rc = fn(call_options, section, req, input, &cib, output); if (*output == NULL) { // Do nothing @@ -519,7 +519,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__enable_acl(*cib, *cib, user); } - rc = fn(op, call_options, section, req, input, cib, output); + rc = fn(call_options, section, req, input, cib, output); // Allow ourselves to make any additional necessary changes xml_acl_disable(*cib); @@ -872,8 +872,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, output, - NULL); + rc = cib__process_apply_patch(cib_none, NULL, event, diff, output, NULL); rc = pcmk_rc2legacy(rc); if (rc == pcmk_ok) { return pcmk_ok; From 91894284c5b3f771209a8ca6f59724c0c8046882 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 17:42:13 -0800 Subject: [PATCH 260/350] Refactor: libcib: Drop options arg from cib__op_fn_t The request argument provides the options value for functions that need it. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 47 +++++++++++++-------------- daemons/based/based_messages.h | 48 +++++++++++++-------------- include/crm/cib/internal.h | 38 +++++++++++----------- lib/cib/cib_file.c | 4 +-- lib/cib/cib_ops.c | 59 ++++++++++++++++++++++------------ lib/cib/cib_utils.c | 6 ++-- 6 files changed, 110 insertions(+), 92 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d4a19795757..028a4c2acfc 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -37,7 +37,6 @@ xmlNode *the_cib = NULL; * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * - * \param[in] options Ignored * \param[in] section Ignored * \param[in] req Ignored * \param[in] input Ignored @@ -49,8 +48,8 @@ xmlNode *the_cib = NULL; * \note This is unimplemented and simply returns an error. */ int -based_process_abs_delete(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_abs_delete(const char *section, xmlNode *req, xmlNode *input, + 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. @@ -59,8 +58,8 @@ based_process_abs_delete(int options, const char *section, xmlNode *req, } int -based_process_commit_transact(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_commit_transact(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace * notification if appropriate, and sync *cib to all nodes. On failure, our @@ -84,8 +83,8 @@ based_process_commit_transact(int options, const char *section, xmlNode *req, } int -based_process_is_primary(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_is_primary(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -93,16 +92,16 @@ based_process_is_primary(int options, const char *section, xmlNode *req, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -based_process_noop(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_noop(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { *answer = NULL; return pcmk_rc_ok; } int -based_process_ping(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_ping(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); @@ -136,8 +135,8 @@ based_process_ping(int options, const char *section, xmlNode *req, } int -based_process_primary(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_primary(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -151,8 +150,8 @@ based_process_primary(int options, const char *section, xmlNode *req, } int -based_process_schemas(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_schemas(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { xmlNode *wrapper = NULL; xmlNode *data = NULL; @@ -196,8 +195,8 @@ based_process_schemas(int options, const char *section, xmlNode *req, } int -based_process_secondary(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_secondary(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -211,8 +210,8 @@ based_process_secondary(int options, const char *section, xmlNode *req, } int -based_process_shutdown(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_shutdown(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -234,15 +233,15 @@ based_process_shutdown(int options, const char *section, xmlNode *req, } int -based_process_sync(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_sync(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { return sync_our_cib(req, true); } int -based_process_upgrade(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +based_process_upgrade(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; @@ -254,7 +253,7 @@ based_process_upgrade(int options, const char *section, xmlNode *req, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(options, section, req, input, cib, answer); + return cib__process_upgrade(section, req, input, cib, answer); } else { xmlNode *scratch = pcmk__xml_copy(NULL, *cib); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 34911a3299d..e6e95874c48 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,42 +17,42 @@ extern bool based_is_primary; extern xmlNode *the_cib; -int based_process_abs_delete(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_abs_delete(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_apply_patch(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_apply_patch(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_commit_transact(int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, +int based_process_commit_transact(const char *section, xmlNode *req, + xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_is_primary(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_is_primary(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_noop(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_noop(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_ping(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_ping(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_primary(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_primary(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_schemas(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_schemas(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_secondary(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_secondary(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_shutdown(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_shutdown(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_sync(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_sync(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int based_process_upgrade(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int based_process_upgrade(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); int sync_our_cib(xmlNode *request, bool all); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 43371591f45..79cf76bb12b 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -103,7 +103,7 @@ enum cib__op_type { * replace *cib, but the replacement must become the root of the original * document. */ -typedef int (*cib__op_fn_t)(int options, const char *section, xmlNode *request, +typedef int (*cib__op_fn_t)(const char *section, xmlNode *request, xmlNode *input, xmlNode **cib, xmlNode **output); typedef struct cib__operation_s { @@ -207,32 +207,32 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); -int cib__process_apply_patch(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_apply_patch(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_bump(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_bump(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_create(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_create(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_delete(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_delete(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_erase(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_erase(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_modify(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_modify(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_query(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_query(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_replace(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_replace(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); -int cib__process_upgrade(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer); +int cib__process_upgrade(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 8e632f6f04d..b9674a2a41c 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -304,8 +304,8 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) } static int -process_commit_transact(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib_xml, xmlNode **answer) +process_commit_transact(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib_xml, xmlNode **answer) { int rc = pcmk_rc_ok; const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index aee2e60c27a..4b7ae924ec0 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -10,6 +10,7 @@ #include #include +#include // uint32_t #include #include #include @@ -163,8 +164,8 @@ cib__get_operation(const char *op, const cib__operation_t **operation) } int -cib__process_apply_patch(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_apply_patch(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { int rc = xml_apply_patchset(*cib, input, true); @@ -189,8 +190,8 @@ update_counter(xmlNode *xml, const char *field, bool reset) } int -cib__process_bump(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_bump(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; @@ -290,8 +291,8 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, } int -cib__process_create(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_create(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); xmlNode *failed = NULL; @@ -310,7 +311,7 @@ cib__process_create(int options, const char *section, xmlNode *req, if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) || pcmk__xe_is(input, PCMK_XE_CIB)) { - return cib__process_modify(options, NULL, req, input, cib, answer); + return cib__process_modify(NULL, req, input, cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -459,9 +460,13 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) } int -cib__process_delete(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_delete(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { + uint32_t options = cib_none; + + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_xpath)) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); @@ -472,8 +477,8 @@ cib__process_delete(int options, const char *section, xmlNode *req, } int -cib__process_erase(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_erase(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -594,9 +599,13 @@ process_modify_section(int options, const char *section, xmlNode *input, } int -cib__process_modify(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_modify(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { + uint32_t options = cib_none; + + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_xpath)) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); @@ -729,9 +738,13 @@ process_query_section(int options, const char *section, xmlNode *cib, } int -cib__process_query(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_query(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { + uint32_t options = cib_none; + + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_xpath)) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); @@ -912,9 +925,13 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } int -cib__process_replace(int options, const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_replace(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { + uint32_t options = cib_none; + + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_xpath)) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); @@ -925,16 +942,18 @@ cib__process_replace(int options, const char *section, xmlNode *req, } int -cib__process_upgrade(int options, const char *section, - xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_upgrade(const char *section, xmlNode *req, xmlNode *input, + xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; + uint32_t options = cib_none; const char *max_schema = pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX); const char *original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); const char *new_schema = NULL; xmlNode *updated = pcmk__xml_copy(NULL, *cib); + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); + rc = pcmk__update_schema(&updated, max_schema, true, !pcmk__is_set(options, cib_verbose)); *cib = pcmk__xml_replace_with_copy(*cib, updated); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 0ce97e1b1c2..18ebb656111 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -226,7 +226,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = fn(call_options, section, req, input, &cib, output); + rc = fn(section, req, input, &cib, output); if (*output == NULL) { // Do nothing @@ -519,7 +519,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__enable_acl(*cib, *cib, user); } - rc = fn(call_options, section, req, input, cib, output); + rc = fn(section, req, input, cib, output); // Allow ourselves to make any additional necessary changes xml_acl_disable(*cib); @@ -872,7 +872,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(cib_none, NULL, event, diff, output, NULL); + rc = cib__process_apply_patch(NULL, event, diff, output, NULL); rc = pcmk_rc2legacy(rc); if (rc == pcmk_ok) { return pcmk_ok; From c364a76f2719bc591161e5f5b3a01805e5acfc1f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 18:00:54 -0800 Subject: [PATCH 261/350] Refactor: libcib: Drop section arg from cib__op_fn_t The request argument provides the section value for functions that need it. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 47 ++++++++++++++++---------------- daemons/based/based_messages.h | 47 ++++++++++++++++---------------- include/crm/cib/internal.h | 40 +++++++++++++-------------- lib/cib/cib_file.c | 4 +-- lib/cib/cib_ops.c | 50 ++++++++++++++++++++-------------- lib/cib/cib_utils.c | 6 ++-- 6 files changed, 101 insertions(+), 93 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 028a4c2acfc..d053f4822a8 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -37,7 +37,6 @@ xmlNode *the_cib = NULL; * \internal * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * - * \param[in] section Ignored * \param[in] req Ignored * \param[in] input Ignored * \param[in] cib Ignored @@ -48,8 +47,8 @@ xmlNode *the_cib = NULL; * \note This is unimplemented and simply returns an error. */ int -based_process_abs_delete(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_abs_delete(xmlNode *req, xmlNode *input, 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. @@ -58,8 +57,8 @@ based_process_abs_delete(const char *section, xmlNode *req, xmlNode *input, } int -based_process_commit_transact(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace * notification if appropriate, and sync *cib to all nodes. On failure, our @@ -83,8 +82,8 @@ based_process_commit_transact(const char *section, xmlNode *req, xmlNode *input, } int -based_process_is_primary(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -92,16 +91,16 @@ based_process_is_primary(const char *section, xmlNode *req, xmlNode *input, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -based_process_noop(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { *answer = NULL; return pcmk_rc_ok; } int -based_process_ping(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); @@ -135,8 +134,8 @@ based_process_ping(const char *section, xmlNode *req, xmlNode *input, } int -based_process_primary(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -150,8 +149,8 @@ based_process_primary(const char *section, xmlNode *req, xmlNode *input, } int -based_process_schemas(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { xmlNode *wrapper = NULL; xmlNode *data = NULL; @@ -195,8 +194,8 @@ based_process_schemas(const char *section, xmlNode *req, xmlNode *input, } int -based_process_secondary(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -210,8 +209,8 @@ based_process_secondary(const char *section, xmlNode *req, xmlNode *input, } int -based_process_shutdown(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -233,15 +232,15 @@ based_process_shutdown(const char *section, xmlNode *req, xmlNode *input, } int -based_process_sync(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { return sync_our_cib(req, true); } int -based_process_upgrade(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { int rc = pcmk_rc_ok; @@ -253,7 +252,7 @@ based_process_upgrade(const char *section, xmlNode *req, xmlNode *input, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(section, req, input, cib, answer); + return cib__process_upgrade(req, input, cib, answer); } else { xmlNode *scratch = pcmk__xml_copy(NULL, *cib); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index e6e95874c48..fddb943e086 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,42 +17,41 @@ extern bool based_is_primary; extern xmlNode *the_cib; -int based_process_abs_delete(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_apply_patch(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_commit_transact(const char *section, xmlNode *req, - xmlNode *input, xmlNode **cib, +int based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); -int based_process_is_primary(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_noop(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_ping(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_primary(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_schemas(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_secondary(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_shutdown(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_sync(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int based_process_upgrade(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int sync_our_cib(xmlNode *request, bool all); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 79cf76bb12b..fe7deea58ff 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -103,8 +103,8 @@ enum cib__op_type { * replace *cib, but the replacement must become the root of the original * document. */ -typedef int (*cib__op_fn_t)(const char *section, xmlNode *request, - xmlNode *input, xmlNode **cib, xmlNode **output); +typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode *input, xmlNode **cib, + xmlNode **output); typedef struct cib__operation_s { const char *name; @@ -207,32 +207,32 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); -int cib__process_apply_patch(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_bump(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_create(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_delete(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_erase(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_modify(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_query(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_replace(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); -int cib__process_upgrade(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer); +int cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index b9674a2a41c..768919363c2 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -304,8 +304,8 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) } static int -process_commit_transact(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib_xml, xmlNode **answer) +process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib_xml, + xmlNode **answer) { int rc = pcmk_rc_ok; const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 4b7ae924ec0..e7ff85c50de 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -164,8 +164,8 @@ cib__get_operation(const char *op, const cib__operation_t **operation) } int -cib__process_apply_patch(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { int rc = xml_apply_patchset(*cib, input, true); @@ -190,8 +190,7 @@ update_counter(xmlNode *xml, const char *field, bool reset) } int -cib__process_bump(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; @@ -291,10 +290,11 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, } int -cib__process_create(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); xmlNode *failed = NULL; int rc = pcmk_rc_ok; xmlNode *update_section = NULL; @@ -311,7 +311,7 @@ cib__process_create(const char *section, xmlNode *req, xmlNode *input, if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) || pcmk__xe_is(input, PCMK_XE_CIB)) { - return cib__process_modify(NULL, req, input, cib, answer); + return cib__process_modify(req, input, cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -460,9 +460,10 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) } int -cib__process_delete(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { + const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -477,8 +478,8 @@ cib__process_delete(const char *section, xmlNode *req, xmlNode *input, } int -cib__process_erase(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -511,10 +512,16 @@ process_modify_xpath(const char *op, int options, const char *xpath, { int num_results = 0; int rc = pcmk_rc_ok; - xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + xmlXPathObject *xpath_obj = NULL; const bool score = pcmk__is_set(options, cib_score_update); const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + if (xpath == NULL) { + xpath = pcmk__cib_abs_xpath_for(PCMK_XE_CIB); + } + + xpath_obj = pcmk__xpath_search(cib->doc, xpath); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -599,9 +606,10 @@ process_modify_section(int options, const char *section, xmlNode *input, } int -cib__process_modify(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { + const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -738,9 +746,10 @@ process_query_section(int options, const char *section, xmlNode *cib, } int -cib__process_query(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { + const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -925,9 +934,10 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } int -cib__process_replace(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { + const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -942,8 +952,8 @@ cib__process_replace(const char *section, xmlNode *req, xmlNode *input, } int -cib__process_upgrade(const char *section, xmlNode *req, xmlNode *input, - xmlNode **cib, xmlNode **answer) +cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { int rc = pcmk_rc_ok; uint32_t options = cib_none; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 18ebb656111..cc80ff5bf76 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -226,7 +226,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = fn(section, req, input, &cib, output); + rc = fn(req, input, &cib, output); if (*output == NULL) { // Do nothing @@ -519,7 +519,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__enable_acl(*cib, *cib, user); } - rc = fn(section, req, input, cib, output); + rc = fn(req, input, cib, output); // Allow ourselves to make any additional necessary changes xml_acl_disable(*cib); @@ -872,7 +872,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(NULL, event, diff, output, NULL); + rc = cib__process_apply_patch(event, diff, output, NULL); rc = pcmk_rc2legacy(rc); if (rc == pcmk_ok) { return pcmk_ok; From 2c8703364dda286cacac09d3bc20ee324a3696fa Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 21:23:59 -0800 Subject: [PATCH 262/350] Refactor: libcib: New cib__get_calldata() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 ++-------- daemons/based/based_messages.c | 4 +--- include/crm/cib/internal.h | 1 + lib/cib/cib_native.c | 4 +--- lib/cib/cib_remote.c | 4 +--- lib/cib/cib_utils.c | 17 ++++++++--------- 6 files changed, 14 insertions(+), 26 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index e222840195e..e1f17ce9629 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -200,10 +200,7 @@ process_ping_reply(xmlNode *reply) uint64_t seq = 0; const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); - xmlNode *wrapper = pcmk__xe_first_child(reply, PCMK__XE_CIB_CALLDATA, NULL, - NULL); - xmlNode *pong = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); - + xmlNode *pong = cib__get_calldata(reply); const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); @@ -245,10 +242,7 @@ process_ping_reply(xmlNode *reply) pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *wrapper = pcmk__xe_first_child(pong, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *remote_versions = pcmk__xe_first_child(wrapper, NULL, NULL, - NULL); + xmlNode *remote_versions = cib__get_calldata(pong); const char *admin_epoch_s = NULL; const char *epoch_s = NULL; diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index d053f4822a8..b6e6a68c31b 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -152,7 +152,6 @@ int based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { - xmlNode *wrapper = NULL; xmlNode *data = NULL; const char *after_ver = NULL; @@ -161,8 +160,7 @@ based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); - wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL); - data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + data = cib__get_calldata(req); if (data == NULL) { pcmk__warn("No data specified in request"); return EPROTO; diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index fe7deea58ff..2c1c0ed057e 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -187,6 +187,7 @@ cib__client_triggers_refresh(const char *name) } int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); +xmlNode *cib__get_calldata(const xmlNode *request); int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index b4ecf4d919c..d7876f7488c 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -103,9 +103,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host, rc = pcmk_ok; pcmk__xe_get_int(op_reply, PCMK__XA_CIB_CALLID, &reply_id); if (reply_id == cib->call_id) { - xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *tmp = cib__get_calldata(op_reply); pcmk__trace("Synchronous reply %d received", reply_id); if (pcmk__xe_get_int(op_reply, PCMK__XA_CIB_RC, &rc) != pcmk_rc_ok) { diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c index 752a326425c..6f0b95d89e6 100644 --- a/lib/cib/cib_remote.c +++ b/lib/cib/cib_remote.c @@ -175,9 +175,7 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host, /* do nothing more */ } else if (!(call_options & cib_discard_reply)) { - xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA, - NULL, NULL); - xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + xmlNode *tmp = cib__get_calldata(op_reply); if (tmp == NULL) { pcmk__trace("No output in reply to \"%s\" command %d", op, diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index cc80ff5bf76..606e1becef3 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -171,12 +171,14 @@ cib_acl_enabled(xmlNode *xml, const char *user) /*! * \internal - * \brief Get input data from a CIB request + * \brief Get call data from a CIB request * * \param[in] request CIB request XML + * + * \return Call data, or \c NULL if none is found */ -static xmlNode * -get_op_input(const xmlNode *request) +xmlNode * +cib__get_calldata(const xmlNode *request) { xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL, NULL); @@ -207,7 +209,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = get_op_input(req); + input = cib__get_calldata(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -495,7 +497,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = get_op_input(req); + input = cib__get_calldata(req); enable_acl = cib_acl_enabled(*cib, user); pcmk__trace("Processing %s for section '%s', user '%s'", op, @@ -733,12 +735,9 @@ cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc) cib_callback_client_t *blob = NULL; if (msg != NULL) { - xmlNode *wrapper = NULL; - pcmk__xe_get_int(msg, PCMK__XA_CIB_RC, &rc); pcmk__xe_get_int(msg, PCMK__XA_CIB_CALLID, &call_id); - wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL); - output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL); + output = cib__get_calldata(msg); } blob = cib__lookup_id(call_id); From f7b19f34bc2cef39b4d4e0ea9279f496d3b8bf93 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 21:33:52 -0800 Subject: [PATCH 263/350] Refactor: libcib: New cib__set_calldata() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 8 +------- daemons/based/based_messages.c | 13 ++++--------- include/crm/cib/internal.h | 1 + lib/cib/cib_utils.c | 32 +++++++++++++++++++++++++------- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index e1f17ce9629..c6244a631f1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -73,13 +73,7 @@ create_cib_reply(const char *op, const char *call_id, const char *client_id, pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, rc); - - if (call_data != NULL) { - xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CIB_CALLDATA); - - pcmk__trace("Attaching reply output"); - pcmk__xml_copy(wrapper, call_data); - } + cib__set_calldata(reply, call_data); crm_log_xml_explicit(reply, "cib:reply"); return reply; diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index b6e6a68c31b..04062ff2108 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -106,21 +106,18 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); char *digest = pcmk__digest_xml(the_cib, true); - xmlNode *wrapper = NULL; - *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); pcmk__xe_set(*answer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); pcmk__xe_set(*answer, PCMK_XA_DIGEST, digest); pcmk__xe_set(*answer, PCMK__XA_CIB_PING_ID, seq); - wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA); - if (the_cib != NULL) { - xmlNode *shallow = pcmk__xe_create(wrapper, - (const char *) the_cib->name); + xmlNode *shallow = pcmk__xe_create(NULL, (const char *) the_cib->name); pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none); + cib__set_calldata(*answer, shallow); + pcmk__xml_free(shallow); } pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", @@ -370,7 +367,6 @@ sync_our_cib(xmlNode *request, bool all) const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); pcmk__node_status_t *peer = NULL; xmlNode *replace_request = NULL; - xmlNode *wrapper = NULL; CRM_CHECK(the_cib != NULL, return EINVAL); CRM_CHECK(all || (host != NULL), return EINVAL); @@ -397,8 +393,7 @@ sync_our_cib(xmlNode *request, bool all) digest = pcmk__digest_xml(the_cib, true); pcmk__xe_set(replace_request, PCMK_XA_DIGEST, digest); - wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA); - pcmk__xml_copy(wrapper, the_cib); + cib__set_calldata(replace_request, the_cib); if (!all) { peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 2c1c0ed057e..7db97640ae8 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -188,6 +188,7 @@ cib__client_triggers_refresh(const char *name) int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); xmlNode *cib__get_calldata(const xmlNode *request); +void cib__set_calldata(xmlNode *request, xmlNode *data); int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 606e1becef3..ade61ca5f65 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -175,7 +175,8 @@ cib_acl_enabled(xmlNode *xml, const char *user) * * \param[in] request CIB request XML * - * \return Call data, or \c NULL if none is found + * \return Call data added by \c cib__set_calldata(), or \c NULL if none is + * found */ xmlNode * cib__get_calldata(const xmlNode *request) @@ -186,6 +187,28 @@ cib__get_calldata(const xmlNode *request) return pcmk__xe_first_child(wrapper, NULL, NULL, NULL); } +/*! + * \internal + * \brief Add call data to a CIB request + * + * Add a copy of \p data to a new \c PCMK__XE_CIB_CALLDATA child of \p request. + * + * \param[in,out] request CIB request XML + * \param[in] data Call data to add a copy of (if \c NULL, do nothing) + */ +void +cib__set_calldata(xmlNode *request, xmlNode *data) +{ + xmlNode *wrapper = NULL; + + if (data == NULL) { + return; + } + + wrapper = pcmk__xe_create(request, PCMK__XE_CIB_CALLDATA); + pcmk__xml_copy(wrapper, data); +} + int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output) @@ -645,12 +668,7 @@ cib__create_op(cib_t *cib, const char *op, const char *host, pcmk__trace("Sending call options: %.8lx, %d", (long) call_options, call_options); pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options); - - if (data != NULL) { - xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA); - - pcmk__xml_copy(wrapper, data); - } + cib__set_calldata(*op_msg, data); return pcmk_rc_ok; } From 1cecb71ac5a67c27b332ed81b05347e7f80f7953 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 22:09:22 -0800 Subject: [PATCH 264/350] Refactor: libcib: Call xml_apply_patchset() in cib_apply_patch_event() ...directly, instead of calling cib__process_apply_patch(). This will make an upcoming commit simpler. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index ade61ca5f65..1a36fec94b2 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -889,8 +889,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, *output = pcmk__xml_copy(NULL, input); } - rc = cib__process_apply_patch(event, diff, output, NULL); - rc = pcmk_rc2legacy(rc); + rc = xml_apply_patchset(*output, diff, true); if (rc == pcmk_ok) { return pcmk_ok; } From 850907fda41731f446aafe0157965582970a1924 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 22:13:48 -0800 Subject: [PATCH 265/350] Refactor: libcib: Drop input arg from cib__op_fn_t The request argument provides the input value for functions that need it, via cib__get_calldata(). Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 37 ++++++++++----------------- daemons/based/based_messages.h | 46 +++++++++------------------------- include/crm/cib/internal.h | 38 ++++++++-------------------- lib/cib/cib_file.c | 4 +-- lib/cib/cib_ops.c | 33 +++++++++++------------- lib/cib/cib_utils.c | 6 ++--- 6 files changed, 54 insertions(+), 110 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 04062ff2108..6082afd36be 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -38,7 +38,6 @@ xmlNode *the_cib = NULL; * \brief Process a \c PCMK__CIB_REQUEST_ABS_DELETE * * \param[in] req Ignored - * \param[in] input Ignored * \param[in] cib Ignored * \param[in] answer Ignored * @@ -47,8 +46,7 @@ xmlNode *the_cib = NULL; * \note This is unimplemented and simply returns an error. */ int -based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* @COMPAT Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed. Note that * external clients with Pacemaker versions < 3.0.0 can send it. @@ -57,14 +55,14 @@ based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace * notification if appropriate, and sync *cib to all nodes. On failure, our * caller will free *cib. */ int rc = pcmk_rc_ok; + xmlNode *input = cib__get_calldata(req); const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); const char *origin = pcmk__xe_get(req, PCMK__XA_SRC); pcmk__client_t *client = pcmk__find_client_by_id(client_id); @@ -82,8 +80,7 @@ based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this return (based_is_primary? pcmk_rc_ok : EPERM); @@ -91,16 +88,14 @@ based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, // @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed int -based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) { *answer = NULL; return pcmk_rc_ok; } int -based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); @@ -131,8 +126,7 @@ based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) { if (!based_is_primary) { pcmk__info("We are now in R/W mode"); @@ -146,8 +140,7 @@ based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer) { xmlNode *data = NULL; @@ -189,8 +182,7 @@ based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer) { if (based_is_primary) { pcmk__info("We are now in R/O mode"); @@ -204,8 +196,7 @@ based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -227,15 +218,13 @@ based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, } int -based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer) { return sync_our_cib(req, true); } int -based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; @@ -247,7 +236,7 @@ based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node * performs the upgrade (and notifies its local clients) here. */ - return cib__process_upgrade(req, input, cib, answer); + return cib__process_upgrade(req, cib, answer); } else { xmlNode *scratch = pcmk__xml_copy(NULL, *cib); diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index fddb943e086..78d0d7e5218 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,41 +17,19 @@ extern bool based_is_primary; extern xmlNode *the_cib; -int based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, +int based_process_abs_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_commit_transact(xmlNode *req, xmlNode **cib, xmlNode **answer); - -int based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_sync(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_primary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_schemas(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_secondary(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer); +int based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); int sync_our_cib(xmlNode *request, bool all); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 7db97640ae8..fb04ee4ab09 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -103,8 +103,7 @@ enum cib__op_type { * replace *cib, but the replacement must become the root of the original * document. */ -typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode *input, xmlNode **cib, - xmlNode **output); +typedef int (*cib__op_fn_t)(xmlNode *request, xmlNode **cib, xmlNode **output); typedef struct cib__operation_s { const char *name; @@ -209,32 +208,15 @@ void cib_native_notify(gpointer data, gpointer user_data); int cib__get_operation(const char *op, const cib__operation_t **operation); -int cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); - -int cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer); +int cib__process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_bump(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer); +int cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); int cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 768919363c2..27efd3647a9 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -304,10 +304,10 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) } static int -process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib_xml, - xmlNode **answer) +process_commit_transact(xmlNode *req, xmlNode **cib_xml, xmlNode **answer) { int rc = pcmk_rc_ok; + xmlNode *input = cib__get_calldata(req); const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); cib_t *cib = NULL; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index e7ff85c50de..b27ad90ee4d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -164,9 +164,9 @@ cib__get_operation(const char *op, const cib__operation_t **operation) } int -cib__process_apply_patch(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_apply_patch(xmlNode *req, xmlNode **cib, xmlNode **answer) { + const xmlNode *input = cib__get_calldata(req); int rc = xml_apply_patchset(*cib, input, true); return pcmk_legacy2rc(rc); @@ -190,7 +190,7 @@ update_counter(xmlNode *xml, const char *field, bool reset) } int -cib__process_bump(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) +cib__process_bump(xmlNode *req, xmlNode **cib, xmlNode **answer) { update_counter(*cib, PCMK_XA_EPOCH, false); return pcmk_rc_ok; @@ -290,11 +290,11 @@ process_create_xpath(const char *op, const char *xpath, xmlNode *input, } int -cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); xmlNode *failed = NULL; int rc = pcmk_rc_ok; xmlNode *update_section = NULL; @@ -311,7 +311,7 @@ cib__process_create(xmlNode *req, xmlNode *input, xmlNode **cib, if (pcmk__strcase_any_of(section, PCMK__XE_ALL, PCMK_XE_CIB, NULL) || pcmk__xe_is(input, PCMK_XE_CIB)) { - return cib__process_modify(req, input, cib, answer); + return cib__process_modify(req, cib, answer); } // @COMPAT Deprecated since 2.1.8 @@ -460,10 +460,10 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) } int -cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -478,8 +478,7 @@ cib__process_delete(xmlNode *req, xmlNode *input, xmlNode **cib, } int -cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer) { xmlNode *empty = createEmptyCib(0); xmlNode *empty_config = pcmk__xe_first_child(empty, PCMK_XE_CONFIGURATION, @@ -606,10 +605,10 @@ process_modify_section(int options, const char *section, xmlNode *input, } int -cib__process_modify(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -746,8 +745,7 @@ process_query_section(int options, const char *section, xmlNode *cib, } int -cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; @@ -934,10 +932,10 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } int -cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); @@ -952,8 +950,7 @@ cib__process_replace(xmlNode *req, xmlNode *input, xmlNode **cib, } int -cib__process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, - xmlNode **answer) +cib__process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; uint32_t options = cib_none; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 1a36fec94b2..f8b59606575 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -218,7 +218,6 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; - xmlNode *input = NULL; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -232,7 +231,6 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = cib__get_calldata(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -251,7 +249,7 @@ cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, pcmk__s(section, "(null)"), pcmk__s(user, "(null)")); pcmk__log_xml_trace(req, "request"); - rc = fn(req, input, &cib, output); + rc = fn(req, &cib, output); if (*output == NULL) { // Do nothing @@ -544,7 +542,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, pcmk__enable_acl(*cib, *cib, user); } - rc = fn(req, input, cib, output); + rc = fn(req, cib, output); // Allow ourselves to make any additional necessary changes xml_acl_disable(*cib); From 8c1b3e4ef6f96a9f5a4db476c655cef17bb65e04 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Fri, 2 Jan 2026 22:51:43 -0800 Subject: [PATCH 266/350] Refactor: libcib: Drop input argument from cib_perform_op helpers We were passing the input argument just for logging on error. But we're already logging the request, and the request always contains the input (if non-NULL) as the child of a PCMK__XE_CIB_CALLDATA child. Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index f8b59606575..c6cdcab808a 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -359,7 +359,6 @@ check_new_feature_set(const xmlNode *new_cib) * \param[in] old_cib \c PCMK_XE_CIB element before performing operation * \param[in] new_cib \c PCMK_XE_CIB element from result of operation * \param[in] request CIB request - * \param[in] input Input data for CIB request * * \return Standard Pacemaker return code * @@ -368,8 +367,7 @@ check_new_feature_set(const xmlNode *new_cib) */ static int check_cib_version_attr(const char *attr, const xmlNode *old_cib, - const xmlNode *new_cib, const xmlNode *request, - const xmlNode *input) + const xmlNode *new_cib, const xmlNode *request) { const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); int old_version = 0; @@ -389,7 +387,6 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, pcmk__err("%s went backwards in %s request: %d -> %d", attr, op, old_version, new_version); pcmk__log_xml_warn(request, "bad-request"); - pcmk__log_xml_warn(input, "bad-input"); return pcmk_rc_old_data; } @@ -406,7 +403,6 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, * \param[in] old_cib \c PCMK_XE_CIB element before performing operation * \param[in] new_cib \c PCMK_XE_CIB element from result of operation * \param[in] request CIB request - * \param[in] input Input data for CIB request * * \return Standard Pacemaker return code * @@ -415,18 +411,17 @@ check_cib_version_attr(const char *attr, const xmlNode *old_cib, */ static int check_cib_versions(const xmlNode *old_cib, const xmlNode *new_cib, - const xmlNode *request, const xmlNode *input) + const xmlNode *request) { int rc = check_cib_version_attr(PCMK_XA_ADMIN_EPOCH, old_cib, new_cib, - request, input); + request); if (rc != pcmk_rc_undetermined) { return rc; } // @TODO Why aren't we checking PCMK_XA_NUM_UPDATES if epochs are equal? - rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request, - input); + rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request); if (rc == pcmk_rc_undetermined) { rc = pcmk_rc_ok; } @@ -495,7 +490,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, const char *section = NULL; const char *user = NULL; uint32_t call_options = cib_none; - xmlNode *input = NULL; bool enable_acl = false; bool manage_version = true; @@ -518,7 +512,6 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, user = pcmk__xe_get(req, PCMK__XA_CIB_USER); pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - input = cib__get_calldata(req); enable_acl = cib_acl_enabled(*cib, user); pcmk__trace("Processing %s for section '%s', user '%s'", op, @@ -573,7 +566,7 @@ cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, } } - rc = check_cib_versions(old_versions, *cib, req, input); + rc = check_cib_versions(old_versions, *cib, req); pcmk__strip_xml_text(*cib); From dc4ce08dc049cb6b9491881cb956286317305460 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 01:14:58 -0800 Subject: [PATCH 267/350] Refactor: libcib: Reduce number of arguments in cib_ops.c I'm ambivalent about this, since it adds lines of code, but request contains everything needed by a lot of these helper functions. Signed-off-by: Reid Wahl --- lib/cib/cib_ops.c | 119 ++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index b27ad90ee4d..0c062bc2d22 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -354,14 +354,19 @@ cib__process_create(xmlNode *req, xmlNode **cib, xmlNode **answer) } static int -process_delete_xpath(const char *op, int options, const char *xpath, - xmlNode *cib) +process_delete_xpath(const xmlNode *request, xmlNode *cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s was already removed", xpath); @@ -434,8 +439,10 @@ delete_child(xmlNode *child, void *userdata) } static int -process_delete_section(const char *section, xmlNode *input, xmlNode *cib) +process_delete_section(const xmlNode *request, xmlNode *cib) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); xmlNode *obj_root = NULL; if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { @@ -462,19 +469,15 @@ process_delete_section(const char *section, xmlNode *input, xmlNode *cib) int cib__process_delete(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); - xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_delete_xpath(op, options, section, *cib); + return process_delete_xpath(req, *cib); } - return process_delete_section(section, input, *cib); + return process_delete_section(req, *cib); } int @@ -506,14 +509,22 @@ cib__process_erase(xmlNode *req, xmlNode **cib, xmlNode **answer) } static int -process_modify_xpath(const char *op, int options, const char *xpath, - xmlNode *input, xmlNode *cib) +process_modify_xpath(const xmlNode *request, xmlNode *cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = NULL; - const bool score = pcmk__is_set(options, cib_score_update); - const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + uint32_t flags = pcmk__xaf_none; + + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_score_update)) { + flags = pcmk__xaf_score_update; + } if (xpath == NULL) { xpath = pcmk__cib_abs_xpath_for(PCMK_XE_CIB); @@ -555,13 +566,20 @@ process_modify_xpath(const char *op, int options, const char *xpath, } static int -process_modify_section(int options, const char *section, xmlNode *input, - xmlNode *cib) +process_modify_section(const xmlNode *request, xmlNode *cib) { - const bool score = pcmk__is_set(options, cib_score_update); - const uint32_t flags = (score? pcmk__xaf_score_update : pcmk__xaf_none); + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + + uint32_t flags = pcmk__xaf_none; xmlNode *obj_root = NULL; + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + if (pcmk__is_set(options, cib_score_update)) { + flags = pcmk__xaf_score_update; + } + if ((section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) { input = pcmk_find_cib_element(input, section); } @@ -607,29 +625,30 @@ process_modify_section(int options, const char *section, xmlNode *input, int cib__process_modify(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); - xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_modify_xpath(op, options, section, input, *cib); + return process_modify_xpath(req, *cib); } - return process_modify_section(options, section, input, *cib); + return process_modify_section(req, *cib); } static int -process_query_xpath(const char *op, int options, const char *xpath, - xmlNode *cib, xmlNode **answer) +process_query_xpath(const xmlNode *request, xmlNode *cib, xmlNode **answer) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search(cib->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -715,10 +734,13 @@ process_query_xpath(const char *op, int options, const char *xpath, } static int -process_query_section(int options, const char *section, xmlNode *cib, - xmlNode **answer) +process_query_section(const xmlNode *request, xmlNode *cib, xmlNode **answer) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); xmlNode *obj_root = NULL; + uint32_t options = cib_none; + + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) { section = NULL; @@ -747,25 +769,23 @@ process_query_section(int options, const char *section, xmlNode *cib, int cib__process_query(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_query_xpath(op, options, section, *cib, answer); + return process_query_xpath(req, *cib, answer); } - return process_query_section(options, section, *cib, answer); + return process_query_section(req, *cib, answer); } static bool -replace_cib_digest_matches(xmlNode *request, xmlNode *input) +replace_cib_digest_matches(const xmlNode *request) { const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); const char *expected = pcmk__xe_get(request, PCMK_XA_DIGEST); + const xmlNode *input = cib__get_calldata(request); char *calculated = NULL; bool matches = false; @@ -790,7 +810,7 @@ replace_cib_digest_matches(xmlNode *request, xmlNode *input) } static int -replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) +replace_cib(xmlNode *request, xmlNode **cib) { int updates = 0; int epoch = 0; @@ -802,12 +822,13 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) const char *reason = NULL; const char *peer = pcmk__xe_get(request, PCMK__XA_SRC); + xmlNode *input = cib__get_calldata(request); cib_version_details(*cib, &admin_epoch, &epoch, &updates); cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates); - if (!replace_cib_digest_matches(request, input)) { + if (!replace_cib_digest_matches(request)) { pcmk__info("Replacement %d.%d.%d from %s not applied to %d.%d.%d: " "digest mismatch", replace_admin_epoch, replace_epoch, replace_updates, peer, admin_epoch, epoch, updates); @@ -847,13 +868,19 @@ replace_cib(xmlNode *request, xmlNode *input, xmlNode **cib) } static int -process_replace_xpath(const char *op, int options, const char *xpath, - xmlNode *request, xmlNode *input, xmlNode **cib) +process_replace_xpath(xmlNode *request, xmlNode **cib) { + const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); + const char *xpath = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + uint32_t options = cib_none; + int num_results = 0; int rc = pcmk_rc_ok; xmlXPathObject *xpath_obj = pcmk__xpath_search((*cib)->doc, xpath); + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &options, cib_none); + num_results = pcmk__xpath_num_results(xpath_obj); if (num_results == 0) { pcmk__debug("%s: %s does not exist", op, xpath); @@ -876,7 +903,7 @@ process_replace_xpath(const char *op, int options, const char *xpath, free(path); if (match == *cib) { - rc = replace_cib(request, input, cib); + rc = replace_cib(request, cib); break; } @@ -896,9 +923,11 @@ process_replace_xpath(const char *op, int options, const char *xpath, } static int -process_replace_section(const char *section, xmlNode *request, xmlNode *input, - xmlNode **cib) +process_replace_section(xmlNode *request, xmlNode **cib) { + const char *section = pcmk__xe_get(request, PCMK__XA_CIB_SECTION); + xmlNode *input = cib__get_calldata(request); + int rc = pcmk_rc_ok; xmlNode *obj_root = NULL; @@ -912,7 +941,7 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, } if (pcmk__xe_is(input, PCMK_XE_CIB)) { - return replace_cib(request, input, cib); + return replace_cib(request, cib); } if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei) @@ -934,19 +963,15 @@ process_replace_section(const char *section, xmlNode *request, xmlNode *input, int cib__process_replace(xmlNode *req, xmlNode **cib, xmlNode **answer) { - const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); - xmlNode *input = cib__get_calldata(req); uint32_t options = cib_none; pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); if (pcmk__is_set(options, cib_xpath)) { - const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - - return process_replace_xpath(op, options, section, req, input, cib); + return process_replace_xpath(req, cib); } - return process_replace_section(section, req, input, cib); + return process_replace_section(req, cib); } int From 99da337a467fbef0fb782d1dc9874188bb81685e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:39:10 -0800 Subject: [PATCH 268/350] Log: based: Drop redundant log from cib_process_command() We already warn for the same thing in based_process_request() if applicable. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c6244a631f1..e77253d9b8c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -531,11 +531,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, *reply = NULL; /* Start processing the request... */ - rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, - cib_none); - if (rc != pcmk_rc_ok) { - pcmk__warn("Couldn't parse options from request: %s", pcmk_rc_str(rc)); - } + pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (!privileged && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { From dd34fd4c77d9cb52170e3ee6962459719d3ba42c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:42:10 -0800 Subject: [PATCH 269/350] Refactor: based: Drop redundant cib_process_command() config_changed set This is already done a few lines prior. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index e77253d9b8c..a706d8fd1b5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -569,10 +569,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { if (result_cib != the_cib) { - if (pcmk__is_set(operation->flags, cib__op_attr_writes_through)) { - config_changed = true; - } - rc = based_activate_cib(result_cib, config_changed, op); } From ef85062961c35d35defd5ec4e770ce7eb34a99de Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:46:30 -0800 Subject: [PATCH 270/350] Refactor: based: Unindent block of cib_process_command() And drop a fairly useless trace message. cib_dryrun always has to be false there, by the way. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a706d8fd1b5..8ced405f17f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -606,15 +606,15 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xml_free(result_cib); } - if (!pcmk__any_flags_set(call_options, - cib_dryrun|cib_inhibit_notify|cib_transaction)) { - pcmk__trace("Sending notifications %d", - pcmk__is_set(call_options, cib_dryrun)); - based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, - client_name, originator, cib_diff); + if (pcmk__any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { + goto done; } - done: + based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, client_name, + originator, cib_diff); + +done: if (!pcmk__is_set(call_options, cib_discard_reply)) { *reply = create_cib_reply(op, call_id, client_id, call_options, pcmk_rc2legacy(rc), output); From 7892c0d65c1ddd3c2cd261cd6694b9de8e2acfc0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 02:54:11 -0800 Subject: [PATCH 271/350] Refactor: based: Unindent some of process_ping_reply() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 98 ++++++++++++++++----------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8ced405f17f..56c832e1f67 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -189,7 +189,7 @@ digest_timer_cb(gpointer data) } static void -process_ping_reply(xmlNode *reply) +process_ping_reply(xmlNode *reply) { uint64_t seq = 0; const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); @@ -197,72 +197,72 @@ process_ping_reply(xmlNode *reply) xmlNode *pong = cib__get_calldata(reply); const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); + long long seq_ll = 0; + int rc = pcmk_rc_ok; if (seq_s == NULL) { pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); return; + } - } else { - long long seq_ll; - int rc = pcmk__scan_ll(seq_s, &seq_ll, 0LL); - - if (rc != pcmk_rc_ok) { - pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID - " '%s': %s", - seq_s, pcmk_rc_str(rc)); - return; - } - seq = (uint64_t) seq_ll; + rc = pcmk__scan_ll(seq_s, &seq_ll, 0); + if (rc != pcmk_rc_ok) { + pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID " " + "'%s': %s", seq_s, pcmk_rc_str(rc)); + return; } - if(digest == NULL) { + // @TODO Check for overflow? + seq = (uint64_t) seq_ll; + + if (digest == NULL) { pcmk__trace("Ignoring ping reply %s from %s with no digest", seq_s, host); + return; + } - } else if(seq != ping_seq) { + if (seq != ping_seq) { pcmk__trace("Ignoring out of sequence ping reply %s from %s", seq_s, host); + return; + } - } else if(ping_modified_since) { + if (ping_modified_since) { pcmk__trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host); + return; + } - } else { - if(ping_digest == NULL) { - pcmk__trace("Calculating new digest"); - ping_digest = pcmk__digest_xml(the_cib, true); - } + if (ping_digest == NULL) { + pcmk__trace("Calculating new digest"); + ping_digest = pcmk__digest_xml(the_cib, true); + } + + pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); + if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { + xmlNode *remote_versions = cib__get_calldata(pong); - pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, - digest); - if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *remote_versions = cib__get_calldata(pong); - - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; - - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, - PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, - PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, - PCMK_XA_NUM_UPDATES); - } - - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); - - pcmk__xml_free(remote_versions); - sync_our_cib(reply, false); + const char *admin_epoch_s = NULL; + const char *epoch_s = NULL; + const char *num_updates_s = NULL; + + if (remote_versions != NULL) { + admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); + epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); + num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); } + + pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", + pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), + ping_digest, host, + pcmk__s(admin_epoch_s, "_"), + pcmk__s(epoch_s, "_"), + pcmk__s(num_updates_s, "_"), digest); + + pcmk__xml_free(remote_versions); + sync_our_cib(reply, false); } } From 2cea825f4f6e504a9ac50b4c7828944d77b0b9a5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 09:13:12 -0800 Subject: [PATCH 272/350] Refactor: based: Unindent rest of process_ping_reply() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 46 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 56c832e1f67..845a51ad40a 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -200,6 +200,11 @@ process_ping_reply(xmlNode *reply) long long seq_ll = 0; int rc = pcmk_rc_ok; + xmlNode *remote_versions = cib__get_calldata(pong); + const char *admin_epoch_s = NULL; + const char *epoch_s = NULL; + const char *num_updates_s = NULL; + if (seq_s == NULL) { pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); return; @@ -239,31 +244,28 @@ process_ping_reply(xmlNode *reply) } pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); - if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { - xmlNode *remote_versions = cib__get_calldata(pong); - - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); - } + if (pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { + return; + } - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", - pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); - - pcmk__xml_free(remote_versions); - sync_our_cib(reply, false); + if (remote_versions != NULL) { + admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); + epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); + num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); } + + pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", + pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_EPOCH), + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), + ping_digest, host, + pcmk__s(admin_epoch_s, "_"), + pcmk__s(epoch_s, "_"), + pcmk__s(num_updates_s, "_"), digest); + + pcmk__xml_free(remote_versions); + sync_our_cib(reply, false); } static void From 73d0d384e0ee9a72fc376c9acab34778c0e3ed81 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 14:07:13 -0800 Subject: [PATCH 273/350] Refactor: based: Convert ping_seq to long long So that we can use pcmk__xe_get_ll(). This also avoids theoretical overflow risk, if a value is sent as uint64_t and then scanned as long long upon receiving it. That overflow should never happen in practice, as it would require sending a massive number of pings before restarting based. Also improve documentation a bit and add a note of non-understanding. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 67 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 845a51ad40a..7a0e0de2f4f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -10,7 +10,6 @@ #include #include // EACCES, ECONNREFUSED -#include // PRIu64 #include #include // NULL, size_t #include // free @@ -41,7 +40,7 @@ #define EXIT_ESCALATION_MS 10000 -static uint64_t ping_seq = 0; +static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; @@ -148,42 +147,53 @@ do_local_notify(const xmlNode *xml, const char *client_id, bool sync_reply, * \internal * \brief Request CIB digests from all peer nodes * - * This is used as a callback that runs 5 seconds after we modify the CIB. It - * sends a ping request to all cluster nodes. They will respond by sending their - * current digests and version info, which we will validate in - * process_ping_reply(). This serves as a check of consistency across the - * cluster after a CIB update. + * This is used as a callback that runs 5 seconds after we modify the CIB on the + * DC. It sends a ping request to all cluster nodes. They will respond by + * sending their current digests and version info, which we will validate in + * process_ping_reply(). If their digest doesn't match, we'll sync our own CIB + * to them. This helps ensure consistency across the cluster after a CIB update. * * \param[in] data Ignored * * \return \c G_SOURCE_REMOVE (to destroy the timeout) + * + * \note It's not clear why we wait 5 seconds rather than sending the ping + * request immediately after a performing a modifying op. Perhaps it's to + * avoid overwhelming other nodes with ping requests when there are a lot + * of modifying requests in a short period. The timer restarts after + * every successful modifying op, so we send ping requests **at most** + * every 5 seconds. Or perhaps it's a remnant of legacy mode (pre-1.1.12). + * In any case, the other nodes shouldn't need time to process the + * modifying op before responding to the ping request. The ping request is + * sent after the op is sent, so it should also be received after the op + * is received. */ static gboolean digest_timer_cb(gpointer data) { - char *buf = NULL; xmlNode *ping = NULL; if (!based_is_primary) { + // Only the DC sends a ping return G_SOURCE_REMOVE; } - ping_seq++; + if (++ping_seq < 0) { + ping_seq = 0; + } + g_clear_pointer(&ping_digest, free); ping_modified_since = false; - buf = pcmk__assert_asprintf("%" PRIu64, ping_seq); - ping = pcmk__xe_create(NULL, PCMK__XE_PING); pcmk__xe_set(ping, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(ping, PCMK__XA_CIB_OP, CRM_OP_PING); - pcmk__xe_set(ping, PCMK__XA_CIB_PING_ID, buf); + pcmk__xe_set_ll(ping, PCMK__XA_CIB_PING_ID, ping_seq); pcmk__xe_set(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET); - pcmk__trace("Requesting peer digests (%s)", buf); + pcmk__trace("Requesting peer digests (%lld)", ping_seq); pcmk__cluster_send_message(NULL, pcmk_ipc_based, ping); - free(buf); pcmk__xml_free(ping); return G_SOURCE_REMOVE; } @@ -191,49 +201,38 @@ digest_timer_cb(gpointer data) static void process_ping_reply(xmlNode *reply) { - uint64_t seq = 0; - const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); - xmlNode *pong = cib__get_calldata(reply); - const char *seq_s = pcmk__xe_get(pong, PCMK__XA_CIB_PING_ID); + long long seq = 0; + const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); - long long seq_ll = 0; - int rc = pcmk_rc_ok; xmlNode *remote_versions = cib__get_calldata(pong); const char *admin_epoch_s = NULL; const char *epoch_s = NULL; const char *num_updates_s = NULL; - if (seq_s == NULL) { - pcmk__debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID); - return; - } + int rc = pcmk__xe_get_ll(pong, PCMK__XA_CIB_PING_ID, &seq); - rc = pcmk__scan_ll(seq_s, &seq_ll, 0); if (rc != pcmk_rc_ok) { - pcmk__debug("Ignoring ping reply with invalid " PCMK__XA_CIB_PING_ID " " - "'%s': %s", seq_s, pcmk_rc_str(rc)); + pcmk__debug("Ignoring ping reply with unset or invalid " + PCMK__XA_CIB_PING_ID ": %s", pcmk_rc_str(rc)); return; } - // @TODO Check for overflow? - seq = (uint64_t) seq_ll; - if (digest == NULL) { - pcmk__trace("Ignoring ping reply %s from %s with no digest", seq_s, + pcmk__trace("Ignoring ping reply %lld from %s with no digest", seq, host); return; } if (seq != ping_seq) { - pcmk__trace("Ignoring out of sequence ping reply %s from %s", seq_s, + pcmk__trace("Ignoring out-of-sequence ping reply %lld from %s", seq, host); return; } if (ping_modified_since) { - pcmk__trace("Ignoring ping reply %s from %s: cib updated since", seq_s, + pcmk__trace("Ignoring ping reply %lld from %s: CIB updated since", seq, host); return; } @@ -243,7 +242,7 @@ process_ping_reply(xmlNode *reply) ping_digest = pcmk__digest_xml(the_cib, true); } - pcmk__trace("Processing ping reply %s from %s (%s)", seq_s, host, digest); + pcmk__trace("Processing ping reply %lld from %s (%s)", seq, host, digest); if (pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) { return; From e758dd372c50a072152e08657281bd172a66d455 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 14:19:04 -0800 Subject: [PATCH 274/350] Low: based: Ignore ping reply if we're no longer DC It might be possible for a new DC to be elected after we send a ping request and before we process a modifying request (which would set ping_modified_since to true and cause the ping reply to be ignored). Add this check just in case. If we're no longer DC, then we don't want to sync our CIB to other nodes. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 7a0e0de2f4f..aa3a979c4a5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -219,6 +219,12 @@ process_ping_reply(xmlNode *reply) return; } + if (!based_is_primary) { + pcmk__trace("Ignoring ping reply %lld from %s because we are no longer " + "DC", seq, host); + return; + } + if (digest == NULL) { pcmk__trace("Ignoring ping reply %lld from %s with no digest", seq, host); From 26f21194cb2b1d79ce78dee1420034ec0abd2440 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:49:17 -0800 Subject: [PATCH 275/350] Refactor: based: Unindent outermost else block in based_process_upgrade Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 6082afd36be..4b4c603decb 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -228,6 +228,14 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; + xmlNode *scratch = NULL; + const char *host = pcmk__xe_get(req, PCMK__XA_SRC); + const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); + const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); + const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); + const char *original_schema = NULL; + const char *new_schema = NULL; + *answer = NULL; if (pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) { @@ -237,80 +245,72 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) * performs the upgrade (and notifies its local clients) here. */ return cib__process_upgrade(req, cib, answer); + } - } else { - xmlNode *scratch = pcmk__xml_copy(NULL, *cib); - const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - const char *original_schema = NULL; - const char *new_schema = NULL; - const char *client_id = pcmk__xe_get(req, PCMK__XA_CIB_CLIENTID); - const char *call_opts = pcmk__xe_get(req, PCMK__XA_CIB_CALLOPT); - const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); - - original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); - if (original_schema == NULL) { - pcmk__info("Rejecting upgrade request from %s: No " - PCMK_XA_VALIDATE_WITH, - host); - return pcmk_rc_cib_corrupt; - } + scratch = pcmk__xml_copy(NULL, *cib); + + original_schema = pcmk__xe_get(*cib, PCMK_XA_VALIDATE_WITH); + if (original_schema == NULL) { + pcmk__info("Rejecting upgrade request from %s: No " + PCMK_XA_VALIDATE_WITH, host); + return pcmk_rc_cib_corrupt; + } - rc = pcmk__update_schema(&scratch, NULL, true, true); - new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); + rc = pcmk__update_schema(&scratch, NULL, true, true); + new_schema = pcmk__xe_get(scratch, PCMK_XA_VALIDATE_WITH); - if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { - xmlNode *up = pcmk__xe_create(NULL, __func__); + if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) { + xmlNode *up = pcmk__xe_create(NULL, __func__); + + rc = pcmk_rc_ok; + pcmk__notice("Upgrade request from %s verified", host); + + pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); + pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); + pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); + pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); + pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); + + pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); + + pcmk__xml_free(up); + + } else if (rc == pcmk_rc_ok) { + rc = pcmk_rc_schema_unchanged; + } + + if (rc != pcmk_rc_ok) { + // Notify originating peer so it can notify its local clients + pcmk__node_status_t *origin = NULL; + + origin = pcmk__search_node_caches(0, host, NULL, + pcmk__node_search_cluster_member); - rc = pcmk_rc_ok; - pcmk__notice("Upgrade request from %s verified", host); + pcmk__info("Rejecting upgrade request from %s: %s " + QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, + ((origin != NULL)? origin->name : "lost")); + + if (origin != NULL) { + xmlNode *up = pcmk__xe_create(NULL, __func__); pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema); pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); - - pcmk__xml_free(up); - - } else if (rc == pcmk_rc_ok) { - rc = pcmk_rc_schema_unchanged; - } - - if (rc != pcmk_rc_ok) { - // Notify originating peer so it can notify its local clients - pcmk__node_status_t *origin = NULL; - - origin = pcmk__search_node_caches(0, host, NULL, - pcmk__node_search_cluster_member); - - pcmk__info("Rejecting upgrade request from %s: %s " - QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, - ((origin != NULL)? origin->name : "lost")); - - if (origin) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, - pcmk_rc2legacy(rc)); - if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { - pcmk__warn("Could not send CIB upgrade result to %s", host); - } - pcmk__xml_free(up); + pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { + pcmk__warn("Could not send CIB upgrade result to %s", host); } + pcmk__xml_free(up); } - pcmk__xml_free(scratch); } + + pcmk__xml_free(scratch); return rc; } From 60ba825690ebfd8250736148405ca70e3fb0a988 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:54:11 -0800 Subject: [PATCH 276/350] Refactor: based: Unindent pcmk_rc_ok section of based_process_upgrade() Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 4b4c603decb..abc9493f3bf 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -235,6 +235,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) const char *call_id = pcmk__xe_get(req, PCMK__XA_CIB_CALLID); const char *original_schema = NULL; const char *new_schema = NULL; + pcmk__node_status_t *origin = NULL; *answer = NULL; @@ -276,40 +277,39 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) pcmk__cluster_send_message(NULL, pcmk_ipc_based, up); pcmk__xml_free(up); + goto done; + } - } else if (rc == pcmk_rc_ok) { + if (rc == pcmk_rc_ok) { rc = pcmk_rc_schema_unchanged; } - if (rc != pcmk_rc_ok) { - // Notify originating peer so it can notify its local clients - pcmk__node_status_t *origin = NULL; - - origin = pcmk__search_node_caches(0, host, NULL, - pcmk__node_search_cluster_member); - - pcmk__info("Rejecting upgrade request from %s: %s " - QB_XS " rc=%d peer=%s", host, pcmk_rc_str(rc), rc, - ((origin != NULL)? origin->name : "lost")); - - if (origin != NULL) { - xmlNode *up = pcmk__xe_create(NULL, __func__); - - pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); - pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); - pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); - pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); - pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); - if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { - pcmk__warn("Could not send CIB upgrade result to %s", host); - } - pcmk__xml_free(up); + // Notify originating peer so it can notify its local clients + origin = pcmk__search_node_caches(0, host, NULL, + pcmk__node_search_cluster_member); + + pcmk__info("Rejecting upgrade request from %s: %s " QB_XS " rc=%d peer=%s", + host, pcmk_rc_str(rc), rc, + ((origin != NULL)? origin->name : "lost")); + + if (origin != NULL) { + xmlNode *up = pcmk__xe_create(NULL, __func__); + + pcmk__xe_set(up, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE); + pcmk__xe_set(up, PCMK__XA_CIB_DELEGATED_FROM, host); + pcmk__xe_set(up, PCMK__XA_CIB_ISREPLYTO, host); + pcmk__xe_set(up, PCMK__XA_CIB_CLIENTID, client_id); + pcmk__xe_set(up, PCMK__XA_CIB_CALLOPT, call_opts); + pcmk__xe_set(up, PCMK__XA_CIB_CALLID, call_id); + pcmk__xe_set_int(up, PCMK__XA_CIB_UPGRADE_RC, pcmk_rc2legacy(rc)); + if (!pcmk__cluster_send_message(origin, pcmk_ipc_based, up)) { + pcmk__warn("Could not send CIB upgrade result to %s", host); } + pcmk__xml_free(up); } +done: pcmk__xml_free(scratch); return rc; } From 5e94d4163b3c337752a26325f0294ff218037378 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 18:56:51 -0800 Subject: [PATCH 277/350] Refactor: based: Don't set answer to NULL in cib__op_fn_t functions These are called only by cib_perform_op()/cib__perform_query(), which assert that *answer is NULL. Signed-off-by: Reid Wahl --- daemons/based/based_messages.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index abc9493f3bf..7edd139e7ea 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -90,7 +90,6 @@ based_process_is_primary(xmlNode *req, xmlNode **cib, xmlNode **answer) int based_process_noop(xmlNode *req, xmlNode **cib, xmlNode **answer) { - *answer = NULL; return pcmk_rc_ok; } @@ -200,8 +199,6 @@ based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); - *answer = NULL; - if (pcmk__xe_get(req, PCMK__XA_CIB_ISREPLYTO) == NULL) { pcmk__info("Peer %s is requesting to shut down", host); return pcmk_rc_ok; @@ -237,8 +234,6 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) const char *new_schema = NULL; pcmk__node_status_t *origin = NULL; - *answer = NULL; - if (pcmk__xe_get(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) { /* The originator of an upgrade request sends it to the DC, without * PCMK__XA_CIB_SCHEMA_MAX. If an upgrade is needed, the DC From c102f488ef8e7908cdb858850a6e4336ee4cc84b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 19:00:01 -0800 Subject: [PATCH 278/350] Refactor: libcib: Rename cib__perform_query() to cib__perform_op_ro() It's used for all non-modifying ops, not just queries. This also enables renaming cib_perform_op() to cib__perform_op_rw() next. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- include/crm/cib/internal.h | 2 +- lib/cib/cib_file.c | 2 +- lib/cib/cib_utils.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index aa3a979c4a5..896082b1ec4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -548,7 +548,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, } if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_query(op_function, request, &the_cib, &output); + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); goto done; } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index fb04ee4ab09..4897d53f1af 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -189,7 +189,7 @@ int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset); xmlNode *cib__get_calldata(const xmlNode *request); void cib__set_calldata(xmlNode *request, xmlNode *data); -int cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, +int cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 27efd3647a9..50df6ba4ca5 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -165,7 +165,7 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) read_only = !pcmk__is_set(operation->flags, cib__op_attr_modifies); if (read_only) { - rc = cib__perform_query(op_function, request, &private->cib_xml, + rc = cib__perform_op_ro(op_function, request, &private->cib_xml, output); } else { result_cib = private->cib_xml; diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index c6cdcab808a..40bccb30f19 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -210,7 +210,7 @@ cib__set_calldata(xmlNode *request, xmlNode *data) } int -cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, +cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output) { int rc = pcmk_rc_ok; From 2a19f1c3dd76a8c753d0f1982824c86431458a00 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 19:01:48 -0800 Subject: [PATCH 279/350] Refactor: libcib: Rename cib_perform_op() to cib__perform_op_rw() Avoid using the public API prefix, and make it clearer that this is not for all CIB ops, but rather the ones that may modify something. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 +++--- daemons/based/based_transaction.c | 10 +++++----- include/crm/cib/internal.h | 6 +++--- lib/cib/cib_file.c | 6 +++--- lib/cib/cib_utils.c | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 896082b1ec4..07b94b5be23 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -554,14 +554,14 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, ping_modified_since = true; - /* result_cib must not be modified after cib_perform_op() returns. + /* result_cib must not be modified after cib__perform_op_rw() returns. * * It's not important whether the client variant is cib_native or * cib_remote. */ result_cib = the_cib; - rc = cib_perform_op(cib_undefined, op_function, request, &config_changed, - &result_cib, &cib_diff, &output); + rc = cib__perform_op_rw(cib_undefined, op_function, request, + &config_changed, &result_cib, &cib_diff, &output); /* Always write to disk for successful ops with the flag set. This also * negates the need to detect ordering changes. diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c index d8edadb5d9c..28d3256ba4d 100644 --- a/daemons/based/based_transaction.c +++ b/daemons/based/based_transaction.c @@ -118,7 +118,7 @@ process_transaction_requests(xmlNode *transaction, const pcmk__client_t *client, * \note This function is expected to be called only by * \p based_process_commit_transact(). * \note \p result_cib is expected to be a copy of the current CIB as created by - * \p cib_perform_op(). + * \p cib__perform_op_rw(). * \note The caller is responsible for activating and syncing \p result_cib on * success, and for freeing it on failure. */ @@ -135,10 +135,10 @@ based_commit_transaction(xmlNode *transaction, const pcmk__client_t *client, CRM_CHECK(pcmk__xe_is(transaction, PCMK__XE_CIB_TRANSACTION), return pcmk_rc_no_transaction); - /* *result_cib should be a copy of the_cib (created by cib_perform_op()). If - * not, make a copy now. Change tracking isn't strictly required here - * because each request in the transaction will have changes tracked and - * ACLs checked if appropriate. + /* *result_cib should be a copy of the_cib (created by + * cib__perform_op_rw()). If not, make a copy now. Change tracking isn't + * strictly required here because each request in the transaction will have + * changes tracked and ACLs checked if appropriate. */ CRM_CHECK((*result_cib != NULL) && (*result_cib != the_cib), *result_cib = pcmk__xml_copy(NULL, the_cib)); diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 4897d53f1af..758ed3cd309 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -192,9 +192,9 @@ void cib__set_calldata(xmlNode *request, xmlNode *data); int cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, xmlNode **output); -int cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **cib, xmlNode **diff, - xmlNode **output); +int cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output); int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 50df6ba4ca5..4c0c31a9f32 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -169,8 +169,8 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) output); } else { result_cib = private->cib_xml; - rc = cib_perform_op(cib_file, op_function, request, &changed, - &result_cib, &cib_diff, output); + rc = cib__perform_op_rw(cib_file, op_function, request, &changed, + &result_cib, &cib_diff, output); } if (pcmk__is_set(call_options, cib_transaction)) { @@ -269,7 +269,7 @@ commit_transaction(cib_t *cib, xmlNode *transaction, xmlNode **result_cib) return pcmk_rc_no_transaction); /* *result_cib should be a copy of private->cib_xml (created by - * cib_perform_op()). If not, make a copy now. Change tracking isn't + * cib__perform_op_rw()). If not, make a copy now. Change tracking isn't * strictly required here because each request in the transaction will have * changes tracked and ACLs checked if appropriate. */ diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 40bccb30f19..28cb7f8d940 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -480,9 +480,9 @@ set_update_origin(xmlNode *new_cib, const xmlNode *request) } int -cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, - bool *config_changed, xmlNode **cib, xmlNode **diff, - xmlNode **output) +cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, + bool *config_changed, xmlNode **cib, xmlNode **diff, + xmlNode **output) { int rc = pcmk_rc_ok; From 8a7f1fdca4f9c1ba61361c9bdc44e275b7bd6c6f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 21:33:35 -0800 Subject: [PATCH 280/350] Refactor: based: Don't free remote_versions in process_ping_reply() This is part of request and will be freed when request is freed. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 07b94b5be23..4638a9b1477 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -269,7 +269,6 @@ process_ping_reply(xmlNode *reply) pcmk__s(epoch_s, "_"), pcmk__s(num_updates_s, "_"), digest); - pcmk__xml_free(remote_versions); sync_our_cib(reply, false); } From 486867b6a231b08bc6134e23c4412ac05d49156b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sat, 3 Jan 2026 21:36:06 -0800 Subject: [PATCH 281/350] Refactor: based: Assume the_cib is not NULL We ensure that it's non-NULL in cib_init() before we start the mainloop. After that, no CIB operation should be able to set it to NULL. If it would do so, the operation should fail, and the current (non-NULL) CIB should be kept. When processing requests within a transaction, we don't make a copy, so the_cib may temporarily become NULL. However, we don't return to the mainloop from processing a commit-transaction request until we're finished. At that point, either all requests were processed successfully (and the_cib != NULL) or something failed and we revert to the copy we made at the beginning of the commit-transaction. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 21 ++++++--------------- daemons/based/based_messages.c | 13 +++++-------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 4638a9b1477..2c188ac401c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -201,15 +201,13 @@ digest_timer_cb(gpointer data) static void process_ping_reply(xmlNode *reply) { + const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); + xmlNode *pong = cib__get_calldata(reply); long long seq = 0; - const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); const char *digest = pcmk__xe_get(pong, PCMK_XA_DIGEST); xmlNode *remote_versions = cib__get_calldata(pong); - const char *admin_epoch_s = NULL; - const char *epoch_s = NULL; - const char *num_updates_s = NULL; int rc = pcmk__xe_get_ll(pong, PCMK__XA_CIB_PING_ID, &seq); @@ -254,20 +252,13 @@ process_ping_reply(xmlNode *reply) return; } - if (remote_versions != NULL) { - admin_epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH); - epoch_s = pcmk__xe_get(remote_versions, PCMK_XA_EPOCH); - num_updates_s = pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES); - } - pcmk__notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s", pcmk__xe_get(the_cib, PCMK_XA_ADMIN_EPOCH), pcmk__xe_get(the_cib, PCMK_XA_EPOCH), - pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), - ping_digest, host, - pcmk__s(admin_epoch_s, "_"), - pcmk__s(epoch_s, "_"), - pcmk__s(num_updates_s, "_"), digest); + pcmk__xe_get(the_cib, PCMK_XA_NUM_UPDATES), ping_digest, host, + pcmk__xe_get(remote_versions, PCMK_XA_ADMIN_EPOCH), + pcmk__xe_get(remote_versions, PCMK_XA_EPOCH), + pcmk__xe_get(remote_versions, PCMK_XA_NUM_UPDATES), digest); sync_our_cib(reply, false); } diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 7edd139e7ea..361529f1b84 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -99,6 +99,7 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) const char *host = pcmk__xe_get(req, PCMK__XA_SRC); const char *seq = pcmk__xe_get(req, PCMK__XA_CIB_PING_ID); char *digest = pcmk__digest_xml(the_cib, true); + xmlNode *shallow = NULL; *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE); @@ -106,13 +107,10 @@ based_process_ping(xmlNode *req, xmlNode **cib, xmlNode **answer) pcmk__xe_set(*answer, PCMK_XA_DIGEST, digest); pcmk__xe_set(*answer, PCMK__XA_CIB_PING_ID, seq); - if (the_cib != NULL) { - xmlNode *shallow = pcmk__xe_create(NULL, (const char *) the_cib->name); - - pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none); - cib__set_calldata(*answer, shallow); - pcmk__xml_free(shallow); - } + shallow = pcmk__xe_create(NULL, (const char *) the_cib->name); + pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none); + cib__set_calldata(*answer, shallow); + pcmk__xml_free(shallow); pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", host, digest, pcmk__xe_get(*cib, PCMK_XA_ADMIN_EPOCH), @@ -352,7 +350,6 @@ sync_our_cib(xmlNode *request, bool all) pcmk__node_status_t *peer = NULL; xmlNode *replace_request = NULL; - CRM_CHECK(the_cib != NULL, return EINVAL); CRM_CHECK(all || (host != NULL), return EINVAL); pcmk__debug("Syncing CIB to %s", (all? "all peers" : host)); From 007c8b41737887f5657fe67fcb7af65c929c8f60 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:26:26 -0800 Subject: [PATCH 282/350] Doc: based, libcib: Clarify cib__op_attr_modifies Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 +++++++ include/crm/cib/internal.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 2c188ac401c..ecadfa47313 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -542,6 +542,13 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } + /* @TODO The cib__op_attr_modifies flag means the request *may* modify + * *something*. A successful request with this flag set may not have + * modified anything (for example, a delete request when there is no match + * to delete), or it may have modified something other than the CIB (for + * example, the CIB manager's primary/secondary status). Thus we may be + * setting the ping_modified_since flag when the CIB has not been modified. + */ ping_modified_since = true; /* result_cib must not be modified after cib__perform_op_rw() returns. diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 758ed3cd309..5e1a838f410 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -48,7 +48,7 @@ enum cib__op_attr { //! No special attributes cib__op_attr_none = 0, - //! Modifies CIB + //! May modify state (of the CIB itself or of the CIB manager) cib__op_attr_modifies = (UINT32_C(1) << 1), //! Requires privileges From 4bbc9720e914eb51a701da407624625cd683a49c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:34:55 -0800 Subject: [PATCH 283/350] Doc: based: Add Doxygen for process_ping_reply() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ecadfa47313..b4721ef90cb 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -198,6 +198,24 @@ digest_timer_cb(gpointer data) return G_SOURCE_REMOVE; } +/*! + * \internal + * \brief Process a reply to a \c CRM_OP_PING request + * + * See \c digest_timer_cb() for details on how the ping process works, and see + * \c based_process_ping() for the construction of the ping reply. + * + * We ignore the reply if we are no longer the DC, if the reply is malformed or + * received out of sequence, or if we may have modified the CIB since the last + * time we sent a ping request. + * + * Otherwise, we compare the CIB digest received in the reply against the digest + * of the local CIB. If the digests don't match, we sync our CIB to the node + * that sent the reply. This helps to ensure that all other nodes' views of the + * CIB eventually match the DC's view of the CIB. + * + * \param[in] reply Ping reply + */ static void process_ping_reply(xmlNode *reply) { @@ -242,7 +260,6 @@ process_ping_reply(xmlNode *reply) } if (ping_digest == NULL) { - pcmk__trace("Calculating new digest"); ping_digest = pcmk__digest_xml(the_cib, true); } From 8e1609ddf2eed1513d7ae8cf0cb559eccb05f953 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:37:19 -0800 Subject: [PATCH 284/350] Refactor: based: sync_our_cib() takes const argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- daemons/based/based_messages.c | 4 ++-- daemons/based/based_messages.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b4721ef90cb..3a25bc02121 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -217,7 +217,7 @@ digest_timer_cb(gpointer data) * \param[in] reply Ping reply */ static void -process_ping_reply(xmlNode *reply) +process_ping_reply(const xmlNode *reply) { const char *host = pcmk__xe_get(reply, PCMK__XA_SRC); diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c index 361529f1b84..246a08af7b8 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -308,7 +308,7 @@ based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer) } static xmlNode * -cib_msg_copy(xmlNode *msg) +cib_msg_copy(const xmlNode *msg) { static const char *field_list[] = { PCMK__XA_T, @@ -341,7 +341,7 @@ cib_msg_copy(xmlNode *msg) } int -sync_our_cib(xmlNode *request, bool all) +sync_our_cib(const xmlNode *request, bool all) { int rc = pcmk_rc_ok; char *digest = NULL; diff --git a/daemons/based/based_messages.h b/daemons/based/based_messages.h index 78d0d7e5218..30b934a8600 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -31,6 +31,6 @@ int based_process_shutdown(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_sync(xmlNode *req, xmlNode **cib, xmlNode **answer); int based_process_upgrade(xmlNode *req, xmlNode **cib, xmlNode **answer); -int sync_our_cib(xmlNode *request, bool all); +int sync_our_cib(const xmlNode *request, bool all); #endif // BASED_MESSAGES__H From 4de028247eb3e9ac42c474ef072ce7f63241c0e2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 00:39:10 -0800 Subject: [PATCH 285/350] Low: based: Free ping_digest on exit Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3a25bc02121..3da02d4cc47 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -1029,6 +1029,7 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error From dba6237ba5931700d201c2fec5b101a74eeb38f2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 01:12:29 -0800 Subject: [PATCH 286/350] Refactor: based: Minor reorganization of cib_process_command() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 38 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3da02d4cc47..42a849a466f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -517,33 +517,27 @@ static int cib_process_command(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **reply, bool privileged) { + static mainloop_timer_t *digest_timer = NULL; + xmlNode *cib_diff = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; - uint32_t call_options = cib_none; - const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); - - int rc = pcmk_rc_ok; + uint32_t call_options = cib_none; bool config_changed = false; - - static mainloop_timer_t *digest_timer = NULL; - - pcmk__assert(cib_status == pcmk_rc_ok); + int rc = pcmk_rc_ok; if (digest_timer == NULL) { digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, digest_timer_cb, NULL); } - *reply = NULL; - /* Start processing the request... */ pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); @@ -577,20 +571,21 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, &output); - /* Always write to disk for successful ops with the flag set. This also - * negates the need to detect ordering changes. - */ if ((rc == pcmk_rc_ok) - && pcmk__is_set(operation->flags, cib__op_attr_writes_through)) { + && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - config_changed = true; - } + /* Always write to disk for successful ops with the writes-through flag + * set. This also avoids the need to detect ordering changes. + */ + const bool to_disk = config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through); - if ((rc == pcmk_rc_ok) - && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { + const char *feature_set = pcmk__xe_get(the_cib, + PCMK_XA_CRM_FEATURE_SET); if (result_cib != the_cib) { - rc = based_activate_cib(result_cib, config_changed, op); + rc = based_activate_cib(result_cib, to_disk, op); } /* @COMPAT Nodes older than feature set 3.19.0 don't support @@ -604,9 +599,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, */ if ((operation->type == cib__op_commit_transact) && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) - && (pcmk__compare_versions(pcmk__xe_get(the_cib, - PCMK_XA_CRM_FEATURE_SET), - "3.19.0") < 0)) { + && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { sync_our_cib(request, true); } @@ -645,7 +638,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xml_free(output); } - pcmk__trace("done"); pcmk__xml_free(cib_diff); return rc; } From 583ad2f7be8260a885ac53acd69e8f7869ed16bf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 23:47:31 -0800 Subject: [PATCH 287/350] Feature: libcrmcommon: Assert on memory error in mainloop_add_fd() This technically changes public-facing behavior. I don't see any reason why anything external should be using our mainloop functions, however, so it's only a matter of time until they're deprecated from public API. Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index a63854c8d9d..3c72a4d97a5 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.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. * @@ -956,11 +956,8 @@ mainloop_add_fd(const char *name, int priority, int fd, void *userdata, mainloop_io_t *client = NULL; if (fd >= 0) { - client = calloc(1, sizeof(mainloop_io_t)); - if (client == NULL) { - return NULL; - } - client->name = strdup(name); + client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); + client->name = pcmk__str_copy(name); client->userdata = userdata; if (callbacks) { From 89019ead35a49a908f25572a9d7205f892cfb36c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 23:57:55 -0800 Subject: [PATCH 288/350] Refactor: libcrmcommon: Unindent mainloop_add_fd() Signed-off-by: Reid Wahl --- lib/common/mainloop.c | 59 ++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c index 3c72a4d97a5..da5a1082734 100644 --- a/lib/common/mainloop.c +++ b/lib/common/mainloop.c @@ -954,40 +954,41 @@ mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks * callbacks) { mainloop_io_t *client = NULL; + const GIOCondition condition = G_IO_IN|G_IO_HUP|G_IO_NVAL|G_IO_ERR; - if (fd >= 0) { - client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); - client->name = pcmk__str_copy(name); - client->userdata = userdata; - - if (callbacks) { - client->destroy_fn = callbacks->destroy; - client->dispatch_fn_io = callbacks->dispatch; - } + if (fd < 0) { + errno = EINVAL; + return NULL; + } - client->fd = fd; - client->channel = g_io_channel_unix_new(fd); - client->source = - g_io_add_watch_full(client->channel, priority, - (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR), mainloop_gio_callback, - client, mainloop_gio_destroy); + client = pcmk__assert_alloc(1, sizeof(mainloop_io_t)); + client->name = pcmk__str_copy(name); + client->userdata = userdata; - /* Now that mainloop now holds a reference to channel, - * thanks to g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). - * - * This means that channel will be free'd by: - * g_main_context_dispatch() or g_source_remove() - * -> g_source_destroy_internal() - * -> g_source_callback_unref() - * shortly after mainloop_gio_destroy() completes - */ - g_io_channel_unref(client->channel); - pcmk__trace("Added connection %d for %s[%p].%d", client->source, - client->name, client, fd); - } else { - errno = EINVAL; + if (callbacks != NULL) { + client->destroy_fn = callbacks->destroy; + client->dispatch_fn_io = callbacks->dispatch; } + client->fd = fd; + client->channel = g_io_channel_unix_new(fd); + client->source = g_io_add_watch_full(client->channel, priority, condition, + mainloop_gio_callback, client, + mainloop_gio_destroy); + + /* Now that mainloop now holds a reference to channel, thanks to + * g_io_add_watch_full(), drop ours from g_io_channel_unix_new(). + * + * This means that channel will be free'd by: + * g_main_context_dispatch() or g_source_remove() + * -> g_source_destroy_internal() + * -> g_source_callback_unref() + * shortly after mainloop_gio_destroy() completes + */ + g_io_channel_unref(client->channel); + + pcmk__trace("Added connection %d for %s[%p].%d", client->source, + client->name, client, fd); return client; } From 3653975d30ba60f3c170c90bf72215f730a28eb5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 23:39:09 -0800 Subject: [PATCH 289/350] Low: based: Free remote client if TLS session creation fails This wasn't a memory leak, because we would eventually free the client table. Now every based remote client is guaranteed to have a remote->source. Signed-off-by: Reid Wahl --- daemons/based/based_remote.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index c52e99de50d..07e28eff160 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -573,9 +573,13 @@ cib_remote_listen(gpointer data) /* create gnutls session for the server socket */ new_client->remote->tls_session = pcmk__new_tls_session(tls, csock); if (new_client->remote->tls_session == NULL) { + pcmk__err("Dropping remote connection from %s because we failed to " + "create a TLS session for it", ipstr); + pcmk__free_client(new_client); close(csock); return 0; } + } else { pcmk__set_client_flags(new_client, pcmk__client_tcp); new_client->remote->tcp_socket = csock; From 039f89fe4c1ee28aef89bda6d953b41e28230859 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 22:15:34 -0800 Subject: [PATCH 290/350] Refactor: based: Drop qb_ipcs_stats_get() call Other daemons don't check the number of active IPC client connections via libqb. It seems reasonable to follow the other daemons' lead and assume that the return value of pcmk__ipc_client_count() is all we need to care about. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 42a849a466f..075b3c7d842 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -20,7 +20,6 @@ #include // gboolean, gpointer, g_*, etc. #include // xmlNode -#include // QB_FALSE #include // qb_ipcs_connection_t #include // LOG_TRACE @@ -938,8 +937,6 @@ initiate_exit(void) void based_shutdown(int nsig) { - struct qb_ipcs_stats srv_stats; - if (!cib_shutdown_flag) { int disconnects = 0; qb_ipcs_connection_t *c = NULL; @@ -990,15 +987,13 @@ based_shutdown(int nsig) pcmk__info("Disconnected %d clients", disconnects); } - qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE); - if (pcmk__ipc_client_count() == 0) { - pcmk__info("All clients disconnected (%d)", srv_stats.active_connections); + pcmk__info("All clients disconnected"); initiate_exit(); } else { - pcmk__info("Waiting on %d clients to disconnect (%d)", - pcmk__ipc_client_count(), srv_stats.active_connections); + pcmk__info("Waiting on %d clients to disconnect", + pcmk__ipc_client_count()); } } From dd311e77038411881de43c4f2075879a1130a7b0 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:04:20 -0800 Subject: [PATCH 291/350] Low: based: Drop remote clients during shutdown I dug into the GLib code and it seems safe to assume that the client has been freed when we return from mainloop_del_fd(). It definitely seems like an implementation detail though. It depends on the fact(?) that we're not currently in the source's dispatch function, called by g_main_dispatch(), when we call based_drop_remote_clients(). g_main_dispatch() holds a lock when adding a reference to the client's GSource. It then releases the lock and does a small amount of work. Of that work, everything is inline except a call to a dispatch function. Then it drops the reference that it created and reacquires the lock. We have the following call chain: mainloop_del_fd() -> g_source_remove() -> g_source_delete() -> g_source_delete_internal() g_source_destroy_internal() acquires the lock and then unrefs the client's source. If this was the last reference, the client's GDestroyNotify function (mainloop_gio_destroy()) gets called, which calls based_remote_client_destroy() and frees the client. We will use this assumption to drop some of the based_shutdown() calls in an upcoming commit. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +------- daemons/based/based_remote.c | 50 +++++++++++++++++++++++++++++++++ daemons/based/based_remote.h | 3 +- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 075b3c7d842..8c696b0afc4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -899,15 +899,6 @@ cib_force_exit(gpointer data) return FALSE; } -static void -disconnect_remote_client(gpointer key, gpointer value, gpointer user_data) -{ - pcmk__client_t *a_client = value; - - pcmk__err("Can't disconnect client %s: Not implemented", - pcmk__client_name(a_client)); -} - static void initiate_exit(void) { @@ -983,7 +974,7 @@ based_shutdown(int nsig) pcmk__debug("Disconnecting %d remote clients", pcmk__ipc_client_count()); - pcmk__foreach_ipc_client(disconnect_remote_client, NULL); + based_drop_remote_clients(); pcmk__info("Disconnected %d clients", disconnects); } diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 07e28eff160..5b05cf0f9da 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -700,3 +700,53 @@ based_remote_init(void) remote_fd = init_remote_listener(port); } } + +/*! + * \internal + * \brief Disconnect and free a remote CIB manager client + * + * Drop the client by deleting it from the mainloop. It will be freed by + * \c based_remote_client_destroy() via \c remote_client_fd_callbacks. + * + * \param[in] key Ignored + * \param[in,out] value Remote client (pcmk__client_t *) + * \param[in] user_data Ignored + * + * \note It is safe to call this more than once for the same client, as long as + * the client pointer itself is still valid. \c mainloop_del_fd() calls + * \c g_source_remove(), which calls \c g_source_destroy(). See the + * documentation for \c g_source_destroy(). + * \note The client gets freed via \c mainloop_gio_destroy() when the last + * reference to the client's \c GSource has been removed. There is likely + * only one reference when we call this function, and thus the client is + * likely to be freed before we return. The current GLib code looks as if + * this is always the case. However, that is a GLib implementation detail. + */ +static void +drop_remote_client(gpointer key, gpointer value, gpointer user_data) +{ + pcmk__client_t *client = value; + + CRM_CHECK((client->remote != NULL) && (client->remote->source != NULL), + return); + + pcmk__notice("Disconnecting remote client %s", pcmk__client_name(client)); + mainloop_del_fd(client->remote->source); +} + +/*! + * \internal + * \brief Disconnect and free all remote CIB manager clients + * + * Drop each client by deleting it from the mainloop. It will be freed by + * \c based_remote_client_destroy() via \c remote_client_fd_callbacks. + * + * \param[in] key Ignored + * \param[in,out] value Remote client (pcmk__client_t *) + * \param[in] user_data Ignored + */ +void +based_drop_remote_clients(void) +{ + pcmk__foreach_ipc_client(drop_remote_client, NULL); +} diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index f75661c93ac..c302d513200 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -1,5 +1,5 @@ /* - * Copyright 2025 the Pacemaker project contributors + * Copyright 2025-2026 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -16,5 +16,6 @@ extern int remote_fd; extern int remote_tls_fd; void based_remote_init(void); +void based_drop_remote_clients(void); #endif // BASED_REMOTE__H From f7617988e1183e0218f336bd6af045879ffbd770 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 01:22:39 -0800 Subject: [PATCH 292/350] Refactor: based: Use pcmk__drop_all_clients() in based_shutdown() To align better with attrd, fenced, and schedulerd. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 47 +++++++-------------------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8c696b0afc4..cffdda3a055 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -929,53 +929,24 @@ void based_shutdown(int nsig) { if (!cib_shutdown_flag) { - int disconnects = 0; - qb_ipcs_connection_t *c = NULL; - cib_shutdown_flag = true; - c = qb_ipcs_connection_first_get(ipcs_rw); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_rw, last); - - pcmk__debug("Disconnecting r/w client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; + if (ipcs_ro != NULL) { + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); } - c = qb_ipcs_connection_first_get(ipcs_ro); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_ro, last); - - pcmk__debug("Disconnecting r/o client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; + if (ipcs_rw != NULL) { + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); } - c = qb_ipcs_connection_first_get(ipcs_shm); - while (c != NULL) { - qb_ipcs_connection_t *last = c; - - c = qb_ipcs_connection_next_get(ipcs_shm, last); - - pcmk__debug("Disconnecting non-blocking r/w client %p...", last); - qb_ipcs_disconnect(last); - qb_ipcs_connection_unref(last); - disconnects++; + if (ipcs_shm != NULL) { + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); } - disconnects += pcmk__ipc_client_count(); - - pcmk__debug("Disconnecting %d remote clients", - pcmk__ipc_client_count()); based_drop_remote_clients(); - pcmk__info("Disconnected %d clients", disconnects); } if (pcmk__ipc_client_count() == 0) { From 456ded1893f66a439faa027013e29576a202e5ee Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:26:34 -0800 Subject: [PATCH 293/350] Refactor: based: Assume dropped remote clients are freed The commit message from a couple of commits ago explains this situation in much more detail. It seems safe to assume that a remote client has been freed when drop_remote_client() returns. We generally assume that all libqb IPC clients have been freed when we return from pcmk__drop_all_clients(). With these assumptions in mind, we can get rid of this "wait for clients to disconnect and call back into based_shutdown()" logic, simplifying the based shutdown a bit. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 49 ++++++++++++++++++--------------- daemons/based/based_ipc.c | 9 ------ daemons/based/based_remote.c | 4 --- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cffdda3a055..90360762f41 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -928,35 +928,40 @@ initiate_exit(void) void based_shutdown(int nsig) { - if (!cib_shutdown_flag) { - cib_shutdown_flag = true; + if (cib_shutdown_flag) { + // Already shutting down + return; + } - if (ipcs_ro != NULL) { - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - } + cib_shutdown_flag = true; - if (ipcs_rw != NULL) { - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - } + if (ipcs_ro != NULL) { + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); + } - if (ipcs_shm != NULL) { - pcmk__drop_all_clients(ipcs_shm); - g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); - } + if (ipcs_rw != NULL) { + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + } - based_drop_remote_clients(); + if (ipcs_shm != NULL) { + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); } - if (pcmk__ipc_client_count() == 0) { - pcmk__info("All clients disconnected"); - initiate_exit(); + based_drop_remote_clients(); - } else { - pcmk__info("Waiting on %d clients to disconnect", - pcmk__ipc_client_count()); - } + /* The client count should be 0 by now. If it isn't, that means GLib had + * some other reference to a remote client's source when we dropped the + * remote client. This is believed not to be possible. + * + * If this occurs, continue the shutdown. The clients will be dropped + * eventually. + */ + CRM_LOG_ASSERT(pcmk__ipc_client_count() == 0); + + initiate_exit(); } /*! diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 11ecb399706..5fe66bb5d60 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -279,15 +279,6 @@ based_ipc_destroy(qb_ipcs_connection_t *c) { pcmk__trace("Destroying client connection %p", c); based_ipc_closed(c); - - /* Shut down if this was the last client to leave. - * - * @TODO Is it correct to do this for destroy but not for closed? Other - * daemons handle closed and destroyed connections in the same way. - */ - if (cib_shutdown_flag) { - based_shutdown(0); - } } struct qb_ipcs_service_handlers ipc_ro_callbacks = { diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index 5b05cf0f9da..b5d6f5b8c7c 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -520,10 +520,6 @@ based_remote_client_destroy(gpointer user_data) pcmk__free_client(client); pcmk__trace("Freed the cib client"); - - if (cib_shutdown_flag) { - based_shutdown(0); - } } static int From 765339f74f2c03c77969c3e80ef76fd85f98a1a8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:34:43 -0800 Subject: [PATCH 294/350] Refactor: based: Inline initiate_exit() It no longer has a distinct role; this was no longer a logical split. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 48 +++++++++++++++------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 90360762f41..31646e8c181 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -899,35 +899,12 @@ cib_force_exit(gpointer data) return FALSE; } -static void -initiate_exit(void) -{ - int active = 0; - xmlNode *leaving = NULL; - - active = pcmk__cluster_num_active_nodes(); - if (active < 2) { // This is the last active node - pcmk__info("Exiting without sending shutdown request (no active " - "peers)"); - based_terminate(CRM_EX_OK); - return; - } - - pcmk__info("Sending shutdown request to %d peers", active); - - leaving = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); - pcmk__xe_set(leaving, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(leaving, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, leaving); - pcmk__xml_free(leaving); - - pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); -} - void based_shutdown(int nsig) { + int active = 0; + xmlNode *notification = NULL; + if (cib_shutdown_flag) { // Already shutting down return; @@ -961,7 +938,24 @@ based_shutdown(int nsig) */ CRM_LOG_ASSERT(pcmk__ipc_client_count() == 0); - initiate_exit(); + active = pcmk__cluster_num_active_nodes(); + if (active < 2) { + pcmk__info("Exiting without sending shutdown request (no active " + "peers)"); + based_terminate(CRM_EX_OK); + return; + } + + pcmk__info("Sending shutdown request to %d peers", active); + + notification = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); + pcmk__xe_set(notification, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(notification, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); + + pcmk__cluster_send_message(NULL, pcmk_ipc_based, notification); + pcmk__xml_free(notification); + + pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); } /*! From ecb55eb7bed00b6f211f15805acb3f363238f7f5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 12:58:16 -0800 Subject: [PATCH 295/350] Refactor: based: create_cib_reply/based_diff_notify take standard rc Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 16 ++++++++-------- daemons/based/based_notify.c | 4 ++-- daemons/based/based_notify.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 31646e8c181..8e4e3d7432b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -51,7 +51,7 @@ static bool ping_modified_since = false; * \param[in] call_id CIB call ID * \param[in] client_id CIB client ID * \param[in] call_options Group of enum cib_call_options flags - * \param[in] rc Request return code + * \param[in] rc Request return code (standard Pacemaker return code) * \param[in] call_data Request output data * * \return Reply XML (guaranteed not to be \c NULL) @@ -70,7 +70,7 @@ create_cib_reply(const char *op, const char *call_id, const char *client_id, pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, call_id); pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); - pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, rc); + pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); cib__set_calldata(reply, call_data); crm_log_xml_explicit(reply, "cib:reply"); @@ -624,13 +624,13 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - based_diff_notify(op, pcmk_rc2legacy(rc), call_id, client_id, client_name, - originator, cib_diff); + based_diff_notify(op, rc, call_id, client_id, client_name, originator, + cib_diff); done: if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), output); + *reply = create_cib_reply(op, call_id, client_id, call_options, rc, + output); } if (output != the_cib) { @@ -826,8 +826,8 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(op, call_id, client_id, call_options, - pcmk_rc2legacy(rc), the_cib); + reply = create_cib_reply(op, call_id, client_id, call_options, rc, + the_cib); } else if (process) { time_t start_time = time(NULL); diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 16e31e909b5..7a528ea5a39 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -199,7 +199,7 @@ cib_notify_send(const xmlNode *xml) } void -based_diff_notify(const char *op, int result, const char *call_id, +based_diff_notify(const char *op, int rc, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *diff) { @@ -219,7 +219,7 @@ based_diff_notify(const char *op, int result, const char *call_id, pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, client_name); pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, call_id); pcmk__xe_set(update_msg, PCMK__XA_SRC, origin); - pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, result); + pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE_RESULT); pcmk__xml_copy(wrapper, diff); diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index 79886f79aa1..a4641513085 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -16,7 +16,7 @@ int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); -void based_diff_notify(const char *op, int result, const char *call_id, +void based_diff_notify(const char *op, int rc, const char *call_id, const char *client_id, const char *client_name, const char *origin, xmlNode *diff); From 7bf64d7a6c8e4c44c3faa6c2403094c72e2d15c4 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 13:06:53 -0800 Subject: [PATCH 296/350] Refactor: based: based_diff_notify() takes request argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 +--- daemons/based/based_notify.c | 27 +++++++++++++++++++-------- daemons/based/based_notify.h | 4 +--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 8e4e3d7432b..305f6751e69 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -525,7 +525,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); - const char *client_name = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); uint32_t call_options = cib_none; @@ -624,8 +623,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - based_diff_notify(op, rc, call_id, client_id, client_name, originator, - cib_diff); + based_diff_notify(request, rc, cib_diff); done: if (!pcmk__is_set(call_options, cib_discard_reply)) { diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c index 7a528ea5a39..22686d1c8c5 100644 --- a/daemons/based/based_notify.c +++ b/daemons/based/based_notify.c @@ -199,9 +199,7 @@ cib_notify_send(const xmlNode *xml) } void -based_diff_notify(const char *op, int rc, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *diff) +based_diff_notify(const xmlNode *request, int rc, xmlNode *diff) { xmlNode *update_msg = NULL; xmlNode *wrapper = NULL; @@ -212,13 +210,26 @@ based_diff_notify(const char *op, int rc, const char *call_id, update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY); + /* We could simplify by copying all attributes from request. We would just + * have to ensure that there are never "private" attributes that we want to + * hide from external clients with notify callbacks. + */ pcmk__xe_set(update_msg, PCMK__XA_T, PCMK__VALUE_CIB_NOTIFY); pcmk__xe_set(update_msg, PCMK__XA_SUBT, PCMK__VALUE_CIB_DIFF_NOTIFY); - pcmk__xe_set(update_msg, PCMK__XA_CIB_OP, op); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, client_name); - pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set(update_msg, PCMK__XA_SRC, origin); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_OP, + pcmk__xe_get(request, PCMK__XA_CIB_OP)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTID, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CLIENTNAME, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME)); + + pcmk__xe_set(update_msg, PCMK__XA_CIB_CALLID, + pcmk__xe_get(request, PCMK__XA_CIB_CALLID)); + + pcmk__xe_set(update_msg, PCMK__XA_SRC, pcmk__xe_get(request, PCMK__XA_SRC)); pcmk__xe_set_int(update_msg, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE_RESULT); diff --git a/daemons/based/based_notify.h b/daemons/based/based_notify.h index a4641513085..aec79e7b0b7 100644 --- a/daemons/based/based_notify.h +++ b/daemons/based/based_notify.h @@ -16,8 +16,6 @@ int based_update_notify_flags(const xmlNode *xml, pcmk__client_t *client); -void based_diff_notify(const char *op, int rc, const char *call_id, - const char *client_id, const char *client_name, - const char *origin, xmlNode *diff); +void based_diff_notify(const xmlNode *request, int rc, xmlNode *diff); #endif // BASED_NOTIFY__H From 2ba5ddbbacb0fbfe9b2717810b67ab438b5add86 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 13:17:54 -0800 Subject: [PATCH 297/350] Refactor: based: create_cib_reply() takes request argument Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 42 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 305f6751e69..14d9a520c59 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -47,12 +47,10 @@ static bool ping_modified_since = false; * \internal * \brief Create reply XML for a CIB request * - * \param[in] op CIB operation type - * \param[in] call_id CIB call ID - * \param[in] client_id CIB client ID - * \param[in] call_options Group of enum cib_call_options flags - * \param[in] rc Request return code (standard Pacemaker return code) - * \param[in] call_data Request output data + * \param[in] request CIB request + * \param[in] rc Request return code (standard Pacemaker return code) + * \param[in] call_data Request output data (may be entire live CIB or result + * CIB in case of error) * * \return Reply XML (guaranteed not to be \c NULL) * @@ -60,16 +58,28 @@ static bool ping_modified_since = false; * \p pcmk__xml_free(). */ static xmlNode * -create_cib_reply(const char *op, const char *call_id, const char *client_id, - uint32_t call_options, int rc, xmlNode *call_data) +create_cib_reply(const xmlNode *request, int rc, xmlNode *call_data) { xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_CIB_REPLY); pcmk__xe_set(reply, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(reply, PCMK__XA_CIB_OP, op); - pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, call_id); - pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, client_id); - pcmk__xe_set_int(reply, PCMK__XA_CIB_CALLOPT, call_options); + + /* We could simplify by copying all attributes from request. We would just + * have to ensure that there are never "private" attributes that we want to + * hide from external clients with notify callbacks. + */ + pcmk__xe_set(reply, PCMK__XA_CIB_OP, + pcmk__xe_get(request, PCMK__XA_CIB_OP)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CALLID, + pcmk__xe_get(request, PCMK__XA_CIB_CALLID)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CLIENTID, + pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID)); + + pcmk__xe_set(reply, PCMK__XA_CIB_CALLOPT, + pcmk__xe_get(request, PCMK__XA_CIB_CALLOPT)); + pcmk__xe_set_int(reply, PCMK__XA_CIB_RC, pcmk_rc2legacy(rc)); cib__set_calldata(reply, call_data); @@ -523,8 +533,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, xmlNode *result_cib = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); - const char *call_id = pcmk__xe_get(request, PCMK__XA_CIB_CALLID); - const char *client_id = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTID); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); uint32_t call_options = cib_none; @@ -627,8 +635,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, done: if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(op, call_id, client_id, call_options, rc, - output); + *reply = create_cib_reply(request, rc, output); } if (output != the_cib) { @@ -824,8 +831,7 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(op, call_id, client_id, call_options, rc, - the_cib); + reply = create_cib_reply(request, rc, the_cib); } else if (process) { time_t start_time = time(NULL); From 1182dd6ea23ea1a5d0a5d869e2d17bd71b267354 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 13:25:25 -0800 Subject: [PATCH 298/350] Doc: based: Drop irrelevant comment about legacy mode Legacy mode was removed by commit 7198fa62 (by making cib_legacy_mode() return FALSE). Then commit 061277ee made this comment's irrelevance explicit. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 14d9a520c59..081af02e2c9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -813,10 +813,6 @@ based_process_request(xmlNode *request, bool privileged, } if (pcmk__is_set(call_options, cib_discard_reply)) { - /* If the request will modify the CIB, and we are in legacy mode, we - * need to build a reply so we can broadcast a diff, even if the - * requester doesn't want one. - */ needs_reply = false; local_notify = false; pcmk__trace("Client is not interested in the reply"); From ab31d74e78774789a49a39ead91a139712b1f86b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:27:06 -0800 Subject: [PATCH 299/350] Refactor: based: Unindent do_local_notify() call Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 081af02e2c9..2783349e632 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -853,12 +853,14 @@ based_process_request(xmlNode *request, bool privileged, send_peer_reply(reply, originator); } - if (local_notify && (client_id != NULL)) { - do_local_notify((process? reply : request), client_id, - pcmk__is_set(call_options, cib_sync_call), - (client == NULL)); + if (!local_notify || (client_id == NULL)) { + goto done; } + do_local_notify((process? reply : request), client_id, + pcmk__is_set(call_options, cib_sync_call), + (client == NULL)); + done: pcmk__xml_free(reply); return rc; From b5980117c20a930e3356984d44cad46c56d72d7a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:33:21 -0800 Subject: [PATCH 300/350] Refactor: based: Guard create_cib_reply() call with !cib_discard_reply The other call is already guarded. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 2783349e632..971b0534265 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -827,7 +827,10 @@ based_process_request(xmlNode *request, bool privileged, rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " "(please repair and restart): %s", pcmk_rc_str(rc)); - reply = create_cib_reply(request, rc, the_cib); + + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, the_cib); + } } else if (process) { time_t start_time = time(NULL); From 2c08d127caa1c166e78b6a8df623d7f147327850 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:37:25 -0800 Subject: [PATCH 301/350] Refactor: based: Move privilege check to based_process_request() Just an incremental change. This will simplify further in upcoming commits. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 971b0534265..a2604d00ec0 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -524,7 +524,7 @@ forward_request(xmlNode *request) static int cib_process_command(xmlNode *request, const cib__operation_t *operation, - cib__op_fn_t op_function, xmlNode **reply, bool privileged) + cib__op_fn_t op_function, xmlNode **reply) { static mainloop_timer_t *digest_timer = NULL; @@ -547,13 +547,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, /* Start processing the request... */ pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { - - rc = EACCES; - goto done; - } - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { rc = cib__perform_op_ro(op_function, request, &the_cib, &output); goto done; @@ -835,8 +828,18 @@ based_process_request(xmlNode *request, bool privileged, } else if (process) { time_t start_time = time(NULL); - rc = cib_process_command(request, operation, op_function, &reply, - privileged); + if (!privileged + && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + + rc = EACCES; + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, NULL); + } + + } else { + rc = cib_process_command(request, operation, op_function, &reply); + } + log_op_result(request, operation, rc, (time(NULL) - start_time)); if ((reply == NULL) && (needs_reply || local_notify)) { From 0ce16647ce1f59cd9a9673f599d6d30adb3e8767 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:43:52 -0800 Subject: [PATCH 302/350] Refactor: based: Drop NULL-check of reply from cib_process_command() If needs_reply or local_notify is true, then cib_discard_reply is not set. (If cib_discard_reply is set, then we set needs_reply and local_notify to false earlier in based_process_request().) If cib_discard_reply is not set, then cib_process_command() always creates a non-NULL reply (in the done section). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a2604d00ec0..9d5a4bb5678 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -841,12 +841,6 @@ based_process_request(xmlNode *request, bool privileged, } log_op_result(request, operation, rc, (time(NULL) - start_time)); - - if ((reply == NULL) && (needs_reply || local_notify)) { - pcmk__err("Unexpected NULL reply to message"); - pcmk__log_xml_err(request, "null reply"); - goto done; - } } if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { From 6e5426d2e642f20668afea0676cbdc185b786b0e Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 14:47:17 -0800 Subject: [PATCH 303/350] Refactor: based: Drop based_process_request last cib_discard_reply check It's redundant. If needs_reply is true, then cib_discard_reply is not set. (If cib_discard_reply is set, then we set needs_reply and local_notify to false earlier in based_process_request().) Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 9d5a4bb5678..00cf02c9f40 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -848,8 +848,7 @@ based_process_request(xmlNode *request, bool privileged, pcmk__s(originator, "local"), client_name, call_id, (local_notify? " with local notification" : "")); - } else if (needs_reply && !stand_alone && (client == NULL) - && !pcmk__is_set(call_options, cib_discard_reply)) { + } else if (needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } From d08942e95a7c1aad8b7110c71e08cfffb7c5fce1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 15:18:11 -0800 Subject: [PATCH 304/350] Refactor: libcib: Use done label in cib__perform_op_ro() Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 28cb7f8d940..e261b680012 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -251,26 +251,34 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, rc = fn(req, &cib, output); - if (*output == NULL) { - // Do nothing + if (cib_filtered == *output) { + // Let the caller have this copy + return rc; + } - } else if (cib_filtered == *output) { - // Let them have this copy - cib_filtered = NULL; + if (*output == NULL) { + goto done; + } - } else if (*output == *current_cib) { - // They already know not to free it + if (*output == *current_cib) { + // Trust the caller to check this and not free *output + goto done; + } - } else if ((cib_filtered != NULL) - && ((*output)->doc == cib_filtered->doc)) { - // We're about to free the document of which *output is a part + if ((*output)->doc == (*current_cib)->doc) { + // Give the caller a copy that it can free *output = pcmk__xml_copy(NULL, *output); + goto done; + } - } else if ((*output)->doc == (*current_cib)->doc) { - // Give them a copy they can free - *output = pcmk__xml_copy(NULL, *output); + if ((cib_filtered == NULL) || ((*output)->doc != cib_filtered->doc)) { + goto done; } + // We're about to free the document of which *output is a part + *output = pcmk__xml_copy(NULL, *output); + +done: pcmk__xml_free(cib_filtered); return rc; } From e0298b658fe5e5d9aad2850c8a934152339d996a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 15:53:03 -0800 Subject: [PATCH 305/350] Low: libcib: Avoid memory leak in processing CIB file commit transaction process_transaction_requests() wasn't freeing output. Do that now. Also simplify the freeing of output. * process_request(): Make a copy of *output if it's not in the same doc as private->cib_xml. This lets us simplify file_perform_op_delegate(). * process_request(): No operation leaves result_cib set to the same value as *output unless it's also equal to private->cib_xml. (A query op can do this.) So drop a check in the done section. * file_perform_op_delegate(): Return only at the end of the function, within the done section. * file_perform_op_delegate(): Don't assign *output_data = NULL at the beginning. We'll do this in the done section if output is NULL. * file_perform_op_delegate(): Assume that output is not in the same document as private->cib_xml. We ensured this in process_request(). * file_perform_op_delegate(): Free output if and only if it has not been assigned to *output_data. And do the standard-to-legacy conversion at the end of file_perform_op_delegate(), in the done section. Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 59 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 4c0c31a9f32..133e2c19dbd 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -192,8 +192,16 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) set_file_flags(private, file_flag_dirty); } + if (*output == NULL) { + goto done; + } + + if ((*output)->doc == private->cib_xml->doc) { + *output = pcmk__xml_copy(NULL, *output); + } + done: - if ((result_cib != private->cib_xml) && (result_cib != *output)) { + if (result_cib != private->cib_xml) { pcmk__xml_free(result_cib); } pcmk__xml_free(cib_diff); @@ -227,6 +235,8 @@ process_transaction_requests(cib_t *cib, xmlNode *transaction) int rc = process_request(cib, request, &output); + pcmk__xml_free(output); + if (rc != pcmk_rc_ok) { pcmk__err("Aborting transaction for CIB file client (%s) on file " "'%s' due to failed %s request: %s", @@ -406,38 +416,35 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, const cib__operation_t *operation = NULL; - pcmk__info("Handling %s operation for %s as %s", - pcmk__s(op, "invalid"), pcmk__s(section, "entire CIB"), + pcmk__info("Handling %s operation for %s as %s", pcmk__s(op, "invalid"), + pcmk__s(section, "entire CIB"), pcmk__s(user_name, "default user")); - if (output_data != NULL) { - *output_data = NULL; - } - if (cib->state == cib_disconnected) { - return -ENOTCONN; + rc = ENOTCONN; + goto done; } rc = cib__get_operation(op, &operation); - rc = pcmk_rc2legacy(rc); - if (rc != pcmk_ok) { + if (rc != pcmk_rc_ok) { // @COMPAT: At compatibility break, use rc directly - return -EPROTONOSUPPORT; + rc = EPROTONOSUPPORT; + goto done; } if (get_op_function(operation) == NULL) { // @COMPAT: At compatibility break, use EOPNOTSUPP pcmk__err("Operation %s is not supported by CIB file clients", op); - return -EPROTONOSUPPORT; + rc = EPROTONOSUPPORT; + goto done; } cib__set_call_options(call_options, "file operation", cib_no_mtime); rc = cib__create_op(cib, op, host, section, data, call_options, user_name, NULL, &request); - rc = pcmk_rc2legacy(rc); - if (rc != pcmk_ok) { - return rc; + if (rc != pcmk_rc_ok) { + goto done; } pcmk__xe_set(request, PCMK__XA_ACL_TARGET, user_name); @@ -445,30 +452,22 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, if (pcmk__is_set(call_options, cib_transaction)) { rc = cib__extend_transaction(cib, request); - rc = pcmk_rc2legacy(rc); goto done; } rc = process_request(cib, request, &output); - rc = pcmk_rc2legacy(rc); - - if ((output_data != NULL) && (output != NULL)) { - if (output->doc == private->cib_xml->doc) { - *output_data = pcmk__xml_copy(NULL, output); - } else { - *output_data = output; - } - } done: - if ((output != NULL) - && (output->doc != private->cib_xml->doc) - && ((output_data == NULL) || (output != *output_data))) { + pcmk__xml_free(request); + if (output_data != NULL) { + *output_data = output; + + } else { pcmk__xml_free(output); } - pcmk__xml_free(request); - return rc; + + return pcmk_rc2legacy(rc); } /*! From 7bcfdeb3bf50f4bb331830c4629423d990390b18 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 16:35:55 -0800 Subject: [PATCH 306/350] Refactor: libcib: Avoid unnecessary copy in process_request() If file_perform_op_delegate()'s output_data argument is NULL, then we would just free the copy (output) as soon as we return from process_request(). Signed-off-by: Reid Wahl --- lib/cib/cib_file.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c index 133e2c19dbd..55ebb8639fa 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -149,9 +149,14 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) bool read_only = false; xmlNode *result_cib = NULL; xmlNode *cib_diff = NULL; + xmlNode *local_output = NULL; file_opaque_t *private = cib->variant_opaque; + if (output != NULL) { + *output = NULL; + } + // We error checked these in callers cib__get_operation(op, &operation); op_function = get_op_function(operation); @@ -166,11 +171,11 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) if (read_only) { rc = cib__perform_op_ro(op_function, request, &private->cib_xml, - output); + &local_output); } else { result_cib = private->cib_xml; rc = cib__perform_op_rw(cib_file, op_function, request, &changed, - &result_cib, &cib_diff, output); + &result_cib, &cib_diff, &local_output); } if (pcmk__is_set(call_options, cib_transaction)) { @@ -192,12 +197,22 @@ process_request(cib_t *cib, xmlNode *request, xmlNode **output) set_file_flags(private, file_flag_dirty); } - if (*output == NULL) { + if (local_output == NULL) { + goto done; + } + + if ((output != NULL) && (local_output->doc != private->cib_xml->doc)) { + *output = local_output; + goto done; + } + + if (output != NULL) { + *output = pcmk__xml_copy(NULL, local_output); goto done; } - if ((*output)->doc == private->cib_xml->doc) { - *output = pcmk__xml_copy(NULL, *output); + if (local_output->doc != private->cib_xml->doc) { + pcmk__xml_free(local_output); } done: @@ -411,7 +426,6 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, { int rc = pcmk_ok; xmlNode *request = NULL; - xmlNode *output = NULL; file_opaque_t *private = cib->variant_opaque; const cib__operation_t *operation = NULL; @@ -455,18 +469,10 @@ file_perform_op_delegate(cib_t *cib, const char *op, const char *host, goto done; } - rc = process_request(cib, request, &output); + rc = process_request(cib, request, output_data); done: pcmk__xml_free(request); - - if (output_data != NULL) { - *output_data = output; - - } else { - pcmk__xml_free(output); - } - return pcmk_rc2legacy(rc); } From 4ca1a7f18065b09adb00acf0d083ed1d55d539bf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 17:22:34 -0800 Subject: [PATCH 307/350] Refactor: libcib: Don't copy output that's part of the current CIB's doc cib_file.c was already checking for this and would not free output if it was part of the same doc as the current CIB. The CIB manager was preserving output only if it was equal to the current CIB. So we just have to update the CIB manager to compare the docs, and update cib__perform_op_ro() to skip the copy if the docs are the same. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 2 +- lib/cib/cib_utils.c | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 00cf02c9f40..182960fd189 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -631,7 +631,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, *reply = create_cib_reply(request, rc, output); } - if (output != the_cib) { + if ((output != NULL) && (output->doc != the_cib->doc)) { pcmk__xml_free(output); } diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index e261b680012..687f0d34d77 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -260,14 +260,8 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, goto done; } - if (*output == *current_cib) { - // Trust the caller to check this and not free *output - goto done; - } - if ((*output)->doc == (*current_cib)->doc) { - // Give the caller a copy that it can free - *output = pcmk__xml_copy(NULL, *output); + // Trust the caller to check this and not free *output goto done; } @@ -823,8 +817,10 @@ cib_internal_op(cib_t * cib, const char *op, const char *host, const char *section, xmlNode * data, xmlNode ** output_data, int call_options, const char *user_name) { - /* Note: *output_data gets set only for create and query requests. There are - * a lot of opportunities to clean up, clarify, check/enforce things, etc. + /* @COMPAT *output_data gets set only for create and query requests. Setting + * it for create requests is deprecated since 2.1.8. When that behavior is + * removed, we can restrict freeing it to read-only operations + * (cib__perform_op_ro()). */ int (*delegate)(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, From e2511a45cd8c1a19452234abb1502c2372d7bf0d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 17:46:22 -0800 Subject: [PATCH 308/350] Refactor: libcib: Minor cib__perform_op_ro() cleanup And drop an unhelpful trace message in cib__perform_op_rw(). Signed-off-by: Reid Wahl --- lib/cib/cib_utils.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 687f0d34d77..57fe5ed03a1 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -217,7 +217,6 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, const char *op = NULL; const char *section = NULL; const char *user = NULL; - uint32_t call_options = cib_none; xmlNode *cib = NULL; xmlNode *cib_filtered = NULL; @@ -229,13 +228,11 @@ cib__perform_op_ro(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib, op = pcmk__xe_get(req, PCMK__XA_CIB_OP); section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); user = pcmk__xe_get(req, PCMK__XA_CIB_USER); - pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); cib = *current_cib; - if (cib_acl_enabled(*current_cib, user) - && xml_acl_filtered_copy(user, *current_cib, *current_cib, - &cib_filtered)) { + if (cib_acl_enabled(cib, user) + && xml_acl_filtered_copy(user, cib, cib, &cib_filtered)) { if (cib_filtered == NULL) { pcmk__debug("Pre-filtered the entire cib"); @@ -631,7 +628,6 @@ cib__perform_op_rw(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req, } pcmk__xml_free(top); - pcmk__trace("Done"); return rc; } From a1570da08849df1cd273c0d55029429dc949dcf8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 17:58:26 -0800 Subject: [PATCH 309/350] Refactor: based: based_process_request gets last create_cib_reply check The other two were already in based_process_request(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 37 +++++++++++++++------------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 182960fd189..767b1903c7a 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -524,12 +524,11 @@ forward_request(xmlNode *request) static int cib_process_command(xmlNode *request, const cib__operation_t *operation, - cib__op_fn_t op_function, xmlNode **reply) + cib__op_fn_t op_function, xmlNode **output) { static mainloop_timer_t *digest_timer = NULL; xmlNode *cib_diff = NULL; - xmlNode *output = NULL; xmlNode *result_cib = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); @@ -548,7 +547,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + rc = cib__perform_op_ro(op_function, request, &the_cib, output); goto done; } @@ -568,7 +567,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, */ result_cib = the_cib; rc = cib__perform_op_rw(cib_undefined, op_function, request, - &config_changed, &result_cib, &cib_diff, &output); + &config_changed, &result_cib, &cib_diff, output); if ((rc == pcmk_rc_ok) && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { @@ -608,12 +607,12 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, } else if (rc == pcmk_rc_schema_validation) { pcmk__assert(result_cib != the_cib); - if (output != NULL) { - pcmk__log_xml_info(output, "cib:output"); - pcmk__xml_free(output); + if (*output != NULL) { + pcmk__log_xml_info(*output, "cib:output"); + pcmk__xml_free(*output); } - output = result_cib; + *output = result_cib; } else if (result_cib != the_cib) { pcmk__xml_free(result_cib); @@ -627,14 +626,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, based_diff_notify(request, rc, cib_diff); done: - if (!pcmk__is_set(call_options, cib_discard_reply)) { - *reply = create_cib_reply(request, rc, output); - } - - if ((output != NULL) && (output->doc != the_cib->doc)) { - pcmk__xml_free(output); - } - pcmk__xml_free(cib_diff); return rc; } @@ -826,21 +817,27 @@ based_process_request(xmlNode *request, bool privileged, } } else if (process) { + xmlNode *output = NULL; time_t start_time = time(NULL); if (!privileged && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { rc = EACCES; - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, NULL); - } } else { - rc = cib_process_command(request, operation, op_function, &reply); + rc = cib_process_command(request, operation, op_function, &output); } log_op_result(request, operation, rc, (time(NULL) - start_time)); + + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, output); + } + + if ((output != NULL) && (output->doc != the_cib->doc)) { + pcmk__xml_free(output); + } } if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { From ceb7738390b50d994074169a48178737392dc57d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:09:30 -0800 Subject: [PATCH 310/350] Refactor: based: Pull read-only ops out of cib_process_command() And rename to based_perform_op_rw(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 767b1903c7a..ef4ad5ba541 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -523,13 +523,13 @@ forward_request(xmlNode *request) } static int -cib_process_command(xmlNode *request, const cib__operation_t *operation, +based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { static mainloop_timer_t *digest_timer = NULL; + xmlNode *result_cib = the_cib; xmlNode *cib_diff = NULL; - xmlNode *result_cib = NULL; const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP); const char *originator = pcmk__xe_get(request, PCMK__XA_SRC); @@ -538,19 +538,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, bool config_changed = false; int rc = pcmk_rc_ok; - if (digest_timer == NULL) { - digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, - digest_timer_cb, NULL); - } - - /* Start processing the request... */ pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); - if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, output); - goto done; - } - /* @TODO The cib__op_attr_modifies flag means the request *may* modify * *something*. A successful request with this flag set may not have * modified anything (for example, a delete request when there is no match @@ -565,7 +554,6 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, * It's not important whether the client variant is cib_native or * cib_remote. */ - result_cib = the_cib; rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, output); @@ -602,6 +590,11 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, sync_our_cib(request, true); } + if (digest_timer == NULL) { + digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, + digest_timer_cb, NULL); + } + mainloop_timer_start(digest_timer); } else if (rc == pcmk_rc_schema_validation) { @@ -825,8 +818,11 @@ based_process_request(xmlNode *request, bool privileged, rc = EACCES; + } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + } else { - rc = cib_process_command(request, operation, op_function, &output); + rc = based_perform_op_rw(request, operation, op_function, &output); } log_op_result(request, operation, rc, (time(NULL) - start_time)); From a7d4a5e567790f47d97e71851e68def7046b6e81 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:12:05 -0800 Subject: [PATCH 311/350] Low: based: Free digest_timer on exit Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ef4ad5ba541..c1cd3420bf7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -39,6 +39,7 @@ #define EXIT_ESCALATION_MS 10000 +static mainloop_timer_t *digest_timer = NULL; static long long ping_seq = 0; static char *ping_digest = NULL; static bool ping_modified_since = false; @@ -526,8 +527,6 @@ static int based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { - static mainloop_timer_t *digest_timer = NULL; - xmlNode *result_cib = the_cib; xmlNode *cib_diff = NULL; @@ -971,6 +970,7 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); From 9a523dc171b9bd4b796b04316d3772d7fe9d7ec2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:18:27 -0800 Subject: [PATCH 312/350] Refactor: based: Move notify to done section in based_perform_op_rw() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c1cd3420bf7..f1a2e9978a9 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -595,8 +595,10 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } mainloop_timer_start(digest_timer); + goto done; + } - } else if (rc == pcmk_rc_schema_validation) { + if (rc == pcmk_rc_schema_validation) { pcmk__assert(result_cib != the_cib); if (*output != NULL) { @@ -605,19 +607,20 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, } *output = result_cib; + goto done; + } - } else if (result_cib != the_cib) { + if (result_cib != the_cib) { pcmk__xml_free(result_cib); } - if (pcmk__any_flags_set(call_options, - cib_dryrun|cib_inhibit_notify|cib_transaction)) { - goto done; - } +done: + if (!pcmk__any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { - based_diff_notify(request, rc, cib_diff); + based_diff_notify(request, rc, cib_diff); + } -done: pcmk__xml_free(cib_diff); return rc; } From e07dfdb3d150581d2ac1ff004f21a56bfea0ba0d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:28:08 -0800 Subject: [PATCH 313/350] Refactor: based: Unindent successful common case in based_perform_op_rw Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 84 ++++++++++++++++----------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f1a2e9978a9..1d7cc9ea6bd 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -527,6 +527,8 @@ static int based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, cib__op_fn_t op_function, xmlNode **output) { + const char *feature_set = pcmk__xe_get(the_cib, + PCMK_XA_CRM_FEATURE_SET); xmlNode *result_cib = the_cib; xmlNode *cib_diff = NULL; @@ -556,64 +558,62 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, output); - if ((rc == pcmk_rc_ok) - && !pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - - /* Always write to disk for successful ops with the writes-through flag - * set. This also avoids the need to detect ordering changes. - */ - const bool to_disk = config_changed - || pcmk__is_set(operation->flags, - cib__op_attr_writes_through); - - const char *feature_set = pcmk__xe_get(the_cib, - PCMK_XA_CRM_FEATURE_SET); + if (rc == pcmk_rc_schema_validation) { + pcmk__assert(result_cib != the_cib); - if (result_cib != the_cib) { - rc = based_activate_cib(result_cib, to_disk, op); + if (*output != NULL) { + pcmk__log_xml_info(*output, "cib:output"); + pcmk__xml_free(*output); } - /* @COMPAT Nodes older than feature set 3.19.0 don't support - * transactions. In a mixed-version cluster with nodes <3.19.0, we must - * sync the updated CIB, so that the older nodes receive the changes. - * Any node that has already applied the transaction will ignore the - * synced CIB. - * - * To ensure the updated CIB is synced from only one node, we sync it - * from the originator. - */ - if ((operation->type == cib__op_commit_transact) - && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) - && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { + *output = result_cib; + goto done; + } - sync_our_cib(request, true); - } + // Discard result for failure, dry run, or within-transaction request + if ((rc != pcmk_rc_ok) + || pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - if (digest_timer == NULL) { - digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, - digest_timer_cb, NULL); + if (result_cib != the_cib) { + pcmk__xml_free(result_cib); } - mainloop_timer_start(digest_timer); goto done; } - if (rc == pcmk_rc_schema_validation) { - pcmk__assert(result_cib != the_cib); + if (result_cib != the_cib) { + /* Always write to disk for successful ops with the writes-through flag + * set. This also avoids the need to detect ordering changes. + */ + const bool to_disk = config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through); - if (*output != NULL) { - pcmk__log_xml_info(*output, "cib:output"); - pcmk__xml_free(*output); - } + rc = based_activate_cib(result_cib, to_disk, op); + } - *output = result_cib; - goto done; + /* @COMPAT Nodes older than feature set 3.19.0 don't support transactions. + * In a mixed-version cluster with nodes <3.19.0, we must sync the updated + * CIB, so that the older nodes receive the changes. Any node that has + * already applied the transaction will ignore the synced CIB. + * + * To ensure the updated CIB is synced from only one node, we sync it from + * the originator. + */ + if ((operation->type == cib__op_commit_transact) + && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) + && (pcmk__compare_versions(feature_set, "3.19.0") < 0)) { + + sync_our_cib(request, true); } - if (result_cib != the_cib) { - pcmk__xml_free(result_cib); + if (digest_timer == NULL) { + digest_timer = mainloop_timer_add("based_digest_timer", 5000, false, + digest_timer_cb, NULL); } + mainloop_timer_start(digest_timer); + done: if (!pcmk__any_flags_set(call_options, cib_dryrun|cib_inhibit_notify|cib_transaction)) { From 7343cea741cd10a547d188216403494c61b4d7c9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:44:11 -0800 Subject: [PATCH 314/350] Refactor: based: Drop dead output log msg for pcmk_rc_schema_validation "Create" is the only read-write operation that may set *output. There is no path by which a create operation can return pcmk_rc_schema_validation. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 1d7cc9ea6bd..683135aee83 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -558,14 +558,11 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, rc = cib__perform_op_rw(cib_undefined, op_function, request, &config_changed, &result_cib, &cib_diff, output); + /* On validation error, include the schema-violating result CIB in any reply + * or notification that we will send. + */ if (rc == pcmk_rc_schema_validation) { - pcmk__assert(result_cib != the_cib); - - if (*output != NULL) { - pcmk__log_xml_info(*output, "cib:output"); - pcmk__xml_free(*output); - } - + pcmk__assert((result_cib != the_cib) && (*output == NULL)); *output = result_cib; goto done; } From e592e5906ab2a260f35b931944ad1fd1a3d942eb Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 18:46:48 -0800 Subject: [PATCH 315/350] Fix: based: Fix handling of intermediate transaction results If a request within a transaction succeeds, we don't want to discard the result CIB. We need it for the next request in the transaction, or for the end result if this is the last request to process. So we want to activate it. However, we do NOT want to write it to disk. If the system or CIB manager crashes while committing a transaction, and we restore an intermediate result, the transaction was not atomic. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 683135aee83..a05e2eee9e5 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -567,10 +567,8 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, goto done; } - // Discard result for failure, dry run, or within-transaction request - if ((rc != pcmk_rc_ok) - || pcmk__any_flags_set(call_options, cib_dryrun|cib_transaction)) { - + // Discard result for failure or dry run + if ((rc != pcmk_rc_ok) || pcmk__any_flags_set(call_options, cib_dryrun)) { if (result_cib != the_cib) { pcmk__xml_free(result_cib); } @@ -581,10 +579,14 @@ based_perform_op_rw(xmlNode *request, const cib__operation_t *operation, if (result_cib != the_cib) { /* Always write to disk for successful ops with the writes-through flag * set. This also avoids the need to detect ordering changes. + * + * An exception is a request within a transaction. Since a transaction + * is atomic, intermediate results must not be written to disk. */ - const bool to_disk = config_changed - || pcmk__is_set(operation->flags, - cib__op_attr_writes_through); + const bool to_disk = !pcmk__is_set(call_options, cib_transaction) + && (config_changed + || pcmk__is_set(operation->flags, + cib__op_attr_writes_through)); rc = based_activate_cib(result_cib, to_disk, op); } From 60f249fb5d0aa45a32404ccf783f543de47701c3 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 19:09:15 -0800 Subject: [PATCH 316/350] Log: based: Drop redundant log in based_process_request() We get the same information from log_op_result() and do_local_notify(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a05e2eee9e5..cd1b1034f36 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -837,12 +837,9 @@ based_process_request(xmlNode *request, bool privileged, } } - if (pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - pcmk__trace("Completed pre-sync update from %s/%s/%s%s", - pcmk__s(originator, "local"), client_name, call_id, - (local_notify? " with local notification" : "")); + if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) + && needs_reply && !stand_alone && (client == NULL)) { - } else if (needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } From d92ea1664da326edd17c1de886ba0c2307833a8b Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 19:13:43 -0800 Subject: [PATCH 317/350] Refactor: based: Unindent "if (process)" block of based_process_request Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 54 ++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index cd1b1034f36..d925c111074 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -734,6 +734,9 @@ based_process_request(xmlNode *request, bool privileged, const cib__operation_t *operation = NULL; cib__op_fn_t op_function = NULL; + xmlNode *output = NULL; + time_t start_time = 0; + rc = pcmk__xe_get_flags(request, PCMK__XA_CIB_CALLOPT, &call_options, cib_none); if (rc != pcmk_rc_ok) { @@ -810,48 +813,49 @@ based_process_request(xmlNode *request, bool privileged, reply = create_cib_reply(request, rc, the_cib); } - } else if (process) { - xmlNode *output = NULL; - time_t start_time = time(NULL); + goto done; + } - if (!privileged - && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { + if (!process) { + goto done; + } - rc = EACCES; + start_time = time(NULL); - } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + if (!privileged + && pcmk__is_set(operation->flags, cib__op_attr_privileged)) { - } else { - rc = based_perform_op_rw(request, operation, op_function, &output); - } + rc = EACCES; + + } else if (!pcmk__is_set(operation->flags, cib__op_attr_modifies)) { + rc = cib__perform_op_ro(op_function, request, &the_cib, &output); + } else { + rc = based_perform_op_rw(request, operation, op_function, &output); + } - log_op_result(request, operation, rc, (time(NULL) - start_time)); + log_op_result(request, operation, rc, (time(NULL) - start_time)); - if (!pcmk__is_set(call_options, cib_discard_reply)) { - reply = create_cib_reply(request, rc, output); - } + if (!pcmk__is_set(call_options, cib_discard_reply)) { + reply = create_cib_reply(request, rc, output); + } - if ((output != NULL) && (output->doc != the_cib->doc)) { - pcmk__xml_free(output); - } + if ((output != NULL) && (output->doc != the_cib->doc)) { + pcmk__xml_free(output); } +done: if (!pcmk__is_set(operation->flags, cib__op_attr_modifies) && needs_reply && !stand_alone && (client == NULL)) { send_peer_reply(reply, originator); } - if (!local_notify || (client_id == NULL)) { - goto done; + if (local_notify && (client_id != NULL)) { + do_local_notify((process? reply : request), client_id, + pcmk__is_set(call_options, cib_sync_call), + (client == NULL)); } - do_local_notify((process? reply : request), client_id, - pcmk__is_set(call_options, cib_sync_call), - (client == NULL)); - -done: pcmk__xml_free(reply); return rc; } From f125775ca445bdbdb4e6906a7ccd02aba19077ee Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 23:39:57 -0800 Subject: [PATCH 318/350] Refactor: based: Don't call option parser when cib_transaction is set We make this change despite the previous comment saying we wanted log messages and the target check. Some of the messages are misleading or confusing in the context of a within-transaction request, and skipping that call will facilitate some other changes. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index d925c111074..d8d092db3b3 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -770,27 +770,26 @@ based_process_request(xmlNode *request, bool privileged, return EOPNOTSUPP; } - if (client != NULL) { - parse_local_options(client, operation, host, op, &local_notify, - &needs_reply, &process, &needs_forward); - - } else if (!parse_peer_options(operation, request, &local_notify, - &needs_reply, &process)) { - return pcmk_rc_ok; - } - if (pcmk__is_set(call_options, cib_transaction)) { /* All requests in a transaction are processed locally against a working * CIB copy, and we don't notify for individual requests because the * entire transaction is atomic. - * - * We still call the option parser functions above, for the sake of log - * messages and checking whether we're the target for peer requests. */ process = true; needs_reply = false; local_notify = false; needs_forward = false; + + pcmk__trace("Processing %s op from client %s locally because it's part " + "of a transaction", op, pcmk__client_name(client)); + + } else if (client != NULL) { + parse_local_options(client, operation, host, op, &local_notify, + &needs_reply, &process, &needs_forward); + + } else if (!parse_peer_options(operation, request, &local_notify, + &needs_reply, &process)) { + return pcmk_rc_ok; } if (pcmk__is_set(call_options, cib_discard_reply)) { From 9f389f0485361bb2e9cfeff5ad837f85b7c15a6f Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Sun, 4 Jan 2026 23:51:58 -0800 Subject: [PATCH 319/350] Refactor: based: Drop needs_forward variable Pull the check inline. If it's true, then we can call forward_request() and return immediately. We can also drop the trace message, because we have one in forward_request(). Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 37 +++++++++++---------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index d8d092db3b3..205420c1cf6 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -294,13 +294,12 @@ static void parse_local_options(const pcmk__client_t *client, const cib__operation_t *operation, const char *host, const char *op, bool *local_notify, - bool *needs_reply, bool *process, bool *needs_forward) + bool *needs_reply, bool *process) { // Process locally and notify local client *process = true; *needs_reply = false; *local_notify = true; - *needs_forward = false; if (pcmk__is_set(operation->flags, cib__op_attr_local)) { /* Always process locally if cib__op_attr_local is set. @@ -322,21 +321,6 @@ parse_local_options(const pcmk__client_t *client, return; } - if (pcmk__is_set(operation->flags, cib__op_attr_modifies) - || !pcmk__str_eq(host, OUR_NODENAME, - pcmk__str_casei|pcmk__str_null_matches)) { - - // Forward modifying and non-local requests via cluster - *process = false; - *needs_reply = false; - *local_notify = false; - *needs_forward = true; - - pcmk__trace("%s op from %s needs to be forwarded to %s", op, - pcmk__client_name(client), pcmk__s(host, "all nodes")); - return; - } - if (stand_alone) { pcmk__trace("Processing %s op from client %s (stand-alone)", op, pcmk__client_name(client)); @@ -717,7 +701,6 @@ based_process_request(xmlNode *request, bool privileged, bool process = true; // Whether to process request locally now bool needs_reply = true; // Whether to build a reply bool local_notify = false; // Whether to notify (local) requester - bool needs_forward = false; // Whether to forward request somewhere else xmlNode *reply = NULL; @@ -778,14 +761,23 @@ based_process_request(xmlNode *request, bool privileged, process = true; needs_reply = false; local_notify = false; - needs_forward = false; pcmk__trace("Processing %s op from client %s locally because it's part " "of a transaction", op, pcmk__client_name(client)); } else if (client != NULL) { + // Forward modifying and non-local requests via cluster + if (!pcmk__is_set(operation->flags, cib__op_attr_local) + && (pcmk__is_set(operation->flags, cib__op_attr_modifies) + || !pcmk__str_eq(host, OUR_NODENAME, + pcmk__str_casei|pcmk__str_null_matches))) { + + forward_request(request); + return pcmk_rc_ok; + } + parse_local_options(client, operation, host, op, &local_notify, - &needs_reply, &process, &needs_forward); + &needs_reply, &process); } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { @@ -798,11 +790,6 @@ based_process_request(xmlNode *request, bool privileged, pcmk__trace("Client is not interested in the reply"); } - if (needs_forward) { - forward_request(request); - return pcmk_rc_ok; - } - if (cib_status != pcmk_rc_ok) { rc = cib_status; pcmk__err("Ignoring request because cluster configuration is invalid " From 5973f08dc16e0fbd1e92e1bad2eff7b116c52ea9 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:05:21 -0800 Subject: [PATCH 320/350] API: libcib: Deprecate cib_api_operations_t:sync Nothing uses it internally. sync_from is still available. Signed-off-by: Reid Wahl --- include/crm/cib/cib_types.h | 2 ++ lib/cib/cib_client.c | 1 + 2 files changed, 3 insertions(+) diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h index e1495423aed..9cc5640244b 100644 --- a/include/crm/cib/cib_types.h +++ b/include/crm/cib/cib_types.h @@ -166,7 +166,9 @@ typedef struct cib_api_operations_s { int (*query_from) (cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options); + //! \deprecated Do not use int (*sync) (cib_t *cib, const char *section, int call_options); + int (*sync_from) (cib_t *cib, const char *host, const char *section, int call_options); int (*upgrade) (cib_t *cib, int call_options); diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c index 2009c5d4739..8d44a34bf10 100644 --- a/lib/cib/cib_client.c +++ b/lib/cib/cib_client.c @@ -281,6 +281,7 @@ cib_client_upgrade(cib_t * cib, int call_options) NULL, call_options, cib->user); } +// @COMPAT cib_api_operations_t:sync is deprecated since 3.0.2 static int cib_client_sync(cib_t * cib, const char *section, int call_options) { From 57a80fd50cf234a894467631a6e541aa4203fbbf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 00:45:33 -0800 Subject: [PATCH 321/350] Refactor: based: Minor easy changes to parse_peer_options() Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 205420c1cf6..5b82b34bbca 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -343,7 +343,7 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, * (This may no longer be relevant since legacy mode was dropped; need to * trace code more closely to check.) */ - const char *host = NULL; + const char *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); @@ -366,7 +366,7 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { // Nothing to do - } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) { + } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { process_ping_reply(request); return false; @@ -433,24 +433,24 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - skip_is_reply: +skip_is_reply: *process = true; *needs_reply = false; - *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); - host = pcmk__xe_get(request, PCMK__XA_CIB_HOST); if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", op, originator); *needs_reply = true; return true; + } - } else if (host != NULL) { + if (host != NULL) { pcmk__trace("Ignoring %s request intended for CIB manager on %s", op, host); return false; + } - } else if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) { + if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { *needs_reply = true; } From ca97f14bbe4de9a2e4700ebf52721d5575ce507a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:34:35 -0800 Subject: [PATCH 322/350] Refactor: based: Move two parse_peer_options() early returns upward These are mutually exclusive with the other cases that appeared earlier in the if/else chain. * If op is CRM_OP_PING or PCMK__CIB_REQUEST_SHUTDOWN, then it isn't PCMK__CIB_REQUEST_REPLACE, PCMK__CIB_REQUEST_SYNC, or PCMK__CIB_REQUEST_UPGRADE. * If op is PCMK__CIB_REQUEST_SHUTDOWN, then the cib__op_attr_modifies flag isn't set. Also, return true explicitly from the shutdown case, and don't set *process. It's already set to true when this function is called. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 5b82b34bbca..89435fcc7b4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -355,6 +355,24 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, originator = "peer"; } + if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { + process_ping_reply(request); + return false; + } + + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { + *local_notify = false; + + if (reply_to == NULL) { + return true; + } + + // @TODO Is this possible? + pcmk__debug("Ignoring shutdown request from %s because reply_to=%s", + originator, reply_to); + return true; + } + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO if (reply_to) { @@ -366,10 +384,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { // Nothing to do - } else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - process_ping_reply(request); - return false; - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. @@ -412,16 +426,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, pcmk__trace("Ignoring legacy %s reply sent from %s to local clients", op, originator); return false; - - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - *local_notify = false; - if (reply_to == NULL) { - *process = true; - } else { // Not possible? - pcmk__debug("Ignoring shutdown request from %s because reply_to=%s", - originator, reply_to); - } - return *process; } if (is_reply) { From 74539c8b2295f88e8aa886607003db2ffaffe374 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:44:11 -0800 Subject: [PATCH 323/350] Refactor: based: Drop legacy code from parse_peer_options() This was added by commit 15c4d2ac to deal with legacy mode. We haven't supported legacy mode (which was for clusters with pre-1.1.12 systems) since 3.0.0. Currently, the only modifying ops that may set PCMK__XA_CIB_ISREPLYTO are replace (as a sync reply) and upgrade. Those two cases are explicitly addressed here. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 89435fcc7b4..ae2a6ddb0b3 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -419,13 +419,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, // Ignore broadcast client requests when we're not primary return false; } - - } else if (is_reply - && pcmk__is_set(operation->flags, cib__op_attr_modifies)) { - - pcmk__trace("Ignoring legacy %s reply sent from %s to local clients", - op, originator); - return false; } if (is_reply) { From 4db3da3b7d9ea2e25833499e9fee5a0cf4ccad32 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 01:56:57 -0800 Subject: [PATCH 324/350] Refactor: based: Move is_reply block to where it's used This is an incremental change; more is coming. Yes, this introduces duplication. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 45 +++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ae2a6ddb0b3..ee3b4472793 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -382,9 +382,19 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { - // Nothing to do + if (is_reply) { + pcmk__trace("Will notify local clients for %s reply from %s", op, + originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; + } - } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { + goto skip_is_reply; + } + + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. * @@ -407,27 +417,30 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, // Our upgrade request was rejected by DC, notify clients of result pcmk__xe_set(request, PCMK__XA_CIB_RC, upgrade_rc); - } else if ((max == NULL) && based_is_primary) { + if (is_reply) { + pcmk__trace("Will notify local clients for %s reply from %s", + op, originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; + } + + goto skip_is_reply; + } + + if ((max == NULL) && based_is_primary) { /* We are the DC, check if this upgrade is allowed */ goto skip_is_reply; + } - } else if(max) { + if (max != NULL) { /* Ok, go ahead and upgrade to 'max' */ goto skip_is_reply; - - } else { - // Ignore broadcast client requests when we're not primary - return false; } - } - if (is_reply) { - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); - *process = false; - *needs_reply = false; - *local_notify = true; - return true; + // Ignore broadcast client requests when we're not primary + return false; } skip_is_reply: From 0a176c25971da5d2c274d3dd0a1a169b6ba7f6d8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 02:27:51 -0800 Subject: [PATCH 325/350] Refactor: based: Drop redundant check for upgrade RC Any PCMK__CIB_REQUEST_UPGRADE with PCMK__XA_CIB_UPGRADE_RC set also has PCMK__XA_CIB_ISREPLYTO set. The cluster message is addressed to the isreplyto host (see based_process_upgrade()), which must be non-NULL because origin is non-NULL. So we cannot receive a cluster message with PCMK__XA_CIB_UPGRADE_RC and a NULL host. If we receive a message that is addressed to some OTHER host, we drop it. cib_cs_dispatch() calls pcmk__cpg_message_data(), which ignores messages that aren't for the local node. Therefore, if we're processing a PCMK__CIB_REQUEST_UPGRADE request with PCMK__XA_CIB_UPGRADE_RC set, then is_reply must be true. Note that the equivalent of PCMK__XA_CIB_UPGRADE_RC was introduced by commit 1f05f5e2. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ee3b4472793..24a478e90a4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -415,18 +415,15 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, if (upgrade_rc != NULL) { // Our upgrade request was rejected by DC, notify clients of result + pcmk__assert(is_reply); pcmk__xe_set(request, PCMK__XA_CIB_RC, upgrade_rc); - if (is_reply) { - pcmk__trace("Will notify local clients for %s reply from %s", - op, originator); - *process = false; - *needs_reply = false; - *local_notify = true; - return true; - } - - goto skip_is_reply; + pcmk__trace("Will notify local clients for %s reply from %s", op, + originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; } if ((max == NULL) && based_is_primary) { From d98f4fe389c5a3d08b6372a8a1e18ee2db4f03f5 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 02:05:37 -0800 Subject: [PATCH 326/350] Refactor: based: Move early return to beginning of upgrade block ...in parse_peer_options(). Another incremental change for easy review. Note that the direct negation of the conditions that came before the previous "return false" would be if (((max != NULL) || !based_is_primary) && (max == NULL)) { ... } It cannot be the case that (max == NULL) and (max != NULL), so we can replace ((max != NULL) || !based_is_primary) with !based_is_primary. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 24a478e90a4..06efdda74f8 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -426,18 +426,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - if ((max == NULL) && based_is_primary) { - /* We are the DC, check if this upgrade is allowed */ - goto skip_is_reply; + if ((max == NULL) && !based_is_primary) { + // Ignore broadcast client requests when we're not the DC + return false; } - - if (max != NULL) { - /* Ok, go ahead and upgrade to 'max' */ - goto skip_is_reply; - } - - // Ignore broadcast client requests when we're not primary - return false; } skip_is_reply: From 0c457a992872a9b115f99ae9d55e0cc2c0c36d58 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 02:53:28 -0800 Subject: [PATCH 327/350] Refactor: based: Drop skip_is_reply label But not the code below it. I've come to appreciate a nice "goto done" or similar, but in this case removing the label seems to make things less convoluted, at least for now. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 06efdda74f8..f62bbeb3016 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -373,28 +373,22 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, return true; } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { - // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO - if (reply_to) { - delegated = reply_to; - } - goto skip_is_reply; + if (is_reply && pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { + pcmk__trace("Will notify local clients for %s reply from %s", op, + originator); + *process = false; + *needs_reply = false; + *local_notify = true; + return true; } - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC, pcmk__str_none)) { - if (is_reply) { - pcmk__trace("Will notify local clients for %s reply from %s", op, - originator); - *process = false; - *needs_reply = false; - *local_notify = true; - return true; - } + if ((reply_to != NULL) + && pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { - goto skip_is_reply; - } + // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO + delegated = reply_to; - if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { + } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) { /* Only the DC (node with the oldest software) should process * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset. * @@ -432,7 +426,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } } -skip_is_reply: *process = true; *needs_reply = false; *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); From 2402209e683d089f12d56459c1250a51968e44b6 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 03:07:07 -0800 Subject: [PATCH 328/350] Refactor: based: parse_local_options() -> log_local_options() It doesn't really do anything now. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 39 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index f62bbeb3016..42b785b79e1 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -291,33 +291,25 @@ process_ping_reply(const xmlNode *reply) } static void -parse_local_options(const pcmk__client_t *client, - const cib__operation_t *operation, - const char *host, const char *op, bool *local_notify, - bool *needs_reply, bool *process) +log_local_options(const pcmk__client_t *client, + const cib__operation_t *operation, const char *host, + const char *op) { - // Process locally and notify local client - *process = true; - *needs_reply = false; - *local_notify = true; - if (pcmk__is_set(operation->flags, cib__op_attr_local)) { - /* Always process locally if cib__op_attr_local is set. - * - * @COMPAT: Currently host is ignored. At a compatibility break, throw - * an error (from based_process_request() or earlier) if host is not - * NULL or OUR_NODENAME. + /* @COMPAT Currently host is ignored. At a compatibility break, throw an + * error (from based_process_request() or earlier) if host is not NULL + * or OUR_NODENAME. */ pcmk__trace("Processing always-local %s op from client %s", op, pcmk__client_name(client)); - if (!pcmk__str_eq(host, OUR_NODENAME, - pcmk__str_casei|pcmk__str_null_matches)) { - - pcmk__warn("Operation '%s' is always local but its target host is " - "set to '%s'", - op, host); + if (pcmk__str_eq(host, OUR_NODENAME, + pcmk__str_casei|pcmk__str_null_matches)) { + return; } + + pcmk__warn("Operation '%s' is always local but its target host is set " + "to '%s'", op, host); return; } @@ -768,8 +760,11 @@ based_process_request(xmlNode *request, bool privileged, return pcmk_rc_ok; } - parse_local_options(client, operation, host, op, &local_notify, - &needs_reply, &process); + // Process locally and notify local client; no peer to reply to + needs_reply = false; + local_notify = true; + + log_local_options(client, operation, host, op); } else if (!parse_peer_options(operation, request, &local_notify, &needs_reply, &process)) { From 07a61a983ec35e43350801f230a00d7c7440db60 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 03:11:24 -0800 Subject: [PATCH 329/350] Refactor: based: Set control variables only if they override defaults Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 42b785b79e1..0d6e92ced8f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -353,8 +353,6 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) { - *local_notify = false; - if (reply_to == NULL) { return true; } @@ -418,13 +416,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } } - *process = true; - *needs_reply = false; *local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei); if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) { pcmk__trace("Processing %s request sent to us from %s", op, originator); - *needs_reply = true; return true; } @@ -435,9 +430,10 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request, } if (!is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_none)) { - *needs_reply = true; + return true; } + *needs_reply = false; pcmk__trace("Processing %s request broadcast by %s call %s on %s " "(local clients will%s be notified)", op, pcmk__s(pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME), @@ -742,10 +738,7 @@ based_process_request(xmlNode *request, bool privileged, * CIB copy, and we don't notify for individual requests because the * entire transaction is atomic. */ - process = true; needs_reply = false; - local_notify = false; - pcmk__trace("Processing %s op from client %s locally because it's part " "of a transaction", op, pcmk__client_name(client)); From 01199955bdefd6fc982356c1120558119a64f4f2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 16:50:43 -0800 Subject: [PATCH 330/350] Refactor: based: Rename crm_cluster to based_cluster Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 4 ++-- daemons/based/pacemaker-based.c | 16 ++++++++-------- daemons/based/pacemaker-based.h | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 0d6e92ced8f..c729ad43e97 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -957,7 +957,7 @@ based_terminate(int exit_status) * messing with the peer caches). */ if (exit_status == CRM_EX_OK) { - pcmk_cluster_disconnect(crm_cluster); + pcmk_cluster_disconnect(based_cluster); } g_main_loop_quit(mainloop); return; @@ -966,7 +966,7 @@ based_terminate(int exit_status) /* Exit cleanly. Even the peer status callback can disconnect here, because * we're not returning control to the caller. */ - pcmk_cluster_disconnect(crm_cluster); + pcmk_cluster_disconnect(based_cluster); pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index de3bfaefee0..bb5df77aea3 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -38,7 +38,7 @@ bool cib_shutdown_flag = false; int cib_status = pcmk_rc_ok; -pcmk_cluster_t *crm_cluster = NULL; +pcmk_cluster_t *based_cluster = NULL; GMainLoop *mainloop = NULL; gchar *cib_root = NULL; @@ -275,7 +275,7 @@ main(int argc, char **argv) /* If main loop returned, clean up and exit. We disconnect in case * based_terminate(-1) was called. */ - pcmk_cluster_disconnect(crm_cluster); + pcmk_cluster_disconnect(based_cluster); pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); done: @@ -284,7 +284,7 @@ main(int argc, char **argv) pcmk__cluster_destroy_node_caches(); pcmk__client_cleanup(); - pcmk_cluster_free(crm_cluster); + pcmk_cluster_free(based_cluster); g_free(cib_root); pcmk__output_and_clear_error(&error, out); @@ -368,20 +368,20 @@ cib_init(void) } based_remote_init(); - crm_cluster = pcmk_cluster_new(); + based_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(crm_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(crm_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(crm_cluster, pcmk__cpg_confchg_cb); + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); } #endif // SUPPORT_COROSYNC if (!stand_alone) { pcmk__cluster_set_status_callback(&cib_peer_update_callback); - if (pcmk_cluster_connect(crm_cluster) != pcmk_rc_ok) { + if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { pcmk__crit("Cannot sign in to the cluster... terminating"); crm_exit(CRM_EX_FATAL); } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index 9349bbb0145..fb2d6c1da12 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -24,10 +24,10 @@ #include "based_remote.h" #include "based_transaction.h" -#define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->priv->node_name) +#define OUR_NODENAME (stand_alone? "localhost" : based_cluster->priv->node_name) extern GMainLoop *mainloop; -extern pcmk_cluster_t *crm_cluster; +extern pcmk_cluster_t *based_cluster; extern gboolean stand_alone; extern bool cib_shutdown_flag; extern gchar *cib_root; From 1bf58d5866331946c8b52323fe95817d4435d604 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 16:56:33 -0800 Subject: [PATCH 331/350] Refactor: based: Don't create based_cluster if in stand-alone mode The NULL checks will get de-duplicated in an upcoming commit. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 7 +++++-- daemons/based/pacemaker-based.c | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index c729ad43e97..a44fbbd064f 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -956,7 +956,7 @@ based_terminate(int exit_status) * main loop returns (this allows the peer status callback to avoid * messing with the peer caches). */ - if (exit_status == CRM_EX_OK) { + if ((exit_status == CRM_EX_OK) && (based_cluster != NULL)) { pcmk_cluster_disconnect(based_cluster); } g_main_loop_quit(mainloop); @@ -966,7 +966,10 @@ based_terminate(int exit_status) /* Exit cleanly. Even the peer status callback can disconnect here, because * we're not returning control to the caller. */ - pcmk_cluster_disconnect(based_cluster); + if (based_cluster != NULL) { + pcmk_cluster_disconnect(based_cluster); + } + pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index bb5df77aea3..7c06e5e00a8 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -275,7 +275,10 @@ main(int argc, char **argv) /* If main loop returned, clean up and exit. We disconnect in case * based_terminate(-1) was called. */ - pcmk_cluster_disconnect(based_cluster); + if (based_cluster != NULL) { + pcmk_cluster_disconnect(based_cluster); + } + pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); done: @@ -368,17 +371,18 @@ cib_init(void) } based_remote_init(); - based_cluster = pcmk_cluster_new(); + + if (!stand_alone) { + based_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } #endif // SUPPORT_COROSYNC - if (!stand_alone) { pcmk__cluster_set_status_callback(&cib_peer_update_callback); if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { From 29efee180f6f93e81235a4d34b983f939cd672d8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Mon, 5 Jan 2026 17:05:03 -0800 Subject: [PATCH 332/350] Refactor: based: New based_ipc_init() and based_ipc.h Also move the ipcs_{ro,rw,shm} extern declarations from based_callbacks.h to based_ipc.h, now that there is a more appropriate home for them. And make ipc_{ro,rw}_callbacks static to based_ipc.c, since we no longer use them in pacemaker-based.c. The goal is to look more like attrd, the fencer, and the scheduler. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 1 + daemons/based/based_callbacks.h | 8 -------- daemons/based/based_ipc.c | 15 +++++++++++++-- daemons/based/based_ipc.h | 21 +++++++++++++++++++++ daemons/based/pacemaker-based.c | 5 ++--- daemons/based/pacemaker-based.h | 1 + 6 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 daemons/based/based_ipc.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index f814ce8b478..b8cc28d7002 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -16,6 +16,7 @@ halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_callbacks.h \ based_io.h \ + based_ipc.h \ based_messages.h \ based_notify.h \ based_operation.h \ diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index d52bf52431e..c72ffa129ae 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -13,17 +13,9 @@ #include #include // xmlNode -#include // qb_* #include // pcmk__client_t -extern struct qb_ipcs_service_handlers ipc_ro_callbacks; -extern struct qb_ipcs_service_handlers ipc_rw_callbacks; - -extern qb_ipcs_service_t *ipcs_ro; -extern qb_ipcs_service_t *ipcs_rw; -extern qb_ipcs_service_t *ipcs_shm; - void based_peer_callback(xmlNode *msg, void *private_data); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 5fe66bb5d60..8e26a474b9f 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -281,7 +281,7 @@ based_ipc_destroy(qb_ipcs_connection_t *c) based_ipc_closed(c); } -struct qb_ipcs_service_handlers ipc_ro_callbacks = { +static struct qb_ipcs_service_handlers ipc_ro_callbacks = { .connection_accept = based_ipc_accept, .connection_created = NULL, .msg_process = based_ipc_dispatch_ro, @@ -289,10 +289,21 @@ struct qb_ipcs_service_handlers ipc_ro_callbacks = { .connection_destroyed = based_ipc_destroy, }; -struct qb_ipcs_service_handlers ipc_rw_callbacks = { +static struct qb_ipcs_service_handlers ipc_rw_callbacks = { .connection_accept = based_ipc_accept, .connection_created = NULL, .msg_process = based_ipc_dispatch_rw, .connection_closed = based_ipc_closed, .connection_destroyed = based_ipc_destroy, }; + +/*! + * \internal + * \brief Set up \c based IPC communication + */ +void +based_ipc_init(void) +{ + pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, + &ipc_rw_callbacks); +} diff --git a/daemons/based/based_ipc.h b/daemons/based/based_ipc.h new file mode 100644 index 00000000000..98875d1e2a1 --- /dev/null +++ b/daemons/based/based_ipc.h @@ -0,0 +1,21 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_IPC__H +#define BASED_IPC__H + +#include // qb_* + +extern qb_ipcs_service_t *ipcs_ro; +extern qb_ipcs_service_t *ipcs_rw; +extern qb_ipcs_service_t *ipcs_shm; + +void based_ipc_init(void); + +#endif // BASED_IPC__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 7c06e5e00a8..f17e0dfa308 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -266,6 +266,8 @@ main(int argc, char **argv) // Read initial CIB, connect to cluster, and start IPC servers cib_init(); + based_ipc_init(); + // Run the main loop mainloop = g_main_loop_new(NULL, FALSE); pcmk__notice("Pacemaker CIB manager successfully started and accepting " @@ -391,9 +393,6 @@ cib_init(void) } } - pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, - &ipc_rw_callbacks); - if (stand_alone) { based_is_primary = true; } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index fb2d6c1da12..ed784c82289 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -18,6 +18,7 @@ #include "based_callbacks.h" #include "based_io.h" +#include "based_ipc.h" #include "based_messages.h" #include "based_operation.h" #include "based_notify.h" From 4f0957522fc0d3231f6eb5f3cff74ac9e7e873d1 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 18:52:51 -0800 Subject: [PATCH 333/350] Refactor: based: Unindent cib_init() Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index f17e0dfa308..d8a1a89f6bd 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -374,26 +374,25 @@ cib_init(void) based_remote_init(); - if (!stand_alone) { - based_cluster = pcmk_cluster_new(); + if (stand_alone) { + based_is_primary = true; + return; + } + + based_cluster = pcmk_cluster_new(); #if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } #endif // SUPPORT_COROSYNC - pcmk__cluster_set_status_callback(&cib_peer_update_callback); - - if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { - pcmk__crit("Cannot sign in to the cluster... terminating"); - crm_exit(CRM_EX_FATAL); - } - } + pcmk__cluster_set_status_callback(&cib_peer_update_callback); - if (stand_alone) { - based_is_primary = true; + if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { + pcmk__crit("Cannot sign in to the cluster... terminating"); + crm_exit(CRM_EX_FATAL); } } From 464f0cfea6ed45a983fa43f493399b0369054f5a Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:44:07 -0800 Subject: [PATCH 334/350] Refactor: based: New based_ipc_cleanup() The goal is to look more like attrd, the fencer, and the scheduler. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 19 +----------------- daemons/based/based_ipc.c | 35 +++++++++++++++++++++++++++++++++ daemons/based/based_ipc.h | 1 + 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a44fbbd064f..ae15e3e39f7 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -20,7 +20,6 @@ #include // gboolean, gpointer, g_*, etc. #include // xmlNode -#include // qb_ipcs_connection_t #include // LOG_TRACE #include // cib_call_options values @@ -873,23 +872,7 @@ based_shutdown(int nsig) } cib_shutdown_flag = true; - - if (ipcs_ro != NULL) { - pcmk__drop_all_clients(ipcs_ro); - g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); - } - - if (ipcs_rw != NULL) { - pcmk__drop_all_clients(ipcs_rw); - g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); - } - - if (ipcs_shm != NULL) { - pcmk__drop_all_clients(ipcs_shm); - g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); - } - - based_drop_remote_clients(); + based_ipc_cleanup(); /* The client count should be 0 by now. If it isn't, that means GLib had * some other reference to a remote client's source when we dropped the diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index 8e26a474b9f..f87712f1eed 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -307,3 +307,38 @@ based_ipc_init(void) pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks, &ipc_rw_callbacks); } + +/*! + * \internal + * \brief Clean up \c based IPC communication + */ +void +based_ipc_cleanup(void) +{ + if (ipcs_ro != NULL) { + pcmk__drop_all_clients(ipcs_ro); + g_clear_pointer(&ipcs_ro, qb_ipcs_destroy); + } + + if (ipcs_rw != NULL) { + pcmk__drop_all_clients(ipcs_rw); + g_clear_pointer(&ipcs_rw, qb_ipcs_destroy); + } + + if (ipcs_shm != NULL) { + pcmk__drop_all_clients(ipcs_shm); + g_clear_pointer(&ipcs_shm, qb_ipcs_destroy); + } + + /* Drop remote clients here because they're part of the IPC client table and + * must be dropped before \c pcmk__client_cleanup() + */ + based_drop_remote_clients(); + + /* @TODO This is where we would call a based_unregister_handlers() to align + * with other daemons' IPC cleanup functions. Such a function does not yet + * exist; based doesn't use pcmk__request_t yet. + */ + + pcmk__client_cleanup(); +} diff --git a/daemons/based/based_ipc.h b/daemons/based/based_ipc.h index 98875d1e2a1..15b4cc31af8 100644 --- a/daemons/based/based_ipc.h +++ b/daemons/based/based_ipc.h @@ -17,5 +17,6 @@ extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; void based_ipc_init(void); +void based_ipc_cleanup(void); #endif // BASED_IPC__H From b4240a53cccbc555b371a4877e46437a9cb820d7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 01:54:35 -0800 Subject: [PATCH 335/350] Low: based: Move based_ipc_cleanup() call to based_terminate() This seems like a more correct location for it. based_terminate() is where we free data structures. This also makes the calls to pcmk__stop_based_ipc() redundant, and allows making the qb_ipcs_service_t declarations static. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 5 ++--- daemons/based/based_ipc.c | 6 +++--- daemons/based/based_ipc.h | 6 ------ daemons/based/pacemaker-based.c | 2 -- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ae15e3e39f7..7db0271666c 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -872,7 +872,6 @@ based_shutdown(int nsig) } cib_shutdown_flag = true; - based_ipc_cleanup(); /* The client count should be 0 by now. If it isn't, that means GLib had * some other reference to a remote client's source when we dropped the @@ -922,13 +921,14 @@ based_terminate(int exit_status) remote_tls_fd = 0; } + based_ipc_cleanup(); + g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); g_clear_pointer(&the_cib, pcmk__xml_free); // Exit immediately on error if (exit_status > CRM_EX_OK) { - pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(exit_status); return; } @@ -953,6 +953,5 @@ based_terminate(int exit_status) pcmk_cluster_disconnect(based_cluster); } - pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/based_ipc.c b/daemons/based/based_ipc.c index f87712f1eed..ea76c42ea99 100644 --- a/daemons/based/based_ipc.c +++ b/daemons/based/based_ipc.c @@ -28,9 +28,9 @@ #include "pacemaker-based.h" -qb_ipcs_service_t *ipcs_ro = NULL; -qb_ipcs_service_t *ipcs_rw = NULL; -qb_ipcs_service_t *ipcs_shm = NULL; +static qb_ipcs_service_t *ipcs_ro = NULL; +static qb_ipcs_service_t *ipcs_rw = NULL; +static qb_ipcs_service_t *ipcs_shm = NULL; /*! * \internal diff --git a/daemons/based/based_ipc.h b/daemons/based/based_ipc.h index 15b4cc31af8..ed3cdb72981 100644 --- a/daemons/based/based_ipc.h +++ b/daemons/based/based_ipc.h @@ -10,12 +10,6 @@ #ifndef BASED_IPC__H #define BASED_IPC__H -#include // qb_* - -extern qb_ipcs_service_t *ipcs_ro; -extern qb_ipcs_service_t *ipcs_rw; -extern qb_ipcs_service_t *ipcs_shm; - void based_ipc_init(void); void based_ipc_cleanup(void); diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index d8a1a89f6bd..4bce3bbf2bd 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -281,8 +281,6 @@ main(int argc, char **argv) pcmk_cluster_disconnect(based_cluster); } - pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm); - done: g_strfreev(processed_args); pcmk__free_arg_context(context); From 88adb7e45b39ca6aa1261a88ce54e174b1d637bf Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 02:05:00 -0800 Subject: [PATCH 336/350] Refactor: based: Functionize remote fd closure in based_remote.c Also make -1 the "uninitialized" value. 0 is a valid file descriptor, even though it's much more likely to be used for stdin. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 11 +---------- daemons/based/based_remote.c | 26 ++++++++++++++++++++++++-- daemons/based/based_remote.h | 4 +--- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 7db0271666c..aad0bc2b44b 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -16,7 +16,6 @@ #include // u?int*_t, UINT64_C #include // LOG_INFO, LOG_DEBUG #include // time_t -#include // close #include // gboolean, gpointer, g_*, etc. #include // xmlNode @@ -912,16 +911,8 @@ based_shutdown(int nsig) void based_terminate(int exit_status) { - if (remote_fd > 0) { - close(remote_fd); - remote_fd = 0; - } - if (remote_tls_fd > 0) { - close(remote_tls_fd); - remote_tls_fd = 0; - } - based_ipc_cleanup(); + based_remote_cleanup(); g_clear_pointer(&digest_timer, mainloop_timer_del); g_clear_pointer(&ping_digest, free); diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c index b5d6f5b8c7c..0d69a788bb4 100644 --- a/daemons/based/based_remote.c +++ b/daemons/based/based_remote.c @@ -46,8 +46,8 @@ static pcmk__tls_t *tls = NULL; -int remote_fd = 0; -int remote_tls_fd = 0; +static int remote_fd = -1; +static int remote_tls_fd = -1; // @TODO This is rather short for someone to type their password #define REMOTE_AUTH_TIMEOUT 10000 @@ -697,6 +697,28 @@ based_remote_init(void) } } +/*! + * \internal + * \brief Stop remote listeners + * + * \note Remote clients are dropped in \c based_ipc_cleanup() rather than here, + * because they're part of the IPC client table and must be dropped before + * \c pcmk__client_cleanup(). + */ +void +based_remote_cleanup(void) +{ + if (remote_fd >= 0) { + close(remote_fd); + remote_fd = -1; + } + + if (remote_tls_fd >= 0) { + close(remote_tls_fd); + remote_tls_fd = -1; + } +} + /*! * \internal * \brief Disconnect and free a remote CIB manager client diff --git a/daemons/based/based_remote.h b/daemons/based/based_remote.h index c302d513200..3e9eaa5639c 100644 --- a/daemons/based/based_remote.h +++ b/daemons/based/based_remote.h @@ -12,10 +12,8 @@ #include -extern int remote_fd; -extern int remote_tls_fd; - void based_remote_init(void); +void based_remote_cleanup(void); void based_drop_remote_clients(void); #endif // BASED_REMOTE__H From cb444e9af7663a7f746c6e751bd42baa89c6f91d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:11:35 -0800 Subject: [PATCH 337/350] Refactor: based: New based_cluster_connect() To look more like attrd and fenced. More change are coming. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 47 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 4bce3bbf2bd..2e9463f5524 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -161,6 +161,37 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } +/*! + * \internal + * \brief Initialize \c based_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ +static int +based_cluster_connect(void) +{ + int rc = pcmk_rc_ok; + + based_cluster = pcmk_cluster_new(); + +#if SUPPORT_COROSYNC + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } +#endif // SUPPORT_COROSYNC + + pcmk__cluster_set_status_callback(&cib_peer_update_callback); + + rc = pcmk_cluster_connect(based_cluster); + if (rc != pcmk_rc_ok) { + pcmk__err("Cluster connection failed"); + } + + return rc; +} + int main(int argc, char **argv) { @@ -377,20 +408,8 @@ cib_init(void) return; } - based_cluster = pcmk_cluster_new(); - -#if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } -#endif // SUPPORT_COROSYNC - - pcmk__cluster_set_status_callback(&cib_peer_update_callback); - - if (pcmk_cluster_connect(based_cluster) != pcmk_rc_ok) { - pcmk__crit("Cannot sign in to the cluster... terminating"); + if (based_cluster_connect() != pcmk_rc_ok) { + pcmk__crit("Could not connect to the cluster"); crm_exit(CRM_EX_FATAL); } } From 710a84d2062b4e040fb2b7a574bf09c549b0114d Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:22:35 -0800 Subject: [PATCH 338/350] Refactor: based: Pull cib_init() body into main() It doesn't make sense to have this handful of unrelated initializations in a separate function, when most of main() is for initializing things. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 52 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 2e9463f5524..752d6ff7b5c 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -45,8 +45,6 @@ gchar *cib_root = NULL; gboolean stand_alone = FALSE; -static void cib_init(void); - static crm_exit_t exit_code = CRM_EX_OK; /*! @@ -294,10 +292,33 @@ main(int argc, char **argv) pcmk__cluster_init_node_caches(); - // Read initial CIB, connect to cluster, and start IPC servers - cib_init(); + /* Read initial CIB. based_read_cib() returns new, non-NULL XML, so this + * should always succeed. + */ + if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { + exit_code = CRM_EX_SOFTWARE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Bug: failed to activate CIB. Terminating %s.", + pcmk__server_log_name(pcmk_ipc_based)); + goto done; + } based_ipc_init(); + based_remote_init(); + + if (stand_alone) { + based_is_primary = true; + + } else { + if (based_cluster_connect() != pcmk_rc_ok) { + exit_code = CRM_EX_FATAL; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not connect to the cluster"); + goto done; + } + + pcmk__info("Cluster connection active"); + } // Run the main loop mainloop = g_main_loop_new(NULL, FALSE); @@ -390,26 +411,3 @@ cib_peer_update_callback(enum pcmk__node_update type, break; } } - -static void -cib_init(void) -{ - // based_read_cib() returns new, non-NULL XML, so this should always succeed - if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) { - pcmk__crit("Bug: failed to activate CIB. Terminating %s.", - pcmk__server_log_name(pcmk_ipc_based)); - crm_exit(CRM_EX_SOFTWARE); - } - - based_remote_init(); - - if (stand_alone) { - based_is_primary = true; - return; - } - - if (based_cluster_connect() != pcmk_rc_ok) { - pcmk__crit("Could not connect to the cluster"); - crm_exit(CRM_EX_FATAL); - } -} From cff54ca38836ccaa752bd9136ebb5266cbef8a03 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:27:41 -0800 Subject: [PATCH 339/350] Refactor: based: Move based_io_init() call just before based_read_cib() Place the call right before the first thing that needs it (based_activate_cib()). The rest of the initialization helpers are called in this region, so let's collect them. We can't make them all contiguous though -- based_remote_init() requires that the CIB be read and activated first. Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 752d6ff7b5c..eb42054e465 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -229,8 +229,6 @@ main(int argc, char **argv) mainloop_add_signal(SIGTERM, based_shutdown); - based_io_init(); - if ((g_strv_length(processed_args) >= 2) && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) { @@ -291,6 +289,7 @@ main(int argc, char **argv) } pcmk__cluster_init_node_caches(); + based_io_init(); /* Read initial CIB. based_read_cib() returns new, non-NULL XML, so this * should always succeed. From 71b7d77a13b16a4cb254d05fd1bd6abcf5492cbe Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:42:18 -0800 Subject: [PATCH 340/350] Refactor: based: Set based_is_primary in setup_stand_alone() Signed-off-by: Reid Wahl --- daemons/based/pacemaker-based.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index eb42054e465..79d068d5aed 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -62,6 +62,8 @@ setup_stand_alone(GError **error) gid_t gid = 0; int rc = pcmk_rc_ok; + based_is_primary = true; + rc = pcmk__daemon_user(&uid, &gid); if (rc != pcmk_rc_ok) { exit_code = CRM_EX_FATAL; @@ -305,10 +307,7 @@ main(int argc, char **argv) based_ipc_init(); based_remote_init(); - if (stand_alone) { - based_is_primary = true; - - } else { + if (!stand_alone) { if (based_cluster_connect() != pcmk_rc_ok) { exit_code = CRM_EX_FATAL; g_set_error(&error, PCMK__EXITC_ERROR, exit_code, From 3af3b38e1b3e94fa4374b656758a4a7c0f97a8d7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 19:51:30 -0800 Subject: [PATCH 341/350] Refactor: based: New based_corosync.c for cluster-related functions To mirror attrd and fenced. Signed-off-by: Reid Wahl --- daemons/based/Makefile.am | 2 + daemons/based/based_callbacks.c | 26 ------ daemons/based/based_callbacks.h | 1 - daemons/based/based_corosync.c | 146 ++++++++++++++++++++++++++++++++ daemons/based/based_corosync.h | 19 +++++ daemons/based/pacemaker-based.c | 95 --------------------- daemons/based/pacemaker-based.h | 1 + 7 files changed, 168 insertions(+), 122 deletions(-) create mode 100644 daemons/based/based_corosync.c create mode 100644 daemons/based/based_corosync.h diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am index b8cc28d7002..f8ec87f25f7 100644 --- a/daemons/based/Makefile.am +++ b/daemons/based/Makefile.am @@ -15,6 +15,7 @@ halibdir = $(CRM_DAEMON_DIR) halib_PROGRAMS = pacemaker-based noinst_HEADERS = based_callbacks.h \ + based_corosync.h \ based_io.h \ based_ipc.h \ based_messages.h \ @@ -34,6 +35,7 @@ pacemaker_based_LDADD += $(CLUSTERLIBS) $(PAM_LIBS) pacemaker_based_SOURCES = pacemaker-based.c \ based_callbacks.c \ + based_corosync.c \ based_io.c \ based_ipc.c \ based_messages.c \ diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index aad0bc2b44b..b7a88d47fe4 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -824,32 +824,6 @@ based_process_request(xmlNode *request, bool privileged, return rc; } -void -based_peer_callback(xmlNode *msg, void *private_data) -{ - const char *reason = NULL; - const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); - - if (pcmk__peer_cache == NULL) { - reason = "membership not established"; - goto bail; - } - - if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { - pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); - } - - based_process_request(msg, true, NULL); - return; - - bail: - if (reason) { - const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); - - pcmk__warn("Discarding %s message from %s: %s", op, originator, reason); - } -} - static gboolean cib_force_exit(gpointer data) { diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index c72ffa129ae..9f7360d94ed 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -16,7 +16,6 @@ #include // pcmk__client_t -void based_peer_callback(xmlNode *msg, void *private_data); int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); void based_shutdown(int nsig); diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c new file mode 100644 index 00000000000..88027efbb05 --- /dev/null +++ b/daemons/based/based_corosync.c @@ -0,0 +1,146 @@ +/* + * Copyright 2004-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include // NULL, size_t +#include // uint32_t +#include // free + +#include // cpg_* +#include // gpointer +#include // xmlNode + +#include // SUPPORT_COROSYNC +#include // pcmk_cluster_* +#include // pcmk__cluster_*, etc. +#include // pcmk__err, pcmk__xml_free, etc. +#include // CRM_EX_DISCONNECT, pcmk_rc_ok + +#include "pacemaker-based.h" + +pcmk_cluster_t *based_cluster = NULL; + +static void +based_peer_callback(xmlNode *msg, void *private_data) +{ + const char *reason = NULL; + const char *originator = pcmk__xe_get(msg, PCMK__XA_SRC); + + if (pcmk__peer_cache == NULL) { + reason = "membership not established"; + goto bail; + } + + if (pcmk__xe_get(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) { + pcmk__xe_set(msg, PCMK__XA_CIB_CLIENTNAME, originator); + } + + based_process_request(msg, true, NULL); + return; + + bail: + if (reason) { + const char *op = pcmk__xe_get(msg, PCMK__XA_CIB_OP); + + pcmk__warn("Discarding %s message from %s: %s", op, originator, reason); + } +} + +#if SUPPORT_COROSYNC +static void +cib_cs_dispatch(cpg_handle_t handle, + const struct cpg_name *groupName, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +{ + xmlNode *xml = NULL; + const char *from = NULL; + char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); + + if(data == NULL) { + return; + } + + xml = pcmk__xml_parse(data); + if (xml == NULL) { + pcmk__err("Invalid XML: '%.120s'", data); + free(data); + return; + } + pcmk__xe_set(xml, PCMK__XA_SRC, from); + based_peer_callback(xml, NULL); + + pcmk__xml_free(xml); + free(data); +} + +static void +cib_cs_destroy(gpointer user_data) +{ + if (cib_shutdown_flag) { + pcmk__info("Corosync disconnection complete"); + } else { + pcmk__crit("Exiting immediately after losing connection to cluster " + "layer"); + based_terminate(CRM_EX_DISCONNECT); + } +} +#endif + +static void +cib_peer_update_callback(enum pcmk__node_update type, + pcmk__node_status_t *node, const void *data) +{ + switch (type) { + case pcmk__node_update_name: + case pcmk__node_update_state: + if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2) + && (pcmk__ipc_client_count() == 0)) { + + pcmk__info("Exiting after no more peers or clients remain"); + based_terminate(-1); + } + break; + + default: + break; + } +} + +/*! + * \internal + * \brief Initialize \c based_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ +int +based_cluster_connect(void) +{ + int rc = pcmk_rc_ok; + + based_cluster = pcmk_cluster_new(); + +#if SUPPORT_COROSYNC + if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { + pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); + } +#endif // SUPPORT_COROSYNC + + pcmk__cluster_set_status_callback(&cib_peer_update_callback); + + rc = pcmk_cluster_connect(based_cluster); + if (rc != pcmk_rc_ok) { + pcmk__err("Cluster connection failed"); + } + + return rc; +} diff --git a/daemons/based/based_corosync.h b/daemons/based/based_corosync.h new file mode 100644 index 00000000000..5383fd748a8 --- /dev/null +++ b/daemons/based/based_corosync.h @@ -0,0 +1,19 @@ +/* + * Copyright 2025-2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef BASED_COROSYNC__H +#define BASED_COROSYNC__H + +#include // pcmk_cluster_t + +extern pcmk_cluster_t *based_cluster; + +int based_cluster_connect(void); + +#endif // BASED_COROSYNC__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 79d068d5aed..636e51d6b1e 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -14,12 +14,10 @@ #include // SIGTERM #include #include // NULL, size_t -#include // free #include // LOG_INFO #include // gid_t, uid_t #include // setgid, setuid -#include // cpg_* #include // g_*, G_*, etc. #include // xmlNode @@ -38,8 +36,6 @@ bool cib_shutdown_flag = false; int cib_status = pcmk_rc_ok; -pcmk_cluster_t *based_cluster = NULL; - GMainLoop *mainloop = NULL; gchar *cib_root = NULL; @@ -161,37 +157,6 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } -/*! - * \internal - * \brief Initialize \c based_cluster and connect to the cluster layer - * - * \return Standard Pacemaker return code - */ -static int -based_cluster_connect(void) -{ - int rc = pcmk_rc_ok; - - based_cluster = pcmk_cluster_new(); - -#if SUPPORT_COROSYNC - if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); - pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); - } -#endif // SUPPORT_COROSYNC - - pcmk__cluster_set_status_callback(&cib_peer_update_callback); - - rc = pcmk_cluster_connect(based_cluster); - if (rc != pcmk_rc_ok) { - pcmk__err("Cluster connection failed"); - } - - return rc; -} - int main(int argc, char **argv) { @@ -349,63 +314,3 @@ main(int argc, char **argv) pcmk__unregister_formats(); crm_exit(exit_code); } - -#if SUPPORT_COROSYNC -static void -cib_cs_dispatch(cpg_handle_t handle, - const struct cpg_name *groupName, - uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) -{ - xmlNode *xml = NULL; - const char *from = NULL; - char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from); - - if(data == NULL) { - return; - } - - xml = pcmk__xml_parse(data); - if (xml == NULL) { - pcmk__err("Invalid XML: '%.120s'", data); - free(data); - return; - } - pcmk__xe_set(xml, PCMK__XA_SRC, from); - based_peer_callback(xml, NULL); - - pcmk__xml_free(xml); - free(data); -} - -static void -cib_cs_destroy(gpointer user_data) -{ - if (cib_shutdown_flag) { - pcmk__info("Corosync disconnection complete"); - } else { - pcmk__crit("Exiting immediately after losing connection to cluster " - "layer"); - based_terminate(CRM_EX_DISCONNECT); - } -} -#endif - -static void -cib_peer_update_callback(enum pcmk__node_update type, - pcmk__node_status_t *node, const void *data) -{ - switch (type) { - case pcmk__node_update_name: - case pcmk__node_update_state: - if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2) - && (pcmk__ipc_client_count() == 0)) { - - pcmk__info("Exiting after no more peers or clients remain"); - based_terminate(-1); - } - break; - - default: - break; - } -} diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index ed784c82289..c901eb3d45b 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -17,6 +17,7 @@ #include // pcmk_cluster_t #include "based_callbacks.h" +#include "based_corosync.h" #include "based_io.h" #include "based_ipc.h" #include "based_messages.h" From 536a2e42b12c225c0f92c5f0ea9f985fdd33df68 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:27:26 -0800 Subject: [PATCH 342/350] Refactor: libcrmcluster: Don't init caches in pcmk__corosync_connect() pcmk__corosync_connect() doesn't use the caches directly. pcmk__get_node() initializes the caches when it gets called. Signed-off-by: Reid Wahl --- lib/cluster/corosync.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index d354aaf28b6..c15924e1175 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.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. * @@ -466,8 +466,6 @@ pcmk__corosync_connect(pcmk_cluster_t *cluster) pcmk__node_status_t *local_node = NULL; int rc = pcmk_rc_ok; - pcmk__cluster_init_node_caches(); - if (cluster_layer != pcmk_cluster_layer_corosync) { pcmk__err("Invalid cluster layer: %s " QB_XS " cluster_layer=%d", cluster_layer_s, cluster_layer); From 96a493ecd25535e9005fcad67e4aa6a3e29019f8 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:50:43 -0800 Subject: [PATCH 343/350] Refactor: libcrmcluster: Clean up includes in corosync.c Using include-what-you-use. Signed-off-by: Reid Wahl --- lib/cluster/corosync.c | 48 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/cluster/corosync.c b/lib/cluster/corosync.c index c15924e1175..e911d9c6a02 100644 --- a/lib/cluster/corosync.c +++ b/lib/cluster/corosync.c @@ -9,29 +9,33 @@ #include -#include -#include // PRIu64, etc. -#include -#include +#include // ENXIO, EINVAL +#include // PRIu32, PRIu64, PRIx32 #include -#include // uint32_t, etc. -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#include // NULL +#include // uint32_t, uint64_t +#include // sscanf +#include // free +#include // strerror, strchr +#include // gid_t, pid_t, uid_t +#include // sleep + +#include // cmap_* +#include // cs_*, CS_* +#include // quorum_* +#include // gboolean, gpointer, g_*, G_PRIORITY_HIGH +#include // xmlNode +#include // QB_XS + +#include // pcmk_cluster_*, etc. +#include // pcmk__cluster_private_t members +#include // pcmk__corosync2rc, pcmk__err, etc. +#include // crm_ipc_is_authentic_process +#include // CRM_LOG_ASSERT +#include // mainloop_* +#include // PCMK_VALUE_MEMBER +#include // CRM_EX_FATAL, crm_exit, pcmk_rc_*, etc. +#include // PCMK_XA_*, PCMK_XE_* #include "crmcluster_private.h" From 8ede5105a46867c1a5d347c9221753802c2e7950 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 21:13:11 -0800 Subject: [PATCH 344/350] Refactor: libcrmcluster: Destroy caches unconditionally on disconnect It shouldn't matter whether this is a Corosync cluster or not. Signed-off-by: Reid Wahl --- daemons/fenced/pacemaker-fenced.c | 1 - lib/cluster/cluster.c | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c index 4cd58ebdee2..1980a0c5a58 100644 --- a/daemons/fenced/pacemaker-fenced.c +++ b/daemons/fenced/pacemaker-fenced.c @@ -276,7 +276,6 @@ stonith_cleanup(void) { fenced_cib_cleanup(); fenced_ipc_cleanup(); - pcmk__cluster_destroy_node_caches(); free_stonith_remote_op_list(); free_topology_list(); fenced_free_device_table(); diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index 7003f7a6852..1c9ae1900ca 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -119,13 +119,17 @@ pcmk_cluster_disconnect(pcmk_cluster_t *cluster) const enum pcmk_cluster_layer cluster_layer = pcmk_get_cluster_layer(); const char *cluster_layer_s = pcmk_cluster_layer_text(cluster_layer); + /* @TODO Either decouple this from cluster disconnection, or move the caches + * to pcmk_cluster_t as suggested in comments in membership.c. + */ + pcmk__cluster_destroy_node_caches(); + pcmk__info("Disconnecting from %s cluster layer", cluster_layer_s); switch (cluster_layer) { #if SUPPORT_COROSYNC case pcmk_cluster_layer_corosync: pcmk__corosync_disconnect(cluster); - pcmk__cluster_destroy_node_caches(); return pcmk_rc_ok; #endif // SUPPORT_COROSYNC From 815f0f4e8e6e29add7c4a86426cfb84ca45a9eea Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:08:29 -0800 Subject: [PATCH 345/350] Refactor: based: New based_cluster_disconnect() To mirror attrd and fenced. Notes: * Previously, we weren't freeing the cluster object on exit in based_terminate(). Now we are. * pcmk_cluster_free() calls pcmk__cluster_destroy_node_caches(), which is why we drop the call to that function. * I'm fairly certain that the reason the pcmk_cluster_disconnect() call previously occurred before the done section, is that prior to a recent commit, we weren't NULL-checking the cluster argument before disconnect. We should be able to call based_cluster_disconnect() regardless of how based_terminate() wherever we want to free the cluster object. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 10 +++------- daemons/based/based_corosync.c | 15 +++++++++++++++ daemons/based/based_corosync.h | 1 + daemons/based/pacemaker-based.c | 11 +---------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index b7a88d47fe4..a749d299290 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -23,7 +23,6 @@ #include // cib_call_options values #include // cib__* -#include // pcmk_cluster_disconnect #include // pcmk__cluster_send_message #include // pcmk__s, pcmk__str_eq #include // crm_ipc_*, pcmk_ipc_* @@ -904,8 +903,8 @@ based_terminate(int exit_status) * main loop returns (this allows the peer status callback to avoid * messing with the peer caches). */ - if ((exit_status == CRM_EX_OK) && (based_cluster != NULL)) { - pcmk_cluster_disconnect(based_cluster); + if (exit_status == CRM_EX_OK) { + based_cluster_disconnect(); } g_main_loop_quit(mainloop); return; @@ -914,9 +913,6 @@ based_terminate(int exit_status) /* Exit cleanly. Even the peer status callback can disconnect here, because * we're not returning control to the caller. */ - if (based_cluster != NULL) { - pcmk_cluster_disconnect(based_cluster); - } - + based_cluster_disconnect(); crm_exit(CRM_EX_OK); } diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index 88027efbb05..b3d82b6b345 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -144,3 +144,18 @@ based_cluster_connect(void) return rc; } + +/*! + * \internal + * \brief Disconnect from the cluster layer and free \c based_cluster + */ +void +based_cluster_disconnect(void) +{ + if (based_cluster == NULL) { + return; + } + + pcmk_cluster_disconnect(based_cluster); + g_clear_pointer(&based_cluster, pcmk_cluster_free); +} diff --git a/daemons/based/based_corosync.h b/daemons/based/based_corosync.h index 5383fd748a8..4df1ea151b3 100644 --- a/daemons/based/based_corosync.h +++ b/daemons/based/based_corosync.h @@ -15,5 +15,6 @@ extern pcmk_cluster_t *based_cluster; int based_cluster_connect(void); +void based_cluster_disconnect(void); #endif // BASED_COROSYNC__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 636e51d6b1e..6558f20e709 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -22,7 +22,6 @@ #include // xmlNode #include // CRM_CONFIG_DIR, CRM_DAEMON_USER -#include // pcmk_cluster_* #include // pcmk__node_update, etc. #include // crm_ipc_* #include // crm_log_* @@ -289,20 +288,12 @@ main(int argc, char **argv) "connections"); g_main_loop_run(mainloop); - /* If main loop returned, clean up and exit. We disconnect in case - * based_terminate(-1) was called. - */ - if (based_cluster != NULL) { - pcmk_cluster_disconnect(based_cluster); - } - done: g_strfreev(processed_args); pcmk__free_arg_context(context); - pcmk__cluster_destroy_node_caches(); pcmk__client_cleanup(); - pcmk_cluster_free(based_cluster); + based_cluster_disconnect(); g_free(cib_root); pcmk__output_and_clear_error(&error, out); From b8f79d5fca6da14b027b4711f1ee9e8f4b8dccde Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 20:14:49 -0800 Subject: [PATCH 346/350] Refactor: attrd, fencer: Set cluster pointer to NULL after freeing And NULL-check the argument argument. Signed-off-by: Reid Wahl --- daemons/attrd/attrd_corosync.c | 16 +++++++++++++++- daemons/fenced/fenced_corosync.c | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c index 11fca1a9bff..da2b46ccfb8 100644 --- a/daemons/attrd/attrd_corosync.c +++ b/daemons/attrd/attrd_corosync.c @@ -489,6 +489,12 @@ broadcast_unseen_local_values(void) } } +/*! + * \internal + * \brief Initialize \c attrd_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ int attrd_cluster_connect(void) { @@ -514,11 +520,19 @@ attrd_cluster_connect(void) return rc; } +/*! + * \internal + * \brief Disconnect from the cluster layer and free \c attrd_cluster + */ void attrd_cluster_disconnect(void) { + if (attrd_cluster == NULL) { + return; + } + pcmk_cluster_disconnect(attrd_cluster); - pcmk_cluster_free(attrd_cluster); + g_clear_pointer(&attrd_cluster, pcmk_cluster_free); } void diff --git a/daemons/fenced/fenced_corosync.c b/daemons/fenced/fenced_corosync.c index 33984212896..948fc143e06 100644 --- a/daemons/fenced/fenced_corosync.c +++ b/daemons/fenced/fenced_corosync.c @@ -174,6 +174,12 @@ fenced_cpg_destroy(gpointer unused) } #endif // SUPPORT_COROSYNC +/*! + * \internal + * \brief Initialize \c fenced_cluster and connect to the cluster layer + * + * \return Standard Pacemaker return code + */ int fenced_cluster_connect(void) { @@ -199,9 +205,17 @@ fenced_cluster_connect(void) return rc; } +/*! + * \internal + * \brief Disconnect from the cluster layer and free \c fenced_cluster + */ void fenced_cluster_disconnect(void) { + if (fenced_cluster == NULL) { + return; + } + pcmk_cluster_disconnect(fenced_cluster); - pcmk_cluster_free(fenced_cluster); + g_clear_pointer(&fenced_cluster, pcmk_cluster_free); } From 7de820d5a99c28c283422a04c7da917c573b8ae2 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 21:22:12 -0800 Subject: [PATCH 347/350] Refactor: controller: Use g_clear_pointer() in controld_control.c Save some lines. Signed-off-by: Reid Wahl --- daemons/controld/controld_control.c | 37 +++++++++-------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c index a371ce707fc..57b81afa6c7 100644 --- a/daemons/controld/controld_control.c +++ b/daemons/controld/controld_control.c @@ -141,8 +141,7 @@ crmd_fast_exit(crm_exit_t exit_code) if (controld_globals.logger_out != NULL) { controld_globals.logger_out->finish(controld_globals.logger_out, exit_code, true, NULL); - pcmk__output_free(controld_globals.logger_out); - controld_globals.logger_out = NULL; + g_clear_pointer(&controld_globals.logger_out, pcmk__output_free); } crm_exit(exit_code); @@ -178,8 +177,7 @@ crmd_exit(crm_exit_t exit_code) if(ipcs) { pcmk__trace("Closing IPC server"); - mainloop_del_ipc_server(ipcs); - ipcs = NULL; + g_clear_pointer(&ipcs, mainloop_del_ipc_server); } controld_close_attrd_ipc(); @@ -225,8 +223,7 @@ crmd_exit(crm_exit_t exit_code) controld_clear_fsa_input_flags(R_LRM_CONNECTED); lrm_state_destroy_all(); - mainloop_destroy_trigger(config_read_trigger); - config_read_trigger = NULL; + g_clear_pointer(&config_read_trigger, mainloop_destroy_trigger); controld_destroy_fsa_trigger(); controld_destroy_transition_trigger(); @@ -238,20 +235,11 @@ crmd_exit(crm_exit_t exit_code) controld_cleanup_fencing_history_sync(NULL, true); controld_free_sched_timer(); - free(controld_globals.our_uuid); - controld_globals.our_uuid = NULL; - - free(controld_globals.dc_name); - controld_globals.dc_name = NULL; - - free(controld_globals.dc_version); - controld_globals.dc_version = NULL; - - free(controld_globals.cluster_name); - controld_globals.cluster_name = NULL; - - free(controld_globals.te_uuid); - controld_globals.te_uuid = NULL; + g_clear_pointer(&controld_globals.our_uuid, free); + g_clear_pointer(&controld_globals.dc_name, free); + g_clear_pointer(&controld_globals.dc_version, free); + g_clear_pointer(&controld_globals.cluster_name, free); + g_clear_pointer(&controld_globals.te_uuid, free); free_max_generation(); controld_destroy_failed_sync_table(); @@ -295,13 +283,11 @@ crmd_exit(crm_exit_t exit_code) mainloop_destroy_signal(SIGCHLD); } - cib_delete(controld_globals.cib_conn); - controld_globals.cib_conn = NULL; + g_clear_pointer(&controld_globals.cib_conn, cib_delete); throttle_fini(); - pcmk_cluster_free(controld_globals.cluster); - controld_globals.cluster = NULL; + g_clear_pointer(&controld_globals.cluster, pcmk_cluster_free); /* Graceful */ pcmk__trace("Done preparing for exit with status %d (%s)", exit_code, @@ -456,8 +442,7 @@ do_stop(long long action, enum crmd_fsa_cause cause, fsa_data_t *msg_data) { pcmk__trace("Stopping IPC server"); - mainloop_del_ipc_server(ipcs); - ipcs = NULL; + g_clear_pointer(&ipcs, mainloop_del_ipc_server); controld_fsa_append(C_FSA_INTERNAL, I_TERMINATE, NULL); } From 8652207c19d9455c72cc07e56eb2b72ced686f97 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 22:12:00 -0800 Subject: [PATCH 348/350] Fix: libcrmcluster: NULL to pcmk_cluster_disconnect() returns EINVAL pcmk_cluster_disconnect() now returns EINVAL if passed a NULL argument. Previously, if given a NULL argument in a Corosync cluster, pcmk_cluster_disconnect() would call down to pcmk__cpg_disconnect(), which would dereference the NULL pointer. Signed-off-by: Reid Wahl --- lib/cluster/cluster.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/cluster/cluster.c b/lib/cluster/cluster.c index 1c9ae1900ca..f578c2b8e03 100644 --- a/lib/cluster/cluster.c +++ b/lib/cluster/cluster.c @@ -124,6 +124,10 @@ pcmk_cluster_disconnect(pcmk_cluster_t *cluster) */ pcmk__cluster_destroy_node_caches(); + if (cluster == NULL) { + return EINVAL; + } + pcmk__info("Disconnecting from %s cluster layer", cluster_layer_s); switch (cluster_layer) { From 57f932ece63792d435d304a5e3daa9093e428fd7 Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 22:43:55 -0800 Subject: [PATCH 349/350] Refactor: based: Rename corosync callback functions To mirror attrd and the fencer. Signed-off-by: Reid Wahl --- daemons/based/based_corosync.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/daemons/based/based_corosync.c b/daemons/based/based_corosync.c index b3d82b6b345..a600df9063b 100644 --- a/daemons/based/based_corosync.c +++ b/daemons/based/based_corosync.c @@ -56,9 +56,9 @@ based_peer_callback(xmlNode *msg, void *private_data) #if SUPPORT_COROSYNC static void -cib_cs_dispatch(cpg_handle_t handle, - const struct cpg_name *groupName, - uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) +based_cpg_dispatch(cpg_handle_t handle, + const struct cpg_name *groupName, + uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) { xmlNode *xml = NULL; const char *from = NULL; @@ -82,7 +82,7 @@ cib_cs_dispatch(cpg_handle_t handle, } static void -cib_cs_destroy(gpointer user_data) +based_cpg_destroy(gpointer user_data) { if (cib_shutdown_flag) { pcmk__info("Corosync disconnection complete"); @@ -95,8 +95,8 @@ cib_cs_destroy(gpointer user_data) #endif static void -cib_peer_update_callback(enum pcmk__node_update type, - pcmk__node_status_t *node, const void *data) +based_peer_change_cb(enum pcmk__node_update type, pcmk__node_status_t *node, + const void *data) { switch (type) { case pcmk__node_update_name: @@ -107,10 +107,10 @@ cib_peer_update_callback(enum pcmk__node_update type, pcmk__info("Exiting after no more peers or clients remain"); based_terminate(-1); } - break; + return; default: - break; + return; } } @@ -129,13 +129,13 @@ based_cluster_connect(void) #if SUPPORT_COROSYNC if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) { - pcmk_cluster_set_destroy_fn(based_cluster, cib_cs_destroy); - pcmk_cpg_set_deliver_fn(based_cluster, cib_cs_dispatch); + pcmk_cluster_set_destroy_fn(based_cluster, based_cpg_destroy); + pcmk_cpg_set_deliver_fn(based_cluster, based_cpg_dispatch); pcmk_cpg_set_confchg_fn(based_cluster, pcmk__cpg_confchg_cb); } #endif // SUPPORT_COROSYNC - pcmk__cluster_set_status_callback(&cib_peer_update_callback); + pcmk__cluster_set_status_callback(based_peer_change_cb); rc = pcmk_cluster_connect(based_cluster); if (rc != pcmk_rc_ok) { From 3416148d207004265cd5ecc330978e4d42bc741c Mon Sep 17 00:00:00 2001 From: Reid Wahl Date: Tue, 6 Jan 2026 23:21:07 -0800 Subject: [PATCH 350/350] Refactor: based: Move based_shutdown() to pacemaker-based.c And make it static. It seems logical to have it in the same file as the main() function. Signed-off-by: Reid Wahl --- daemons/based/based_callbacks.c | 53 --------------------------------- daemons/based/based_callbacks.h | 1 - daemons/based/pacemaker-based.c | 52 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index a749d299290..d23fb2864cf 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -34,8 +34,6 @@ #include -#define EXIT_ESCALATION_MS 10000 - static mainloop_timer_t *digest_timer = NULL; static long long ping_seq = 0; static char *ping_digest = NULL; @@ -823,57 +821,6 @@ based_process_request(xmlNode *request, bool privileged, return rc; } -static gboolean -cib_force_exit(gpointer data) -{ - pcmk__notice("Exiting immediately after %s without shutdown acknowledgment", - pcmk__readable_interval(EXIT_ESCALATION_MS)); - based_terminate(CRM_EX_ERROR); - return FALSE; -} - -void -based_shutdown(int nsig) -{ - int active = 0; - xmlNode *notification = NULL; - - if (cib_shutdown_flag) { - // Already shutting down - return; - } - - cib_shutdown_flag = true; - - /* The client count should be 0 by now. If it isn't, that means GLib had - * some other reference to a remote client's source when we dropped the - * remote client. This is believed not to be possible. - * - * If this occurs, continue the shutdown. The clients will be dropped - * eventually. - */ - CRM_LOG_ASSERT(pcmk__ipc_client_count() == 0); - - active = pcmk__cluster_num_active_nodes(); - if (active < 2) { - pcmk__info("Exiting without sending shutdown request (no active " - "peers)"); - based_terminate(CRM_EX_OK); - return; - } - - pcmk__info("Sending shutdown request to %d peers", active); - - notification = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); - pcmk__xe_set(notification, PCMK__XA_T, PCMK__VALUE_CIB); - pcmk__xe_set(notification, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); - - pcmk__cluster_send_message(NULL, pcmk_ipc_based, notification); - pcmk__xml_free(notification); - - pcmk__create_timer(EXIT_ESCALATION_MS, cib_force_exit, NULL); -} - /*! * \internal * \brief Close remote sockets, free the global CIB and quit diff --git a/daemons/based/based_callbacks.h b/daemons/based/based_callbacks.h index 9f7360d94ed..57eb5db37eb 100644 --- a/daemons/based/based_callbacks.h +++ b/daemons/based/based_callbacks.h @@ -18,7 +18,6 @@ int based_process_request(xmlNode *request, bool privileged, const pcmk__client_t *client); -void based_shutdown(int nsig); void based_terminate(int exit_status); #endif // BASED_CALLBACKS__H diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 6558f20e709..dd482f5a43c 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -30,6 +30,7 @@ #include +#define EXIT_ESCALATION_MS 10000 #define SUMMARY "daemon for managing the configuration of a Pacemaker cluster" bool cib_shutdown_flag = false; @@ -156,6 +157,57 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) return context; } +static gboolean +based_force_exit(gpointer data) +{ + pcmk__notice("Exiting immediately after %s without shutdown acknowledgment", + pcmk__readable_interval(EXIT_ESCALATION_MS)); + based_terminate(CRM_EX_ERROR); + return G_SOURCE_REMOVE; +} + +static void +based_shutdown(int nsig) +{ + int active = 0; + xmlNode *notification = NULL; + + if (cib_shutdown_flag) { + // Already shutting down + return; + } + + cib_shutdown_flag = true; + + /* The client count should be 0 by now. If it isn't, that means GLib had + * some other reference to a remote client's source when we dropped the + * remote client. This is believed not to be possible. + * + * If this occurs, continue the shutdown. The clients will be dropped + * eventually. + */ + CRM_LOG_ASSERT(pcmk__ipc_client_count() == 0); + + active = pcmk__cluster_num_active_nodes(); + if (active < 2) { + pcmk__info("Exiting without sending shutdown request (no active " + "peers)"); + based_terminate(CRM_EX_OK); + return; + } + + pcmk__info("Sending shutdown request to %d peers", active); + + notification = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION); + pcmk__xe_set(notification, PCMK__XA_T, PCMK__VALUE_CIB); + pcmk__xe_set(notification, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN); + + pcmk__cluster_send_message(NULL, pcmk_ipc_based, notification); + pcmk__xml_free(notification); + + pcmk__create_timer(EXIT_ESCALATION_MS, based_force_exit, NULL); +} + int main(int argc, char **argv) {