diff --git a/cib/io.c b/cib/io.c index 91ca53f753..8dd18e80a4 100644 --- a/cib/io.c +++ b/cib/io.c @@ -1,755 +1,758 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int archive_file(const char *oldname, const char *newname, const char *ext, gboolean preserve); const char * local_resource_path[] = { XML_CIB_TAG_STATUS, }; const char * resource_path[] = { XML_CIB_TAG_RESOURCES, }; const char * node_path[] = { XML_CIB_TAG_NODES, }; const char * constraint_path[] = { XML_CIB_TAG_CONSTRAINTS, }; gboolean initialized = FALSE; xmlNode *node_search = NULL; xmlNode *resource_search = NULL; xmlNode *constraint_search = NULL; xmlNode *status_search = NULL; extern gboolean cib_writes_enabled; extern GTRIGSource *cib_writer; extern enum cib_errors cib_status; int set_connected_peers(xmlNode *xml_obj); void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data); int write_cib_contents(gpointer p); extern void cib_cleanup(void); static gboolean validate_cib_digest(xmlNode *local_cib, const char *sigfile) { int s_res = -1; struct stat buf; char *digest = NULL; char *expected = NULL; gboolean passed = FALSE; FILE *expected_strm = NULL; int start = 0, length = 0, read_len = 0; CRM_ASSERT(sigfile != NULL); s_res = stat(sigfile, &buf); if (s_res != 0) { crm_warn("No on-disk digest present"); return TRUE; } if(local_cib != NULL) { digest = calculate_xml_digest(local_cib, FALSE, FALSE); } expected_strm = fopen(sigfile, "r"); if(expected_strm == NULL) { cl_perror("Could not open signature file %s for reading", sigfile); goto bail; } start = ftell(expected_strm); fseek(expected_strm, 0L, SEEK_END); length = ftell(expected_strm); fseek(expected_strm, 0L, start); CRM_ASSERT(start == ftell(expected_strm)); crm_debug_3("Reading %d bytes from file", length); crm_malloc0(expected, (length+1)); read_len = fread(expected, 1, length, expected_strm); CRM_ASSERT(read_len == length); fclose(expected_strm); bail: if(expected == NULL) { crm_err("On-disk digest is empty"); } else if(safe_str_eq(expected, digest)) { crm_debug_2("Digest comparision passed: %s", digest); passed = TRUE; } else { crm_err("Digest comparision failed: expected %s (%s), calculated %s", expected, sigfile, digest); } crm_free(digest); crm_free(expected); return passed; } static int write_cib_digest(xmlNode *local_cib, char *digest) { int rc = 0; char *local_digest = NULL; FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w"); if(digest_strm == NULL) { cl_perror("Cannot open signature file "CIB_FILENAME ".sig for writing"); return -1; } if(digest == NULL) { local_digest = calculate_xml_digest(local_cib, FALSE, FALSE); CRM_ASSERT(digest != NULL); digest = local_digest; } rc = fprintf(digest_strm, "%s", digest); if(rc < 0) { cl_perror("Cannot write to signature file "CIB_FILENAME ".sig"); } CRM_ASSERT(digest_strm != NULL); if(fflush(digest_strm) != 0) { cl_perror("fflush for %s failed:", digest); rc = -1; } if(fsync(fileno(digest_strm)) < 0) { cl_perror("fsync for %s failed:", digest); rc = -1; } fclose(digest_strm); crm_free(local_digest); return rc; } static gboolean validate_on_disk_cib(const char *filename, xmlNode **on_disk_cib) { int s_res = -1; struct stat buf; FILE *cib_file = NULL; gboolean passed = TRUE; xmlNode *root = NULL; CRM_ASSERT(filename != NULL); s_res = stat(filename, &buf); if (s_res == 0) { char *sigfile = NULL; size_t fnsize; cib_file = fopen(filename, "r"); if(cib_file == NULL) { cl_perror("Couldn't open config file %s for reading", filename); return FALSE; } crm_debug_2("Reading cluster configuration from: %s", filename); root = file2xml(cib_file, FALSE); fclose(cib_file); fnsize = strlen(filename) + 5; crm_malloc0(sigfile, fnsize); snprintf(sigfile, fnsize, "%s.sig", filename); if(validate_cib_digest(root, sigfile) == FALSE) { passed = FALSE; } crm_free(sigfile); } if(on_disk_cib != NULL) { *on_disk_cib = root; } else { free_xml(root); } return passed; } static int cib_unlink(const char *file) { int rc = unlink(file); if (rc < 0) { cl_perror("Could not unlink %s - Disabling disk writes and continuing", file); cib_writes_enabled = FALSE; } return rc; } /* * It is the callers responsibility to free the output of this function */ static xmlNode* retrieveCib(const char *filename, const char *sigfile, gboolean archive_invalid) { struct stat buf; FILE *cib_file = NULL; xmlNode *root = NULL; crm_info("Reading cluster configuration from: %s (digest: %s)", filename, sigfile); if(stat(filename, &buf) != 0) { crm_warn("Cluster configuration not found: %s", filename); return NULL; } cib_file = fopen(filename, "r"); if(cib_file == NULL) { cl_perror("Could not open config file %s for reading", filename); } else { root = file2xml(cib_file, FALSE); fclose(cib_file); } if(root == NULL) { crm_err("%s exists but does NOT contain valid XML. ", filename); crm_warn("Continuing but %s will NOT used.", filename); } else if(validate_cib_digest(root, sigfile) == FALSE) { crm_err("Checksum of %s failed! Configuration contents ignored!", filename); crm_err("Usually this is caused by manual changes, " "please refer to http://linux-ha.org/v2/faq/cib_changes_detected"); crm_warn("Continuing but %s will NOT used.", filename); free_xml(root); root = NULL; if(archive_invalid) { int rc = 0; char *suffix = crm_itoa(getpid()); /* Archive the original files so the contents are not lost */ crm_err("Archiving corrupt or unusable configuration to %s.%s", filename, suffix); rc = archive_file(filename, NULL, suffix, TRUE); if(rc < 0) { crm_err("Archival of %s failed - Disabling disk writes and continuing", filename); cib_writes_enabled = FALSE; } rc = archive_file(sigfile, NULL, suffix, TRUE); if(rc < 0) { crm_err("Archival of %s failed - Disabling disk writes and continuing", sigfile); cib_writes_enabled = FALSE; } /* Unlink the original files so they dont get in the way later */ cib_unlink(filename); cib_unlink(sigfile); crm_free(suffix); } } return root; } xmlNode* readCibXmlFile(const char *dir, const char *file, gboolean discard_status) { char *filename = NULL, *sigfile = NULL; const char *name = NULL; const char *value = NULL; const char *validation = NULL; const char *use_valgrind = getenv("HA_VALGRIND_ENABLED"); xmlNode *root = NULL; xmlNode *status = NULL; if(!crm_is_writable(dir, file, HA_CCMUSER, NULL, FALSE)) { cib_status = cib_bad_permissions; return NULL; } filename = crm_concat(dir, file, '/'); sigfile = crm_concat(filename, "sig", '.'); cib_status = cib_ok; root = retrieveCib(filename, sigfile, TRUE); if(root == NULL) { char *tmp = NULL; /* Try the backups */ tmp = filename; filename = crm_concat(tmp, "last", '.'); crm_free(tmp); tmp = sigfile; sigfile = crm_concat(tmp, "last", '.'); crm_free(tmp); crm_warn("Primary configuration corrupt or unusable, trying backup..."); root = retrieveCib(filename, sigfile, FALSE); } if(root == NULL) { root = createEmptyCib(); + crm_xml_add(root, XML_ATTR_GENERATION, "0"); + crm_xml_add(root, XML_ATTR_NUMUPDATES, "0"); + crm_xml_add(root, XML_ATTR_GENERATION_ADMIN, "0"); crm_warn("Continuing with an empty configuration."); } if(cib_writes_enabled && crm_is_true(use_valgrind)) { cib_writes_enabled = FALSE; crm_err("HA_VALGRIND_ENABLED: %s", getenv("HA_VALGRIND_ENABLED")); crm_err("*********************************************************"); crm_err("*** Disabling disk writes to avoid confusing Valgrind ***"); crm_err("*********************************************************"); } status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE); if(discard_status && status != NULL) { /* strip out the status section if there is one */ free_xml_from_parent(root, status); status = NULL; } if(status == NULL) { create_xml_node(root, XML_CIB_TAG_STATUS); } /* Do this before DTD validation happens */ /* fill in some defaults */ name = XML_ATTR_GENERATION_ADMIN; value = crm_element_value(root, name); if(value == NULL) { crm_warn("No value for %s was specified in the configuration.", name); crm_warn("The reccomended course of action is to shutdown," " run crm_verify and fix any errors it reports."); crm_warn("We will default to zero and continue but may get" " confused about which configuration to use if" " multiple nodes are powered up at the same time."); crm_xml_add_int(root, name, 0); } name = XML_ATTR_GENERATION; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add_int(root, name, 0); } name = XML_ATTR_NUMUPDATES; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add_int(root, name, 0); } /* unset these and require the DC/CCM to update as needed */ xml_remove_prop(root, XML_ATTR_DC_UUID); if(discard_status) { crm_log_xml_info(root, "[on-disk]"); } validation = crm_element_value(root, XML_ATTR_VALIDATION); if(validate_xml(root, validation, TRUE) == FALSE) { crm_err("CIB does not validate with %s", validation); cib_status = cib_dtd_validation; } else if(validation == NULL) { int version = update_validation(&root, FALSE, FALSE); if(version > 0) { crm_notice("Enabling %s validation on" " the existing (sane) configuration", get_schema_name(version)); } else { crm_err("CIB does not validate with any known DTD or schema"); cib_status = cib_dtd_validation; } } crm_free(filename); crm_free(sigfile); return root; } /* * The caller should never free the return value */ xmlNode* get_the_CIB(void) { return the_cib; } gboolean uninitializeCib(void) { xmlNode *tmp_cib = the_cib; if(tmp_cib == NULL) { crm_debug("The CIB has already been deallocated."); return FALSE; } initialized = FALSE; the_cib = NULL; node_search = NULL; resource_search = NULL; constraint_search = NULL; status_search = NULL; crm_debug("Deallocating the CIB."); free_xml(tmp_cib); crm_debug("The CIB has been deallocated."); return TRUE; } /* * This method will not free the old CIB pointer or the new one. * We rely on the caller to have saved a pointer to the old CIB * and to free the old/bad one depending on what is appropriate. */ gboolean initializeCib(xmlNode *new_cib) { if(new_cib == NULL) { return FALSE; } the_cib = new_cib; initialized = TRUE; return TRUE; } static void sync_file(const char *file) { FILE *syncme = fopen(file, "a"); if(syncme == NULL) { cl_perror("Cannot open file %s for syncing", file); return; } if(fsync(fileno(syncme)) < 0) { cl_perror("fsync for %s failed:", file); } fclose(syncme); } int archive_file(const char *oldname, const char *newname, const char *ext, gboolean preserve) { /* move 'oldname' to 'newname' by creating a hard link to it * and then removing the original hard link */ int rc = 0; int res = 0; struct stat tmp; int s_res = 0; char *backup_file = NULL; static const char *back_ext = "bak"; /* calculate the backup name if required */ if(newname != NULL) { backup_file = crm_strdup(newname); } else { int max_name_len = 1024; crm_malloc0(backup_file, max_name_len); if (ext == NULL) { ext = back_ext; } snprintf(backup_file, max_name_len - 1, "%s.%s", oldname, ext); } if(backup_file == NULL || strlen(backup_file) == 0) { crm_err("%s backup filename was %s", newname == NULL?"calculated":"supplied", backup_file == NULL?"null":"empty"); rc = -4; } s_res = stat(backup_file, &tmp); /* move the old backup */ if (rc == 0 && s_res >= 0) { if(preserve == FALSE) { res = unlink(backup_file); if (res < 0) { cl_perror("Could not unlink %s", backup_file); rc = -1; } } else { crm_info("Archive file %s exists... backing it up first", backup_file); res = archive_file(backup_file, NULL, NULL, preserve); if (res < 0) { return res; } } } s_res = stat(oldname, &tmp); /* copy */ if (rc == 0 && s_res >= 0) { res = link(oldname, backup_file); if (res < 0) { cl_perror("Could not create backup %s from %s", backup_file, oldname); rc = -2; } else if(preserve) { crm_info("%s archived as %s", oldname, backup_file); } else { crm_debug("%s archived as %s", oldname, backup_file); } sync_file(backup_file); } crm_free(backup_file); return rc; } /* * This method will free the old CIB pointer on success and the new one * on failure. */ int activateCibXml(xmlNode *new_cib, gboolean to_disk, const char *op) { int error_code = cib_ok; xmlNode *saved_cib = the_cib; crm_debug_2("Activating new CIB"); crm_log_xml_debug_4(new_cib, "Attempting to activate CIB"); CRM_ASSERT(new_cib != saved_cib); if(saved_cib != NULL) { crm_validate_data(saved_cib); } if(initializeCib(new_cib) == FALSE) { error_code = cib_ACTIVATION; crm_err("Ignoring invalid or NULL CIB"); } if(error_code != cib_ok) { if(saved_cib != NULL) { crm_warn("Reverting to last known CIB"); if (initializeCib(saved_cib) == FALSE) { /* oh we are so dead */ crm_crit("Couldn't re-initialize the old CIB!"); cl_flush_logs(); exit(1); } } else { crm_crit("Could not write out new CIB and no saved" " version to revert to"); } } else if(per_action_cib && cib_writes_enabled && cib_status == cib_ok) { crm_err("Per-action CIB"); write_cib_contents(the_cib); } else if(cib_writes_enabled && cib_status == cib_ok && to_disk) { crm_debug("Triggering CIB write for %s op", op); G_main_set_trigger(cib_writer); } else { crm_debug_3("disk: %d, writes: %d", to_disk, cib_writes_enabled); } if(the_cib != saved_cib && the_cib != new_cib) { CRM_DEV_ASSERT(error_code != cib_ok); CRM_DEV_ASSERT(the_cib == NULL); } if(the_cib != new_cib) { free_xml(new_cib); CRM_DEV_ASSERT(error_code != cib_ok); } if(the_cib != saved_cib) { free_xml(saved_cib); } return error_code; } int write_cib_contents(gpointer p) { int rc = 0; gboolean need_archive = FALSE; struct stat buf; char *digest = NULL; int exit_rc = LSB_EXIT_OK; xmlNode *cib_status_root = NULL; /* we can scribble on "the_cib" here and not affect the parent */ const char *epoch = crm_element_value(the_cib, XML_ATTR_GENERATION); const char *updates = crm_element_value(the_cib, XML_ATTR_NUMUPDATES); const char *admin_epoch = crm_element_value( the_cib, XML_ATTR_GENERATION_ADMIN); need_archive = (stat(CIB_FILENAME, &buf) == 0); if (need_archive) { crm_debug("Archiving current version"); /* check the admin didnt modify it underneath us */ if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) { crm_err("%s was manually modified while Heartbeat was active!", CIB_FILENAME); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } /* These calls leak, but we're in a separate process that will exit * when the function does... so it's of no consequence */ CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL); rc = archive_file(CIB_FILENAME, NULL, "last", FALSE); if(rc != 0) { crm_err("Could not make backup of the existing CIB: %d", rc); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } rc = archive_file(CIB_FILENAME".sig", NULL, "last", FALSE); if(rc != 0) { crm_warn("Could not make backup of the existing CIB digest: %d", rc); } CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL); CRM_ASSERT(retrieveCib(CIB_FILENAME".last", CIB_FILENAME".sig.last", FALSE) != NULL); crm_debug("Verified CIB archive"); } /* Given that we discard the status section on startup * there is no point writing it out in the first place * since users just get confused by it * * Although, it does help me once in a while * * So delete the status section before we write it out */ if(p == NULL) { cib_status_root = find_xml_node(the_cib, XML_CIB_TAG_STATUS, TRUE); CRM_DEV_ASSERT(cib_status_root != NULL); if(cib_status_root != NULL) { free_xml_from_parent(the_cib, cib_status_root); } } rc = write_xml_file(the_cib, CIB_FILENAME, FALSE); crm_debug("Wrote CIB to disk"); if(rc <= 0) { crm_err("Changes couldn't be written to disk"); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } digest = calculate_xml_digest(the_cib, FALSE, FALSE); crm_info("Wrote version %s.%s.%s of the CIB to disk (digest: %s)", admin_epoch?admin_epoch:"0", epoch?epoch:"0", updates?updates:"0", digest); rc = write_cib_digest(the_cib, digest); crm_debug("Wrote digest to disk"); if(rc <= 0) { crm_err("Digest couldn't be written to disk"); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL); if(need_archive) { CRM_ASSERT(retrieveCib(CIB_FILENAME".last", CIB_FILENAME".sig.last", FALSE) != NULL); } crm_debug("Wrote and verified CIB"); cleanup: crm_free(digest); if(p == NULL) { /* fork-and-write mode */ exit(exit_rc); } /* stand-alone mode */ return exit_rc; } void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data) { int *active = user_data; if(safe_str_eq(value, ONLINESTATUS)) { (*active)++; } else if(safe_str_eq(value, JOINSTATUS)) { (*active)++; } } diff --git a/lib/crm/cib/cib_utils.c b/lib/crm/cib/cib_utils.c index f60ec5b53c..d1982125ed 100644 --- a/lib/crm/cib/cib_utils.c +++ b/lib/crm/cib/cib_utils.c @@ -1,847 +1,843 @@ /* * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct config_root_s { const char *name; const char *parent; const char *path; }; /* * "//crm_config" will also work in place of "/cib/configuration/crm_config" * The / prefix means find starting from the root, whereas the // prefix means * find anywhere and risks multiple matches */ struct config_root_s known_paths[] = { { NULL, NULL, "/cib" }, { "cib", NULL, "/cib" }, { "status", "/cib", "/cib/status" }, { "configuration", "/cib", "/cib/configuration" }, { "crm_config", "/cib/configuration", "/cib/configuration/crm_config" }, { "nodes", "/cib/configuration", "/cib/configuration/nodes" }, { "resources", "/cib/configuration", "/cib/configuration/resources" }, { "constraints", "/cib/configuration", "/cib/configuration/constraints" }, { "op_defaults", "/cib/configuration", "/cib/configuration/op_defaults" }, { "rsc_defaults", "/cib/configuration", "/cib/configuration/rsc_defaults" }, { "all", NULL, "/cib" }, }; const char * cib_error2string(enum cib_errors return_code) { const char *error_msg = NULL; switch(return_code) { case cib_bad_permissions: error_msg = "bad permissions for the on-disk configuration. shutdown heartbeat and repair."; break; case cib_bad_digest: error_msg = "the on-disk configuration was manually altered. shutdown heartbeat and repair."; break; case cib_bad_config: error_msg = "the on-disk configuration is not valid"; break; case cib_msg_field_add: error_msg = "failed adding field to cib message"; break; case cib_id_check: error_msg = "missing id or id-collision detected"; break; case cib_operation: error_msg = "invalid operation"; break; case cib_create_msg: error_msg = "couldnt create cib message"; break; case cib_client_gone: error_msg = "client left before we could send reply"; break; case cib_not_connected: error_msg = "not connected"; break; case cib_not_authorized: error_msg = "not authorized"; break; case cib_send_failed: error_msg = "send failed"; break; case cib_reply_failed: error_msg = "reply failed"; break; case cib_return_code: error_msg = "no return code"; break; case cib_output_ptr: error_msg = "nowhere to store output"; break; case cib_output_data: error_msg = "corrupt output data"; break; case cib_connection: error_msg = "connection failed"; break; case cib_callback_register: error_msg = "couldnt register callback channel"; break; case cib_authentication: error_msg = ""; break; case cib_registration_msg: error_msg = "invalid registration msg"; break; case cib_callback_token: error_msg = "callback token not found"; break; case cib_missing: error_msg = "cib object missing"; break; case cib_variant: error_msg = "unknown/corrupt cib variant"; break; case CIBRES_MISSING_ID: error_msg = "The id field is missing"; break; case CIBRES_MISSING_TYPE: error_msg = "The type field is missing"; break; case CIBRES_MISSING_FIELD: error_msg = "A required field is missing"; break; case CIBRES_OBJTYPE_MISMATCH: error_msg = "CIBRES_OBJTYPE_MISMATCH"; break; case cib_EXISTS: error_msg = "The object already exists"; break; case cib_NOTEXISTS: error_msg = "The object/attribute does not exist"; break; case CIBRES_CORRUPT: error_msg = "The CIB is corrupt"; break; case cib_NOOBJECT: error_msg = "The update was empty"; break; case cib_NOPARENT: error_msg = "The parent object does not exist"; break; case cib_NODECOPY: error_msg = "Failed while copying update"; break; case CIBRES_OTHER: error_msg = "CIBRES_OTHER"; break; case cib_ok: error_msg = "ok"; break; case cib_unknown: error_msg = "Unknown error"; break; case cib_STALE: error_msg = "Discarded old update"; break; case cib_ACTIVATION: error_msg = "Activation Failed"; break; case cib_NOSECTION: error_msg = "Required section was missing"; break; case cib_NOTSUPPORTED: error_msg = "The action/feature is not supported"; break; case cib_not_master: error_msg = "Local service is not the master instance"; break; case cib_client_corrupt: error_msg = "Service client not valid"; break; case cib_remote_timeout: error_msg = "Remote node did not respond"; break; case cib_master_timeout: error_msg = "No master service is currently active"; break; case cib_revision_unsupported: error_msg = "The required CIB revision number is not supported"; break; case cib_revision_unknown: error_msg = "The CIB revision number could not be determined"; break; case cib_missing_data: error_msg = "Required data for this CIB API call not found"; break; case cib_no_quorum: error_msg = "Write requires quorum"; break; case cib_diff_failed: error_msg = "Application of an update diff failed"; break; case cib_diff_resync: error_msg = "Application of an update diff failed, requesting a full refresh"; break; case cib_bad_section: error_msg = "Invalid CIB section specified"; break; case cib_old_data: error_msg = "Update was older than existing configuration"; break; case cib_dtd_validation: error_msg = "Update does not conform to the configured schema/DTD"; break; case cib_invalid_argument: error_msg = "Invalid argument"; break; } if(error_msg == NULL) { crm_err("Unknown CIB Error Code: %d", return_code); error_msg = ""; } return error_msg; } int cib_section2enum(const char *a_section) { if(a_section == NULL || strcasecmp(a_section, "all") == 0) { return cib_section_all; } else if(strcasecmp(a_section, XML_CIB_TAG_NODES) == 0) { return cib_section_nodes; } else if(strcasecmp(a_section, XML_CIB_TAG_STATUS) == 0) { return cib_section_status; } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) { return cib_section_constraints; } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCES) == 0) { return cib_section_resources; } else if(strcasecmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) { return cib_section_crmconfig; } crm_err("Unknown CIB section: %s", a_section); return cib_section_none; } int cib_compare_generation(xmlNode *left, xmlNode *right) { int lpc = 0; const char *attributes[] = { XML_ATTR_GENERATION_ADMIN, XML_ATTR_GENERATION, XML_ATTR_NUMUPDATES, }; crm_log_xml_debug_3(left, "left"); crm_log_xml_debug_3(right, "right"); for(lpc = 0; lpc < DIMOF(attributes); lpc++) { int int_elem_l = -1; int int_elem_r = -1; const char *elem_r = NULL; const char *elem_l = crm_element_value(left, attributes[lpc]); if(right != NULL) { elem_r = crm_element_value(right, attributes[lpc]); } if(elem_l != NULL) { int_elem_l = crm_parse_int(elem_l, NULL); } if(elem_r != NULL) { int_elem_r = crm_parse_int(elem_r, NULL); } if(int_elem_l < int_elem_r) { crm_debug_2("%s (%s < %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r)); return -1; } else if(int_elem_l > int_elem_r) { crm_debug_2("%s (%s > %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r)); return 1; } } return 0; } xmlNode* get_cib_copy(cib_t *cib) { xmlNode *xml_cib; int options = cib_scope_local|cib_sync_call; if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) { crm_err("Couldnt retrieve the CIB"); return NULL; } else if(xml_cib == NULL) { crm_err("The CIB result was empty"); return NULL; } if(safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) { return xml_cib; } free_xml(xml_cib); return NULL; } xmlNode* cib_get_generation(cib_t *cib) { xmlNode *the_cib = get_cib_copy(cib); xmlNode *generation = create_xml_node( NULL, XML_CIB_TAG_GENERATION_TUPPLE); if(the_cib != NULL) { copy_in_properties(generation, the_cib); free_xml(the_cib); } return generation; } void log_cib_diff(int log_level, xmlNode *diff, const char *function) { int add_updates = 0; int add_epoch = 0; int add_admin_epoch = 0; int del_updates = 0; int del_epoch = 0; int del_admin_epoch = 0; if(diff == NULL) { return; } cib_diff_version_details( diff, &add_admin_epoch, &add_epoch, &add_updates, &del_admin_epoch, &del_epoch, &del_updates); if(add_updates != del_updates) { do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function, del_admin_epoch, del_epoch, del_updates); do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function, add_admin_epoch, add_epoch, add_updates); } else if(diff != NULL) { do_crm_log(log_level, "%s: Local-only Change: %d.%d.%d", function, add_admin_epoch, add_epoch, add_updates); } log_xml_diff(log_level, diff, function); } gboolean cib_version_details( xmlNode *cib, int *admin_epoch, int *epoch, int *updates) { const char *value = NULL; if(cib == NULL) { *admin_epoch = -1; *epoch = -1; *updates = -1; return FALSE; } else { value = crm_element_value(cib, XML_ATTR_GENERATION_ADMIN); *admin_epoch = crm_parse_int(value, "-1"); value = crm_element_value(cib, XML_ATTR_GENERATION); *epoch = crm_parse_int(value, "-1"); value = crm_element_value(cib, XML_ATTR_NUMUPDATES); *updates = crm_parse_int(value, "-1"); } return TRUE; } gboolean cib_diff_version_details( xmlNode *diff, int *admin_epoch, int *epoch, int *updates, int *_admin_epoch, int *_epoch, int *_updates) { xmlNode *tmp = NULL; tmp = find_xml_node(diff, "diff-added", FALSE); cib_version_details(tmp, admin_epoch, epoch, updates); tmp = find_xml_node(diff, "diff-removed", FALSE); cib_version_details(tmp, _admin_epoch, _epoch, _updates); return TRUE; } /* * The caller should never free the return value */ const char *get_object_path(const char *object_type) { int lpc = 0; int max = DIMOF(known_paths); for(; lpc < max; lpc++) { if((object_type == NULL && known_paths[lpc].name == NULL) || safe_str_eq(object_type, known_paths[lpc].name)) { return known_paths[lpc].path; } } return NULL; } const char *get_object_parent(const char *object_type) { int lpc = 0; int max = DIMOF(known_paths); for(; lpc < max; lpc++) { if(safe_str_eq(object_type, known_paths[lpc].name)) { return known_paths[lpc].parent; } } return NULL; } xmlNode* get_object_root(const char *object_type, xmlNode *the_root) { xmlNode *result = NULL; xmlXPathObjectPtr xpathObj = NULL; const char *xpath = get_object_path(object_type); if(xpath == NULL) { return the_root; /* or return NULL? */ } xpathObj = xpath_search(the_root, xpath); if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) { crm_debug_2("Object %s not found", crm_str(object_type)); } else if(xpathObj->nodesetval->nodeNr > 1) { crm_err("Too many matches for %s", crm_str(object_type)); } else { result = xpathObj->nodesetval->nodeTab[0]; CRM_CHECK(result->type == XML_ELEMENT_NODE, result = NULL); } if(xpathObj) { xmlXPathFreeObject(xpathObj); } return result; } const char * get_crm_option(xmlNode *cib, const char *name, gboolean do_warn) { const char * value = NULL; xmlNode * a_default = NULL; xmlNode * config = get_object_root(XML_CIB_TAG_CRMCONFIG, cib); if(config != NULL) { a_default = find_entity(config, XML_CIB_TAG_NVPAIR, name); } if(a_default == NULL) { if(do_warn) { crm_warn("Option %s not set", name); } return NULL; } value = crm_element_value(a_default, XML_NVPAIR_ATTR_VALUE); if(safe_str_eq(value, "")) { value = NULL; } return value; } xmlNode* create_cib_fragment_adv( xmlNode *update, const char *update_section, const char *source) { xmlNode *cib = NULL; gboolean whole_cib = FALSE; xmlNode *object_root = NULL; char *local_section = NULL; /* crm_debug("Creating a blank fragment: %s", update_section); */ if(update == NULL && update_section == NULL) { crm_debug_3("Creating a blank fragment"); update = createEmptyCib(); crm_xml_add(cib, XML_ATTR_ORIGIN, source); return update; } else if(update == NULL) { crm_err("No update to create a fragment for"); return NULL; } CRM_CHECK(update_section != NULL, return NULL); if(safe_str_eq(crm_element_name(update), XML_TAG_CIB)) { whole_cib = TRUE; } if(whole_cib == FALSE) { cib = createEmptyCib(); crm_xml_add(cib, XML_ATTR_ORIGIN, source); object_root = get_object_root(update_section, cib); add_node_copy(object_root, update); } else { cib = copy_xml(update); crm_xml_add(cib, XML_ATTR_ORIGIN, source); } crm_free(local_section); crm_debug_3("Verifying created fragment"); return cib; } /* * It is the callers responsibility to free both the new CIB (output) * and the new CIB (input) */ xmlNode* createEmptyCib(void) { xmlNode *cib_root = NULL, *config = NULL, *status = NULL; cib_root = create_xml_node(NULL, XML_TAG_CIB); config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); status = create_xml_node(cib_root, XML_CIB_TAG_STATUS); /* crm_xml_add(cib_root, "version", "1"); */ - crm_xml_add(cib_root, XML_ATTR_GENERATION, "0"); - crm_xml_add(cib_root, XML_ATTR_GENERATION_ADMIN, "0"); - crm_xml_add(cib_root, XML_ATTR_NUMUPDATES, "0"); - 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); return cib_root; } enum cib_errors 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 **output) { int rc = cib_ok; xmlNode *scratch = NULL; CRM_CHECK(output != NULL && result_cib != NULL && config_changed != NULL, return cib_output_data); *output = NULL; *result_cib = NULL; *config_changed = FALSE; if(fn == NULL) { return cib_operation; } if(rc != cib_ok) { return rc; } if(is_query) { rc = (*fn)(op, call_options, section, req, input, current_cib, result_cib, output); return rc; } scratch = copy_xml(current_cib); rc = (*fn)(op, call_options, section, req, input, current_cib, &scratch, output); /* crm_log_xml_debug(current_cib, "old"); crm_log_xml_debug(scratch, "new"); crm_log_xml_debug(*output, "output"); */ CRM_CHECK(current_cib != scratch, return cib_unknown); if(rc == cib_ok) { CRM_CHECK(scratch != NULL, return cib_unknown); if(do_id_check(scratch, NULL, TRUE, FALSE)) { rc = cib_id_check; if(call_options & cib_force_diff) { crm_err("Global update introduces id collision!"); } } if(rc == cib_ok && current_cib && scratch) { 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_CHECK(old <= new, crm_err("%s went backwrads: %d -> %d (Opts: 0x%x)", XML_ATTR_GENERATION_ADMIN, old, new, call_options); crm_log_xml_warn(req, "Bad Op"); crm_log_xml_warn(input, "Bad Data"); rc = cib_old_data); if(old == new) { crm_element_value_int(scratch, XML_ATTR_GENERATION, &new); crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old); CRM_CHECK(old <= new, crm_err("%s went backwrads: %d -> %d (Opts: 0x%x)", XML_ATTR_GENERATION_ADMIN, old, new, call_options); crm_log_xml_warn(req, "Bad Op"); crm_log_xml_warn(input, "Bad Data"); rc = cib_old_data); } } if(rc == cib_ok) { gboolean dtd_ok; const char *current_dtd; fix_plus_plus_recursive(scratch); /* crm_log_xml_debug(scratch, "newer"); */ *config_changed = cib_config_changed(current_cib, scratch, NULL); /* crm_log_xml_debug(scratch, "newest"); */ if(manage_counters && *config_changed) { cib_update_counter(scratch, XML_ATTR_NUMUPDATES, TRUE); cib_update_counter(scratch, XML_ATTR_GENERATION, FALSE); } else if(manage_counters) { cib_update_counter(scratch, XML_ATTR_NUMUPDATES, FALSE); } current_dtd = crm_element_value(scratch, "validate-with"); dtd_ok = validate_xml(scratch, NULL, TRUE); if(dtd_ok == FALSE) { crm_err("Updated CIB does not validate against %s schema/dtd", current_dtd); rc = cib_dtd_validation; } } } *result_cib = scratch; return rc; } int get_channel_token(IPC_Channel *ch, char **token) { int rc = cib_ok; xmlNode *reg_msg = NULL; const char *msg_type = NULL; const char *tmp_ticket = NULL; CRM_CHECK(ch != NULL, return cib_missing); CRM_CHECK(token != NULL, return cib_output_ptr); crm_debug_4("Waiting for msg on command channel"); reg_msg = xmlfromIPC(ch, 0); if(ch->ops->get_chan_status(ch) != IPC_CONNECT) { crm_err("No reply message - disconnected"); free_xml(reg_msg); return cib_not_connected; } else if(reg_msg == NULL) { crm_err("No reply message - empty"); return cib_reply_failed; } msg_type = crm_element_value(reg_msg, F_CIB_OPERATION); tmp_ticket = crm_element_value(reg_msg, F_CIB_CLIENTID); if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) { crm_err("Invalid registration message: %s", msg_type); rc = cib_registration_msg; } else if(tmp_ticket == NULL) { rc = cib_callback_token; } else { *token = crm_strdup(tmp_ticket); } free_xml(reg_msg); return cib_ok; } xmlNode * cib_create_op( int call_id, const char *token, const char *op, const char *host, const char *section, xmlNode *data, int call_options) { int rc = HA_OK; xmlNode *op_msg = create_xml_node(NULL, "cib_command"); CRM_CHECK(op_msg != NULL, return NULL); CRM_CHECK(token != 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_CALLBACK_TOKEN, token); 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); crm_debug_4("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 (rc != HA_OK) { crm_err("Failed to create CIB operation message"); crm_log_xml(LOG_ERR, "op", op_msg); free_xml(op_msg); return NULL; } 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; cib_callback_client_t local_blob; local_blob.id = NULL; local_blob.callback = NULL; local_blob.user_data = NULL; local_blob.only_success = FALSE; 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 = g_hash_table_lookup( cib_op_callback_table, GINT_TO_POINTER(call_id)); if(blob != NULL) { local_blob = *blob; blob = NULL; remove_cib_op_callback(call_id, FALSE); } else { crm_debug_2("No callback found for call %d", call_id); local_blob.callback = NULL; } if(cib == NULL) { crm_debug("No cib object supplied"); } if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } if(local_blob.callback != NULL && (rc == cib_ok || local_blob.only_success == FALSE)) { crm_debug_2("Invoking callback %s for call %d", crm_str(local_blob.id), call_id); local_blob.callback(msg, call_id, rc, output, local_blob.user_data); } else if(cib && cib->op_callback == NULL && rc != cib_ok) { crm_warn("CIB command failed: %s", cib_error2string(rc)); crm_log_xml(LOG_DEBUG, "Failed CIB Update", msg); } if(cib && cib->op_callback != NULL) { crm_debug_2("Invoking global callback for call %d", call_id); cib->op_callback(msg, call_id, rc, output); } crm_debug_4("OP callback activated."); } 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(safe_str_neq(entry->event, event)) { crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event); return; } crm_debug_4("Invoking callback for %p/%s event...", entry, event); entry->callback(event, msg); crm_debug_4("Callback invoked..."); }