diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index ce49437253c..664210f180c 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 @@ -248,37 +247,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); } } @@ -517,54 +511,17 @@ 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[in] type CIB operation type - * \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, enum cib__op_type type, - 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); - } - - // 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 *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); @@ -573,7 +530,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; @@ -600,26 +556,11 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation, goto done; } - 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, - input, &the_cib, &output); + rc = cib__perform_query(op_function, request, &the_cib, &output); 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)) { - 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. @@ -627,9 +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. */ - 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); + result_cib = the_cib; + rc = cib_perform_op(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_messages.c b/daemons/based/based_messages.c index e21544a43b1..7cd608aa0ef 100644 --- a/daemons/based/based_messages.c +++ b/daemons/based/based_messages.c @@ -37,9 +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 * \param[in] input Ignored * \param[in] cib Ignored @@ -50,8 +47,7 @@ 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, +based_process_abs_delete(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { /* @COMPAT Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed. Note that @@ -61,8 +57,7 @@ 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, +based_process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { /* On success, our caller will activate *cib locally, trigger a replace @@ -87,8 +82,7 @@ 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, +based_process_is_primary(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { // @COMPAT Pacemaker Remote clients <3.0.0 may send this @@ -97,8 +91,7 @@ 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, +based_process_noop(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { *answer = NULL; @@ -106,8 +99,7 @@ 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 **cib, +based_process_ping(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { /* existing_cib and *cib should be identical. In the absence of ACL @@ -133,21 +125,10 @@ based_process_ping(const char *op, int options, const char *section, if (*cib != NULL) { // Use *cib so that ACL filtering is applied to the answer - pcmk__if_tracing( - { - /* Append additional detail so the receiver can log the - * differences - */ - pcmk__xml_copy(wrapper, *cib); - }, - { - // Always include at least the version details - const char *name = (const char *) (*cib)->name; - xmlNode *shallow = pcmk__xe_create(wrapper, name); - - pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); - } - ); + xmlNode *shallow = pcmk__xe_create(wrapper, + (const char *) (*cib)->name); + + pcmk__xe_copy_attrs(shallow, *cib, pcmk__xaf_none); } pcmk__info("Reporting our current digest to %s: %s for %s.%s.%s", @@ -162,8 +143,7 @@ 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, +based_process_primary(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { if (!based_is_primary) { @@ -178,8 +158,7 @@ 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, +based_process_schemas(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { xmlNode *wrapper = NULL; @@ -224,8 +203,7 @@ 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, +based_process_secondary(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { if (based_is_primary) { @@ -240,8 +218,7 @@ 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, +based_process_shutdown(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { const char *host = pcmk__xe_get(req, PCMK__XA_SRC); @@ -264,16 +241,14 @@ 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, +based_process_sync(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, +based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { int rc = pcmk_rc_ok; @@ -286,8 +261,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(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..fddb943e086 100644 --- a/daemons/based/based_messages.h +++ b/daemons/based/based_messages.h @@ -17,53 +17,40 @@ 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, +int based_process_abs_delete(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, +int based_process_apply_patch(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(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, +int based_process_is_primary(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, +int based_process_noop(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, +int based_process_ping(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, +int based_process_primary(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, +int based_process_schemas(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, +int based_process_secondary(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, +int based_process_shutdown(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, +int based_process_sync(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, +int based_process_upgrade(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer); int sync_our_cib(xmlNode *request, bool all); 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/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); } } diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h index 6eaf1516d7d..63b5eda8a6e 100644 --- a/include/crm/cib/internal.h +++ b/include/crm/cib/internal.h @@ -93,8 +93,18 @@ 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)(xmlNode *request, xmlNode *input, xmlNode **cib, + xmlNode **output); typedef struct { const char *name; @@ -178,15 +188,12 @@ 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 **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, 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, 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, @@ -200,40 +207,31 @@ 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, +int cib__process_apply_patch(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, +int cib__process_bump(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, +int cib__process_create(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, +int cib__process_delete(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, +int cib__process_erase(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, +int cib__process_modify(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, +int cib__process_query(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, +int cib__process_replace(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, +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, diff --git a/include/crm/common/results.h b/include/crm/common/results.h index a2a3eae0e02..236e0859c50 100644 --- a/include/crm/common/results.h +++ b/include/crm/common/results.h @@ -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, 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 310ffdd0cca..768919363c2 100644 --- a/lib/cib/cib_file.c +++ b/lib/cib/cib_file.c @@ -142,13 +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); - 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; @@ -161,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) { @@ -170,18 +164,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, call_options, op_function, section, request, - data, &private->cib_xml, output); + rc = cib__perform_query(op_function, request, &private->cib_xml, + output); } else { - rc = cib_perform_op(cib_file, op, call_options, op_function, section, - request, data, true, &changed, &private->cib_xml, + result_cib = private->cib_xml; + rc = cib_perform_op(cib_file, op_function, request, &changed, &result_cib, &cib_diff, output); } @@ -194,7 +182,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) { @@ -282,10 +270,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)); @@ -318,8 +304,7 @@ 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, +process_commit_transact(xmlNode *req, xmlNode *input, xmlNode **cib_xml, xmlNode **answer) { int rc = pcmk_rc_ok; diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index ab4a341bc6e..c955c4749ca 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,7 @@ 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, +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 *op, int options, 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,34 +290,28 @@ 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, +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; - 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, - answer); + 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); } // @COMPAT Deprecated since 2.1.8 @@ -445,6 +438,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; @@ -463,11 +460,17 @@ 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, +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); + 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); } @@ -475,8 +478,7 @@ 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, +cib__process_erase(xmlNode *req, xmlNode *input, xmlNode **cib, xmlNode **answer) { xmlNode *empty = createEmptyCib(0); @@ -510,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); @@ -555,6 +563,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; @@ -594,11 +606,17 @@ 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, +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); + 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); } @@ -728,58 +746,21 @@ 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) -{ - if (pcmk__is_set(options, cib_xpath)) { - return process_query_xpath(op, options, section, *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 *cib) +cib__process_query(xmlNode *req, xmlNode *input, xmlNode **cib, + xmlNode **answer) { - 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; + const char *section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION); + uint32_t options = cib_none; - match = pcmk__xpath_result(xpath_obj, i); - if (match == NULL) { - continue; - } + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); - 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_xpath)) { + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); - if (!pcmk__is_set(options, cib_multiple)) { - break; - } + return process_query_xpath(op, options, section, *cib, answer); } -done: - xmlXPathFreeObject(xpath_obj); - return rc; + return process_query_section(options, section, *cib, answer); } static bool @@ -867,6 +848,55 @@ 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 *request, 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); + + if (match == *cib) { + rc = replace_cib(request, input, cib); + break; + } + + 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) @@ -874,6 +904,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; @@ -900,30 +934,41 @@ 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, +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); + if (pcmk__is_set(options, cib_xpath)) { - return process_replace_xpath(op, options, section, input, *cib); + const char *op = pcmk__xe_get(req, PCMK__XA_CIB_OP); + + return process_replace_xpath(op, options, section, req, input, 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 **cib, +cib__process_upgrade(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 = 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); + + pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &options, cib_none); - 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) { diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c index 505080591fc..53d76d2e20e 100644 --- a/lib/cib/cib_utils.c +++ b/lib/cib/cib_utils.c @@ -169,22 +169,45 @@ cib_acl_enabled(xmlNode *xml, const char *user) return rc; } +/*! + * \internal + * \brief Get input data from a CIB request + * + * \param[in] request CIB request XML + */ +static xmlNode * +get_op_input(const xmlNode *request) +{ + xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, + NULL, NULL); + + return pcmk__xe_first_child(wrapper, NULL, NULL, NULL); +} + 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(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; 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); + 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); + + input = get_op_input(req); cib = *current_cib; if (cib_acl_enabled(*current_cib, user) @@ -203,7 +226,7 @@ cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn, 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(req, input, &cib, output); if (*output == NULL) { // Do nothing @@ -439,14 +462,20 @@ 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, +cib_perform_op(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; + const char *op = NULL; + 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; + /* 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. @@ -454,94 +483,57 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, xmlNode *old_versions = NULL; xmlNode *top = NULL; - xmlNode *working_cib = NULL; - const char *user = NULL; - bool enable_acl = false; - - 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) + && (cib != NULL) && (*cib != NULL) && (diff != NULL) && (*diff == NULL) && (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); - enable_acl = cib_acl_enabled(*current_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(*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); - 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); - if (enable_acl) { - pcmk__enable_acls((*current_cib)->doc, (*current_cib)->doc, 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 - * points somewhere else now (for example, after a erase or full-CIB - * replace op). - */ - 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; - - pcmk__xml_doc_set_flags(working_cib->doc, pcmk__xf_tracking); - if (enable_acl) { - pcmk__enable_acls((*current_cib)->doc, working_cib->doc, 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); - - /* @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_acls((*current_cib)->doc, working_cib->doc, user); - } - pcmk__xml_mark_changes(*current_cib, working_cib); - } + old_versions = *cib; + *cib = pcmk__xml_copy(NULL, *cib); + } - pcmk__assert(*current_cib != working_cib); + pcmk__xml_commit_changes((*cib)->doc); + pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking); + if (enable_acl) { + pcmk__enable_acls((*cib)->doc, (*cib)->doc, user); } + rc = fn(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; @@ -552,26 +544,33 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, * 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 + * 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); + *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; @@ -579,12 +578,12 @@ cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options, 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; } @@ -593,24 +592,24 @@ cib_perform_op(enum cib_variant variant, 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(working_cib)) { + && !pcmk__configured_schema_validates(*cib)) { rc = pcmk_rc_schema_validation; } done: - *result_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)) { + if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user)) { + xmlNode *saved_cib = *cib; - if (*result_cib == NULL) { - pcmk__debug("Pre-filtered the entire cib result"); + 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); @@ -873,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(event, diff, output, NULL); rc = pcmk_rc2legacy(rc); if (rc == pcmk_ok) { return pcmk_ok; diff --git a/lib/common/schemas.c b/lib/common/schemas.c index 87d87f95910..c006c34d012 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. * @@ -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); @@ -785,28 +786,29 @@ validate_with(xmlNode *xml, 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, schema, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR)); + rc = validate_with(doc, schema, (xmlRelaxNGValidityErrorFunc) xml_log, + GUINT_TO_POINTER(LOG_ERR)); silent_logging = sl_backup; return rc; } 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); @@ -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->doc, schema, error_handler, error_handler_context); } @@ -833,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)); } @@ -968,23 +969,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 +1001,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,14 +1013,14 @@ apply_transformation(const xmlNode *xml, const char *transform, free(xform); - return out; + return result_doc; } /*! * \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 @@ -1031,15 +1028,15 @@ apply_transformation(const xmlNode *xml, const char *transform, * * \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; - xmlNode *new_xml = NULL; + xmlDoc *old_doc = NULL; + xmlDoc *new_doc = NULL; xmlRelaxNGValidityErrorFunc error_handler = NULL; pcmk__assert((schema != NULL) && (upgraded_schema != NULL)); @@ -1055,34 +1052,36 @@ 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); - pcmk__xml_free(old_xml); + new_doc = apply_transformation(input_doc, transform, to_logs); + pcmk__xml_free_doc(old_doc); - if (new_xml == NULL) { + if (new_doc == NULL) { pcmk__err("XSL transform %s failed, aborting upgrade", transform); return NULL; } - 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); // 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; } pcmk__info("Schema upgrade from %s to %s succeeded", schema->name, upgraded_schema->name); - return new_xml; + return new_doc; } /*! @@ -1126,12 +1125,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); @@ -1156,13 +1158,13 @@ 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; } - 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) { @@ -1182,7 +1184,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. @@ -1190,17 +1192,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); } } 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; }