diff options
Diffstat (limited to 'lib/cib/cib_utils.c')
-rw-r--r-- | lib/cib/cib_utils.c | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c new file mode 100644 index 0000000..c75d844 --- /dev/null +++ b/lib/cib/cib_utils.c @@ -0,0 +1,837 @@ +/* + * Original copyright 2004 International Business Machines + * Later changes copyright 2008-2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ +#include <crm_internal.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <sys/utsname.h> + +#include <glib.h> + +#include <crm/crm.h> +#include <crm/cib/internal.h> +#include <crm/msg_xml.h> +#include <crm/common/xml.h> +#include <crm/common/xml_internal.h> +#include <crm/pengine/rules.h> + +xmlNode * +cib_get_generation(cib_t * cib) +{ + xmlNode *the_cib = NULL; + xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE); + + cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call); + if (the_cib != NULL) { + copy_in_properties(generation, the_cib); + free_xml(the_cib); + } + + return generation; +} + +gboolean +cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates) +{ + *epoch = -1; + *updates = -1; + *admin_epoch = -1; + + if (cib == NULL) { + return FALSE; + + } else { + crm_element_value_int(cib, XML_ATTR_GENERATION, epoch); + crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates); + crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch); + } + return TRUE; +} + +gboolean +cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates, + int *_admin_epoch, int *_epoch, int *_updates) +{ + int add[] = { 0, 0, 0 }; + int del[] = { 0, 0, 0 }; + + xml_patch_versions(diff, add, del); + + *admin_epoch = add[0]; + *epoch = add[1]; + *updates = add[2]; + + *_admin_epoch = del[0]; + *_epoch = del[1]; + *_updates = del[2]; + + return TRUE; +} + +/*! + * \brief Create XML for a new (empty) CIB + * + * \param[in] cib_epoch What to use as "epoch" CIB property + * + * \return Newly created XML for empty CIB + * \note It is the caller's responsibility to free the result with free_xml(). + */ +xmlNode * +createEmptyCib(int cib_epoch) +{ + xmlNode *cib_root = NULL, *config = NULL; + + cib_root = create_xml_node(NULL, XML_TAG_CIB); + crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); + crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema()); + + crm_xml_add_int(cib_root, XML_ATTR_GENERATION, cib_epoch); + crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0); + crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0); + + config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); + create_xml_node(cib_root, XML_CIB_TAG_STATUS); + + create_xml_node(config, XML_CIB_TAG_CRMCONFIG); + create_xml_node(config, XML_CIB_TAG_NODES); + create_xml_node(config, XML_CIB_TAG_RESOURCES); + create_xml_node(config, XML_CIB_TAG_CONSTRAINTS); + +#if PCMK__RESOURCE_STICKINESS_DEFAULT != 0 + { + xmlNode *rsc_defaults = create_xml_node(config, XML_CIB_TAG_RSCCONFIG); + xmlNode *meta = create_xml_node(rsc_defaults, XML_TAG_META_SETS); + xmlNode *nvpair = create_xml_node(meta, XML_CIB_TAG_NVPAIR); + + crm_xml_add(meta, XML_ATTR_ID, "build-resource-defaults"); + crm_xml_add(nvpair, XML_ATTR_ID, "build-" XML_RSC_ATTR_STICKINESS); + crm_xml_add(nvpair, XML_NVPAIR_ATTR_NAME, XML_RSC_ATTR_STICKINESS); + crm_xml_add_int(nvpair, XML_NVPAIR_ATTR_VALUE, + PCMK__RESOURCE_STICKINESS_DEFAULT); + } +#endif + return cib_root; +} + +static bool +cib_acl_enabled(xmlNode *xml, const char *user) +{ + bool rc = FALSE; + + if(pcmk_acl_required(user)) { + const char *value = NULL; + GHashTable *options = pcmk__strkey_table(free, free); + + cib_read_config(options, xml); + value = cib_pref(options, "enable-acl"); + rc = crm_is_true(value); + g_hash_table_destroy(options); + } + + crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled"); + return rc; +} + +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) +{ + int rc = pcmk_ok; + gboolean check_schema = TRUE; + xmlNode *top = NULL; + xmlNode *scratch = 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; + + 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(result_cib != NULL, return -ENOMSG); + CRM_CHECK(config_changed != NULL, return -ENOMSG); + + if(output) { + *output = NULL; + } + + *result_cib = NULL; + *config_changed = FALSE; + + if (fn == NULL) { + return -EINVAL; + } + + if (is_query) { + 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"); + } + } + + rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output); + + if(output == NULL || *output == NULL) { + /* nothing */ + + } else if(cib_filtered == *output) { + cib_filtered = NULL; /* Let them have this copy */ + + } else if(*output == current_cib) { + /* They already know not to free it */ + + } else if(cib_filtered && (*output)->doc == cib_filtered->doc) { + /* We're about to free the document of which *output is a part */ + *output = copy_xml(*output); + + } else if((*output)->doc == current_cib->doc) { + /* Give them a copy they can free */ + *output = copy_xml(*output); + } + + free_xml(cib_filtered); + return rc; + } + + + if (pcmk_is_set(call_options, cib_zero_copy)) { + /* Conditional on v2 patch style */ + + 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; + + xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user)); + rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output); + + } else { + scratch = copy_xml(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); + + if(scratch && xml_tracking_changes(scratch) == FALSE) { + 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); + } + CRM_CHECK(current_cib != scratch, return -EINVAL); + } + + xml_acl_disable(scratch); /* Allow the system to make any additional changes */ + + if (rc == pcmk_ok && scratch == NULL) { + rc = -EINVAL; + goto done; + + } else if(rc == pcmk_ok && xml_acl_denied(scratch)) { + crm_trace("ACL rejected part or all of the proposed changes"); + rc = -EACCES; + goto done; + + } else if (rc != pcmk_ok) { + goto done; + } + + if (scratch) { + new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION); + + if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) { + crm_err("Discarding update with feature set '%s' greater than our own '%s'", + new_version, CRM_FEATURE_SET); + rc = -EPROTONOSUPPORT; + goto done; + } + } + + if (current_cib) { + 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); + + if (old > new) { + crm_err("%s went backwards: %d -> %d (Opts: %#x)", + XML_ATTR_GENERATION_ADMIN, old, new, call_options); + crm_log_xml_warn(req, "Bad Op"); + crm_log_xml_warn(input, "Bad Data"); + rc = -pcmk_err_old_data; + + } else if (old == new) { + crm_element_value_int(scratch, XML_ATTR_GENERATION, &new); + crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old); + if (old > new) { + crm_err("%s went backwards: %d -> %d (Opts: %#x)", + XML_ATTR_GENERATION, old, new, call_options); + crm_log_xml_warn(req, "Bad Op"); + crm_log_xml_warn(input, "Bad Data"); + rc = -pcmk_err_old_data; + } + } + } + + crm_trace("Massaging CIB contents"); + 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, + * + * 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); + + } else { + static time_t expires = 0; + time_t tm_now = time(NULL); + + if (expires < tm_now) { + expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */ + with_digest = TRUE; + } + + local_diff = xml_create_patchset(0, current_cib, scratch, (bool*)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); + }, + {} + ); + 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); + + 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)) { + // 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); + + crm_element_value_int(local_diff, "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(scratch, "PatchApply:actual", NULL); + save_xml_to_file(local_diff, "PatchApply:diff", NULL); + crm_err("v%d patchset error, patch failed to apply: %s " + "(%d)", + format, pcmk_rc_str(pcmk_legacy2rc(test_rc)), + test_rc); + } + free_xml(cib_copy); + }, + {} + ); + } + + if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) { + /* Throttle the amount of costly validation we perform due to status updates + * a) we don't really care whats in the status section + * b) we don't validate any of its contents at the moment anyway + */ + check_schema = FALSE; + } + + /* === scratch must not be modified after this point === + * Exceptions, anything in: + + static filter_t filter[] = { + { 0, XML_ATTR_ORIGIN }, + { 0, XML_CIB_ATTR_WRITTEN }, + { 0, XML_ATTR_UPDATE_ORIG }, + { 0, XML_ATTR_UPDATE_CLIENT }, + { 0, XML_ATTR_UPDATE_USER }, + }; + */ + + if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) { + const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION); + + pcmk__xe_add_last_written(scratch); + if (schema) { + static int minimum_schema = 0; + int current_schema = get_schema_version(schema); + + if (minimum_schema == 0) { + minimum_schema = get_schema_version("pacemaker-1.2"); + } + + /* Does the CIB support the "update-*" attributes... */ + if (current_schema >= minimum_schema) { + const char *origin = crm_element_value(req, F_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)); + } + } + } + + crm_trace("Perform validation: %s", pcmk__btoa(check_schema)); + if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, TRUE)) { + const char *current_schema = crm_element_value(scratch, + XML_ATTR_VALIDATION); + + crm_warn("Updated CIB does not validate against %s schema", + pcmk__s(current_schema, "unspecified")); + rc = -pcmk_err_schema_validation; + } + + 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); + } + } + + if(diff) { + *diff = local_diff; + } else { + free_xml(local_diff); + } + + free_xml(top); + crm_trace("Done"); + 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) +{ + xmlNode *op_msg = create_xml_node(NULL, "cib_command"); + + CRM_CHECK(op_msg != NULL, return NULL); + + crm_xml_add(op_msg, F_XML_TAGNAME, "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_int(op_msg, F_CIB_CALLID, call_id); + if (user_name) { + crm_xml_add(op_msg, F_CIB_USER, user_name); + } + crm_trace("Sending call options: %.8lx, %d", (long)call_options, 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); + } + + if (call_options & cib_inhibit_bcast) { + CRM_CHECK((call_options & cib_scope_local), return NULL); + } + return op_msg; +} + +void +cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc) +{ + xmlNode *output = NULL; + cib_callback_client_t *blob = NULL; + + if (msg != NULL) { + crm_element_value_int(msg, F_CIB_RC, &rc); + crm_element_value_int(msg, F_CIB_CALLID, &call_id); + output = get_message_xml(msg, F_CIB_CALLDATA); + } + + blob = cib__lookup_id(call_id); + + if (blob == NULL) { + crm_trace("No callback found for call %d", call_id); + } + + if (cib == NULL) { + crm_debug("No cib object supplied"); + } + + if (rc == -pcmk_err_diff_resync) { + /* This is an internal value that clients do not and should not care about */ + rc = pcmk_ok; + } + + if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) { + crm_trace("Invoking callback %s for call %d", + pcmk__s(blob->id, "without ID"), call_id); + blob->callback(msg, call_id, rc, output, blob->user_data); + + } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) { + crm_warn("CIB command failed: %s", pcmk_strerror(rc)); + crm_log_xml_debug(msg, "Failed CIB Update"); + } + + /* This may free user_data, so do it after the callback */ + if (blob) { + remove_cib_op_callback(call_id, FALSE); + } + + if (cib && cib->op_callback != NULL) { + crm_trace("Invoking global callback for call %d", call_id); + cib->op_callback(msg, call_id, rc, output); + } + crm_trace("OP callback activated for %d", call_id); +} + +void +cib_native_notify(gpointer data, gpointer user_data) +{ + xmlNode *msg = user_data; + cib_notify_client_t *entry = data; + const char *event = NULL; + + if (msg == NULL) { + crm_warn("Skipping callback - NULL message"); + return; + } + + event = crm_element_value(msg, F_SUBTYPE); + + if (entry == NULL) { + crm_warn("Skipping callback - NULL callback client"); + return; + + } else if (entry->callback == NULL) { + crm_warn("Skipping callback - NULL callback"); + return; + + } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) { + crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event); + return; + } + + crm_trace("Invoking callback for %p/%s event...", entry, event); + entry->callback(event, msg); + crm_trace("Callback invoked..."); +} + +static pcmk__cluster_option_t cib_opts[] = { + /* name, legacy name, type, allowed values, + * default value, validator, + * short description, + * long description + */ + { + "enable-acl", NULL, "boolean", NULL, + "false", pcmk__valid_boolean, + N_("Enable Access Control Lists (ACLs) for the CIB"), + NULL + }, + { + "cluster-ipc-limit", NULL, "integer", NULL, + "500", pcmk__valid_positive_number, + N_("Maximum IPC message backlog before disconnecting a cluster daemon"), + N_("Raise this if log has \"Evicting client\" messages for cluster daemon" + " PIDs (a good value is the number of resources in the cluster" + " multiplied by the number of nodes).") + }, +}; + +void +cib_metadata(void) +{ + const char *desc_short = "Cluster Information Base manager options"; + const char *desc_long = "Cluster options used by Pacemaker's Cluster " + "Information Base manager"; + + gchar *s = pcmk__format_option_metadata("pacemaker-based", desc_short, + desc_long, cib_opts, + PCMK__NELEM(cib_opts)); + printf("%s", s); + g_free(s); +} + +static void +verify_cib_options(GHashTable *options) +{ + pcmk__validate_cluster_options(options, cib_opts, PCMK__NELEM(cib_opts)); +} + +const char * +cib_pref(GHashTable * options, const char *name) +{ + return pcmk__cluster_option(options, cib_opts, PCMK__NELEM(cib_opts), + name); +} + +gboolean +cib_read_config(GHashTable * options, xmlNode * current_cib) +{ + xmlNode *config = NULL; + crm_time_t *now = NULL; + + if (options == NULL || current_cib == NULL) { + return FALSE; + } + + now = crm_time_new(NULL); + + g_hash_table_remove_all(options); + + config = pcmk_find_cib_element(current_cib, XML_CIB_TAG_CRMCONFIG); + if (config) { + pe_unpack_nvpairs(current_cib, config, XML_CIB_TAG_PROPSET, NULL, + options, CIB_OPTIONS_FIRST, TRUE, now, NULL); + } + + verify_cib_options(options); + + crm_time_free(now); + + return TRUE; +} + +int +cib_internal_op(cib_t * cib, const char *op, const char *host, + const char *section, xmlNode * data, + xmlNode ** output_data, int call_options, const char *user_name) +{ + int (*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->delegate_fn; + + if(user_name == NULL) { + user_name = getenv("CIB_user"); + } + + return delegate(cib, op, host, section, data, output_data, call_options, user_name); +} + +/*! + * \brief Apply a CIB update patch to a given CIB + * + * \param[in] event CIB update patch + * \param[in] input CIB to patch + * \param[out] output Resulting CIB after patch + * \param[in] level Log the patch at this log level (unless LOG_CRIT) + * + * \return Legacy Pacemaker return code + * \note sbd calls this function + */ +int +cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, + int level) +{ + int rc = pcmk_err_generic; + + xmlNode *diff = NULL; + + CRM_ASSERT(event); + CRM_ASSERT(input); + CRM_ASSERT(output); + + crm_element_value_int(event, F_CIB_RC, &rc); + diff = get_message_xml(event, F_CIB_UPDATE_RESULT); + + if (rc < pcmk_ok || diff == NULL) { + return rc; + } + + 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; + } + + if (input != NULL) { + rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output, + NULL); + + if (rc != pcmk_ok) { + crm_debug("Update didn't apply: %s (%d) %p", + pcmk_strerror(rc), rc, *output); + + if (rc == -pcmk_err_old_data) { + crm_trace("Masking error, we already have the supplied update"); + return pcmk_ok; + } + free_xml(*output); + *output = NULL; + return rc; + } + } + return rc; +} + +#define log_signon_query_err(out, fmt, args...) do { \ + if (out != NULL) { \ + out->err(out, fmt, ##args); \ + } else { \ + crm_err(fmt, ##args); \ + } \ + } while (0) + +int +cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object) +{ + int rc = pcmk_rc_ok; + cib_t *cib_conn = NULL; + + CRM_ASSERT(cib_object != NULL); + + if (cib == NULL) { + cib_conn = cib_new(); + } else { + if (*cib == NULL) { + *cib = cib_new(); + } + cib_conn = *cib; + } + + if (cib_conn == NULL) { + return ENOMEM; + } + + if (cib_conn->state == cib_disconnected) { + rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); + rc = pcmk_legacy2rc(rc); + } + + if (rc != pcmk_rc_ok) { + log_signon_query_err(out, "Could not connect to the CIB: %s", + pcmk_rc_str(rc)); + goto done; + } + + if (out != NULL) { + out->transient(out, "Querying CIB..."); + } + rc = cib_conn->cmds->query(cib_conn, NULL, cib_object, + cib_scope_local|cib_sync_call); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc)); + } + +done: + if (cib == NULL) { + cib__clean_up_connection(&cib_conn); + } + + if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) { + return pcmk_rc_no_input; + } + return rc; +} + +int +cib__clean_up_connection(cib_t **cib) +{ + int rc; + + if (*cib == NULL) { + return pcmk_rc_ok; + } + + rc = (*cib)->cmds->signoff(*cib); + cib_delete(*cib); + *cib = NULL; + return pcmk_legacy2rc(rc); +} + +// Deprecated functions kept only for backward API compatibility +// LCOV_EXCL_START + +#include <crm/cib/util_compat.h> + +const char * +get_object_path(const char *object_type) +{ + return pcmk_cib_xpath_for(object_type); +} + +const char * +get_object_parent(const char *object_type) +{ + return pcmk_cib_parent_name_for(object_type); +} + +xmlNode * +get_object_root(const char *object_type, xmlNode *the_root) +{ + return pcmk_find_cib_element(the_root, object_type); +} + +// LCOV_EXCL_STOP +// End deprecated API |