summaryrefslogtreecommitdiffstats
path: root/lib/cib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 07:45:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 07:45:40 +0000
commit07d7f4cfa4b10de87a31b68191036ff446add675 (patch)
tree7162524d8aaf1aef62d2f4fa51f595ed113981ff /lib/cib
parentAdding upstream version 2.1.6. (diff)
downloadpacemaker-0d560556df519c6626cda7660f843a815b3c227e.tar.xz
pacemaker-0d560556df519c6626cda7660f843a815b3c227e.zip
Adding upstream version 2.1.7.upstream/2.1.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/cib')
-rw-r--r--lib/cib/Makefile.am22
-rw-r--r--lib/cib/cib_attrs.c19
-rw-r--r--lib/cib/cib_client.c112
-rw-r--r--lib/cib/cib_file.c477
-rw-r--r--lib/cib/cib_native.c56
-rw-r--r--lib/cib/cib_ops.c228
-rw-r--r--lib/cib/cib_remote.c38
-rw-r--r--lib/cib/cib_utils.c511
8 files changed, 1112 insertions, 351 deletions
diff --git a/lib/cib/Makefile.am b/lib/cib/Makefile.am
index 721fca1..a74c4b1 100644
--- a/lib/cib/Makefile.am
+++ b/lib/cib/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2004-2018 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,18 +11,20 @@ include $(top_srcdir)/mk/common.mk
## libraries
lib_LTLIBRARIES = libcib.la
-## SOURCES
-libcib_la_SOURCES = cib_ops.c cib_utils.c cib_client.c cib_native.c cib_attrs.c
-libcib_la_SOURCES += cib_file.c cib_remote.c
+## Library sources (*must* use += format for bumplibs)
+libcib_la_SOURCES = cib_attrs.c
+libcib_la_SOURCES += cib_client.c
+libcib_la_SOURCES += cib_file.c
+libcib_la_SOURCES += cib_native.c
+libcib_la_SOURCES += cib_ops.c
+libcib_la_SOURCES += cib_remote.c
+libcib_la_SOURCES += cib_utils.c
-libcib_la_LDFLAGS = -version-info 31:0:4
+libcib_la_LDFLAGS = -version-info 32:0:5
libcib_la_CPPFLAGS = -I$(top_srcdir) $(AM_CPPFLAGS)
libcib_la_CFLAGS = $(CFLAGS_HARDENED_LIB)
libcib_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB)
-libcib_la_LIBADD = $(top_builddir)/lib/pengine/libpe_rules.la \
- $(top_builddir)/lib/common/libcrmcommon.la
-
-clean-generic:
- rm -f *.log *.debug *.xml *~
+libcib_la_LIBADD = $(top_builddir)/lib/pengine/libpe_rules.la \
+ $(top_builddir)/lib/common/libcrmcommon.la
diff --git a/lib/cib/cib_attrs.c b/lib/cib/cib_attrs.c
index 5f3a722..11629b8 100644
--- a/lib/cib/cib_attrs.c
+++ b/lib/cib/cib_attrs.c
@@ -152,16 +152,15 @@ find_attr(cib_t *cib, const char *section, const char *node_uuid,
static int
handle_multiples(pcmk__output_t *out, xmlNode *search, const char *attr_name)
{
- if (xml_has_children(search)) {
+ if ((search != NULL) && (search->children != NULL)) {
xmlNode *child = NULL;
- out->info(out, "Multiple attributes match name=%s", attr_name);
+ out->info(out, "Multiple attributes match name=%s", attr_name);
for (child = pcmk__xml_first_child(search); child != NULL;
child = pcmk__xml_next(child)) {
out->info(out, " Value: %s \t(id=%s)",
crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child));
}
-
return ENOTUNIQ;
} else {
@@ -184,9 +183,9 @@ cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, const c
char *local_attr_id = NULL;
char *local_set_name = NULL;
- CRM_CHECK(section != NULL, return EINVAL);
- CRM_CHECK(attr_value != NULL, return EINVAL);
- CRM_CHECK(attr_name != NULL || attr_id != NULL, return EINVAL);
+ CRM_CHECK((out != NULL) && (cib != NULL) && (section != NULL)
+ && ((attr_id != NULL) || (attr_name != NULL))
+ && (attr_value != NULL), return EINVAL);
rc = find_attr(cib, section, node_uuid, set_type, set_name, attr_id,
attr_name, user_name, &xml_search);
@@ -360,7 +359,7 @@ cib__get_node_attrs(pcmk__output_t *out, cib_t *cib, const char *section,
crm_trace("Query failed for attribute %s (section=%s node=%s set=%s): %s",
pcmk__s(attr_name, "with unspecified name"),
section, pcmk__s(set_name, "<null>"),
- pcmk__s(node_uuid, "<null>"), pcmk_strerror(rc));
+ pcmk__s(node_uuid, "<null>"), pcmk_rc_str(rc));
}
return rc;
@@ -487,7 +486,7 @@ read_attr_delegate(cib_t *cib, const char *section, const char *node_uuid,
attr_id, attr_name, user_name, &result);
if (rc == pcmk_rc_ok) {
- if (!xml_has_children(result)) {
+ if (result->children == NULL) {
pcmk__str_update(attr_value, crm_element_value(result, XML_NVPAIR_ATTR_VALUE));
} else {
rc = ENOTUNIQ;
@@ -677,9 +676,7 @@ query_node_uname(cib_t * the_cib, const char *uuid, char **uname)
}
xml_obj = fragment;
- CRM_CHECK(pcmk__str_eq(crm_element_name(xml_obj), XML_CIB_TAG_NODES, pcmk__str_casei),
- return -ENOMSG);
- CRM_ASSERT(xml_obj != NULL);
+ CRM_CHECK(pcmk__xe_is(xml_obj, XML_CIB_TAG_NODES), return -ENOMSG);
crm_log_xml_trace(xml_obj, "Result section");
rc = -ENXIO;
diff --git a/lib/cib/cib_client.c b/lib/cib/cib_client.c
index 2d179e0..32e1f83 100644
--- a/lib/cib/cib_client.c
+++ b/lib/cib/cib_client.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2022 the Pacemaker project contributors
+ * Copyright 2004-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -253,14 +253,15 @@ cib_client_noop(cib_t * cib, int call_options)
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_NOOP, NULL, NULL, NULL, NULL,
- call_options, NULL);
+ call_options, cib->user);
}
static int
cib_client_ping(cib_t * cib, xmlNode ** output_data, int call_options)
{
op_common(cib);
- return cib_internal_op(cib, CRM_OP_PING, NULL, NULL, NULL, output_data, call_options, NULL);
+ return cib_internal_op(cib, CRM_OP_PING, NULL, NULL, NULL, output_data,
+ call_options, cib->user);
}
static int
@@ -275,7 +276,7 @@ cib_client_query_from(cib_t * cib, const char *host, const char *section,
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, host, section, NULL,
- output_data, call_options, NULL);
+ output_data, call_options, cib->user);
}
static int
@@ -283,7 +284,7 @@ is_primary(cib_t *cib)
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_IS_PRIMARY, NULL, NULL, NULL,
- NULL, cib_scope_local|cib_sync_call, NULL);
+ NULL, cib_scope_local|cib_sync_call, cib->user);
}
static int
@@ -291,7 +292,7 @@ set_secondary(cib_t *cib, int call_options)
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_SECONDARY, NULL, NULL, NULL,
- NULL, call_options, NULL);
+ NULL, call_options, cib->user);
}
static int
@@ -306,7 +307,7 @@ set_primary(cib_t *cib, int call_options)
op_common(cib);
crm_trace("Adding cib_scope_local to options");
return cib_internal_op(cib, PCMK__CIB_REQUEST_PRIMARY, NULL, NULL, NULL,
- NULL, call_options|cib_scope_local, NULL);
+ NULL, call_options|cib_scope_local, cib->user);
}
static int
@@ -314,7 +315,7 @@ cib_client_bump_epoch(cib_t * cib, int call_options)
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_BUMP, NULL, NULL, NULL, NULL,
- call_options, NULL);
+ call_options, cib->user);
}
static int
@@ -322,7 +323,7 @@ cib_client_upgrade(cib_t * cib, int call_options)
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_UPGRADE, NULL, NULL, NULL,
- NULL, call_options, NULL);
+ NULL, call_options, cib->user);
}
static int
@@ -336,7 +337,7 @@ cib_client_sync_from(cib_t * cib, const char *host, const char *section, int cal
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_SYNC_TO_ALL, host, section,
- NULL, NULL, call_options, NULL);
+ NULL, NULL, call_options, cib->user);
}
static int
@@ -344,7 +345,7 @@ cib_client_create(cib_t * cib, const char *section, xmlNode * data, int call_opt
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_CREATE, NULL, section, data,
- NULL, call_options, NULL);
+ NULL, call_options, cib->user);
}
static int
@@ -352,7 +353,7 @@ cib_client_modify(cib_t * cib, const char *section, xmlNode * data, int call_opt
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_MODIFY, NULL, section, data,
- NULL, call_options, NULL);
+ NULL, call_options, cib->user);
}
static int
@@ -360,7 +361,7 @@ cib_client_replace(cib_t * cib, const char *section, xmlNode * data, int call_op
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_REPLACE, NULL, section, data,
- NULL, call_options, NULL);
+ NULL, call_options, cib->user);
}
static int
@@ -368,7 +369,7 @@ cib_client_delete(cib_t * cib, const char *section, xmlNode * data, int call_opt
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_DELETE, NULL, section, data,
- NULL, call_options, NULL);
+ NULL, call_options, cib->user);
}
static int
@@ -376,7 +377,7 @@ cib_client_delete_absolute(cib_t * cib, const char *section, xmlNode * data, int
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_ABS_DELETE, NULL, section,
- data, NULL, call_options, NULL);
+ data, NULL, call_options, cib->user);
}
static int
@@ -384,7 +385,76 @@ cib_client_erase(cib_t * cib, xmlNode ** output_data, int call_options)
{
op_common(cib);
return cib_internal_op(cib, PCMK__CIB_REQUEST_ERASE, NULL, NULL, NULL,
- output_data, call_options, NULL);
+ output_data, call_options, cib->user);
+}
+
+static int
+cib_client_init_transaction(cib_t *cib)
+{
+ int rc = pcmk_rc_ok;
+
+ op_common(cib);
+
+ if (cib->transaction != NULL) {
+ // A client can have at most one transaction at a time
+ rc = pcmk_rc_already;
+ }
+
+ if (rc == pcmk_rc_ok) {
+ cib->transaction = create_xml_node(NULL, T_CIB_TRANSACTION);
+ if (cib->transaction == NULL) {
+ rc = ENOMEM;
+ }
+ }
+
+ if (rc != pcmk_rc_ok) {
+ const char *client_id = NULL;
+
+ cib->cmds->client_id(cib, NULL, &client_id);
+ crm_err("Failed to initialize CIB transaction for client %s: %s",
+ client_id, pcmk_rc_str(rc));
+ }
+ return pcmk_rc2legacy(rc);
+}
+
+static int
+cib_client_end_transaction(cib_t *cib, bool commit, int call_options)
+{
+ const char *client_id = NULL;
+ int rc = pcmk_ok;
+
+ op_common(cib);
+ cib->cmds->client_id(cib, NULL, &client_id);
+ client_id = pcmk__s(client_id, "(unidentified)");
+
+ if (commit) {
+ if (cib->transaction == NULL) {
+ rc = pcmk_rc_no_transaction;
+
+ crm_err("Failed to commit transaction for CIB client %s: %s",
+ client_id, pcmk_rc_str(rc));
+ return pcmk_rc2legacy(rc);
+ }
+ rc = cib_internal_op(cib, PCMK__CIB_REQUEST_COMMIT_TRANSACT, NULL, NULL,
+ cib->transaction, NULL, call_options, cib->user);
+
+ } else {
+ // Discard always succeeds
+ if (cib->transaction != NULL) {
+ crm_trace("Discarded transaction for CIB client %s", client_id);
+ } else {
+ crm_trace("No transaction found for CIB client %s", client_id);
+ }
+ }
+ free_xml(cib->transaction);
+ cib->transaction = NULL;
+ return rc;
+}
+
+static void
+cib_client_set_user(cib_t *cib, const char *user)
+{
+ pcmk__str_update(&(cib->user), user);
}
static void
@@ -622,13 +692,15 @@ cib_new_variant(void)
return NULL;
}
+ // Deprecated method
new_cib->cmds->set_op_callback = cib_client_set_op_callback;
+
new_cib->cmds->add_notify_callback = cib_client_add_notify_callback;
new_cib->cmds->del_notify_callback = cib_client_del_notify_callback;
new_cib->cmds->register_callback = cib_client_register_callback;
new_cib->cmds->register_callback_full = cib_client_register_callback_full;
- new_cib->cmds->noop = cib_client_noop;
+ new_cib->cmds->noop = cib_client_noop; // Deprecated method
new_cib->cmds->ping = cib_client_ping;
new_cib->cmds->query = cib_client_query;
new_cib->cmds->sync = cib_client_sync;
@@ -656,8 +728,14 @@ cib_new_variant(void)
new_cib->cmds->remove = cib_client_delete;
new_cib->cmds->erase = cib_client_erase;
+ // Deprecated method
new_cib->cmds->delete_absolute = cib_client_delete_absolute;
+ new_cib->cmds->init_transaction = cib_client_init_transaction;
+ new_cib->cmds->end_transaction = cib_client_end_transaction;
+
+ new_cib->cmds->set_user = cib_client_set_user;
+
return new_cib;
}
diff --git a/lib/cib/cib_file.c b/lib/cib/cib_file.c
index 7d05965..a279823 100644
--- a/lib/cib/cib_file.c
+++ b/lib/cib/cib_file.c
@@ -37,35 +37,100 @@
#define CIB_LIVE_NAME CIB_SERIES ".xml"
+// key: client ID (const char *) -> value: client (cib_t *)
+static GHashTable *client_table = NULL;
+
enum cib_file_flags {
cib_file_flag_dirty = (1 << 0),
cib_file_flag_live = (1 << 1),
};
typedef struct cib_file_opaque_s {
- uint32_t flags; // Group of enum cib_file_flags
+ char *id;
char *filename;
+ uint32_t flags; // Group of enum cib_file_flags
+ xmlNode *cib_xml;
} cib_file_opaque_t;
-struct cib_func_entry {
- const char *op;
- gboolean read_only;
- cib_op_t fn;
-};
+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 struct cib_func_entry cib_file_ops[] = {
- { PCMK__CIB_REQUEST_QUERY, TRUE, cib_process_query },
- { PCMK__CIB_REQUEST_MODIFY, FALSE, cib_process_modify },
- { PCMK__CIB_REQUEST_APPLY_PATCH, FALSE, cib_process_diff },
- { PCMK__CIB_REQUEST_BUMP, FALSE, cib_process_bump },
- { PCMK__CIB_REQUEST_REPLACE, FALSE, cib_process_replace },
- { PCMK__CIB_REQUEST_CREATE, FALSE, cib_process_create },
- { PCMK__CIB_REQUEST_DELETE, FALSE, cib_process_delete },
- { PCMK__CIB_REQUEST_ERASE, FALSE, cib_process_erase },
- { PCMK__CIB_REQUEST_UPGRADE, FALSE, cib_process_upgrade },
-};
+/*!
+ * \internal
+ * \brief Add a CIB file client to client table
+ *
+ * \param[in] cib CIB client
+ */
+static void
+register_client(const cib_t *cib)
+{
+ cib_file_opaque_t *private = cib->variant_opaque;
+
+ if (client_table == NULL) {
+ client_table = pcmk__strkey_table(NULL, NULL);
+ }
+ g_hash_table_insert(client_table, private->id, (gpointer) cib);
+}
+
+/*!
+ * \internal
+ * \brief Remove a CIB file client from client table
+ *
+ * \param[in] cib CIB client
+ */
+static void
+unregister_client(const cib_t *cib)
+{
+ cib_file_opaque_t *private = cib->variant_opaque;
-static xmlNode *in_mem_cib = NULL;
+ if (client_table == NULL) {
+ return;
+ }
+
+ g_hash_table_remove(client_table, private->id);
+
+ /* @COMPAT: Add to crm_exit() when libcib and libcrmcommon are merged,
+ * instead of destroying the client table when there are no more clients.
+ */
+ if (g_hash_table_size(client_table) == 0) {
+ g_hash_table_destroy(client_table);
+ client_table = NULL;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Look up a CIB file client by its ID
+ *
+ * \param[in] client_id CIB client ID
+ *
+ * \return CIB client with matching ID if found, or \p NULL otherwise
+ */
+static cib_t *
+get_client(const char *client_id)
+{
+ if (client_table == NULL) {
+ return NULL;
+ }
+ return g_hash_table_lookup(client_table, (gpointer) client_id);
+}
+
+static const cib__op_fn_t cib_op_functions[] = {
+ [cib__op_apply_patch] = cib_process_diff,
+ [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,
+ [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,
+};
/* cib_file_backup() and cib_file_write_with_digest() need to chown the
* written files only in limited circumstances, so these variables allow
@@ -95,6 +160,27 @@ static gboolean cib_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
+file_get_op_function(const cib__operation_t *operation)
+{
+ enum cib__op_type type = operation->type;
+
+ CRM_ASSERT(type >= 0);
+
+ if (type >= PCMK__NELEM(cib_op_functions)) {
+ return NULL;
+ }
+ return cib_op_functions[type];
+}
+
+/*!
+ * \internal
* \brief Check whether a file is the live CIB
*
* \param[in] filename Name of file to check
@@ -125,114 +211,148 @@ cib_file_is_live(const char *filename)
}
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)
+cib_file_process_request(cib_t *cib, xmlNode *request, xmlNode **output)
{
int rc = pcmk_ok;
- char *effective_user = NULL;
- gboolean query = FALSE;
- gboolean changed = FALSE;
- xmlNode *request = NULL;
- xmlNode *output = NULL;
- xmlNode *cib_diff = NULL;
+ const cib__operation_t *operation = NULL;
+ cib__op_fn_t op_function = NULL;
+
+ int call_id = 0;
+ int call_options = cib_none;
+ const char *op = crm_element_value(request, F_CIB_OPERATION);
+ const char *section = crm_element_value(request, F_CIB_SECTION);
+ xmlNode *data = get_message_xml(request, F_CIB_CALLDATA);
+
+ bool changed = false;
+ bool read_only = false;
xmlNode *result_cib = NULL;
- cib_op_t *fn = NULL;
- int lpc = 0;
- static int max_msg_types = PCMK__NELEM(cib_file_ops);
+ xmlNode *cib_diff = NULL;
+
cib_file_opaque_t *private = cib->variant_opaque;
- crm_info("Handling %s operation for %s as %s",
- (op? op : "invalid"), (section? section : "entire CIB"),
- (user_name? user_name : "default user"));
+ // We error checked these in callers
+ cib__get_operation(op, &operation);
+ op_function = file_get_op_function(operation);
- cib__set_call_options(call_options, "file operation",
- cib_no_mtime|cib_inhibit_bcast|cib_scope_local);
+ crm_element_value_int(request, F_CIB_CALLID, &call_id);
+ crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
- if (cib->state == cib_disconnected) {
- return -ENOTCONN;
- }
+ read_only = !pcmk_is_set(operation->flags, cib__op_attr_modifies);
- if (output_data != NULL) {
- *output_data = NULL;
+ // Mirror the logic in prepare_input() in pacemaker-based
+ if ((section != NULL) && pcmk__xe_is(data, XML_TAG_CIB)) {
+
+ data = pcmk_find_cib_element(data, section);
}
- if (op == NULL) {
- return -EINVAL;
+ rc = cib_perform_op(op, call_options, op_function, read_only, 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
+ * to individual requests.
+ */
+ goto done;
}
- for (lpc = 0; lpc < max_msg_types; lpc++) {
- if (pcmk__str_eq(op, cib_file_ops[lpc].op, pcmk__str_casei)) {
- fn = &(cib_file_ops[lpc].fn);
- query = cib_file_ops[lpc].read_only;
- break;
+ if (rc == -pcmk_err_schema_validation) {
+ validate_xml_verbose(result_cib);
+
+ } else if ((rc == pcmk_ok) && !read_only) {
+ pcmk__log_xml_patchset(LOG_DEBUG, cib_diff);
+
+ if (result_cib != private->cib_xml) {
+ free_xml(private->cib_xml);
+ private->cib_xml = result_cib;
}
+ cib_set_file_flags(private, cib_file_flag_dirty);
}
- if (fn == NULL) {
- return -EPROTONOSUPPORT;
+ // Global operation callback (deprecated)
+ if (cib->op_callback != NULL) {
+ cib->op_callback(NULL, call_id, rc, *output);
}
- cib->call_id++;
- request = cib_create_op(cib->call_id, op, host, section, data, call_options,
- user_name);
- if(user_name) {
- crm_xml_add(request, XML_ACL_TAG_USER, user_name);
+done:
+ if ((result_cib != private->cib_xml) && (result_cib != *output)) {
+ free_xml(result_cib);
}
+ free_xml(cib_diff);
+ return rc;
+}
- /* Mirror the logic in cib_prepare_common() */
- if (section != NULL && data != NULL && pcmk__str_eq(crm_element_name(data), XML_TAG_CIB, pcmk__str_none)) {
- data = pcmk_find_cib_element(data, section);
- }
+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)
+{
+ int rc = pcmk_ok;
+ xmlNode *request = NULL;
+ xmlNode *output = NULL;
+ cib_file_opaque_t *private = cib->variant_opaque;
- rc = cib_perform_op(op, call_options, fn, query,
- section, request, data, TRUE, &changed, in_mem_cib, &result_cib, &cib_diff,
- &output);
+ const cib__operation_t *operation = NULL;
- free_xml(request);
- if (rc == -pcmk_err_schema_validation) {
- validate_xml_verbose(result_cib);
+ crm_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 (rc != pcmk_ok) {
- free_xml(result_cib);
+ if (cib->state == cib_disconnected) {
+ return -ENOTCONN;
+ }
- } else if (query == FALSE) {
- pcmk__output_t *out = NULL;
+ rc = cib__get_operation(op, &operation);
+ rc = pcmk_rc2legacy(rc);
+ if (rc != pcmk_ok) {
+ // @COMPAT: At compatibility break, use rc directly
+ return -EPROTONOSUPPORT;
+ }
- rc = pcmk_rc2legacy(pcmk__log_output_new(&out));
- CRM_CHECK(rc == pcmk_ok, goto done);
+ if (file_get_op_function(operation) == NULL) {
+ // @COMPAT: At compatibility break, use EOPNOTSUPP
+ crm_err("Operation %s is not supported by CIB file clients", op);
+ return -EPROTONOSUPPORT;
+ }
- pcmk__output_set_log_level(out, LOG_DEBUG);
- rc = out->message(out, "xml-patchset", cib_diff);
- out->finish(out, pcmk_rc2exitc(rc), true, NULL);
- pcmk__output_free(out);
- rc = pcmk_ok;
+ cib__set_call_options(call_options, "file operation", cib_no_mtime);
- free_xml(in_mem_cib);
- in_mem_cib = result_cib;
- cib_set_file_flags(private, cib_file_flag_dirty);
+ rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
+ NULL, &request);
+ if (rc != pcmk_ok) {
+ return rc;
}
+ crm_xml_add(request, XML_ACL_TAG_USER, user_name);
+ crm_xml_add(request, F_CIB_CLIENTID, private->id);
- if (cib->op_callback != NULL) {
- cib->op_callback(NULL, cib->call_id, rc, output);
+ if (pcmk_is_set(call_options, cib_transaction)) {
+ rc = cib__extend_transaction(cib, request);
+ goto done;
}
+ rc = cib_file_process_request(cib, request, &output);
+
if ((output_data != NULL) && (output != NULL)) {
- *output_data = (output == in_mem_cib)? copy_xml(output) : output;
+ if (output->doc == private->cib_xml->doc) {
+ *output_data = copy_xml(output);
+ } else {
+ *output_data = output;
+ }
}
done:
- free_xml(cib_diff);
+ if ((output != NULL)
+ && (output->doc != private->cib_xml->doc)
+ && ((output_data == NULL) || (output != *output_data))) {
- if ((output_data == NULL) && (output != in_mem_cib)) {
- /* Don't free output if we're still using it. (output_data != NULL)
- * means we may have assigned *output_data = output above.
- */
free_xml(output);
}
- free(effective_user);
+ free_xml(request);
return rc;
}
@@ -240,7 +360,8 @@ done:
* \internal
* \brief Read CIB from disk and validate it against XML schema
*
- * \param[in] filename Name of file to read CIB from
+ * \param[in] filename Name of file to read CIB from
+ * \param[out] output Where to store the read CIB XML
*
* \return pcmk_ok on success,
* -ENXIO if file does not exist (or stat() otherwise fails), or
@@ -251,7 +372,7 @@ done:
* because some callers might not need to write.
*/
static int
-load_file_cib(const char *filename)
+load_file_cib(const char *filename, xmlNode **output)
{
struct stat buf;
xmlNode *root = NULL;
@@ -282,7 +403,7 @@ load_file_cib(const char *filename)
}
/* Remember the parsed XML for later use */
- in_mem_cib = root;
+ *output = root;
return pcmk_ok;
}
@@ -295,7 +416,7 @@ cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
if (private->filename == NULL) {
rc = -EINVAL;
} else {
- rc = load_file_cib(private->filename);
+ rc = load_file_cib(private->filename, &private->cib_xml);
}
if (rc == pcmk_ok) {
@@ -303,10 +424,11 @@ cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
private->filename, name);
cib->state = cib_connected_command;
cib->type = cib_command;
+ register_client(cib);
} else {
- crm_info("Connection to local file '%s' for %s failed: %s\n",
- private->filename, name, pcmk_strerror(rc));
+ crm_info("Connection to local file '%s' for %s (client %s) failed: %s",
+ private->filename, name, private->id, pcmk_strerror(rc));
}
return rc;
}
@@ -315,12 +437,13 @@ cib_file_signon(cib_t *cib, const char *name, enum cib_conn_type type)
* \internal
* \brief Write out the in-memory CIB to a live CIB file
*
- * param[in,out] path Full path to file to write
+ * param[in] cib_root Root of XML tree to write
+ * param[in,out] path Full path to file to write
*
* \return 0 on success, -1 on failure
*/
static int
-cib_file_write_live(char *path)
+cib_file_write_live(xmlNode *cib_root, char *path)
{
uid_t uid = geteuid();
struct passwd *daemon_pwent;
@@ -370,7 +493,7 @@ cib_file_write_live(char *path)
}
/* write the file */
- if (cib_file_write_with_digest(in_mem_cib, cib_dirname,
+ if (cib_file_write_with_digest(cib_root, cib_dirname,
cib_filename) != pcmk_ok) {
rc = -1;
}
@@ -410,13 +533,15 @@ cib_file_signoff(cib_t *cib)
crm_debug("Disconnecting from the CIB manager");
cib->state = cib_disconnected;
cib->type = cib_no_connection;
+ unregister_client(cib);
+ cib->cmds->end_transaction(cib, false, cib_none);
/* If the in-memory CIB has been changed, write it to disk */
if (pcmk_is_set(private->flags, cib_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 (cib_file_write_live(private->filename) < 0) {
+ if (cib_file_write_live(private->cib_xml, private->filename) < 0) {
rc = pcmk_err_generic;
}
@@ -424,7 +549,8 @@ cib_file_signoff(cib_t *cib)
} else {
gboolean do_bzip = pcmk__ends_with_ext(private->filename, ".bz2");
- if (write_xml_file(in_mem_cib, private->filename, do_bzip) <= 0) {
+ if (write_xml_file(private->cib_xml, private->filename,
+ do_bzip) <= 0) {
rc = pcmk_err_generic;
}
}
@@ -438,8 +564,8 @@ cib_file_signoff(cib_t *cib)
}
/* Free the in-memory CIB */
- free_xml(in_mem_cib);
- in_mem_cib = NULL;
+ free_xml(private->cib_xml);
+ private->cib_xml = NULL;
return rc;
}
@@ -455,9 +581,11 @@ cib_file_free(cib_t *cib)
if (rc == pcmk_ok) {
cib_file_opaque_t *private = cib->variant_opaque;
+ free(private->id);
free(private->filename);
- free(cib->cmds);
free(private);
+ free(cib->cmds);
+ free(cib->user);
free(cib);
} else {
@@ -494,24 +622,24 @@ cib_file_set_connection_dnotify(cib_t *cib,
* \param[out] async_id If not \p NULL, where to store asynchronous client ID
* \param[out] sync_id If not \p NULL, where to store synchronous client ID
*
- * \return Legacy Pacemaker return code (specifically, \p -EPROTONOSUPPORT)
+ * \return Legacy Pacemaker return code
*
* \note This is the \p cib_file variant implementation of
* \p cib_api_operations_t:client_id().
- * \note A \p cib_file object doesn't connect to the CIB and is never assigned a
- * client ID.
*/
static int
cib_file_client_id(const cib_t *cib, const char **async_id,
const char **sync_id)
{
+ cib_file_opaque_t *private = cib->variant_opaque;
+
if (async_id != NULL) {
- *async_id = NULL;
+ *async_id = private->id;
}
if (sync_id != NULL) {
- *sync_id = NULL;
+ *sync_id = private->id;
}
- return -EPROTONOSUPPORT;
+ return pcmk_ok;
}
cib_t *
@@ -530,6 +658,7 @@ cib_file_new(const char *cib_location)
free(cib);
return NULL;
}
+ private->id = crm_generate_uuid();
cib->variant = cib_file;
cib->variant_opaque = private;
@@ -550,7 +679,7 @@ cib_file_new(const char *cib_location)
cib->cmds->signon = cib_file_signon;
cib->cmds->signoff = cib_file_signoff;
cib->cmds->free = cib_file_free;
- cib->cmds->inputfd = cib_file_inputfd;
+ cib->cmds->inputfd = cib_file_inputfd; // Deprecated method
cib->cmds->register_notification = cib_file_register_notification;
cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify;
@@ -917,3 +1046,133 @@ 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
+cib_file_process_transaction_requests(cib_t *cib, xmlNode *transaction)
+{
+ cib_file_opaque_t *private = cib->variant_opaque;
+
+ for (xmlNode *request = first_named_child(transaction, T_CIB_COMMAND);
+ request != NULL; request = crm_next_same_xml(request)) {
+
+ xmlNode *output = NULL;
+ const char *op = crm_element_value(request, F_CIB_OPERATION);
+
+ int rc = cib_file_process_request(cib, request, &output);
+
+ rc = pcmk_legacy2rc(rc);
+ if (rc != pcmk_rc_ok) {
+ crm_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));
+ crm_log_xml_info(request, "Failed request");
+ return rc;
+ }
+
+ crm_trace("Applied %s request to transaction working CIB for CIB file "
+ "client (%s) on file '%s'",
+ op, private->id, private->filename);
+ crm_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 free_xml() on failure.
+ */
+static int
+cib_file_commit_transaction(cib_t *cib, xmlNode *transaction,
+ xmlNode **result_cib)
+{
+ int rc = pcmk_rc_ok;
+ cib_file_opaque_t *private = cib->variant_opaque;
+ xmlNode *saved_cib = private->cib_xml;
+
+ CRM_CHECK(pcmk__xe_is(transaction, T_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 = copy_xml(private->cib_xml));
+
+ crm_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 = cib_file_process_transaction_requests(cib, transaction);
+
+ crm_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
+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)
+{
+ int rc = pcmk_rc_ok;
+ const char *client_id = crm_element_value(req, F_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 = cib_file_commit_transaction(cib, input, result_cib);
+ if (rc != pcmk_rc_ok) {
+ cib_file_opaque_t *private = cib->variant_opaque;
+
+ crm_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);
+}
diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c
index 4a87f56..c5e8b9e 100644
--- a/lib/cib/cib_native.c
+++ b/lib/cib/cib_native.c
@@ -69,20 +69,19 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host,
pcmk__set_ipc_flags(ipc_flags, "client", crm_ipc_client_response);
}
- cib->call_id++;
- if (cib->call_id < 1) {
- cib->call_id = 1;
+ rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
+ NULL, &op_msg);
+ if (rc != pcmk_ok) {
+ return rc;
}
- op_msg = cib_create_op(cib->call_id, op, host, section, data, call_options,
- user_name);
- if (op_msg == NULL) {
- return -EPROTO;
+ if (pcmk_is_set(call_options, cib_transaction)) {
+ rc = cib__extend_transaction(cib, op_msg);
+ goto done;
}
crm_trace("Sending %s message to the CIB manager (timeout=%ds)", op, cib->call_timeout);
rc = crm_ipc_send(native->ipc, op_msg, ipc_flags, cib->call_timeout * 1000, &op_reply);
- free_xml(op_msg);
if (rc < 0) {
crm_err("Couldn't perform %s operation (timeout=%ds): %s (%d)", op,
@@ -168,6 +167,7 @@ cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host,
cib->state = cib_disconnected;
}
+ free_xml(op_msg);
free_xml(op_reply);
return rc;
}
@@ -255,6 +255,7 @@ cib_native_signoff(cib_t *cib)
crm_ipc_destroy(ipc);
}
+ cib->cmds->end_transaction(cib, false, cib_none);
cib->state = cib_disconnected;
cib->type = cib_no_connection;
@@ -268,6 +269,7 @@ cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type,
int rc = pcmk_ok;
const char *channel = NULL;
cib_native_opaque_t *native = cib->variant_opaque;
+ xmlNode *hello = NULL;
struct ipc_client_callbacks cib_callbacks = {
.dispatch = cib_native_dispatch_internal,
@@ -296,12 +298,16 @@ cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type,
if (async_fd != NULL) {
native->ipc = crm_ipc_new(channel, 0);
-
- if (native->ipc && crm_ipc_connect(native->ipc)) {
- *async_fd = crm_ipc_get_fd(native->ipc);
-
- } else if (native->ipc) {
- rc = -ENOTCONN;
+ if (native->ipc != NULL) {
+ rc = pcmk__connect_generic_ipc(native->ipc);
+ if (rc == pcmk_rc_ok) {
+ rc = pcmk__ipc_fd(native->ipc, async_fd);
+ if (rc != pcmk_rc_ok) {
+ crm_info("Couldn't get file descriptor for %s IPC",
+ channel);
+ }
+ }
+ rc = pcmk_rc2legacy(rc);
}
} else {
@@ -317,23 +323,23 @@ cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type,
}
if (rc == pcmk_ok) {
- xmlNode *reply = NULL;
- xmlNode *hello = create_xml_node(NULL, "cib_command");
+ rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL,
+ cib_sync_call, NULL, name, &hello);
+ }
- crm_xml_add(hello, F_TYPE, T_CIB);
- crm_xml_add(hello, F_CIB_OPERATION, CRM_OP_REGISTER);
- crm_xml_add(hello, F_CIB_CLIENTNAME, name);
- crm_xml_add_int(hello, F_CIB_CALLOPTS, cib_sync_call);
+ if (rc == pcmk_ok) {
+ xmlNode *reply = NULL;
- if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply) > 0) {
+ if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1,
+ &reply) > 0) {
const char *msg_type = crm_element_value(reply, F_CIB_OPERATION);
- rc = pcmk_ok;
crm_log_xml_trace(reply, "reg-reply");
if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
- crm_info("Reply to CIB registration message has "
- "unknown type '%s'", msg_type);
+ crm_info("Reply to CIB registration message has unknown type "
+ "'%s'",
+ msg_type);
rc = -EPROTO;
} else {
@@ -347,7 +353,6 @@ cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type,
} else {
rc = -ECOMM;
}
-
free_xml(hello);
}
@@ -383,6 +388,7 @@ cib_native_free(cib_t *cib)
free(native->token);
free(cib->variant_opaque);
free(cib->cmds);
+ free(cib->user);
free(cib);
}
diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c
index d3293c4..c324304 100644
--- a/lib/cib/cib_ops.c
+++ b/lib/cib/cib_ops.c
@@ -19,6 +19,9 @@
#include <sys/param.h>
#include <sys/types.h>
+#include <glib.h>
+#include <libxml/tree.h>
+
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
@@ -26,6 +29,139 @@
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
+// @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon
+static GHashTable *operation_table = NULL;
+
+static const cib__operation_t cib_ops[] = {
+ {
+ PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete,
+ cib__op_attr_modifies|cib__op_attr_privileged
+ },
+ {
+ PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_BUMP, cib__op_bump,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_replaces
+ |cib__op_attr_writes_through
+ },
+ {
+ PCMK__CIB_REQUEST_CREATE, cib__op_create,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_DELETE, cib__op_delete,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_ERASE, cib__op_erase,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_replaces
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary,
+ cib__op_attr_privileged
+ },
+ {
+ PCMK__CIB_REQUEST_MODIFY, cib__op_modify,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none
+ },
+ {
+ CRM_OP_PING, cib__op_ping, cib__op_attr_none
+ },
+ {
+ // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support
+ PCMK__CIB_REQUEST_PRIMARY, cib__op_primary,
+ cib__op_attr_modifies|cib__op_attr_privileged|cib__op_attr_local
+ },
+ {
+ PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none
+ },
+ {
+ PCMK__CIB_REQUEST_REPLACE, cib__op_replace,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_replaces
+ |cib__op_attr_writes_through
+ |cib__op_attr_transaction
+ },
+ {
+ PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary,
+ cib__op_attr_privileged|cib__op_attr_local
+ },
+ {
+ 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_ONE, cib__op_sync_one, cib__op_attr_privileged
+ },
+ {
+ PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade,
+ cib__op_attr_modifies
+ |cib__op_attr_privileged
+ |cib__op_attr_writes_through
+ |cib__op_attr_transaction
+ },
+};
+
+/*!
+ * \internal
+ * \brief Get the \c cib__operation_t object for a given CIB operation name
+ *
+ * \param[in] op CIB operation name
+ * \param[out] operation Where to store CIB operation object
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+cib__get_operation(const char *op, const cib__operation_t **operation)
+{
+ CRM_ASSERT((op != NULL) && (operation != NULL));
+
+ if (operation_table == NULL) {
+ operation_table = pcmk__strkey_table(NULL, NULL);
+
+ for (int lpc = 0; lpc < PCMK__NELEM(cib_ops); lpc++) {
+ const cib__operation_t *oper = &(cib_ops[lpc]);
+
+ g_hash_table_insert(operation_table, (gpointer) oper->name,
+ (gpointer) oper);
+ }
+ }
+
+ *operation = g_hash_table_lookup(operation_table, op);
+ if (*operation == NULL) {
+ crm_err("Operation %s is invalid", op);
+ return EINVAL;
+ }
+ return pcmk_rc_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)
@@ -54,8 +190,8 @@ cib_process_query(const char *op, int options, const char *section, xmlNode * re
result = -ENXIO;
} else if (options & cib_no_children) {
- const char *tag = TYPE(obj_root);
- xmlNode *shallow = create_xml_node(*answer, tag);
+ xmlNode *shallow = create_xml_node(*answer,
+ (const char *) obj_root->name);
copy_in_properties(shallow, obj_root);
*answer = shallow;
@@ -107,12 +243,14 @@ cib_process_erase(const char *op, int options, const char *section, xmlNode * re
int result = pcmk_ok;
crm_trace("Processing \"%s\" event", op);
- *answer = NULL;
- free_xml(*result_cib);
- *result_cib = createEmptyCib(0);
+ if (*result_cib != existing_cib) {
+ free_xml(*result_cib);
+ }
+ *result_cib = createEmptyCib(0);
copy_in_properties(*result_cib, existing_cib);
update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
+ *answer = NULL;
return result;
}
@@ -172,7 +310,6 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode *
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer)
{
- const char *tag = NULL;
int result = pcmk_ok;
crm_trace("Processing %s for %s section",
@@ -189,16 +326,14 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode *
return -EINVAL;
}
- tag = crm_element_name(input);
-
if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
section = NULL;
- } else if (pcmk__str_eq(tag, section, pcmk__str_casei)) {
+ } else if (pcmk__xe_is(input, section)) {
section = NULL;
}
- if (pcmk__str_eq(tag, XML_TAG_CIB, pcmk__str_casei)) {
+ if (pcmk__xe_is(input, XML_TAG_CIB)) {
int updates = 0;
int epoch = 0;
int admin_epoch = 0;
@@ -262,7 +397,9 @@ cib_process_replace(const char *op, int options, const char *section, xmlNode *
replace_admin_epoch, replace_epoch, replace_updates, peer);
}
- free_xml(*result_cib);
+ if (*result_cib != existing_cib) {
+ free_xml(*result_cib);
+ }
*result_cib = copy_xml(input);
} else {
@@ -299,7 +436,7 @@ cib_process_delete(const char *op, int options, const char *section, xmlNode * r
}
obj_root = pcmk_find_cib_element(*result_cib, section);
- if(pcmk__str_eq(crm_element_name(input), section, pcmk__str_casei)) {
+ if (pcmk__xe_is(input, section)) {
xmlNode *child = NULL;
for (child = pcmk__xml_first_child(input); child;
child = pcmk__xml_next(child)) {
@@ -360,7 +497,8 @@ cib_process_modify(const char *op, int options, const char *section, xmlNode * r
}
}
- if(options & cib_mixed_update) {
+ // @COMPAT cib_mixed_update is deprecated as of 2.1.7
+ if (pcmk_is_set(options, cib_mixed_update)) {
int max = 0, lpc;
xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
@@ -396,7 +534,7 @@ update_cib_object(xmlNode * parent, xmlNode * update)
CRM_CHECK(update != NULL, return -EINVAL);
CRM_CHECK(parent != NULL, return -EINVAL);
- object_name = crm_element_name(update);
+ object_name = (const char *) update->name;
CRM_CHECK(object_name != NULL, return -EINVAL);
object_id = ID(update);
@@ -425,33 +563,25 @@ update_cib_object(xmlNode * parent, xmlNode * update)
// @COMPAT: XML_CIB_ATTR_REPLACE is unused internally. Remove at break.
replace = crm_element_value(update, XML_CIB_ATTR_REPLACE);
if (replace != NULL) {
- xmlNode *remove = NULL;
- int last = 0, lpc = 0, len = 0;
+ int last = 0;
+ int len = strlen(replace);
- len = strlen(replace);
- while (lpc <= len) {
+ for (int lpc = 0; lpc <= len; ++lpc) {
if (replace[lpc] == ',' || replace[lpc] == 0) {
- char *replace_item = NULL;
-
- if (last == lpc) {
- /* nothing to do */
- last = lpc + 1;
- goto incr;
- }
-
- replace_item = strndup(replace + last, lpc - last);
- remove = find_xml_node(target, replace_item, FALSE);
- if (remove != NULL) {
- crm_trace("Replacing node <%s> in <%s>",
- replace_item, crm_element_name(target));
- free_xml(remove);
- remove = NULL;
+ if (last != lpc) {
+ char *replace_item = strndup(replace + last, lpc - last);
+ xmlNode *remove = find_xml_node(target, replace_item,
+ FALSE);
+
+ if (remove != NULL) {
+ crm_trace("Replacing node <%s> in <%s>",
+ replace_item, target->name);
+ free_xml(remove);
+ }
+ free(replace_item);
}
- free(replace_item);
last = lpc + 1;
}
- incr:
- lpc++;
}
xml_remove_prop(update, XML_CIB_ATTR_REPLACE);
xml_remove_prop(target, XML_CIB_ATTR_REPLACE);
@@ -475,7 +605,7 @@ update_cib_object(xmlNode * parent, xmlNode * update)
a_child = pcmk__xml_next(a_child)) {
int tmp_result = 0;
- crm_trace("Updating child <%s%s%s%s>", crm_element_name(a_child),
+ crm_trace("Updating child <%s%s%s%s>", a_child->name,
((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
pcmk__s(ID(a_child), ""), ((ID(a_child) == NULL)? "" : "'"));
@@ -484,7 +614,7 @@ update_cib_object(xmlNode * parent, xmlNode * update)
/* only the first error is likely to be interesting */
if (tmp_result != pcmk_ok) {
crm_err("Error updating child <%s%s%s%s>",
- crm_element_name(a_child),
+ a_child->name,
((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
pcmk__s(ID(a_child), ""),
((ID(a_child) == NULL)? "" : "'"));
@@ -514,7 +644,7 @@ add_cib_object(xmlNode * parent, xmlNode * new_obj)
return -EINVAL;
}
- object_name = crm_element_name(new_obj);
+ object_name = (const char *) new_obj->name;
if (object_name == NULL) {
return -EINVAL;
}
@@ -555,7 +685,8 @@ update_results(xmlNode *failed, xmlNode *target, const char *operation,
add_node_copy(xml_node, target);
crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target));
- crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE, TYPE(target));
+ crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE,
+ (const char *) target->name);
crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation);
crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg);
@@ -582,7 +713,7 @@ cib_process_create(const char *op, int options, const char *section, xmlNode * r
} else if (pcmk__str_eq(XML_TAG_CIB, section, pcmk__str_casei)) {
section = NULL;
- } else if (pcmk__str_eq(crm_element_name(input), XML_TAG_CIB, pcmk__str_casei)) {
+ } else if (pcmk__xe_is(input, XML_TAG_CIB)) {
section = NULL;
}
@@ -601,7 +732,7 @@ cib_process_create(const char *op, int options, const char *section, xmlNode * r
failed = create_xml_node(NULL, XML_TAG_FAILED);
update_section = pcmk_find_cib_element(*result_cib, section);
- if (pcmk__str_eq(crm_element_name(input), section, pcmk__str_casei)) {
+ if (pcmk__xe_is(input, section)) {
xmlNode *a_child = NULL;
for (a_child = pcmk__xml_first_child(input); a_child != NULL;
@@ -617,7 +748,7 @@ cib_process_create(const char *op, int options, const char *section, xmlNode * r
update_results(failed, input, op, result);
}
- if ((result == pcmk_ok) && xml_has_children(failed)) {
+ if ((result == pcmk_ok) && (failed->children != NULL)) {
result = -EINVAL;
}
@@ -646,8 +777,11 @@ cib_process_diff(const char *op, int options, const char *section, xmlNode * req
op, originator,
(pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
- free_xml(*result_cib);
+ if (*result_cib != existing_cib) {
+ free_xml(*result_cib);
+ }
*result_cib = copy_xml(existing_cib);
+
return xml_apply_patchset(*result_cib, input, TRUE);
}
@@ -670,7 +804,7 @@ cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
goto done;
}
- crm_element_value_int(*diff, "format", &format);
+ crm_element_value_int(*diff, PCMK_XA_FORMAT, &format);
CRM_LOG_ASSERT(format == 1);
xpathObj = xpath_search(*diff, "//" XML_CIB_TAG_CONFIGURATION);
@@ -803,8 +937,8 @@ cib_process_xpath(const char *op, int options, const char *section,
} else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
if (options & cib_no_children) {
- const char *tag = TYPE(match);
- xmlNode *shallow = create_xml_node(*answer, tag);
+ xmlNode *shallow = create_xml_node(*answer,
+ (const char *) match->name);
copy_in_properties(shallow, match);
diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c
index 28095b3..77479d7 100644
--- a/lib/cib/cib_remote.c
+++ b/lib/cib/cib_remote.c
@@ -55,7 +55,8 @@ typedef struct cib_remote_opaque_s {
static int
cib_remote_perform_op(cib_t *cib, const char *op, const char *host,
const char *section, xmlNode *data,
- xmlNode **output_data, int call_options, const char *name)
+ xmlNode **output_data, int call_options,
+ const char *user_name)
{
int rc;
int remaining_time = 0;
@@ -79,15 +80,16 @@ cib_remote_perform_op(cib_t *cib, const char *op, const char *host,
return -EINVAL;
}
- cib->call_id++;
- if (cib->call_id < 1) {
- cib->call_id = 1;
+ rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
+ NULL, &op_msg);
+ if (rc != pcmk_ok) {
+ return rc;
}
- op_msg = cib_create_op(cib->call_id, op, host, section, data, call_options,
- NULL);
- if (op_msg == NULL) {
- return -EPROTO;
+ if (pcmk_is_set(call_options, cib_transaction)) {
+ rc = cib__extend_transaction(cib, op_msg);
+ free_xml(op_msg);
+ return rc;
}
crm_trace("Sending %s message to the CIB manager", op);
@@ -378,7 +380,7 @@ cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel)
}
/* login to server */
- login = create_xml_node(NULL, "cib_command");
+ login = create_xml_node(NULL, T_CIB_COMMAND);
crm_xml_add(login, "op", "authenticate");
crm_xml_add(login, "user", private->user);
crm_xml_add(login, "password", private->passwd);
@@ -434,6 +436,7 @@ cib_remote_signon(cib_t *cib, const char *name, enum cib_conn_type type)
{
int rc = pcmk_ok;
cib_remote_opaque_t *private = cib->variant_opaque;
+ xmlNode *hello = NULL;
if (private->passwd == NULL) {
if (private->out == NULL) {
@@ -459,10 +462,13 @@ cib_remote_signon(cib_t *cib, const char *name, enum cib_conn_type type)
}
if (rc == pcmk_ok) {
- xmlNode *hello = cib_create_op(0, CRM_OP_REGISTER, NULL, NULL, NULL, 0,
- NULL);
- crm_xml_add(hello, F_CIB_CLIENTNAME, name);
- pcmk__remote_send_xml(&private->command, hello);
+ rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL, cib_none,
+ NULL, name, &hello);
+ }
+
+ if (rc == pcmk_ok) {
+ rc = pcmk__remote_send_xml(&private->command, hello);
+ rc = pcmk_rc2legacy(rc);
free_xml(hello);
}
@@ -490,6 +496,7 @@ cib_remote_signoff(cib_t *cib)
cib_tls_close(cib);
#endif
+ cib->cmds->end_transaction(cib, false, cib_none);
cib->state = cib_disconnected;
cib->type = cib_no_connection;
@@ -511,6 +518,7 @@ cib_remote_free(cib_t *cib)
free(private->user);
free(private->passwd);
free(cib->cmds);
+ free(cib->user);
free(private);
free(cib);
}
@@ -530,7 +538,7 @@ cib_remote_inputfd(cib_t * cib)
static int
cib_remote_register_notification(cib_t * cib, const char *callback, int enabled)
{
- xmlNode *notify_msg = create_xml_node(NULL, "cib_command");
+ xmlNode *notify_msg = create_xml_node(NULL, T_CIB_COMMAND);
cib_remote_opaque_t *private = cib->variant_opaque;
crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY);
@@ -614,7 +622,7 @@ cib_remote_new(const char *server, const char *user, const char *passwd, int por
cib->cmds->signon = cib_remote_signon;
cib->cmds->signoff = cib_remote_signoff;
cib->cmds->free = cib_remote_free;
- cib->cmds->inputfd = cib_remote_inputfd;
+ cib->cmds->inputfd = cib_remote_inputfd; // Deprecated method
cib->cmds->register_notification = cib_remote_register_notification;
cib->cmds->set_connection_dnotify = cib_remote_set_connection_dnotify;
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c
index c75d844..0082eef 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -20,6 +20,7 @@
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
+#include <crm/common/cib_internal.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/pengine/rules.h>
@@ -78,6 +79,154 @@ cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *upda
}
/*!
+ * \internal
+ * \brief Get the XML patchset from a CIB diff notification
+ *
+ * \param[in] msg CIB diff notification
+ * \param[out] patchset Where to store XML patchset
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
+{
+ int rc = pcmk_err_generic;
+
+ CRM_ASSERT(patchset != NULL);
+ *patchset = NULL;
+
+ if (msg == NULL) {
+ crm_err("CIB diff notification received with no XML");
+ return ENOMSG;
+ }
+
+ if ((crm_element_value_int(msg, F_CIB_RC, &rc) != 0) || (rc != pcmk_ok)) {
+ crm_warn("Ignore failed CIB update: %s " CRM_XS " rc=%d",
+ pcmk_strerror(rc), rc);
+ crm_log_xml_debug(msg, "failed");
+ return pcmk_legacy2rc(rc);
+ }
+
+ *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+
+ if (*patchset == NULL) {
+ crm_err("CIB diff notification received with no patchset");
+ return ENOMSG;
+ }
+ return pcmk_rc_ok;
+}
+
+#define XPATH_DIFF_V1 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
+
+/*!
+ * \internal
+ * \brief Check whether a given CIB element was modified in a CIB patchset (v1)
+ *
+ * \param[in] patchset CIB XML patchset
+ * \param[in] element XML tag of CIB element to check (\c NULL is equivalent
+ * to \c XML_TAG_CIB)
+ *
+ * \return \c true if \p element was modified, or \c false otherwise
+ */
+static bool
+element_in_patchset_v1(const xmlNode *patchset, const char *element)
+{
+ char *xpath = crm_strdup_printf(XPATH_DIFF_V1 "//%s",
+ pcmk__s(element, XML_TAG_CIB));
+ xmlXPathObject *xpath_obj = xpath_search(patchset, xpath);
+
+ free(xpath);
+
+ if (xpath_obj == NULL) {
+ return false;
+ }
+ freeXpathObject(xpath_obj);
+ return true;
+}
+
+/*!
+ * \internal
+ * \brief Check whether a given CIB element was modified in a CIB patchset (v2)
+ *
+ * \param[in] patchset CIB XML patchset
+ * \param[in] element XML tag of CIB element to check (\c NULL is equivalent
+ * to \c XML_TAG_CIB). Supported values include any CIB
+ * element supported by \c pcmk__cib_abs_xpath_for().
+ *
+ * \return \c true if \p element was modified, or \c false otherwise
+ */
+static bool
+element_in_patchset_v2(const xmlNode *patchset, const char *element)
+{
+ const char *element_xpath = pcmk__cib_abs_xpath_for(element);
+ const char *parent_xpath = pcmk_cib_parent_name_for(element);
+ char *element_regex = NULL;
+ bool rc = false;
+
+ CRM_CHECK(element_xpath != NULL, return false); // Unsupported element
+
+ // Matches if and only if element_xpath is part of a changed path
+ element_regex = crm_strdup_printf("^%s(/|$)", element_xpath);
+
+ for (const xmlNode *change = first_named_child(patchset, XML_DIFF_CHANGE);
+ change != NULL; change = crm_next_same_xml(change)) {
+
+ const char *op = crm_element_value(change, F_CIB_OPERATION);
+ const char *diff_xpath = crm_element_value(change, XML_DIFF_PATH);
+
+ if (pcmk__str_eq(diff_xpath, element_regex, pcmk__str_regex)) {
+ // Change to an existing element
+ rc = true;
+ break;
+ }
+
+ if (pcmk__str_eq(op, "create", pcmk__str_none)
+ && pcmk__str_eq(diff_xpath, parent_xpath, pcmk__str_none)
+ && pcmk__xe_is(pcmk__xml_first_child(change), element)) {
+
+ // Newly added element
+ rc = true;
+ break;
+ }
+ }
+
+ free(element_regex);
+ return rc;
+}
+
+/*!
+ * \internal
+ * \brief Check whether a given CIB element was modified in a CIB patchset
+ *
+ * \param[in] patchset CIB XML patchset
+ * \param[in] element XML tag of CIB element to check (\c NULL is equivalent
+ * to \c XML_TAG_CIB). Supported values include any CIB
+ * element supported by \c pcmk__cib_abs_xpath_for().
+ *
+ * \return \c true if \p element was modified, or \c false otherwise
+ */
+bool
+cib__element_in_patchset(const xmlNode *patchset, const char *element)
+{
+ int format = 1;
+
+ CRM_ASSERT(patchset != NULL);
+
+ crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
+ switch (format) {
+ case 1:
+ return element_in_patchset_v1(patchset, element);
+
+ case 2:
+ return element_in_patchset_v2(patchset, element);
+
+ default:
+ crm_warn("Unknown patch format: %d", format);
+ return false;
+ }
+}
+
+/*!
* \brief Create XML for a new (empty) CIB
*
* \param[in] cib_epoch What to use as "epoch" CIB property
@@ -141,30 +290,79 @@ cib_acl_enabled(xmlNode *xml, const char *user)
return rc;
}
+/*!
+ * \internal
+ * \brief Determine whether to perform operations on a scratch copy of the CIB
+ *
+ * \param[in] op CIB operation
+ * \param[in] section CIB section
+ * \param[in] call_options CIB call options
+ *
+ * \return \p true if we should make a copy of the CIB, or \p false otherwise
+ */
+static bool
+should_copy_cib(const char *op, const char *section, int call_options)
+{
+ if (pcmk_is_set(call_options, cib_dryrun)) {
+ // cib_dryrun implies a scratch copy by definition; no side effects
+ return true;
+ }
+
+ if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
+ /* Commit-transaction must make a copy for atomicity. We must revert to
+ * the original CIB if the entire transaction cannot be applied
+ * successfully.
+ */
+ return true;
+ }
+
+ if (pcmk_is_set(call_options, cib_transaction)) {
+ /* If cib_transaction is set, then we're in the process of committing a
+ * transaction. The commit-transaction request already made a scratch
+ * copy, and we're accumulating changes in that copy.
+ */
+ return false;
+ }
+
+ if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_none)) {
+ /* Copying large CIBs accounts for a huge percentage of our CIB usage,
+ * and this avoids some of it.
+ *
+ * @TODO: Is this safe? See discussion at
+ * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
+ */
+ return false;
+ }
+
+ // Default behavior is to operate on a scratch copy
+ return true;
+}
+
int
-cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query,
- const char *section, xmlNode * req, xmlNode * input,
- gboolean manage_counters, gboolean * config_changed,
- xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, xmlNode ** output)
+cib_perform_op(const char *op, int 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)
{
int rc = pcmk_ok;
- gboolean check_schema = TRUE;
+ bool check_schema = true;
+ bool make_copy = true;
xmlNode *top = NULL;
xmlNode *scratch = NULL;
+ xmlNode *patchset_cib = NULL;
xmlNode *local_diff = NULL;
const char *new_version = NULL;
const char *user = crm_element_value(req, F_CIB_USER);
- bool with_digest = FALSE;
-
- pcmk__output_t *out = NULL;
- int out_rc = pcmk_rc_no_output;
+ bool with_digest = false;
crm_trace("Begin %s%s%s op",
(pcmk_is_set(call_options, cib_dryrun)? "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);
@@ -173,25 +371,26 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
}
*result_cib = NULL;
- *config_changed = FALSE;
+ *config_changed = false;
if (fn == NULL) {
return -EINVAL;
}
if (is_query) {
- xmlNode *cib_ro = current_cib;
+ xmlNode *cib_ro = *current_cib;
xmlNode *cib_filtered = NULL;
- if(cib_acl_enabled(cib_ro, user)) {
- if(xml_acl_filtered_copy(user, current_cib, current_cib, &cib_filtered)) {
- if (cib_filtered == NULL) {
- crm_debug("Pre-filtered the entire cib");
- return -EACCES;
- }
- cib_ro = cib_filtered;
- crm_log_xml_trace(cib_ro, "filtered");
+ if (cib_acl_enabled(cib_ro, user)
+ && xml_acl_filtered_copy(user, *current_cib, *current_cib,
+ &cib_filtered)) {
+
+ if (cib_filtered == NULL) {
+ crm_debug("Pre-filtered the entire cib");
+ return -EACCES;
}
+ cib_ro = cib_filtered;
+ crm_log_xml_trace(cib_ro, "filtered");
}
rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
@@ -202,14 +401,14 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
} else if(cib_filtered == *output) {
cib_filtered = NULL; /* Let them have this copy */
- } else if(*output == current_cib) {
+ } 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 = copy_xml(*output);
- } else if((*output)->doc == current_cib->doc) {
+ } else if ((*output)->doc == (*current_cib)->doc) {
/* Give them a copy they can free */
*output = copy_xml(*output);
}
@@ -218,31 +417,41 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
return rc;
}
+ make_copy = should_copy_cib(op, section, call_options);
- if (pcmk_is_set(call_options, cib_zero_copy)) {
+ if (!make_copy) {
/* Conditional on v2 patch style */
- scratch = current_cib;
+ scratch = *current_cib;
- /* Create a shallow copy of current_cib for the version details */
- current_cib = create_xml_node(NULL, (const char *)scratch->name);
- copy_in_properties(current_cib, scratch);
- top = current_cib;
+ // Make a copy of the top-level element to store version details
+ top = create_xml_node(NULL, (const char *) scratch->name);
+ copy_in_properties(top, scratch);
+ patchset_cib = top;
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
+ /* If scratch points to a new object now (for example, after an erase
+ * operation), then *current_cib should point to the same object.
+ */
+ *current_cib = scratch;
+
} else {
- scratch = copy_xml(current_cib);
+ scratch = copy_xml(*current_cib);
+ patchset_cib = *current_cib;
+
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
- rc = (*fn) (op, call_options, section, req, input, current_cib, &scratch, output);
+ rc = (*fn) (op, call_options, section, req, input, *current_cib,
+ &scratch, output);
- if(scratch && xml_tracking_changes(scratch) == FALSE) {
+ if ((scratch != NULL) && !xml_tracking_changes(scratch)) {
crm_trace("Inferring changes after %s op", op);
- xml_track_changes(scratch, user, current_cib, cib_acl_enabled(current_cib, user));
- xml_calculate_changes(current_cib, scratch);
+ xml_track_changes(scratch, user, *current_cib,
+ cib_acl_enabled(*current_cib, user));
+ xml_calculate_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 */
@@ -271,12 +480,12 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
}
}
- if (current_cib) {
+ if (patchset_cib != NULL) {
int old = 0;
int new = 0;
crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
- crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
+ crm_element_value_int(patchset_cib, XML_ATTR_GENERATION_ADMIN, &old);
if (old > new) {
crm_err("%s went backwards: %d -> %d (Opts: %#x)",
@@ -287,7 +496,7 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
} else if (old == new) {
crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
- crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
+ crm_element_value_int(patchset_cib, XML_ATTR_GENERATION, &old);
if (old > new) {
crm_err("%s went backwards: %d -> %d (Opts: %#x)",
XML_ATTR_GENERATION, old, new, call_options);
@@ -302,13 +511,14 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
pcmk__strip_xml_text(scratch);
fix_plus_plus_recursive(scratch);
- if (pcmk_is_set(call_options, cib_zero_copy)) {
- /* At this point, current_cib is just the 'cib' tag and its properties,
+ if (!make_copy) {
+ /* At this point, patchset_cib is just the "cib" tag and its properties.
*
* The v1 format would barf on this, but we know the v2 patch
* format only needs it for the top-level version fields
*/
- local_diff = xml_create_patchset(2, current_cib, scratch, (bool*)config_changed, manage_counters);
+ local_diff = xml_create_patchset(2, patchset_cib, scratch,
+ config_changed, manage_counters);
} else {
static time_t expires = 0;
@@ -316,63 +526,38 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
if (expires < tm_now) {
expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */
- with_digest = TRUE;
+ with_digest = true;
}
- local_diff = xml_create_patchset(0, current_cib, scratch, (bool*)config_changed, manage_counters);
+ local_diff = xml_create_patchset(0, patchset_cib, scratch,
+ config_changed, manage_counters);
}
- // Create a log output object only if we're going to use it
- pcmk__if_tracing(
- {
- rc = pcmk_rc2legacy(pcmk__log_output_new(&out));
- CRM_CHECK(rc == pcmk_ok, goto done);
-
- pcmk__output_set_log_level(out, LOG_TRACE);
- out_rc = pcmk__xml_show_changes(out, scratch);
- },
- {}
- );
+ pcmk__log_xml_changes(LOG_TRACE, scratch);
xml_accept_changes(scratch);
if(local_diff) {
- int temp_rc = pcmk_rc_no_output;
-
- patchset_process_digest(local_diff, current_cib, scratch, with_digest);
-
- if (out == NULL) {
- rc = pcmk_rc2legacy(pcmk__log_output_new(&out));
- CRM_CHECK(rc == pcmk_ok, goto done);
- }
- pcmk__output_set_log_level(out, LOG_INFO);
- temp_rc = out->message(out, "xml-patchset", local_diff);
- out_rc = pcmk__output_select_rc(rc, temp_rc);
-
+ patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
+ pcmk__log_xml_patchset(LOG_INFO, local_diff);
crm_log_xml_trace(local_diff, "raw patch");
}
- if (out != NULL) {
- out->finish(out, pcmk_rc2exitc(out_rc), true, NULL);
- pcmk__output_free(out);
- out = NULL;
- }
-
- if (!pcmk_is_set(call_options, cib_zero_copy) && (local_diff != NULL)) {
+ 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 = copy_xml(current_cib);
+ xmlNode *cib_copy = copy_xml(patchset_cib);
- crm_element_value_int(local_diff, "format", &format);
+ crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format);
test_rc = xml_apply_patchset(cib_copy, local_diff,
manage_counters);
if (test_rc != pcmk_ok) {
save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
- save_xml_to_file(current_cib, "PatchApply:input", NULL);
+ save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
save_xml_to_file(scratch, "PatchApply:actual", NULL);
save_xml_to_file(local_diff, "PatchApply:diff", NULL);
crm_err("v%d patchset error, patch failed to apply: %s "
@@ -391,7 +576,7 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
* 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;
+ check_schema = false;
}
/* === scratch must not be modified after this point ===
@@ -420,19 +605,35 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
/* Does the CIB support the "update-*" attributes... */
if (current_schema >= minimum_schema) {
+ /* Ensure values of origin, client, and user in scratch match
+ * the values in req
+ */
const char *origin = crm_element_value(req, F_ORIG);
+ const char *client = crm_element_value(req, F_CIB_CLIENTNAME);
+
+ if (origin != NULL) {
+ crm_xml_add(scratch, XML_ATTR_UPDATE_ORIG, origin);
+ } else {
+ xml_remove_prop(scratch, XML_ATTR_UPDATE_ORIG);
+ }
- CRM_LOG_ASSERT(origin != NULL);
- crm_xml_replace(scratch, XML_ATTR_UPDATE_ORIG, origin);
- crm_xml_replace(scratch, XML_ATTR_UPDATE_CLIENT,
- crm_element_value(req, F_CIB_CLIENTNAME));
- crm_xml_replace(scratch, XML_ATTR_UPDATE_USER, crm_element_value(req, F_CIB_USER));
+ if (client != NULL) {
+ crm_xml_add(scratch, XML_ATTR_UPDATE_CLIENT, user);
+ } else {
+ xml_remove_prop(scratch, XML_ATTR_UPDATE_CLIENT);
+ }
+
+ if (user != NULL) {
+ crm_xml_add(scratch, XML_ATTR_UPDATE_USER, user);
+ } else {
+ xml_remove_prop(scratch, XML_ATTR_UPDATE_USER);
+ }
}
}
}
crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
- if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, TRUE)) {
+ if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, true)) {
const char *current_schema = crm_element_value(scratch,
XML_ATTR_VALIDATION);
@@ -444,13 +645,17 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
done:
*result_cib = scratch;
- if(rc != pcmk_ok && cib_acl_enabled(current_cib, user)) {
- if(xml_acl_filtered_copy(user, current_cib, scratch, result_cib)) {
- if (*result_cib == NULL) {
- crm_debug("Pre-filtered the entire cib result");
- }
- free_xml(scratch);
+
+ /* @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)
+ && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
+
+ if (*result_cib == NULL) {
+ crm_debug("Pre-filtered the entire cib result");
}
+ free_xml(scratch);
}
if(diff) {
@@ -464,36 +669,117 @@ cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_quer
return rc;
}
-xmlNode *
-cib_create_op(int call_id, const char *op, const char *host,
- const char *section, xmlNode *data, int call_options,
- const char *user_name)
+int
+cib__create_op(cib_t *cib, const char *op, const char *host,
+ const char *section, xmlNode *data, int call_options,
+ const char *user_name, const char *client_name,
+ xmlNode **op_msg)
{
- xmlNode *op_msg = create_xml_node(NULL, "cib_command");
+ CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO);
- CRM_CHECK(op_msg != NULL, return NULL);
-
- crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
+ *op_msg = create_xml_node(NULL, T_CIB_COMMAND);
+ if (*op_msg == NULL) {
+ return -EPROTO;
+ }
- crm_xml_add(op_msg, F_TYPE, T_CIB);
- crm_xml_add(op_msg, F_CIB_OPERATION, op);
- crm_xml_add(op_msg, F_CIB_HOST, host);
- crm_xml_add(op_msg, F_CIB_SECTION, section);
- crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
- if (user_name) {
- crm_xml_add(op_msg, F_CIB_USER, user_name);
+ cib->call_id++;
+ if (cib->call_id < 1) {
+ cib->call_id = 1;
}
+
+ crm_xml_add(*op_msg, F_XML_TAGNAME, T_CIB_COMMAND);
+ crm_xml_add(*op_msg, F_TYPE, T_CIB);
+ crm_xml_add(*op_msg, F_CIB_OPERATION, op);
+ crm_xml_add(*op_msg, F_CIB_HOST, host);
+ crm_xml_add(*op_msg, F_CIB_SECTION, section);
+ crm_xml_add(*op_msg, F_CIB_USER, user_name);
+ crm_xml_add(*op_msg, F_CIB_CLIENTNAME, client_name);
+ crm_xml_add_int(*op_msg, F_CIB_CALLID, cib->call_id);
+
crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
- crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
+ crm_xml_add_int(*op_msg, F_CIB_CALLOPTS, call_options);
if (data != NULL) {
- add_message_xml(op_msg, F_CIB_CALLDATA, data);
+ add_message_xml(*op_msg, F_CIB_CALLDATA, data);
}
- if (call_options & cib_inhibit_bcast) {
- CRM_CHECK((call_options & cib_scope_local), return NULL);
+ if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
+ CRM_CHECK(pcmk_is_set(call_options, cib_scope_local),
+ free_xml(*op_msg); return -EPROTO);
}
- return op_msg;
+ return pcmk_ok;
+}
+
+/*!
+ * \internal
+ * \brief Check whether a CIB request is supported in a transaction
+ *
+ * \param[in] request CIB request
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+validate_transaction_request(const xmlNode *request)
+{
+ const char *op = crm_element_value(request, F_CIB_OPERATION);
+ const char *host = crm_element_value(request, F_CIB_HOST);
+ const cib__operation_t *operation = NULL;
+ int rc = cib__get_operation(op, &operation);
+
+ if (rc != pcmk_rc_ok) {
+ // cib__get_operation() logs error
+ return rc;
+ }
+
+ if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
+ crm_err("Operation %s is not supported in CIB transactions", op);
+ return EOPNOTSUPP;
+ }
+
+ if (host != NULL) {
+ crm_err("Operation targeting a specific node (%s) is not supported in "
+ "a CIB transaction",
+ host);
+ return EOPNOTSUPP;
+ }
+ return pcmk_rc_ok;
+}
+
+/*!
+ * \internal
+ * \brief Append a CIB request to a CIB transaction
+ *
+ * \param[in,out] cib CIB client whose transaction to extend
+ * \param[in,out] request Request to add to transaction
+ *
+ * \return Legacy Pacemaker return code
+ */
+int
+cib__extend_transaction(cib_t *cib, xmlNode *request)
+{
+ int rc = pcmk_rc_ok;
+
+ CRM_ASSERT((cib != NULL) && (request != NULL));
+
+ rc = validate_transaction_request(request);
+
+ if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
+ rc = pcmk_rc_no_transaction;
+ }
+
+ if (rc == pcmk_rc_ok) {
+ add_node_copy(cib->transaction, request);
+
+ } else {
+ const char *op = crm_element_value(request, F_CIB_OPERATION);
+ const char *client_id = NULL;
+
+ cib->cmds->client_id(cib, NULL, &client_id);
+ crm_err("Failed to add '%s' operation to transaction for client %s: %s",
+ op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
+ crm_log_xml_info(request, "failed");
+ }
+ return pcmk_rc2legacy(rc);
}
void
@@ -701,16 +987,7 @@ cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
}
if (level > LOG_CRIT) {
- pcmk__output_t *out = NULL;
-
- rc = pcmk_rc2legacy(pcmk__log_output_new(&out));
- CRM_CHECK(rc == pcmk_ok, return rc);
-
- pcmk__output_set_log_level(out, level);
- rc = out->message(out, "xml-patchset", diff);
- out->finish(out, pcmk_rc2exitc(rc), true, NULL);
- pcmk__output_free(out);
- rc = pcmk_ok;
+ pcmk__log_xml_patchset(level, diff);
}
if (input != NULL) {