diff --git a/crm/cib/io.c b/crm/cib/io.c index 46a62d33cd..ba0ab0fb44 100644 --- a/crm/cib/io.c +++ b/crm/cib/io.c @@ -1,683 +1,683 @@ -/* $Id: io.c,v 1.53 2006/03/31 11:50:23 andrew Exp $ */ +/* $Id: io.c,v 1.54 2006/03/31 12:45:02 andrew Exp $ */ /* * 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 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; crm_data_t *the_cib = NULL; crm_data_t *node_search = NULL; crm_data_t *resource_search = NULL; crm_data_t *constraint_search = NULL; crm_data_t *status_search = NULL; extern gboolean cib_writes_enabled; extern char *ccm_transition_id; extern gboolean cib_have_quorum; extern GHashTable *peer_hash; extern GHashTable *ccm_membership; extern GTRIGSource *cib_writer; int set_connected_peers(crm_data_t *xml_obj); void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data); int write_cib_contents(gpointer p); #include #include #include static gboolean validate_cib_digest(crm_data_t *local_cib) { 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; if(local_cib != NULL) { - digest = calculate_xml_digest(local_cib); + digest = calculate_xml_digest(local_cib, FALSE); } s_res = stat(CIB_FILENAME ".sig", &buf); if (s_res != 0) { crm_warn("No on-disk digest present"); return TRUE; } expected_strm = fopen(CIB_FILENAME ".sig", "r"); 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, sizeof(char) * (length+1)); read_len = fread(expected, sizeof(char), length, expected_strm); CRM_ASSERT(read_len == length); if(expected == NULL) { crm_err("On-disk digest is empty"); } else if(safe_str_eq(expected, digest)) { crm_debug("Digest comparision passed: %s", digest); passed = TRUE; } else { crm_err("Digest comparision failed: %s vs. %s", expected, digest); } crm_free(digest); crm_free(expected); return passed; } static int write_cib_digest(crm_data_t *local_cib, char *digest) { int rc = 0; FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w"); char *local_digest = NULL; CRM_ASSERT(digest_strm != NULL); if(digest == NULL) { - digest = calculate_xml_digest(local_cib); + digest = calculate_xml_digest(local_cib, FALSE); CRM_ASSERT(digest != NULL); local_digest = digest; } rc = fprintf(digest_strm, "%s", digest); if(rc < 0) { cl_perror("Cannot write output to %s.sig", CIB_FILENAME); } fflush(digest_strm); fclose(digest_strm); crm_free(local_digest); return rc; } static gboolean validate_on_disk_cib(const char *filename, crm_data_t **on_disk_cib) { int s_res = -1; struct stat buf; FILE *cib_file = NULL; gboolean passed = TRUE; crm_data_t *root = NULL; if(filename != NULL) { s_res = stat(filename, &buf); } if (s_res == 0) { cib_file = fopen(filename, "r"); crm_debug_2("Reading cluster configuration from: %s", filename); root = file2xml(cib_file); fclose(cib_file); if(validate_cib_digest(root) == FALSE) { passed = FALSE; } } if(on_disk_cib != NULL) { *on_disk_cib = root; } else { free_xml(root); } return passed; } /* * It is the callers responsibility to free the output of this function */ crm_data_t* readCibXmlFile(const char *filename) { int s_res = -1; struct stat buf; gboolean valid = FALSE; const char *name = NULL; const char *value = NULL; crm_data_t *root = NULL; crm_data_t *status = NULL; struct passwd *cib_user = NULL; gboolean user_readwritable = FALSE; if(filename != NULL) { s_res = stat(filename, &buf); } if (s_res != 0) { return NULL; } cib_user = getpwnam(HA_CCMUSER); user_readwritable = (cib_user != NULL && buf.st_uid == cib_user->pw_uid && (buf.st_mode & (S_IRUSR|S_IWUSR))); if( S_ISREG(buf.st_mode) == FALSE ) { crm_err("%s must be a regular file", filename); cl_flush_logs(); exit(100); } else if( user_readwritable == FALSE ) { struct group *cib_grp = getgrnam(HA_APIGROUP); gboolean group_readwritable = ( cib_grp != NULL && buf.st_gid == cib_grp->gr_gid && (buf.st_mode & (S_IRGRP|S_IWGRP))); if( group_readwritable == FALSE ) { crm_err("%s must be owned and read/writeable by user %s," " or owned and read/writable by group %s", filename, HA_CCMUSER, HA_APIGROUP); cl_flush_logs(); exit(100); } crm_warn("%s should be owned and read/writeable by user %s", filename, HA_CCMUSER); } crm_info("Reading cluster configuration from: %s", filename); valid = validate_on_disk_cib(filename, &root); crm_log_xml_info(root, "[on-disk]"); if(root == NULL) { crm_crit("Parse ERROR reading %s.", filename); crm_crit("Inhibiting respawn by Heartbeat to avoid loss" " of configuration data."); cl_flush_logs(); exit(100); } else if(valid == FALSE) { crm_err("%s has been manually changed" " - please update the md5 digest in %s.sig", filename, filename); cl_flush_logs(); exit(100); } crm_xml_add(root, "generated", XML_BOOLEAN_FALSE); /* strip out the status section if there is one */ status = find_xml_node(root, XML_CIB_TAG_STATUS, TRUE); if(status != NULL) { free_xml_from_parent(root, status); } create_xml_node(root, XML_CIB_TAG_STATUS); /* fill in some defaults */ name = XML_ATTR_GENERATION_ADMIN; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } name = XML_ATTR_GENERATION; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } name = XML_ATTR_NUMUPDATES; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } do_id_check(root, NULL); if (verifyCibXml(root) == FALSE) { crm_crit("%s does not contain a vaild configuration.", filename); crm_crit("Inhibiting respawn by Heartbeat to avoid loss" " of configuration data."); cl_flush_logs(); exit(100); } return root; } /* * The caller should never free the return value */ crm_data_t* get_the_CIB(void) { return the_cib; } gboolean uninitializeCib(void) { crm_data_t *tmp_cib = the_cib; if(tmp_cib == NULL) { crm_err("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_err("Deallocating the CIB."); free_xml(tmp_cib); crm_err("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(crm_data_t *new_cib) { gboolean is_valid = TRUE; crm_data_t *tmp_node = NULL; if(new_cib == NULL) { return FALSE; } xml_validate(new_cib); tmp_node = get_object_root(XML_CIB_TAG_NODES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_STATUS, new_cib); if (is_valid && tmp_node == NULL) { create_xml_node(new_cib, XML_CIB_TAG_STATUS); } if(is_valid == FALSE) { crm_warn("CIB Verification failed"); return FALSE; } update_counters(__FILE__, __FUNCTION__, new_cib); the_cib = new_cib; initialized = TRUE; return TRUE; } static int archive_file(const char *oldname, const char *newname, const char *ext) { /* 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, sizeof(char) * 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); /* unlink the old backup */ if (rc == 0 && s_res >= 0) { res = unlink(backup_file); if (res < 0) { cl_perror("Could not unlink %s", backup_file); rc = -1; } } 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; } } /* unlink the original */ if (rc == 0 && s_res >= 0) { res = unlink(oldname); if (res < 0) { cl_perror("Could not unlink %s", oldname); rc = -3; } } crm_free(backup_file); return rc; } /* * This method will free the old CIB pointer on success and the new one * on failure. */ int activateCibXml(crm_data_t *new_cib, const char *ignored) { int error_code = cib_ok; crm_data_t *saved_cib = get_the_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) { crm_err("Ignoring invalid or NULL CIB"); error_code = -1; if(saved_cib != NULL) { crm_warn("Reverting to last known CIB"); if (initializeCib(saved_cib)) { /* oh we are so dead */ crm_crit("Couldn't re-initialize the old CIB!"); cl_flush_logs(); exit(1); } } else if(error_code != cib_ok) { crm_crit("Could not write out new CIB and no saved" " version to revert to"); } } else if(cib_writes_enabled) { crm_debug_2("Triggering CIB write"); G_main_set_trigger(cib_writer); } 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; char *digest = NULL; crm_data_t *cib_status_root = NULL; const char *digest_filename = CIB_FILENAME ".sig"; /* 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); /* 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(LSB_EXIT_GENERIC); } rc = archive_file(CIB_FILENAME, NULL, "last"); if(rc != 0) { crm_err("Could not make backup of the existing CIB: %d", rc); exit(LSB_EXIT_GENERIC); } rc = archive_file(digest_filename, NULL, "last"); if(rc != 0) { crm_warn("Could not make backup of the existing CIB digest: %d", rc); } /* 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 */ 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); if(rc <= 0) { crm_err("Changes couldn't be written to disk"); exit(LSB_EXIT_GENERIC); } - digest = calculate_xml_digest(the_cib); + digest = calculate_xml_digest(the_cib, 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); if(rc <= 0) { crm_err("Digest couldn't be written to disk"); exit(LSB_EXIT_GENERIC); } #if 0 if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) { crm_err("wrote incorrect digest"); exit(LSB_EXIT_GENERIC); } #endif exit(LSB_EXIT_OK); return HA_OK; } gboolean set_transition(crm_data_t *xml_obj) { const char *current = crm_element_value( xml_obj, XML_ATTR_CCM_TRANSITION); if(safe_str_neq(current, ccm_transition_id)) { crm_debug("CCM transition: old=%s, new=%s", current, ccm_transition_id); crm_xml_add(xml_obj, XML_ATTR_CCM_TRANSITION,ccm_transition_id); return TRUE; } return FALSE; } gboolean set_connected_peers(crm_data_t *xml_obj) { int active = 0; int current = 0; char *peers_s = NULL; const char *current_s = crm_element_value(xml_obj, XML_ATTR_NUMPEERS); g_hash_table_foreach(peer_hash, GHFunc_count_peers, &active); current = crm_parse_int(current_s, "0"); if(current != active) { peers_s = crm_itoa(active); crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s); crm_debug("We now have %s active peers", peers_s); crm_free(peers_s); return TRUE; } return FALSE; } gboolean update_quorum(crm_data_t *xml_obj) { const char *quorum_value = XML_BOOLEAN_FALSE; const char *current = crm_element_value(xml_obj, XML_ATTR_HAVE_QUORUM); if(cib_have_quorum) { quorum_value = XML_BOOLEAN_TRUE; } if(safe_str_neq(current, quorum_value)) { crm_debug("CCM quorum: old=%s, new=%s", current, quorum_value); crm_xml_add(xml_obj, XML_ATTR_HAVE_QUORUM, quorum_value); return TRUE; } return FALSE; } gboolean update_counters(const char *file, const char *fn, crm_data_t *xml_obj) { gboolean did_update = FALSE; did_update = did_update || update_quorum(xml_obj); did_update = did_update || set_transition(xml_obj); did_update = did_update || set_connected_peers(xml_obj); if(did_update) { do_crm_log(LOG_DEBUG, file, fn, "Counters updated"); } return did_update; } 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/crm/crmd/lrm.c b/crm/crmd/lrm.c index 5dfb8261cc..f19ef90430 100644 --- a/crm/crmd/lrm.c +++ b/crm/crmd/lrm.c @@ -1,1581 +1,1581 @@ /* * 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 /* for access */ #include #include #include #include #include #include #include #include #include #include #include #include char *make_stop_id(const char *rsc, int call_id); void ghash_print_pending(gpointer key, gpointer value, gpointer user_data); gboolean stop_all_resources(void); gboolean resource_stopped(gpointer key, gpointer value, gpointer user_data); gboolean build_operation_update( crm_data_t *rsc_list, lrm_op_t *op, const char *src, int lpc); gboolean build_active_RAs(crm_data_t *rsc_list); gboolean is_rsc_active(const char *rsc_id); void do_update_resource(lrm_op_t *op); enum crmd_fsa_input do_lrm_rsc_op( lrm_rsc_t *rsc, char *rid, const char *operation, crm_data_t *msg, HA_Message *request); enum crmd_fsa_input do_fake_lrm_op(gpointer data); void stop_recurring_action( gpointer key, gpointer value, gpointer user_data); gboolean remove_recurring_action( gpointer key, gpointer value, gpointer user_data); lrm_op_t *construct_op( crm_data_t *rsc_op, const char *rsc_id, const char *operation); void send_direct_ack(const char *to_host, const char *to_sys, lrm_op_t* op, const char *rsc_id); void free_recurring_op(gpointer value); GHashTable *monitors = NULL; GHashTable *resources = NULL; GHashTable *resources_confirmed = NULL; GHashTable *shutdown_ops = NULL; int num_lrm_register_fails = 0; int max_lrm_register_fails = 30; enum crmd_rscstate { crmd_rscstate_NULL, crmd_rscstate_START, crmd_rscstate_START_PENDING, crmd_rscstate_START_OK, crmd_rscstate_START_FAIL, crmd_rscstate_STOP, crmd_rscstate_STOP_PENDING, crmd_rscstate_STOP_OK, crmd_rscstate_STOP_FAIL, crmd_rscstate_MON, crmd_rscstate_MON_PENDING, crmd_rscstate_MON_OK, crmd_rscstate_MON_FAIL, crmd_rscstate_GENERIC_PENDING, crmd_rscstate_GENERIC_OK, crmd_rscstate_GENERIC_FAIL }; const char *crmd_rscstate2string(enum crmd_rscstate state); const char * crmd_rscstate2string(enum crmd_rscstate state) { switch(state) { case crmd_rscstate_NULL: return NULL; case crmd_rscstate_START: return CRMD_ACTION_START; case crmd_rscstate_START_PENDING: return CRMD_ACTION_START_PENDING; case crmd_rscstate_START_OK: return CRMD_ACTION_STARTED; case crmd_rscstate_START_FAIL: return CRMD_ACTION_START_FAIL; case crmd_rscstate_STOP: return CRMD_ACTION_STOP; case crmd_rscstate_STOP_PENDING: return CRMD_ACTION_STOP_PENDING; case crmd_rscstate_STOP_OK: return CRMD_ACTION_STOPPED; case crmd_rscstate_STOP_FAIL: return CRMD_ACTION_STOP_FAIL; case crmd_rscstate_MON: return CRMD_ACTION_MON; case crmd_rscstate_MON_PENDING: return CRMD_ACTION_MON_PENDING; case crmd_rscstate_MON_OK: return CRMD_ACTION_MON_OK; case crmd_rscstate_MON_FAIL: return CRMD_ACTION_MON_FAIL; case crmd_rscstate_GENERIC_PENDING: return CRMD_ACTION_GENERIC_PENDING; case crmd_rscstate_GENERIC_OK: return CRMD_ACTION_GENERIC_OK; case crmd_rscstate_GENERIC_FAIL: return CRMD_ACTION_GENERIC_FAIL; } return ""; } GCHSource *lrm_source = NULL; /* A_LRM_CONNECT */ enum crmd_fsa_input do_lrm_control(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { int ret = HA_OK; if(action & A_LRM_DISCONNECT) { if(lrm_source) { crm_debug("Removing LRM connection from MainLoop"); if(G_main_del_IPC_Channel(lrm_source) == FALSE) { crm_err("Could not remove LRM connection" " from MainLoop"); } lrm_source = NULL; } if(fsa_lrm_conn) { fsa_lrm_conn->lrm_ops->signoff(fsa_lrm_conn); crm_info("Disconnected from the LRM"); clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); fsa_lrm_conn = NULL; } /* TODO: Clean up the hashtable */ } if(action & A_LRM_CONNECT) { ret = HA_OK; monitors = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, free_recurring_op); resources = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); resources_confirmed = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); shutdown_ops = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); fsa_lrm_conn = ll_lrm_new(XML_CIB_TAG_LRM); if(NULL == fsa_lrm_conn) { register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); ret = HA_FAIL; } if(ret == HA_OK) { crm_debug("Connecting to the LRM"); ret = fsa_lrm_conn->lrm_ops->signon( fsa_lrm_conn, CRM_SYSTEM_CRMD); } if(ret != HA_OK) { if(++num_lrm_register_fails < max_lrm_register_fails) { crm_warn("Failed to sign on to the LRM %d" " (%d max) times", num_lrm_register_fails, max_lrm_register_fails); crm_timer_start(wait_timer); crmd_fsa_stall(NULL); return I_NULL; } } if(ret == HA_OK) { crm_debug_4("LRM: set_lrm_callback..."); ret = fsa_lrm_conn->lrm_ops->set_lrm_callback( fsa_lrm_conn, lrm_op_callback); if(ret != HA_OK) { crm_err("Failed to set LRM callbacks"); } } if(ret != HA_OK) { crm_err("Failed to sign on to the LRM %d" " (max) times", num_lrm_register_fails); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return I_NULL; } /* TODO: create a destroy handler that causes * some recovery to happen */ lrm_source = G_main_add_IPC_Channel( G_PRIORITY_LOW, fsa_lrm_conn->lrm_ops->ipcchan(fsa_lrm_conn), FALSE, lrm_dispatch, fsa_lrm_conn, default_ipc_connection_destroy); set_bit_inplace(fsa_input_register, R_LRM_CONNECTED); crm_debug("LRM connection established"); } if(action & ~(A_LRM_CONNECT|A_LRM_DISCONNECT)) { crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__); } return I_NULL; } gboolean stop_all_resources(void) { GListPtr lrm_list = NULL; crm_info("Making sure all active resources are stopped before exit"); if(fsa_lrm_conn == NULL) { return TRUE; } else if(is_set(fsa_input_register, R_SENT_RSC_STOP)) { crm_debug("Already sent stop operation"); return TRUE; } lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn); slist_iter( rsc_id, char, lrm_list, lpc, if(is_rsc_active(rsc_id)) { crm_err("Resource %s was active at shutdown." " You may ignore this error if it is unmanaged.", rsc_id); /* do_lrm_rsc_op(NULL, rsc_id, CRMD_ACTION_STOP, NULL, NULL); */ } ); set_bit_inplace(fsa_input_register, R_SENT_RSC_STOP); if(g_hash_table_size(shutdown_ops) == 0) { register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL); } else { crm_info("Waiting for %d pending stop operations " " to complete before exiting", g_hash_table_size(shutdown_ops)); } return TRUE; } gboolean build_operation_update( crm_data_t *xml_rsc, lrm_op_t *op, const char *src, int lpc) { int len = 0; char *tmp = NULL; char *fail_state = NULL; const char *state = NULL; crm_data_t *xml_op = NULL; char *op_id = NULL; const char *caller_version = NULL; CRM_DEV_ASSERT(op != NULL); if(crm_assert_failed) { return FALSE; } crm_debug_2("%s: Updating resouce %s after %s %s op", src, op->rsc_id, op_status2text(op->op_status), op->op_type); if(op->op_status == LRM_OP_CANCELLED) { crm_debug_3("Ignoring cancelled op"); return TRUE; } if(safe_str_eq(op->op_type, CRMD_ACTION_NOTIFY)) { const char *n_type = g_hash_table_lookup( op->params, "notify_type"); const char *n_task = g_hash_table_lookup( op->params, "notify_operation"); CRM_DEV_ASSERT(n_type != NULL); CRM_DEV_ASSERT(n_task != NULL); op_id = generate_notify_key(op->rsc_id, n_type, n_task); /* these are not yet allowed to fail */ op->op_status = LRM_OP_DONE; op->rc = 0; } else { op_id = generate_op_key(op->rsc_id, op->op_type, op->interval); } /* Handle recurring ops - infer last op_status */ if(op->op_status == LRM_OP_PENDING && op->interval > 0) { if(op->rc == 0) { crm_debug("Mapping pending operation to DONE"); op->op_status = LRM_OP_DONE; } else { crm_debug("Mapping pending operation to ERROR"); op->op_status = LRM_OP_ERROR; } } xml_op = find_entity(xml_rsc, XML_LRM_TAG_RSC_OP, op_id); if(xml_op != NULL) { const char *old_status_s = crm_element_value( xml_op, XML_LRM_ATTR_OPSTATUS); int old_status = crm_parse_int(old_status_s, "-2"); int log_level = LOG_ERR; if(old_status_s == NULL) { crm_err("No value for "XML_LRM_ATTR_OPSTATUS); } else if(old_status == op->op_status) { /* safe to mask */ log_level = LOG_WARNING; } else if(old_status == LRM_OP_PENDING){ /* ??safe to mask?? */ /* log_level = LOG_WARNING; */ } crm_log_maybe(log_level, "Duplicate %s operations in get_cur_state()", op_id); crm_log_maybe(log_level+2, "New entry: %s %s (call=%d, status=%s)", op_id, op->user_data, op->call_id, op_status2text(op->op_status)); crm_log_xml(log_level+2, "Existing entry", xml_op); crm_free(op_id); return FALSE; } else { xml_op = create_xml_node(xml_rsc, XML_LRM_TAG_RSC_OP); } crm_xml_add(xml_op, XML_ATTR_ID, op_id); crm_free(op_id); crm_xml_add(xml_op, XML_LRM_ATTR_TASK, op->op_type); crm_xml_add(xml_op, XML_ATTR_ORIGIN, src); if(op->user_data == NULL) { op->user_data = generate_transition_key(-1, fsa_our_uname); } #if CRM_DEPRECATED_SINCE_2_0_3 caller_version = g_hash_table_lookup(op->params, XML_ATTR_CRM_VERSION); if(compare_version("1.0.3", caller_version) > 0) { CRM_CHECK(FALSE, ; ); fail_state = generate_transition_magic_v202( op->user_data, op->op_status); } else { fail_state = generate_transition_magic( op->user_data, op->op_status, op->rc); } #else fail_state = generate_transition_magic( op->user_data, op->op_status, op->rc); #endif crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data); crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, fail_state); crm_free(fail_state); switch(op->op_status) { case LRM_OP_PENDING: break; case LRM_OP_CANCELLED: crm_err("What to do here"); break; case LRM_OP_ERROR: case LRM_OP_TIMEOUT: case LRM_OP_NOTSUPPORTED: crm_debug("Resource action %s/%s %s: %d", op->rsc_id, op->op_type, op_status2text(op->op_status), op->rc); len = strlen(op->op_type); len += strlen("_failed_"); crm_malloc0(fail_state, sizeof(char)*len); if(fail_state != NULL) { sprintf(fail_state, "%s_failed", op->op_type); } crm_xml_add(xml_op, XML_LRM_ATTR_RSCSTATE, fail_state); crm_free(fail_state); break; case LRM_OP_DONE: if(safe_str_eq(CRMD_ACTION_START, op->op_type)) { state = CRMD_ACTION_STARTED; } else if(safe_str_eq(CRMD_ACTION_STOP, op->op_type)) { state = CRMD_ACTION_STOPPED; } else if(safe_str_eq(CRMD_ACTION_MON, op->op_type)) { state = CRMD_ACTION_STARTED; } else { crm_warn("Using status \"%s\" for op \"%s\"" "... this is still in the experimental stage.", CRMD_ACTION_GENERIC_OK, op->op_type); state = CRMD_ACTION_GENERIC_OK; } crm_xml_add(xml_op, XML_LRM_ATTR_RSCSTATE, state); break; } tmp = crm_itoa(op->call_id); crm_xml_add(xml_op, XML_LRM_ATTR_CALLID, tmp); crm_free(tmp); /* set these on 'xml_rsc' too to make life easy for the TE */ tmp = crm_itoa(op->rc); crm_xml_add(xml_op, XML_LRM_ATTR_RC, tmp); crm_free(tmp); tmp = crm_itoa(op->op_status); crm_xml_add(xml_op, XML_LRM_ATTR_OPSTATUS, tmp); crm_free(tmp); if(safe_str_neq(op->op_type, CRMD_ACTION_STOP)) { /* this will enable us to later determin that the * resource's parameters have changed and we should force * a restart * however it will come at the cost of a potentially much * larger CIB */ crm_data_t *args_xml = NULL; char *digest = NULL; #if CRM_DEPRECATED_SINCE_2_0_4 args_xml = create_xml_node(xml_op, XML_TAG_PARAMS); #else args_xml = create_xml_node(NULL, XML_TAG_PARAMS); #endif g_hash_table_foreach(op->params, hash2field, args_xml); filter_action_parameters(args_xml); - digest = calculate_xml_digest(args_xml); + digest = calculate_xml_digest(args_xml, TRUE); crm_xml_add(xml_op, XML_LRM_ATTR_OP_DIGEST, digest); #if CRM_DEPRECATED_SINCE_2_0_4 #else free_xml(args_xml); #endif } return TRUE; } gboolean is_rsc_active(const char *rsc_id) { GList *op_list = NULL; gboolean active = FALSE; lrm_rsc_t *the_rsc = NULL; state_flag_t cur_state = 0; int max_call_id = -1; if(fsa_lrm_conn == NULL) { return FALSE; } the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rsc_id); crm_debug_2("Processing lrm_rsc_t entry %s", rsc_id); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM"); return FALSE; } op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state); crm_debug_3("\tcurrent state:%s",cur_state==LRM_RSC_IDLE?"Idle":"Busy"); slist_iter( op, lrm_op_t, op_list, llpc, crm_debug_2("Processing op %s for %s (status=%d, rc=%d)", op->op_type, the_rsc->id, op->op_status, op->rc); CRM_ASSERT(max_call_id <= op->call_id); if(safe_str_eq(op->op_type, CRMD_ACTION_STOP)) { active = FALSE; } else if(op->rc == EXECRA_NOT_RUNNING) { active = FALSE; } else { active = TRUE; } max_call_id = op->call_id; lrm_free_op(op); ); g_list_free(op_list); lrm_free_rsc(the_rsc); return active; } gboolean build_active_RAs(crm_data_t *rsc_list) { GList *op_list = NULL; GList *lrm_list = NULL; gboolean found_op = FALSE; state_flag_t cur_state = 0; if(fsa_lrm_conn == NULL) { return FALSE; } lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn); slist_iter( rid, char, lrm_list, lpc, lrm_rsc_t *the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); crm_data_t *xml_rsc = create_xml_node( rsc_list, XML_LRM_TAG_RESOURCE); int max_call_id = -1; crm_debug("Processing lrm_rsc_t entry %s", rid); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM"); continue; } crm_xml_add(xml_rsc, XML_ATTR_ID, the_rsc->id); crm_xml_add(xml_rsc, XML_ATTR_TYPE, the_rsc->type); crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, the_rsc->class); crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER,the_rsc->provider); op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state); crm_debug_2("\tcurrent state:%s", cur_state==LRM_RSC_IDLE?"Idle":"Busy"); slist_iter( op, lrm_op_t, op_list, llpc, crm_debug_2("Processing op %s for %s (status=%d, rc=%d)", op->op_type, the_rsc->id, op->op_status, op->rc); if(max_call_id < op->call_id) { build_operation_update( xml_rsc, op, __FUNCTION__, llpc); } else if(max_call_id > op->call_id) { crm_err("Bad call_id in list=%d. Previous call_id=%d", op->call_id, max_call_id); } else { crm_warn("lrm->get_cur_state() returned" " duplicate entries for call_id=%d", op->call_id); } max_call_id = op->call_id; found_op = TRUE; lrm_free_op(op); ); if(found_op == FALSE && g_list_length(op_list) != 0) { crm_err("Could not properly determin last op" " for %s from %d entries", the_rsc->id, g_list_length(op_list)); } g_list_free(op_list); lrm_free_rsc(the_rsc); ); g_list_free(lrm_list); return TRUE; } crm_data_t* do_lrm_query(gboolean is_replace) { gboolean shut_down = FALSE; crm_data_t *xml_result= NULL; crm_data_t *xml_state = NULL; crm_data_t *xml_data = NULL; crm_data_t *rsc_list = NULL; const char *exp_state = CRMD_JOINSTATE_MEMBER; if(is_set(fsa_input_register, R_SHUTDOWN)) { exp_state = CRMD_STATE_INACTIVE; shut_down = TRUE; } xml_state = create_node_state( fsa_our_uname, ACTIVESTATUS, XML_BOOLEAN_TRUE, ONLINESTATUS, CRMD_JOINSTATE_MEMBER, exp_state, !shut_down, __FUNCTION__); xml_data = create_xml_node(xml_state, XML_CIB_TAG_LRM); rsc_list = create_xml_node(xml_data, XML_LRM_TAG_RESOURCES); /* Build a list of active (not always running) resources */ build_active_RAs(rsc_list); if(is_replace) { crm_xml_add(xml_state, XML_CIB_ATTR_REPLACE, XML_CIB_TAG_LRM); } xml_result = create_cib_fragment(xml_state, XML_CIB_TAG_STATUS); crm_log_xml_debug_3(xml_state, "Current state of the LRM"); return xml_result; } struct recurring_op_s { char *rsc_id; int call_id; }; static void cancel_monitor(lrm_rsc_t *rsc, const char *key) { struct recurring_op_s *existing_op = NULL; if(rsc == NULL) { crm_err("No resource to cancel and operation for"); return; } else if(key == NULL) { crm_err("No operation to cancel"); return; } existing_op = g_hash_table_lookup(monitors, key); if(existing_op != NULL) { crm_debug("Cancelling previous invocation of %s (%d)", key, existing_op->call_id); /* cancel it so we can then restart it without conflict */ if(rsc->ops->cancel_op(rsc, existing_op->call_id) != HA_OK) { crm_info("Couldn't cancel %s (%d)", key, existing_op->call_id); } else { g_hash_table_remove(monitors, key); } } else { crm_debug("No previous invocation of %s", key); } } /* static gboolean is_active(const char *rsc_id) { char *stop_id = NULL; const char *last_op = NULL; gboolean is_active = TRUE; last_op = g_hash_table_lookup(resources, rsc_id); stop_id = generate_op_key(rsc_id, CRMD_ACTION_STOP, 0); if(safe_str_eq(last_op, stop_id)) { is_active = FALSE; } crm_free(stop_id); return is_active; } */ /* A_LRM_INVOKE */ enum crmd_fsa_input do_lrm_invoke(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { const char *crm_op = NULL; const char *from_sys = NULL; const char *from_host = NULL; const char *operation = NULL; enum crmd_fsa_input next_input = I_NULL; ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); crm_op = cl_get_string(input->msg, F_CRM_TASK); from_sys = cl_get_string(input->msg, F_CRM_SYS_FROM); if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) { from_host = cl_get_string(input->msg, F_CRM_HOST_FROM); } crm_debug_2("LRM command from: %s", from_sys); if(safe_str_eq(crm_op, CRM_OP_LRM_DELETE)) { operation = CRMD_ACTION_DELETE; } else if(safe_str_eq(operation, CRM_OP_LRM_REFRESH)) { crm_op = CRM_OP_LRM_REFRESH; } else if(input->xml != NULL) { operation = crm_element_value(input->xml, XML_LRM_ATTR_TASK); } if(crm_op != NULL && safe_str_eq(crm_op, CRM_OP_LRM_REFRESH)) { enum cib_errors rc = cib_ok; crm_data_t *fragment = do_lrm_query(TRUE); crm_info("Forcing a local LRM refresh"); rc = fsa_cib_conn->cmds->update( fsa_cib_conn, XML_CIB_TAG_STATUS, fragment, NULL, cib_quorum_override); free_xml(fragment); } else if(crm_op != NULL && safe_str_eq(crm_op, CRM_OP_LRM_QUERY)) { crm_data_t *data = do_lrm_query(FALSE); HA_Message *reply = create_reply(input->msg, data); if(relay_message(reply, TRUE) == FALSE) { crm_err("Unable to route reply"); crm_log_message(LOG_ERR, reply); crm_msg_del(reply); } free_xml(data); } else if(safe_str_eq(operation, CRM_OP_PROBED) || safe_str_eq(crm_op, CRM_OP_REPROBE)) { char *attr_id = crm_concat("lrm-probe", fsa_our_uuid, '-'); char *attr_set = crm_concat("crmd-transient", fsa_our_uuid,'-'); const char *probed = XML_BOOLEAN_TRUE; if(safe_str_eq(crm_op, CRM_OP_REPROBE)) { probed = XML_BOOLEAN_FALSE; } update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_STATUS, fsa_our_uuid, attr_set, attr_id, CRM_OP_PROBED, probed); crm_free(attr_id); crm_free(attr_set); } else if(operation != NULL) { char rid[64]; lrm_rsc_t *rsc = NULL; const char *id_from_cib = NULL; crm_data_t *xml_rsc = find_xml_node( input->xml, XML_CIB_TAG_RESOURCE, TRUE); CRM_DEV_ASSERT(xml_rsc != NULL); if(crm_assert_failed) { crm_log_xml_err(input->xml, "Bad resource"); return I_NULL; } id_from_cib = crm_element_value(xml_rsc, XML_ATTR_ID); CRM_DEV_ASSERT(id_from_cib != NULL); if(crm_assert_failed) { crm_err("No value for %s in %s", XML_ATTR_ID, crm_element_name(xml_rsc)); crm_log_xml_err(input->xml, "Bad command"); return I_NULL; } /* only the first 16 chars are used by the LRM */ strncpy(rid, id_from_cib, 64); rid[63] = 0; crm_debug_2("Retrieving %s from the LRM.", rid); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); if(safe_str_eq(operation, CRMD_ACTION_CANCEL)) { lrm_op_t* op = NULL; crm_data_t *params = NULL; const char *op_key = NULL; const char *op_task = NULL; crm_log_xml_debug(input->xml, "CancelOp"); op_key = crm_element_value(xml_rsc, "operation_key"); params = find_xml_node(input->xml, XML_TAG_ATTRS,TRUE); if(params != NULL) { op_task = crm_element_value(params, "task"); } op = construct_op(input->xml, id_from_cib, op_task); CRM_ASSERT(op != NULL); if(op_key == NULL) { crm_err("No operation to cancel"); crm_log_message(LOG_ERR, input->msg); } else if(rsc != NULL) { cancel_monitor(rsc, op_key); } op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; send_direct_ack(from_host, from_sys, op, id_from_cib); free_lrm_op(op); } else if(safe_str_eq(operation, CRMD_ACTION_DELETE)) { int rc = HA_OK; lrm_op_t* op = NULL; op = construct_op(input->xml, id_from_cib, operation); CRM_ASSERT(op != NULL); op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; if(rsc == NULL) { crm_debug("Resource %s was already removed", id_from_cib); } else { crm_info("Removing resource %s from the LRM", rsc->id); rc = fsa_lrm_conn->lrm_ops->delete_rsc( fsa_lrm_conn, rid); if(rc != HA_OK) { crm_err("Failed to remove resource %s", rid); op->op_status = LRM_OP_ERROR; op->rc = EXECRA_UNKNOWN_ERROR; } } send_direct_ack(from_host, from_sys, op, id_from_cib); free_lrm_op(op); } else { next_input = do_lrm_rsc_op( rsc, rid, operation, input->xml, input->msg); } lrm_free_rsc(rsc); } else { crm_err("Operation was neither a lrm_query, nor a rsc op. %s", crm_str(crm_op)); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); } return next_input; } lrm_op_t * construct_op(crm_data_t *rsc_op, const char *rsc_id, const char *operation) { lrm_op_t *op = NULL; const char *transition = NULL; CRM_DEV_ASSERT(rsc_id != NULL); crm_malloc0(op, sizeof(lrm_op_t)); op->op_type = crm_strdup(operation); op->op_status = LRM_OP_PENDING; op->rc = -1; op->rsc_id = crm_strdup(rsc_id); op->interval = 0; op->timeout = 0; op->start_delay = 0; op->app_name = crm_strdup(CRM_SYSTEM_CRMD); if(rsc_op == NULL) { CRM_DEV_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation)); op->user_data = NULL; op->user_data_len = 0; /* the stop_all_resources() case * by definition there is no DC (or they'd be shutting * us down). * So we should put our version here. */ op->params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); g_hash_table_insert(op->params, crm_strdup(XML_ATTR_CRM_VERSION), crm_strdup(CRM_FEATURE_SET)); crm_debug_2("Constructed %s op for %s", operation, rsc_id); return op; } op->params = xml2list(rsc_op); #if CRM_DEPRECATED_SINCE_2_0_3 if(g_hash_table_lookup(op->params, XML_ATTR_CRM_VERSION) == NULL) { g_hash_table_destroy(op->params); op->params = xml2list_202(rsc_op); } #endif if(op->params == NULL) { CRM_DEV_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation)); } op->interval = crm_parse_int( g_hash_table_lookup(op->params, "interval"), "0"); op->timeout = crm_parse_int( g_hash_table_lookup(op->params, "timeout"), "0"); op->start_delay = crm_parse_int( g_hash_table_lookup(op->params, "start_delay"), "0"); /* sanity */ if(op->interval < 0) { op->interval = 0; g_hash_table_replace( op->params, crm_strdup("interval"), crm_strdup("0")); } if(op->timeout < 0) { op->timeout = 0; g_hash_table_replace( op->params, crm_strdup("timeout"), crm_strdup("0")); } if(op->start_delay < 0) { op->start_delay = 0; g_hash_table_replace( op->params, crm_strdup("start_delay"), crm_strdup("0")); } transition = crm_element_value(rsc_op, XML_ATTR_TRANSITION_KEY); CRM_CHECK(transition != NULL, return op); op->user_data = crm_strdup(transition); op->user_data_len = 1+strlen(op->user_data); if(safe_str_eq(operation, CRMD_ACTION_START) || safe_str_eq(operation, CRMD_ACTION_STOP)) { char *interval_s = g_hash_table_lookup(op->params, "interval"); CRM_CHECK(op->interval == 0, return NULL); CRM_CHECK(interval_s == NULL, return NULL); } crm_debug_2("Constructed %s op for %s: interval=%d", operation, rsc_id, op->interval); return op; } void send_direct_ack(const char *to_host, const char *to_sys, lrm_op_t* op, const char *rsc_id) { HA_Message *reply = NULL; crm_data_t *update, *iter; crm_data_t *fragment; CRM_DEV_ASSERT(op != NULL); if(crm_assert_failed) { return; } if(op->rsc_id == NULL) { CRM_DEV_ASSERT(rsc_id != NULL); op->rsc_id = crm_strdup(rsc_id); } if(to_sys == NULL) { to_sys = CRM_SYSTEM_TENGINE; } crm_info("ACK'ing resource op: %s for %s", op->op_type, op->rsc_id); update = create_node_state( fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__); iter = create_xml_node(update, XML_CIB_TAG_LRM); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE); crm_xml_add(iter, XML_ATTR_ID, op->rsc_id); build_operation_update(iter, op, __FUNCTION__, 0); fragment = create_cib_fragment(update, XML_CIB_TAG_STATUS); reply = create_request(CRM_OP_INVOKE_LRM, fragment, to_host, to_sys, CRM_SYSTEM_LRMD, NULL); crm_debug("Sending ACK: %s", cl_get_string(reply, XML_ATTR_REFERENCE)); crm_log_xml_debug_2(update, "ACK Update"); crm_log_message_adv(LOG_DEBUG_3, "ACK Reply", reply); if(relay_message(reply, TRUE) == FALSE) { crm_log_message_adv(LOG_ERR, "Unable to route reply", reply); crm_msg_del(reply); } free_xml(fragment); free_xml(update); } enum crmd_fsa_input do_lrm_rsc_op(lrm_rsc_t *rsc, char *rid, const char *operation, crm_data_t *msg, HA_Message *request) { int call_id = 0; char *op_id = NULL; lrm_op_t* op = NULL; const char *type = NULL; const char *class = NULL; const char *provider = NULL; const char *transition = NULL; GHashTable *params = NULL; fsa_data_t *msg_data = NULL; CRM_DEV_ASSERT(rid != NULL); if(rsc != NULL) { class = rsc->class; type = rsc->type; } else if(msg != NULL) { crm_data_t *xml_rsc = find_xml_node( msg, XML_CIB_TAG_RESOURCE, TRUE); class = crm_element_value(xml_rsc, XML_AGENT_ATTR_CLASS); CRM_DEV_ASSERT(class != NULL); if(crm_assert_failed) { return I_NULL; } type = crm_element_value(xml_rsc, XML_ATTR_TYPE); CRM_DEV_ASSERT(type != NULL); if(crm_assert_failed) { return I_NULL; } provider = crm_element_value(xml_rsc, XML_AGENT_ATTR_PROVIDER); } if(msg != NULL) { transition = crm_element_value(msg, XML_ATTR_TRANSITION_KEY); if(transition == NULL) { crm_err("Missing transition"); crm_log_message(LOG_ERR, msg); } } if(rsc == NULL) { /* check if its already there */ rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); } if(rsc == NULL) { /* add it to the list */ crm_debug("adding rsc %s before operation", rid); if(msg != NULL) { params = xml2list(msg); #if CRM_DEPRECATED_SINCE_2_0_3 if(g_hash_table_lookup( params, XML_ATTR_CRM_VERSION) == NULL) { g_hash_table_destroy(params); params = xml2list_202(msg); } #endif } fsa_lrm_conn->lrm_ops->add_rsc( fsa_lrm_conn, rid, class, type, provider, params); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); g_hash_table_destroy(params); } if(rsc == NULL) { crm_err("Could not add resource %s to LRM", rid); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); return I_NULL; } /* stop the monitor before stopping the resource */ if(safe_str_eq(operation, CRMD_ACTION_STOP)) { g_hash_table_foreach(monitors, stop_recurring_action, rsc); g_hash_table_foreach_remove( monitors, remove_recurring_action, rsc); } /* now do the op */ op = construct_op(msg, rsc->id, operation); crm_info("Performing op %s on %s (interval=%dms)", operation, rid, op->interval); if((AM_I_DC == FALSE && fsa_state != S_NOT_DC) || (AM_I_DC && fsa_state != S_TRANSITION_ENGINE)) { if(safe_str_neq(operation, CRMD_ACTION_STOP)) { crm_info("Discarding attempt to perform action %s on %s" " in state %s", operation, rid, fsa_state2string(fsa_state)); op->rc = 99; op->op_status = LRM_OP_ERROR; send_direct_ack(NULL, NULL, op, rsc->id); free_lrm_op(op); crm_free(op_id); return I_NULL; } } op_id = generate_op_key(rsc->id, op->op_type, op->interval); if(op->interval > 0) { cancel_monitor(rsc, op_id); op->target_rc = CHANGED; } else { op->target_rc = EVERYTIME; } g_hash_table_replace(resources,crm_strdup(rsc->id), crm_strdup(op_id)); call_id = rsc->ops->perform_op(rsc, op); if(call_id <= 0) { crm_err("Operation %s on %s failed: %d",operation,rid,call_id); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); } else if(op->interval > 0) { struct recurring_op_s *op = NULL; crm_malloc0(op, sizeof(struct recurring_op_s)); crm_debug_2("Adding recurring %s op for %s (%d)", op_id, rsc->id, call_id); op->call_id = call_id; op->rsc_id = crm_strdup(rsc->id); g_hash_table_insert(monitors, op_id, op); op_id = NULL; } else { /* record all non-recurring operations so we can wait * for them to complete during shutdown */ char *call_id_s = make_stop_id(rsc->id, call_id); g_hash_table_replace( shutdown_ops, call_id_s, crm_strdup(rsc->id)); crm_debug_2("Recording pending op: %s/%s %s", rsc->id, operation, call_id_s); } crm_free(op_id); free_lrm_op(op); return I_NULL; } void stop_recurring_action(gpointer key, gpointer value, gpointer user_data) { lrm_rsc_t *rsc = user_data; struct recurring_op_s *op = (struct recurring_op_s*)value; if(safe_str_eq(op->rsc_id, rsc->id)) { if(op->call_id > 0) { crm_debug("Stopping recurring op %d for %s (%s)", op->call_id, rsc->id, (char*)key); rsc->ops->cancel_op(rsc, op->call_id); } else { crm_err("Invalid call_id %d for %s", op->call_id, rsc->id); /* TODO: we probably need to look up the LRM to find it */ } } } gboolean remove_recurring_action(gpointer key, gpointer value, gpointer user_data) { lrm_rsc_t *rsc = user_data; struct recurring_op_s *op = (struct recurring_op_s*)value; if(safe_str_eq(op->rsc_id, rsc->id)) { return TRUE; } return FALSE; } void free_recurring_op(gpointer value) { struct recurring_op_s *op = (struct recurring_op_s*)value; crm_free(op->rsc_id); crm_free(op); } void free_lrm_op(lrm_op_t *op) { g_hash_table_destroy(op->params); crm_free(op->user_data); crm_free(op->output); crm_free(op->rsc_id); crm_free(op->op_type); crm_free(op->app_name); crm_free(op); } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); } lrm_op_t * copy_lrm_op(const lrm_op_t *op) { lrm_op_t *op_copy = NULL; CRM_DEV_ASSERT(op != NULL); if(crm_assert_failed) { return NULL; } CRM_ASSERT(op->rsc_id != NULL); crm_malloc0(op_copy, sizeof(lrm_op_t)); op_copy->op_type = crm_strdup(op->op_type); /* input fields */ op_copy->params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(op->params != NULL) { g_hash_table_foreach(op->params, dup_attr, op_copy->params); } op_copy->timeout = op->timeout; op_copy->interval = op->interval; op_copy->target_rc = op->target_rc; /* in the CRM, this is always a string */ if(op->user_data != NULL) { op_copy->user_data = crm_strdup(op->user_data); } /* output fields */ op_copy->op_status = op->op_status; op_copy->rc = op->rc; op_copy->call_id = op->call_id; op_copy->output = NULL; op_copy->rsc_id = crm_strdup(op->rsc_id); if(op->app_name != NULL) { op_copy->app_name = crm_strdup(op->app_name); } if(op->output != NULL) { op_copy->output = crm_strdup(op->output); } return op_copy; } lrm_rsc_t * copy_lrm_rsc(const lrm_rsc_t *rsc) { lrm_rsc_t *rsc_copy = NULL; if(rsc == NULL) { return NULL; } crm_malloc0(rsc_copy, sizeof(lrm_rsc_t)); rsc_copy->id = crm_strdup(rsc->id); rsc_copy->type = crm_strdup(rsc->type); rsc_copy->class = NULL; rsc_copy->provider = NULL; if(rsc->class != NULL) { rsc_copy->class = crm_strdup(rsc->class); } if(rsc->provider != NULL) { rsc_copy->provider = crm_strdup(rsc->provider); } /* GHashTable* params; */ rsc_copy->params = NULL; rsc_copy->ops = NULL; return rsc_copy; } void do_update_resource(lrm_op_t* op) { /* */ int rc = cib_ok; crm_data_t *fragment; crm_data_t *update, *iter; CRM_DEV_ASSERT(op != NULL); if(crm_assert_failed) { return; } update = create_node_state( fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__); iter = create_xml_node(update, XML_CIB_TAG_LRM); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE); crm_xml_add(iter, XML_ATTR_ID, op->rsc_id); if(op->interval == 0) { lrm_rsc_t *rsc = NULL; crm_debug_2("Updating %s resource definitions after %s op", op->rsc_id, op->op_type); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, op->rsc_id); crm_xml_add(iter, XML_ATTR_TYPE, rsc->type); crm_xml_add(iter, XML_AGENT_ATTR_CLASS, rsc->class); crm_xml_add(iter, XML_AGENT_ATTR_PROVIDER,rsc->provider); lrm_free_rsc(rsc); } build_operation_update(iter, op, __FUNCTION__, 0); fragment = create_cib_fragment(update, XML_CIB_TAG_STATUS); /* make it an asyncronous call and be done with it * * Best case: * the resource state will be discovered during * the next signup or election. * * Bad case: * we are shutting down and there is no DC at the time, * but then why were we shutting down then anyway? * (probably because of an internal error) * * Worst case: * we get shot for having resources "running" when the really weren't * * the alternative however means blocking here for too long, which * isnt acceptable */ rc = fsa_cib_conn->cmds->update( fsa_cib_conn, XML_CIB_TAG_STATUS, fragment, NULL, cib_quorum_override); if(rc > 0) { /* the return code is a call number, not an error code */ crm_debug_3("Sent resource state update message: %d", rc); } else { crm_err("Resource state update failed: %s", cib_error2string(rc)); CRM_DEV_ASSERT(rc == cib_ok); } free_xml(fragment); free_xml(update); } enum crmd_fsa_input do_lrm_event(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input, fsa_data_t *msg_data) { lrm_op_t* op = NULL; const char *last_op = NULL; const char *probe_s = NULL; gboolean is_probe = FALSE; int log_rsc_err = LOG_WARNING; if(msg_data->fsa_cause != C_LRM_OP_CALLBACK) { register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); return I_NULL; } op = fsa_typed_data(fsa_dt_lrm); CRM_DEV_ASSERT(op != NULL); CRM_DEV_ASSERT(op != NULL && op->rsc_id != NULL); if(crm_assert_failed) { return I_NULL; } probe_s = g_hash_table_lookup(op->params, XML_ATTR_LRM_PROBE); is_probe = crm_is_true(probe_s); switch(op->op_status) { case LRM_OP_PENDING: /* this really shouldnt happen */ crm_err("LRM operation (%d) %s_%d on %s %s: %s", op->call_id, op->op_type, op->interval, crm_str(op->rsc_id), op_status2text(op->op_status), execra_code2string(op->rc)); break; case LRM_OP_ERROR: if(is_probe) { log_rsc_err = LOG_INFO; } crm_log_maybe(log_rsc_err, "LRM operation (%d) %s_%d on %s %s: (%d) %s", op->call_id, op->op_type, op->interval, crm_str(op->rsc_id), op_status2text(op->op_status), op->rc, execra_code2string(op->rc)); if(op->output != NULL) { crm_debug("Result: %s", op->output); } break; case LRM_OP_CANCELLED: crm_warn("LRM operation (%d) %s_%d on %s %s", op->call_id, op->op_type, op->interval, crm_str(op->rsc_id), op_status2text(op->op_status)); return I_NULL; break; case LRM_OP_TIMEOUT: last_op = g_hash_table_lookup( resources_confirmed, crm_strdup(op->rsc_id)); if(safe_str_eq(last_op, CRMD_ACTION_STOP) && safe_str_eq(op->op_type, CRMD_ACTION_MON)) { crm_err("LRM sent a timed out %s operation" " _after_ a confirmed stop", op->op_type); return I_NULL; } crm_err("LRM operation (%d) %s_%d on %s %s", op->call_id, op->op_type, op->interval, crm_str(op->rsc_id), op_status2text(op->op_status)); break; case LRM_OP_NOTSUPPORTED: crm_err("LRM operation (%d) %s_%d on %s %s", op->call_id, op->op_type, op->interval, crm_str(op->rsc_id), op_status2text(op->op_status)); break; case LRM_OP_DONE: crm_info("LRM operation (%d) %s_%d on %s %s", op->call_id, op->op_type, op->interval, crm_str(op->rsc_id), op_status2text(op->op_status)); break; } g_hash_table_replace(resources_confirmed, crm_strdup(op->rsc_id), crm_strdup(op->op_type)); do_update_resource(op); if(g_hash_table_size(shutdown_ops) > 0) { char *op_id = make_stop_id(op->rsc_id, op->call_id); if(g_hash_table_remove(shutdown_ops, op_id)) { crm_debug_2("Op %d (%s %s) confirmed", op->call_id, op->op_type, op->rsc_id); } else if(op->interval == 0) { crm_err("Op %d (%s %s) not matched: %s", op->call_id, op->op_type, op->rsc_id, op_id); } crm_free(op_id); } if(is_set(fsa_input_register, R_SENT_RSC_STOP)) { if(g_hash_table_size(shutdown_ops) == 0) { register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL); } else { crm_info("Still waiting for %d pending stop operations" " to complete before exiting", g_hash_table_size(shutdown_ops)); g_hash_table_foreach( shutdown_ops, ghash_print_pending, NULL); } } return I_NULL; } char * make_stop_id(const char *rsc, int call_id) { char *op_id = NULL; crm_malloc0(op_id, strlen(rsc) + 34); if(op_id != NULL) { snprintf(op_id, strlen(rsc) + 34, "%s:%d", rsc, call_id); } return op_id; } void ghash_print_pending(gpointer key, gpointer value, gpointer user_data) { const char *uname = key; crm_debug("Pending action: %s", uname); } gboolean resource_stopped(gpointer key, gpointer value, gpointer user_data) { const char *this_rsc = value; const char *target_rsc = user_data; if(safe_str_eq(this_rsc, target_rsc)) { return TRUE; } return FALSE; } diff --git a/crm/pengine/unpack.c b/crm/pengine/unpack.c index 6f028bcfd0..a4fbdd6f61 100644 --- a/crm/pengine/unpack.c +++ b/crm/pengine/unpack.c @@ -1,2089 +1,2089 @@ -/* $Id: unpack.c,v 1.173 2006/03/31 12:17:27 andrew Exp $ */ +/* $Id: unpack.c,v 1.174 2006/03/31 12:45:02 andrew Exp $ */ /* * 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 /* for ONLINESTATUS */ #include #include #include gint sort_op_by_callid(gconstpointer a, gconstpointer b); gboolean unpack_rsc_to_attr(crm_data_t *xml_obj, pe_working_set_t *data_set); gboolean unpack_rsc_to_node(crm_data_t *xml_obj, pe_working_set_t *data_set); gboolean unpack_rsc_order(crm_data_t *xml_obj, pe_working_set_t *data_set); gboolean unpack_rsc_colocation(crm_data_t *xml_obj, pe_working_set_t *data_set); gboolean unpack_rsc_location(crm_data_t *xml_obj, pe_working_set_t *data_set); gboolean unpack_lrm_resources( node_t *node, crm_data_t * lrm_state, pe_working_set_t *data_set); gboolean add_node_attrs( crm_data_t * attrs, node_t *node, pe_working_set_t *data_set); gboolean unpack_rsc_op( resource_t *rsc, node_t *node, crm_data_t *xml_op, int *max_call_id, enum action_fail_response *failed, pe_working_set_t *data_set); gboolean determine_online_status( crm_data_t * node_state, node_t *this_node, pe_working_set_t *data_set); gboolean rsc_colocation_new( const char *id, enum con_strength strength, resource_t *rsc_lh, resource_t *rsc_rh, const char *state_lh, const char *state_rh); gboolean create_ordering( const char *id, enum con_strength strength, resource_t *rsc_lh, resource_t *rsc_rh, pe_working_set_t *data_set); const char *param_value( GHashTable *hash, crm_data_t * parent, const char *name); rsc_to_node_t *generate_location_rule( resource_t *rsc, crm_data_t *location_rule, pe_working_set_t *data_set); #define get_cluster_pref(pref) value = g_hash_table_lookup(config_hash, pref); \ if(value == NULL) { \ pe_config_warn("No value specified for cluster preference: %s", pref); \ } gboolean unpack_config(crm_data_t * config, pe_working_set_t *data_set) { /* const char *attr_filter[] = { */ /* "default_resource_stickiness", */ /* "transition_idle_timeout", */ /* "stonith_enabled", */ /* "symmetric_cluster" */ /* }; */ const char *value = NULL; GHashTable *config_hash = g_hash_table_new_full( g_str_hash,g_str_equal, g_hash_destroy_str,g_hash_destroy_str); data_set->config_hash = config_hash; unpack_instance_attributes( config, "cluster_property_set", NULL, config_hash, NULL, 0, data_set); #if CRM_DEPRECATED_SINCE_2_0_1 param_value(config_hash, config, "transition_idle_timeout"); param_value(config_hash, config, "default_resource_stickiness"); param_value(config_hash, config, "default_resource_failure_stickiness"); param_value(config_hash, config, "stonith_enabled"); param_value(config_hash, config, "symmetric_cluster"); param_value(config_hash, config, "no_quorum_policy"); param_value(config_hash, config, "stop_orphan_resources"); param_value(config_hash, config, "stop_orphan_actions"); param_value(config_hash, config, "remove_after_stop"); param_value(config_hash, config, "is_managed_default"); param_value(config_hash, config, "short_resource_names"); param_value(config_hash, config, "stonith_action"); #endif get_cluster_pref("transition_idle_timeout"); if(value != NULL) { long tmp = crm_get_msec(value); if(tmp > 0) { crm_free(data_set->transition_idle_timeout); data_set->transition_idle_timeout = crm_strdup(value); } else { crm_err("Invalid value for transition_idle_timeout: %s", value); } } crm_debug("%s set to: %s", "transition_idle_timeout", data_set->transition_idle_timeout); get_cluster_pref("default_"XML_RSC_ATTR_STICKINESS); data_set->default_resource_stickiness = char2score(value); crm_info("Default stickiness: %d", data_set->default_resource_stickiness); get_cluster_pref("default_"XML_RSC_ATTR_FAIL_STICKINESS); data_set->default_resource_fail_stickiness = char2score(value); crm_info("Default failure stickiness: %d", data_set->default_resource_fail_stickiness); get_cluster_pref("stonith_enabled"); if(value != NULL) { cl_str_to_boolean(value, &data_set->stonith_enabled); } crm_info("STONITH of failed nodes is %s", data_set->stonith_enabled?"enabled":"disabled"); get_cluster_pref("stonith_action"); if(value == NULL || safe_str_neq(value, "poweroff")) { value = "reboot"; } data_set->stonith_action = value; crm_info("STONITH will %s nodes", data_set->stonith_action); get_cluster_pref("symmetric_cluster"); if(value != NULL) { cl_str_to_boolean(value, &data_set->symmetric_cluster); } if(data_set->symmetric_cluster) { crm_info("Cluster is symmetric" " - resources can run anywhere by default"); } get_cluster_pref("short_resource_names"); if(value != NULL) { cl_str_to_boolean(value, &data_set->short_rsc_names); } crm_info("Using short resource names: %s", data_set->short_rsc_names?"true":"false"); get_cluster_pref("no_quorum_policy"); if(safe_str_eq(value, "ignore")) { data_set->no_quorum_policy = no_quorum_ignore; } else if(safe_str_eq(value, "freeze")) { data_set->no_quorum_policy = no_quorum_freeze; } else { data_set->no_quorum_policy = no_quorum_stop; } switch (data_set->no_quorum_policy) { case no_quorum_freeze: crm_info("On loss of CCM Quorum: Freeze resources"); break; case no_quorum_stop: crm_info("On loss of CCM Quorum: Stop ALL resources"); break; case no_quorum_ignore: crm_notice("On loss of CCM Quorum: Ignore"); break; } get_cluster_pref("stop_orphan_resources"); if(value != NULL) { cl_str_to_boolean(value, &data_set->stop_rsc_orphans); } crm_info("Orphan resources are %s", data_set->stop_rsc_orphans?"stopped":"ignored"); get_cluster_pref("stop_orphan_actions"); if(value != NULL) { cl_str_to_boolean(value, &data_set->stop_action_orphans); } crm_info("Orphan resource actions are %s", data_set->stop_action_orphans?"stopped":"ignored"); get_cluster_pref("remove_after_stop"); if(value != NULL) { cl_str_to_boolean(value, &data_set->remove_after_stop); } crm_info("Stopped resources are removed from the status section: %s", data_set->remove_after_stop?"true":"false"); get_cluster_pref("is_managed_default"); if(value != NULL) { cl_str_to_boolean(value, &data_set->is_managed_default); } crm_info("By default resources are %smanaged", data_set->is_managed_default?"":"not "); return TRUE; } const char * param_value(GHashTable *hash, crm_data_t * parent, const char *name) { const char *value = NULL; const char *pref_name = NULL; crm_data_t * a_default = NULL; if(parent != NULL) { crm_validate_data(parent); xml_child_iter_filter( parent, a_child, XML_CIB_TAG_NVPAIR, pref_name = crm_element_value(a_child, XML_NVPAIR_ATTR_NAME); if(safe_str_eq(name, pref_name)) { a_default = a_child; break; } ); } if(a_default == NULL) { return NULL; } value = crm_element_value(a_default, XML_NVPAIR_ATTR_VALUE); if(value && hash) { if(g_hash_table_lookup(hash, name) == NULL) { g_hash_table_insert( hash, crm_strdup(name), crm_strdup(value)); } } return value; } gboolean unpack_nodes(crm_data_t * xml_nodes, pe_working_set_t *data_set) { node_t *new_node = NULL; const char *id = NULL; const char *uname = NULL; const char *type = NULL; crm_debug_2("Begining unpack... %s", xml_nodes?crm_element_name(xml_nodes):""); xml_child_iter_filter( xml_nodes, xml_obj, XML_CIB_TAG_NODE, new_node = NULL; id = crm_element_value(xml_obj, XML_ATTR_ID); uname = crm_element_value(xml_obj, XML_ATTR_UNAME); type = crm_element_value(xml_obj, XML_ATTR_TYPE); crm_debug_3("Processing node %s/%s", uname, id); if(id == NULL) { pe_config_err("Must specify id tag in "); continue; } if(type == NULL) { pe_config_err("Must specify type tag in "); continue; } crm_malloc0(new_node, sizeof(node_t)); if(new_node == NULL) { return FALSE; } new_node->weight = 0; new_node->fixed = FALSE; crm_malloc0(new_node->details, sizeof(struct node_shared_s)); if(new_node->details == NULL) { crm_free(new_node); return FALSE; } crm_debug_3("Creaing node for entry %s/%s", uname, id); new_node->details->id = id; new_node->details->uname = uname; new_node->details->type = node_ping; new_node->details->online = FALSE; new_node->details->shutdown = FALSE; new_node->details->running_rsc = NULL; new_node->details->attrs = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); /* if(data_set->have_quorum == FALSE */ /* && data_set->no_quorum_policy == no_quorum_stop) { */ /* /\* start shutting resources down *\/ */ /* new_node->weight = -INFINITY; */ /* } */ if(data_set->stonith_enabled) { /* all nodes are unclean until we've seen their * status entry */ new_node->details->unclean = TRUE; } else { /* blind faith... */ new_node->details->unclean = FALSE; } if(type == NULL || safe_str_eq(type, "member") || safe_str_eq(type, NORMALNODE)) { new_node->details->type = node_member; } add_node_attrs(xml_obj, new_node, data_set); if(crm_is_true(g_hash_table_lookup( new_node->details->attrs, "standby"))) { crm_info("Node %s is in standby-mode", new_node->details->uname); new_node->weight = -INFINITY; new_node->details->standby = TRUE; } data_set->nodes = g_list_append(data_set->nodes, new_node); crm_debug_3("Done with node %s", crm_element_value(xml_obj, XML_ATTR_UNAME)); crm_action_debug_3(print_node("Added", new_node, FALSE)); ); /* data_set->nodes = g_list_sort(data_set->nodes, sort_node_weight); */ return TRUE; } gboolean unpack_resources(crm_data_t * xml_resources, pe_working_set_t *data_set) { crm_debug_2("Begining unpack... %s", xml_resources?crm_element_name(xml_resources):""); xml_child_iter( xml_resources, xml_obj, resource_t *new_rsc = NULL; crm_debug_2("Begining unpack... %s", xml_obj?crm_element_name(xml_obj):""); if(common_unpack(xml_obj, &new_rsc, NULL, data_set)) { data_set->resources = g_list_append( data_set->resources, new_rsc); print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); } else { pe_config_err("Failed unpacking %s %s", crm_element_name(xml_obj), crm_element_value(xml_obj, XML_ATTR_ID)); } ); data_set->resources = g_list_sort( data_set->resources, sort_rsc_priority); return TRUE; } gboolean unpack_constraints(crm_data_t * xml_constraints, pe_working_set_t *data_set) { crm_data_t *lifetime = NULL; crm_debug_2("Begining unpack... %s", xml_constraints?crm_element_name(xml_constraints):""); xml_child_iter( xml_constraints, xml_obj, const char *id = crm_element_value(xml_obj, XML_ATTR_ID); if(id == NULL) { pe_config_err("Constraint <%s...> must have an id", crm_element_name(xml_obj)); continue; } crm_debug_3("Processing constraint %s %s", crm_element_name(xml_obj),id); lifetime = cl_get_struct(xml_obj, "lifetime"); if(test_ruleset(lifetime, NULL, data_set) == FALSE) { crm_info("Constraint %s %s is not active", crm_element_name(xml_obj), id); } else if(safe_str_eq(XML_CONS_TAG_RSC_ORDER, crm_element_name(xml_obj))) { unpack_rsc_order(xml_obj, data_set); } else if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) { unpack_rsc_colocation(xml_obj, data_set); } else if(safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) { unpack_rsc_location(xml_obj, data_set); } else { pe_err("Unsupported constraint type: %s", crm_element_name(xml_obj)); } ); return TRUE; } /* remove nodes that are down, stopping */ /* create +ve rsc_to_node constraints between resources and the nodes they are running on */ /* anything else? */ gboolean unpack_status(crm_data_t * status, pe_working_set_t *data_set) { const char *id = NULL; const char *uname = NULL; crm_data_t * lrm_rsc = NULL; crm_data_t * attrs = NULL; node_t *this_node = NULL; crm_debug_3("Begining unpack"); xml_child_iter_filter( status, node_state, XML_CIB_TAG_STATE, id = crm_element_value(node_state, XML_ATTR_ID); uname = crm_element_value(node_state, XML_ATTR_UNAME); attrs = find_xml_node( node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE); lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); crm_debug_3("Processing node %s", uname); this_node = pe_find_node_id(data_set->nodes, id); if(uname == NULL) { /* error */ continue; } else if(this_node == NULL) { pe_config_warn("Node %s in status section no longer exists", uname); continue; } /* Mark the node as provisionally clean * - at least we have seen it in the current cluster's lifetime */ this_node->details->unclean = FALSE; crm_debug_3("Adding runtime node attrs"); add_node_attrs(attrs, this_node, data_set); crm_debug_3("determining node state"); determine_online_status(node_state, this_node, data_set); if(this_node->details->online || data_set->stonith_enabled) { /* offline nodes run no resources... * unless stonith is enabled in which case we need to * make sure rsc start events happen after the stonith */ crm_debug_3("Processing lrm resource entries"); unpack_lrm_resources(this_node, lrm_rsc, data_set); } ); return TRUE; } static gboolean determine_online_status_no_fencing(crm_data_t * node_state, node_t *this_node) { gboolean online = FALSE; const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE); const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE); const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM); const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE); const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); if(!crm_is_true(ccm_state) || safe_str_eq(ha_state,DEADSTATUS)){ crm_debug_2("Node is down: ha_state=%s, ccm_state=%s", crm_str(ha_state), crm_str(ccm_state)); } else if(!crm_is_true(ccm_state) || safe_str_eq(ha_state, DEADSTATUS)) { } else if(safe_str_neq(join_state, CRMD_JOINSTATE_DOWN) && safe_str_eq(crm_state, ONLINESTATUS)) { online = TRUE; } else if(this_node->details->expected_up == FALSE) { crm_debug_2("CRMd is down: ha_state=%s, ccm_state=%s", crm_str(ha_state), crm_str(ccm_state)); crm_debug_2("\tcrm_state=%s, join_state=%s, expected=%s", crm_str(crm_state), crm_str(join_state), crm_str(exp_state)); } else { /* mark it unclean */ this_node->details->unclean = TRUE; crm_warn("Node %s is partially & un-expectedly down", this_node->details->uname); crm_info("\tha_state=%s, ccm_state=%s," " crm_state=%s, join_state=%s, expected=%s", crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state), crm_str(join_state), crm_str(exp_state)); } return online; } static gboolean determine_online_status_fencing(crm_data_t * node_state, node_t *this_node) { gboolean online = FALSE; const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE); const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE); const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM); const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE); const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); if(crm_is_true(ccm_state) && (ha_state == NULL || safe_str_eq(ha_state, ACTIVESTATUS)) && safe_str_eq(crm_state, ONLINESTATUS) && safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { online = TRUE; } else if(crm_is_true(ccm_state) == FALSE /* && safe_str_eq(ha_state, DEADSTATUS) */ && safe_str_eq(crm_state, OFFLINESTATUS) && this_node->details->expected_up == FALSE) { crm_debug("Node %s is down: join_state=%s, expected=%s", this_node->details->uname, crm_str(join_state), crm_str(exp_state)); } else if(this_node->details->expected_up == FALSE) { crm_info("Node %s is comming up", this_node->details->uname); crm_debug("\tha_state=%s, ccm_state=%s," " crm_state=%s, join_state=%s, expected=%s", crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state), crm_str(join_state), crm_str(exp_state)); } else { /* mark it unclean */ this_node->details->unclean = TRUE; crm_warn("Node %s (%s)is un-expectedly down", this_node->details->uname, this_node->details->id); crm_info("\tha_state=%s, ccm_state=%s," " crm_state=%s, join_state=%s, expected=%s", crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state), crm_str(join_state), crm_str(exp_state)); } return online; } gboolean determine_online_status( crm_data_t * node_state, node_t *this_node, pe_working_set_t *data_set) { int shutdown = 0; gboolean online = FALSE; const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); if(this_node == NULL) { pe_config_err("No node to check"); return online; } ha_msg_value_int(node_state, XML_CIB_ATTR_SHUTDOWN, &shutdown); this_node->details->expected_up = FALSE; if(safe_str_eq(exp_state, CRMD_JOINSTATE_MEMBER)) { this_node->details->expected_up = TRUE; } this_node->details->shutdown = FALSE; if(shutdown != 0) { this_node->details->shutdown = TRUE; this_node->details->expected_up = FALSE; } if(data_set->stonith_enabled == FALSE) { online = determine_online_status_no_fencing( node_state, this_node); } else { online = determine_online_status_fencing( node_state, this_node); } if(online) { crm_debug_2("Node %s is online", this_node->details->uname); this_node->details->online = TRUE; } else { /* remove node from contention */ this_node->fixed = TRUE; this_node->weight = -INFINITY; crm_debug_2("Node %s is down", this_node->details->uname); } if(online && this_node->details->shutdown) { /* dont run resources here */ this_node->fixed = TRUE; this_node->weight = -INFINITY; crm_debug_2("Node %s is due for shutdown", this_node->details->uname); } if(this_node->details->unclean) { pe_proc_warn("Node %s is unclean", this_node->details->uname); } return online; } #define set_char(x) last_rsc_id[len] = x; complete = TRUE; static void increment_clone(char *last_rsc_id) { gboolean complete = FALSE; int len = 0; CRM_CHECK(last_rsc_id != NULL, return); if(last_rsc_id != NULL) { len = strlen(last_rsc_id); } len--; while(complete == FALSE && len > 0) { switch (last_rsc_id[len]) { case 0: len--; break; case '0': set_char('1'); break; case '1': set_char('2'); break; case '2': set_char('3'); break; case '3': set_char('4'); break; case '4': set_char('5'); break; case '5': set_char('6'); break; case '6': set_char('7'); break; case '7': set_char('8'); break; case '8': set_char('9'); break; case '9': last_rsc_id[len] = '0'; len--; break; default: crm_err("Unexpected char: %c (%d)", last_rsc_id[len], len); break; } } } extern gboolean DeleteRsc(resource_t *rsc, node_t *node, pe_working_set_t *data_set); static resource_t * unpack_find_resource( pe_working_set_t *data_set, node_t *node, const char *rsc_id) { resource_t *rsc = NULL; gboolean is_duped_clone = FALSE; char *alt_rsc_id = crm_strdup(rsc_id); while(rsc == NULL) { crm_debug_3("looking for: %s", alt_rsc_id); rsc = pe_find_resource(data_set->resources, alt_rsc_id); /* no match */ if(rsc == NULL) { crm_debug_3("not found"); break; /* not running anywhere else */ } else if(rsc->running_on == NULL) { crm_debug_3("not active yet"); break; /* always unique */ } else if(rsc->globally_unique) { crm_debug_3("unique"); break; /* running somewhere already but we dont care * find another clone instead */ } else { crm_debug_2("find another one"); rsc = NULL; is_duped_clone = TRUE; increment_clone(alt_rsc_id); } } crm_free(alt_rsc_id); if(is_duped_clone && rsc != NULL) { crm_info("Internally renamed %s on %s to %s", rsc_id, node->details->uname, rsc->id); /* rsc->name = rsc_id; */ } return rsc; } static resource_t * process_orphan_resource(crm_data_t *rsc_entry, node_t *node, pe_working_set_t *data_set) { resource_t *rsc = NULL; gboolean is_duped_clone = FALSE; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); crm_data_t *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE); crm_log_xml_info(rsc_entry, "Orphan resource"); pe_config_warn("Nothing known about resource %s running on %s", rsc_id, node->details->uname); if(pe_find_resource(data_set->resources, rsc_id) != NULL) { is_duped_clone = TRUE; } copy_in_properties(xml_rsc, rsc_entry); common_unpack(xml_rsc, &rsc, NULL, data_set); rsc->orphan = TRUE; data_set->resources = g_list_append(data_set->resources, rsc); if(data_set->stop_rsc_orphans == FALSE && is_duped_clone == FALSE) { rsc->is_managed = FALSE; } else { crm_info("Making sure orphan %s is stopped", rsc_id); print_resource(LOG_DEBUG_3, "Added orphan", rsc, FALSE); CRM_CHECK(rsc != NULL, return NULL); slist_iter( any_node, node_t, data_set->nodes, lpc, rsc2node_new( "__orphan_dont_run__", rsc, -INFINITY, any_node, data_set); ); } return rsc; } static gboolean check_rsc_parameters(resource_t *rsc, node_t *node, crm_data_t *rsc_entry, pe_working_set_t *data_set) { int attr_lpc = 0; gboolean force_restart = FALSE; gboolean delete_resource = FALSE; const char *value = NULL; const char *old_value = NULL; const char *attr_list[] = { XML_ATTR_TYPE, XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER }; for(; attr_lpc < DIMOF(attr_list); attr_lpc++) { value = crm_element_value(rsc->xml, attr_list[attr_lpc]); old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]); if(safe_str_eq(value, old_value)) { continue; } force_restart = TRUE; crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s", rsc->id, node->details->uname, attr_list[attr_lpc], crm_str(old_value), crm_str(value)); } if(force_restart) { /* make sure the restart happens */ stop_action(rsc, node, FALSE); rsc->start_pending = TRUE; delete_resource = TRUE; } return delete_resource; } static void process_rsc_state(resource_t *rsc, node_t *node, enum action_fail_response on_fail, pe_working_set_t *data_set) { crm_debug_2("Resource %s is %s on %s", rsc->id, role2text(rsc->role), node->details->uname); rsc->known_on = g_list_append(rsc->known_on, node); if(rsc->role != RSC_ROLE_STOPPED) { if(on_fail != action_fail_ignore) { rsc->failed = TRUE; crm_debug_2("Force stop"); } crm_debug_2("Adding %s to %s", rsc->id, node->details->uname); native_add_running(rsc, node, data_set); if(on_fail == action_fail_ignore) { /* nothing to do */ } else if(node->details->unclean) { stop_action(rsc, node, FALSE); } else if(on_fail == action_fail_fence) { /* treat it as if it is still running * but also mark the node as unclean */ node->details->unclean = TRUE; stop_action(rsc, node, FALSE); } else if(on_fail == action_fail_block) { /* is_managed == FALSE will prevent any * actions being sent for the resource */ rsc->is_managed = FALSE; } else if(on_fail == action_fail_migrate) { stop_action(rsc, node, FALSE); /* make sure it comes up somewhere else * or not at all */ rsc2node_new("__action_migration_auto__", rsc, -INFINITY, node, data_set); } else { stop_action(rsc, node, FALSE); } } else { char *key = stop_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, node); slist_iter(stop, action_t, possible_matches, lpc, stop->optional = TRUE; ); crm_free(key); /* if(rsc->failed == FALSE && node->details->online) { */ /* delete_resource = TRUE; */ /* } */ } } static void unpack_lrm_rsc_state( node_t *node, crm_data_t * rsc_entry, pe_working_set_t *data_set) { int fail_count = 0; char *fail_attr = NULL; const char *fail_val = NULL; gboolean delete_resource = FALSE; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); const char *rsc_state = crm_element_value(rsc_entry, XML_LRM_ATTR_RSCSTATE); int max_call_id = -1; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; enum action_fail_response on_fail = FALSE; enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN; resource_t *rsc = unpack_find_resource(data_set, node, rsc_id); crm_debug_3("[%s] Processing %s on %s (%s)", crm_element_name(rsc_entry), rsc_id, node->details->uname, rsc_state); if(rsc == NULL) { rsc = process_orphan_resource(rsc_entry, node, data_set); } CRM_ASSERT(rsc != NULL); delete_resource = check_rsc_parameters(rsc, node, rsc_entry, data_set); /* process failure stickiness */ fail_count = 0; fail_attr = crm_concat("fail-count", rsc->id, '-'); fail_val = g_hash_table_lookup(node->details->attrs, fail_attr); if(fail_val != NULL) { crm_debug("%s: %s", fail_attr, fail_val); fail_count = crm_parse_int(fail_val, "0"); } crm_free(fail_attr); if(fail_count > 0 && rsc->fail_stickiness != 0) { rsc2node_new("fail_stickiness", rsc, fail_count * rsc->fail_stickiness, node, data_set); crm_debug("Setting failure stickiness for %s on %s: %d", rsc->id, node->details->uname, fail_count * rsc->fail_stickiness); } /* process operations */ max_call_id = -1; op_list = NULL; sorted_op_list = NULL; xml_child_iter_filter( rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, op_list = g_list_append(op_list, rsc_op); ); if(op_list != NULL) { saved_role = rsc->role; on_fail = action_fail_ignore; rsc->role = RSC_ROLE_STOPPED; sorted_op_list = g_list_sort(op_list, sort_op_by_callid); slist_iter( rsc_op, crm_data_t, sorted_op_list, lpc, unpack_rsc_op(rsc, node, rsc_op, &max_call_id, &on_fail, data_set); ); /* no need to free the contents */ g_list_free(sorted_op_list); process_rsc_state(rsc, node, on_fail, data_set); } if(delete_resource) { DeleteRsc(rsc, node, data_set); } if(saved_role > rsc->role) { rsc->role = saved_role; } } gboolean unpack_lrm_resources(node_t *node, crm_data_t * lrm_rsc_list, pe_working_set_t *data_set) { CRM_CHECK(node != NULL, return FALSE); crm_debug_3("Unpacking resources on %s", node->details->uname); xml_child_iter_filter( lrm_rsc_list, rsc_entry, XML_LRM_TAG_RESOURCE, unpack_lrm_rsc_state(node, rsc_entry, data_set); ); return TRUE; } #define sort_return(an_int) crm_free(a_uuid); crm_free(b_uuid); return an_int gint sort_op_by_callid(gconstpointer a, gconstpointer b) { char *a_uuid = NULL; char *b_uuid = NULL; const char *a_task_id = cl_get_string(a, XML_LRM_ATTR_CALLID); const char *b_task_id = cl_get_string(b, XML_LRM_ATTR_CALLID); const char *a_key = cl_get_string(a, XML_ATTR_TRANSITION_MAGIC); const char *b_key = cl_get_string(b, XML_ATTR_TRANSITION_MAGIC); const char *a_xml_id = ID(a); const char *b_xml_id = ID(b); int a_id = -1; int b_id = -1; int a_rc = -1; int b_rc = -1; int a_status = -1; int b_status = -1; int a_call_id = -1; int b_call_id = -1; if(safe_str_eq(a_xml_id, b_xml_id)) { /* We have duplicate lrm_rsc_op entries in the status * section which is unliklely to be a good thing * - we can handle it easily enough, but we need to get * to the bottom of why its happening. */ pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); sort_return(0); } CRM_CHECK(a_task_id != NULL && b_task_id != NULL, sort_return(0)); a_call_id = crm_parse_int(a_task_id, NULL); b_call_id = crm_parse_int(b_task_id, NULL); if(a_call_id == -1 && b_call_id == -1) { /* both are pending ops so it doesnt matter since * stops are never pending */ sort_return(0); } else if(a_call_id >= 0 && a_call_id < b_call_id) { crm_debug_2("%s (%d) < %s (%d) : call id", ID(a), a_call_id, ID(b), b_call_id); sort_return(-1); } else if(b_call_id >= 0 && a_call_id > b_call_id) { crm_debug_2("%s (%d) > %s (%d) : call id", ID(a), a_call_id, ID(b), b_call_id); sort_return(1); } crm_debug_3("%s (%d) == %s (%d) : continuing", ID(a), a_call_id, ID(b), b_call_id); /* now process pending ops */ CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0)); CRM_CHECK(decode_transition_magic( a_key,&a_uuid,&a_id,&a_status, &a_rc), sort_return(0)); CRM_CHECK(decode_transition_magic( b_key,&b_uuid,&b_id,&b_status, &b_rc), sort_return(0)); /* try and determin the relative age of the operation... * some pending operations (ie. a start) may have been supuerceeded * by a subsequent stop * * [a|b]_id == -1 means its a shutdown operation and _always_ comes last */ if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) { /* * some of the logic in here may be redundant... * * if the UUID from the TE doesnt match then one better * be a pending operation. * pending operations dont survive between elections and joins * because we query the LRM directly */ CRM_CHECK(a_call_id == -1 || b_call_id == -1, sort_return(0)); CRM_CHECK(a_call_id >= 0 || b_call_id >= 0, sort_return(0)); if(b_call_id == -1) { crm_debug_2("%s (%d) < %s (%d) : transition + call id", ID(a), a_call_id, ID(b), b_call_id); sort_return(-1); } if(a_call_id == -1) { crm_debug_2("%s (%d) > %s (%d) : transition + call id", ID(a), a_call_id, ID(b), b_call_id); sort_return(1); } } else if((a_id >= 0 && a_id < b_id) || b_id == -1) { crm_debug_2("%s (%d) < %s (%d) : transition", ID(a), a_id, ID(b), b_id); sort_return(-1); } else if((b_id >= 0 && a_id > b_id) || a_id == -1) { crm_debug_2("%s (%d) > %s (%d) : transition", ID(a), a_id, ID(b), b_id); sort_return(1); } /* we should never end up here */ crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default", ID(a), a_call_id, a_id, a_uuid, ID(b), b_call_id, b_id, b_uuid); CRM_CHECK(FALSE, sort_return(0)); } static gboolean check_action_definition(resource_t *rsc, node_t *active_node, crm_data_t *xml_op, pe_working_set_t *data_set) { gboolean did_change = FALSE; crm_data_t *pnow = NULL; GHashTable *local_rsc_params = NULL; char *pnow_digest = NULL; const char *param_digest = NULL; char *local_param_digest = NULL; const char *id = ID(xml_op); const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); action_t *action = custom_action(rsc, crm_strdup(id), task, active_node, TRUE, FALSE, data_set); local_rsc_params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); unpack_instance_attributes( rsc->xml, XML_TAG_ATTR_SETS, active_node, local_rsc_params, NULL, 0, data_set); pnow = create_xml_node(NULL, XML_TAG_PARAMS); g_hash_table_foreach(action->extra, hash2field, pnow); g_hash_table_foreach(rsc->parameters, hash2field, pnow); g_hash_table_foreach(local_rsc_params, hash2field, pnow); filter_action_parameters(pnow); - pnow_digest = calculate_xml_digest(pnow); + pnow_digest = calculate_xml_digest(pnow, TRUE); param_digest = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); if(param_digest == NULL) { crm_data_t *params = find_xml_node(xml_op, XML_TAG_PARAMS, TRUE); crm_data_t *local_params = copy_xml(params); crm_info("Faking parameter digest creation for %s", ID(xml_op)); filter_action_parameters(local_params); - local_param_digest = calculate_xml_digest(local_params); + local_param_digest = calculate_xml_digest(local_params, TRUE); param_digest = local_param_digest; free_xml(local_params); } if(safe_str_neq(pnow_digest, param_digest)) { crm_data_t *params = find_xml_node(xml_op,XML_TAG_PARAMS,FALSE); crm_data_t *local_params = copy_xml(params); filter_action_parameters(local_params); crm_log_xml_err(pnow, "params:calc"); crm_log_xml_err(local_params, "params:used"); free_xml(local_params); did_change = TRUE; crm_info("Parameters to %s action changed: %s vs. %s", id, pnow_digest, param_digest); custom_action(rsc, crm_strdup(id), task, NULL, FALSE, TRUE, data_set); } g_hash_table_destroy(action->extra); crm_free(action->uuid); crm_free(action); free_xml(pnow); crm_free(pnow_digest); crm_free(local_param_digest); g_hash_table_destroy(local_rsc_params); return did_change; } gboolean unpack_rsc_op(resource_t *rsc, node_t *node, crm_data_t *xml_op, int *max_call_id, enum action_fail_response *on_fail, pe_working_set_t *data_set) { const char *id = NULL; const char *task = NULL; const char *task_id = NULL; const char *actual_rc = NULL; /* const char *target_rc = NULL; */ const char *task_status = NULL; const char *interval_s = NULL; int interval = 0; int task_id_i = -1; int task_status_i = -2; int actual_rc_i = 0; action_t *action = NULL; gboolean is_probe = FALSE; gboolean is_stop_action = FALSE; crm_data_t *params = find_xml_node(xml_op, XML_TAG_PARAMS, FALSE); CRM_CHECK(rsc != NULL, return FALSE); CRM_CHECK(node != NULL, return FALSE); CRM_CHECK(xml_op != NULL, return FALSE); id = ID(xml_op); task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); task_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); CRM_CHECK(id != NULL, return FALSE); CRM_CHECK(task != NULL, return FALSE); CRM_CHECK(task_status != NULL, return FALSE); task_status_i = crm_parse_int(task_status, NULL); CRM_CHECK(task_status_i <= LRM_OP_ERROR, return FALSE); CRM_CHECK(task_status_i >= LRM_OP_PENDING, return FALSE); if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { /* safe to ignore these */ return TRUE; } crm_debug_2("Unpacking task %s/%s (call_id=%s, status=%s) on %s (role=%s)", id, task, task_id, task_status, node->details->uname, role2text(rsc->role)); if(params != NULL) { interval_s = crm_element_value(params, "interval"); if(interval_s != NULL) { interval = crm_parse_int(interval_s, NULL); } } if(interval == 0 && safe_str_eq(task, CRMD_ACTION_STATUS)) { is_probe = TRUE; } if(rsc->orphan) { crm_debug_2("Skipping param check for orphan: %s %s", rsc->id, task); } else if(safe_str_eq(task, CRMD_ACTION_STOP)) { crm_debug_2("Ignoring stop params: %s", id); } else if(params == NULL) { /* for older test cases */ crm_err("Skipping param check: %s %s", id, task); } else if(is_probe || safe_str_eq(task, CRMD_ACTION_START)) { crm_debug_2("Checking resource definition: %s", rsc->id); check_action_definition(rsc, node, xml_op, data_set); } else if(interval > 0 && data_set->stop_action_orphans) { crm_data_t *op_match = NULL; crm_debug_2("Checking parameters for %s %s", id, task); xml_child_iter_filter( rsc->ops_xml, operation, "op", int value = 0; const char *name = NULL; value = crm_get_msec( crm_element_value(operation, "interval")); if(interval <= 0) { break; } else if(value != interval) { continue; } name = crm_element_value(operation, "name"); if(safe_str_neq(name, task)) { continue; } op_match = operation; ); if(op_match == NULL && interval > 0 && data_set->stop_action_orphans) { /* create a cancel action */ pe_config_warn("Orphan action will be stopped: %s", id); action = custom_action( rsc, crm_strdup(id), CRMD_ACTION_CANCEL, node, FALSE, TRUE, data_set); add_hash_param(action->extra, "interval", interval_s); add_hash_param(action->extra, "task", task); custom_action_order( rsc, NULL, action, rsc, stop_key(rsc), NULL, pe_ordering_optional, data_set); } else if(op_match == NULL && interval > 0) { pe_config_warn("Ignoring orphan action: %s", id); } else { check_action_definition(rsc, node, xml_op, data_set); } } if(safe_str_eq(task, CRMD_ACTION_STOP)) { is_stop_action = TRUE; } if(task_status_i != LRM_OP_PENDING) { task_id_i = crm_parse_int(task_id, "-1"); CRM_CHECK(task_id != NULL, return FALSE); CRM_CHECK(task_id_i >= 0, return FALSE); if(task_id_i == *max_call_id) { crm_debug_2("Already processed this call"); return TRUE; } CRM_CHECK(task_id_i > *max_call_id, return FALSE); } if(*max_call_id < task_id_i) { *max_call_id = task_id_i; } if(node->details->unclean) { crm_debug_2("Node %s (where %s is running) is unclean." " Further action depends on the value of %s", node->details->uname, rsc->id, XML_RSC_ATTR_STOPFAIL); } actual_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); CRM_CHECK(actual_rc != NULL, return FALSE); actual_rc_i = crm_parse_int(actual_rc, NULL); #if 0 /* this wont work anymore now that we dont get the set of params, * we only get the hash of them */ if(params != NULL) { target_rc = crm_element_value(params, XML_ATTR_TE_TARGET_RC); } if(target_rc != NULL && task_status_i != LRM_OP_PENDING) { crm_debug_2("Exit code from %s: %s vs. %s", task, target_rc, actual_rc); if(safe_str_eq(target_rc, actual_rc)) { task_status_i = LRM_OP_DONE; } else { task_status_i = LRM_OP_ERROR; } } #endif if(EXECRA_NOT_RUNNING == actual_rc_i) { if(is_probe) { /* treat these like stops */ is_stop_action = TRUE; } if(is_stop_action) { task_status_i = LRM_OP_DONE; } else { CRM_CHECK(task_status_i == LRM_OP_ERROR, task_status_i = LRM_OP_ERROR); } } else if(EXECRA_RUNNING_MASTER == actual_rc_i) { if(is_probe || (rsc->role == RSC_ROLE_MASTER && safe_str_eq(task, CRMD_ACTION_STATUS))) { task_status_i = LRM_OP_DONE; } else { if(rsc->role != RSC_ROLE_MASTER) { crm_err("%s reported %s in master mode on %s", task, rsc->graph_name, node->details->uname); } CRM_CHECK(task_status_i == LRM_OP_ERROR, task_status_i = LRM_OP_ERROR); } rsc->role = RSC_ROLE_MASTER; } else if(EXECRA_FAILED_MASTER == actual_rc_i) { rsc->role = RSC_ROLE_MASTER; task_status_i = LRM_OP_ERROR; } else if(EXECRA_OK == actual_rc_i && is_probe == FALSE - && rsc->role == RSC_ROLE_MASTER - && safe_str_eq(task, CRMD_ACTION_STATUS)) { + && is_stop_action == FALSE + && rsc->role == RSC_ROLE_MASTER) { /* catch status ops that return 0 instead of 8 while they * are supposed to be in master mode */ task_status_i = LRM_OP_ERROR; } if(task_status_i == LRM_OP_ERROR || task_status_i == LRM_OP_TIMEOUT || task_status_i == LRM_OP_NOTSUPPORTED) { action = custom_action(rsc, crm_strdup(id), task, NULL, TRUE, TRUE, data_set); if(action->on_fail == action_fail_ignore) { task_status_i = LRM_OP_DONE; } } switch(task_status_i) { case LRM_OP_PENDING: /* * TODO: this may need some more thought * Some cases: * - PE reinvoked with pending action that will succeed * - PE reinvoked with pending action that will fail * - After DC election * - After startup * * pending start - required start * pending stop - required stop * pending on unavailable node - stonith * * For now this should do */ if(safe_str_eq(task, CRMD_ACTION_START)) { rsc->start_pending = TRUE; rsc->role = RSC_ROLE_STARTED; /* make sure it is re-issued but, * only if we have quorum */ if(data_set->have_quorum == TRUE || data_set->no_quorum_policy == no_quorum_ignore){ /* do not specify the node, we may want * to start it elsewhere */ start_action(rsc, NULL, FALSE); } } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { rsc->role = RSC_ROLE_MASTER; } else if(rsc->role > RSC_ROLE_STOPPED) { crm_debug_2("Re-issuing pending recurring task:" " %s for %s on %s", task, rsc->id, node->details->id); /* do not specify the node, we may want * to start it elsewhere */ custom_action(rsc, crm_strdup(id), task, NULL, FALSE, TRUE, data_set); } break; case LRM_OP_DONE: crm_debug_3("%s/%s completed on %s", rsc->id, task, node->details->uname); if(is_stop_action) { rsc->role = RSC_ROLE_STOPPED; /* clear any previous failure actions */ *on_fail = action_fail_ignore; rsc->next_role = RSC_ROLE_UNKNOWN; } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { rsc->role = RSC_ROLE_MASTER; } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { rsc->role = RSC_ROLE_SLAVE; } else { /* make sure its already created and is optional * * creating it now tells Recurring() * that it can safely leave it optional */ if(rsc->role < RSC_ROLE_STARTED) { crm_debug_2("%s active on %s", rsc->id, node->details->uname); rsc->role = RSC_ROLE_STARTED; } /* the != start check is so i dont have to * update all the old testcases */ if(interval > 0 || safe_str_neq(task, CRMD_ACTION_START)) { crm_debug_2("%s: %s active on %s", rsc->id, id, node->details->uname); /* we have to specify the node so that we know the * monitor is active later on */ custom_action(rsc, crm_strdup(id), task, node, TRUE, TRUE, data_set); } } break; case LRM_OP_ERROR: case LRM_OP_TIMEOUT: case LRM_OP_NOTSUPPORTED: crm_warn("Processing failed op (%s) for %s on %s", id, rsc->id, node->details->uname); action = custom_action( rsc, crm_strdup(id), task, NULL, TRUE, TRUE, data_set); if(*on_fail < action->on_fail) { *on_fail = action->on_fail; } /* if(action->on_fail == action_fail_ignore) { */ /* } else */ if(task_status_i == LRM_OP_NOTSUPPORTED || is_stop_action || safe_str_eq(task, CRMD_ACTION_START) ) { crm_warn("Handling failed %s for %s on %s", task, rsc->id, node->details->uname); rsc2node_new("dont_run__failed_stopstart", rsc, -INFINITY, node, data_set); } if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { rsc->role = RSC_ROLE_MASTER; } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { rsc->role = RSC_ROLE_MASTER; } else if(rsc->role < RSC_ROLE_STARTED) { rsc->role = RSC_ROLE_STARTED; } crm_debug_2("Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s", rsc->id, role2text(rsc->role), node->details->unclean?"true":"false", fail2text(action->on_fail), role2text(action->fail_role)); if(action->fail_role != RSC_ROLE_STARTED && rsc->next_role < action->fail_role) { rsc->next_role = action->fail_role; } if(action->fail_role == RSC_ROLE_STOPPED) { /* make sure it doesnt come up again */ native_assign_color(rsc, data_set->no_color); } break; case LRM_OP_CANCELLED: /* do nothing?? */ pe_err("Dont know what to do for cancelled ops yet"); break; } crm_debug_2("Resource %s after %s: role=%s", rsc->id, task, role2text(rsc->role)); return TRUE; } gboolean rsc_colocation_new(const char *id, enum con_strength strength, resource_t *rsc_lh, resource_t *rsc_rh, const char *state_lh, const char *state_rh) { rsc_colocation_t *new_con = NULL; rsc_colocation_t *inverted_con = NULL; if(rsc_lh == NULL){ pe_config_err("No resource found for LHS %s", id); return FALSE; } else if(rsc_rh == NULL){ pe_config_err("No resource found for RHS of %s", id); return FALSE; } crm_malloc0(new_con, sizeof(rsc_colocation_t)); if(new_con == NULL) { return FALSE; } if(safe_str_eq(state_lh, CRMD_ACTION_STARTED)) { state_lh = NULL; } if(safe_str_eq(state_rh, CRMD_ACTION_STARTED)) { state_rh = NULL; } new_con->id = id; new_con->rsc_lh = rsc_lh; new_con->rsc_rh = rsc_rh; new_con->strength = strength; new_con->state_lh = state_lh; new_con->state_rh = state_rh; inverted_con = invert_constraint(new_con); crm_debug_4("Adding constraint %s (%p) to %s", new_con->id, new_con, rsc_lh->id); rsc_lh->rsc_cons = g_list_insert_sorted( rsc_lh->rsc_cons, new_con, sort_cons_strength); crm_debug_4("Adding constraint %s (%p) to %s", inverted_con->id, inverted_con, rsc_rh->id); rsc_rh->rsc_cons = g_list_insert_sorted( rsc_rh->rsc_cons, inverted_con, sort_cons_strength); return TRUE; } /* LHS before RHS */ gboolean custom_action_order( resource_t *lh_rsc, char *lh_action_task, action_t *lh_action, resource_t *rh_rsc, char *rh_action_task, action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set) { order_constraint_t *order = NULL; if((lh_action == NULL && lh_rsc == NULL) || (rh_action == NULL && rh_rsc == NULL)){ pe_config_err("Invalid inputs lh_rsc=%p, lh_a=%p," " rh_rsc=%p, rh_a=%p", lh_rsc, lh_action, rh_rsc, rh_action); crm_free(lh_action_task); crm_free(rh_action_task); return FALSE; } crm_malloc0(order, sizeof(order_constraint_t)); if(order == NULL) { return FALSE; } order->id = data_set->order_id++; order->type = type; order->lh_rsc = lh_rsc; order->rh_rsc = rh_rsc; order->lh_action = lh_action; order->rh_action = rh_action; order->lh_action_task = lh_action_task; order->rh_action_task = rh_action_task; data_set->ordering_constraints = g_list_append( data_set->ordering_constraints, order); if(lh_rsc != NULL && rh_rsc != NULL) { crm_debug_4("Created ordering constraint %d (%s):" " %s/%s before %s/%s", order->id, ordering_type2text(order->type), lh_rsc->id, lh_action_task, rh_rsc->id, rh_action_task); } else if(lh_rsc != NULL) { crm_debug_4("Created ordering constraint %d (%s):" " %s/%s before action %d (%s)", order->id, ordering_type2text(order->type), lh_rsc->id, lh_action_task, rh_action->id, rh_action_task); } else if(rh_rsc != NULL) { crm_debug_4("Created ordering constraint %d (%s):" " action %d (%s) before %s/%s", order->id, ordering_type2text(order->type), lh_action->id, lh_action_task, rh_rsc->id, rh_action_task); } else { crm_debug_4("Created ordering constraint %d (%s):" " action %d (%s) before action %d (%s)", order->id, ordering_type2text(order->type), lh_action->id, lh_action_task, rh_action->id, rh_action_task); } return TRUE; } gboolean unpack_rsc_colocation(crm_data_t * xml_obj, pe_working_set_t *data_set) { enum con_strength strength_e = pecs_ignore; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); const char *id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); const char *state_lh = crm_element_value(xml_obj, XML_RULE_ATTR_FROMSTATE); const char *state_rh = crm_element_value(xml_obj, XML_RULE_ATTR_TOSTATE); resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); resource_t *rsc_rh = pe_find_resource(data_set->resources, id_rh); if(rsc_lh == NULL) { pe_config_err("No resource (con=%s, rsc=%s)", id, id_lh); return FALSE; } else if(rsc_rh == NULL) { pe_config_err("No resource (con=%s, rsc=%s)", id, id_rh); return FALSE; } /* the docs indicate that only +/- INFINITY are allowed, * but no-one ever reads the docs so all positive values will * count as "must" and negative values as "must not" */ if(score == NULL || score[0] != '-') { strength_e = pecs_must; } else { strength_e = pecs_must_not; } return rsc_colocation_new(id, strength_e, rsc_lh, rsc_rh, state_lh, state_rh); } static const char * invert_action(const char *action) { if(safe_str_eq(action, CRMD_ACTION_START)) { return CRMD_ACTION_STOP; } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { return CRMD_ACTION_START; } else if(safe_str_eq(action, CRMD_ACTION_PROMOTE)) { return CRMD_ACTION_DEMOTE; } else if(safe_str_eq(action, CRMD_ACTION_DEMOTE)) { return CRMD_ACTION_PROMOTE; } else if(safe_str_eq(action, CRMD_ACTION_STARTED)) { return CRMD_ACTION_STOPPED; } else if(safe_str_eq(action, CRMD_ACTION_STOPPED)) { return CRMD_ACTION_STARTED; } pe_err("Unknown action: %s", action); return NULL; } gboolean unpack_rsc_order(crm_data_t * xml_obj, pe_working_set_t *data_set) { gboolean symmetrical_bool = TRUE; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *type = crm_element_value(xml_obj, XML_ATTR_TYPE); const char *id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); const char *id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); const char *action = crm_element_value(xml_obj, XML_CONS_ATTR_ACTION); const char *action_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TOACTION); const char *symmetrical = crm_element_value( xml_obj, XML_CONS_ATTR_SYMMETRICAL); resource_t *rsc_lh = NULL; resource_t *rsc_rh = NULL; if(xml_obj == NULL) { pe_config_err("No constraint object to process."); return FALSE; } else if(id == NULL) { pe_config_err("%s constraint must have an id", crm_element_name(xml_obj)); return FALSE; } else if(id_lh == NULL || id_rh == NULL) { pe_config_err("Constraint %s needs two sides lh: %s rh: %s", id, crm_str(id_lh), crm_str(id_rh)); return FALSE; } if(action == NULL) { action = CRMD_ACTION_START; } if(action_rh == NULL) { action_rh = action; } CRM_CHECK(action != NULL, return FALSE); CRM_CHECK(action_rh != NULL, return FALSE); if(safe_str_eq(type, "before")) { id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); action = crm_element_value(xml_obj, XML_CONS_ATTR_TOACTION); action_rh = crm_element_value(xml_obj, XML_CONS_ATTR_ACTION); if(action_rh == NULL) { action_rh = CRMD_ACTION_START; } if(action == NULL) { action = action_rh; } } CRM_CHECK(action != NULL, return FALSE); CRM_CHECK(action_rh != NULL, return FALSE); rsc_lh = pe_find_resource(data_set->resources, id_rh); rsc_rh = pe_find_resource(data_set->resources, id_lh); if(rsc_lh == NULL) { pe_config_err("Constraint %s: no resource found for LHS of %s", id, id_lh); return FALSE; } else if(rsc_rh == NULL) { pe_config_err("Constraint %s: no resource found for RHS of %s", id, id_rh); return FALSE; } custom_action_order( rsc_lh, generate_op_key(rsc_lh->graph_name, action, 0), NULL, rsc_rh, generate_op_key(rsc_rh->graph_name, action_rh, 0), NULL, pe_ordering_optional, data_set); if(rsc_rh->restart_type == pe_restart_restart && safe_str_eq(action, action_rh)) { if(safe_str_eq(action, CRMD_ACTION_START)) { crm_debug_2("Recover start-start: %s-%s", rsc_lh->id, rsc_rh->id); order_start_start(rsc_lh, rsc_rh, pe_ordering_recover); } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { crm_debug_2("Recover stop-stop: %s-%s", rsc_rh->id, rsc_lh->id); order_stop_stop(rsc_rh, rsc_lh, pe_ordering_recover); } } cl_str_to_boolean(symmetrical, &symmetrical_bool); if(symmetrical_bool == FALSE) { return TRUE; } action = invert_action(action); action_rh = invert_action(action_rh); custom_action_order( rsc_rh, generate_op_key(rsc_rh->graph_name, action_rh, 0), NULL, rsc_lh, generate_op_key(rsc_lh->graph_name, action, 0), NULL, pe_ordering_optional, data_set); if(rsc_lh->restart_type == pe_restart_restart && safe_str_eq(action, action_rh)) { if(safe_str_eq(action, CRMD_ACTION_START)) { crm_debug_2("Recover start-start (2): %s-%s", rsc_lh->id, rsc_rh->id); order_start_start(rsc_lh, rsc_rh, pe_ordering_recover); } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { crm_debug_2("Recover stop-stop (2): %s-%s", rsc_rh->id, rsc_lh->id); order_stop_stop(rsc_rh, rsc_lh, pe_ordering_recover); } } return TRUE; } gboolean add_node_attrs(crm_data_t *xml_obj, node_t *node, pe_working_set_t *data_set) { g_hash_table_insert(node->details->attrs, crm_strdup("#"XML_ATTR_UNAME), crm_strdup(node->details->uname)); g_hash_table_insert(node->details->attrs, crm_strdup("#"XML_ATTR_ID), crm_strdup(node->details->id)); if(safe_str_eq(node->details->id, data_set->dc_uuid)) { data_set->dc_node = node; node->details->is_dc = TRUE; g_hash_table_insert(node->details->attrs, crm_strdup("#"XML_ATTR_DC), crm_strdup(XML_BOOLEAN_TRUE)); } else { g_hash_table_insert(node->details->attrs, crm_strdup("#"XML_ATTR_DC), crm_strdup(XML_BOOLEAN_FALSE)); } unpack_instance_attributes( xml_obj, XML_TAG_ATTR_SETS, node, node->details->attrs, NULL, 0, data_set); return TRUE; } gboolean unpack_rsc_location(crm_data_t * xml_obj, pe_working_set_t *data_set) { const char *id_lh = crm_element_value(xml_obj, "rsc"); const char *id = crm_element_value(xml_obj, XML_ATTR_ID); resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); if(rsc_lh == NULL) { /* only a warn as BSC adds the constraint then the resource */ pe_config_warn("No resource (con=%s, rsc=%s)", id, id_lh); return FALSE; } else if(rsc_lh->is_managed == FALSE) { crm_debug_2("Ignoring constraint %s: resource %s not managed", id, id_lh); return FALSE; } xml_child_iter_filter( xml_obj, rule_xml, XML_TAG_RULE, crm_debug_2("Unpacking %s/%s", id, ID(rule_xml)); generate_location_rule(rsc_lh, rule_xml, data_set); ); return TRUE; } rsc_to_node_t * generate_location_rule( resource_t *rsc, crm_data_t *rule_xml, pe_working_set_t *data_set) { const char *rule_id = NULL; const char *score = NULL; const char *boolean = NULL; const char *role = NULL; const char *attr_score = NULL; GListPtr match_L = NULL; int score_f = 0; gboolean do_and = TRUE; gboolean accept = TRUE; gboolean raw_score = TRUE; rsc_to_node_t *location_rule = NULL; rule_id = crm_element_value(rule_xml, XML_ATTR_ID); boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP); role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE); crm_debug_2("processing rule: %s", rule_id); if(role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) { pe_err("Bad role specified for %s: %s", rule_id, role); return NULL; } score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE); if(score != NULL) { score_f = char2score(score); } else { score = crm_element_value( rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE); if(score == NULL) { score = crm_element_value( rule_xml, XML_RULE_ATTR_SCORE_MANGLED); } if(score != NULL) { raw_score = FALSE; } } if(safe_str_eq(boolean, "or")) { do_and = FALSE; } location_rule = rsc2node_new(rule_id, rsc, 0, NULL, data_set); if(location_rule == NULL) { return NULL; } if(role != NULL) { crm_debug_2("Setting role filter: %s", role); location_rule->role_filter = text2role(role); } if(do_and) { match_L = node_list_dup(data_set->nodes, FALSE); slist_iter( node, node_t, match_L, lpc, node->weight = score_f; ); } xml_child_iter( rule_xml, expr, enum expression_type type = find_expression_type(expr); if(type == not_expr) { pe_err("Expression <%s id=%s...> is not valid", crm_element_name(expr), crm_str(ID(expr))); continue; } slist_iter( node, node_t, data_set->nodes, lpc, if(type == nested_rule) { accept = test_rule(expr, node, rsc, data_set); } else { accept = test_expression( expr, node, rsc, data_set); } if(raw_score == FALSE) { attr_score = g_hash_table_lookup( node->details->attrs, score); if(attr_score == NULL) { accept = FALSE; pe_warn("node %s did not have a value" " for %s", node->details->uname, score); } else { score_f = char2score(score); } } if(!do_and && accept) { node_t *local = pe_find_node_id( match_L, node->details->id); if(local == NULL) { local = node_copy(node); match_L = g_list_append(match_L, local); } local->weight = merge_weights( local->weight, score_f); crm_debug_5("node %s already matched", node->details->uname); } else if(do_and && !accept) { /* remove it */ node_t *delete = pe_find_node_id( match_L, node->details->id); if(delete != NULL) { match_L = g_list_remove(match_L,delete); crm_debug_5("node %s did not match", node->details->uname); } crm_free(delete); } ); ); location_rule->node_list_rh = match_L; if(location_rule->node_list_rh == NULL) { crm_debug_2("No matching nodes for rule %s", rule_id); return NULL; } crm_debug_2("%s: %d nodes matched", rule_id, g_list_length(location_rule->node_list_rh)); crm_action_debug_3(print_rsc_to_node("Added", location_rule, FALSE)); return location_rule; } diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h index 60ae1fee91..bfe18e1694 100644 --- a/include/crm/common/xml.h +++ b/include/crm/common/xml.h @@ -1,331 +1,331 @@ -/* $Id: xml.h,v 1.41 2006/03/31 11:50:24 andrew Exp $ */ +/* $Id: xml.h,v 1.42 2006/03/31 12:45:02 andrew Exp $ */ /* * 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 */ #ifndef CRM_COMMON_XML__H #define CRM_COMMON_XML__H #include #include #include #include #include #include #include #include #include /* #define USE_LIBXML 1 */ #if CRM_DEV_BUILD # define XML_PARANOIA_CHECKS 1 #else # define XML_PARANOIA_CHECKS 0 #endif #ifdef USE_LIBXML # include typedef xmlNode crm_data_t; #else typedef struct ha_msg crm_data_t; #endif extern gboolean add_message_xml( HA_Message *msg, const char *field, const crm_data_t *xml); extern crm_data_t *get_message_xml(HA_Message *msg, const char *field); extern GHashTable *xml2list(crm_data_t *parent); #if CRM_DEPRECATED_SINCE_2_0_3 extern GHashTable *xml2list_202(crm_data_t *parent); #endif extern void hash2nvpair(gpointer key, gpointer value, gpointer user_data); extern void hash2field(gpointer key, gpointer value, gpointer user_data); extern gboolean do_id_check(crm_data_t *xml_obj, GHashTable *id_hash); /* * Replacement function for xmlCopyPropList which at the very least, * doesnt work the way *I* would expect it to. * * Copy all the attributes/properties from src into target. * * Not recursive, does not return anything. * */ extern void copy_in_properties(crm_data_t *target, const crm_data_t *src); /* * Find a child named search_path[i] at level i in the XML fragment where i=0 * is an immediate child of root. * * Terminate with success if i == len, or search_path[i] == NULL. * * On success, returns the sub-fragment described by search_path. * On failure, returns NULL. */ extern crm_data_t *find_xml_node_nested( crm_data_t *root, const char **search_path, int len); /* * Find a child named search_path[i] at level i in the XML fragment where i=0 * is an immediate child of root. * * Once the last child specified by node_path is found, find the value * of attr_name. * * If error is set to TRUE, then it is an error for the attribute not * to be found and the function will log accordingly. * * On success, returns the value of attr_name. * On failure, returns NULL. */ extern const char *get_xml_attr_nested(crm_data_t *parent, const char **node_path, int length, const char *attr_name, gboolean error); /* * Free the XML "stuff" associated with a_node * * If a_node is part of another XML blob, barf. * (Should be using free_xml_from_parent) * * Otherwise, free everything recursivly * * Wont barf on NULL. * */ extern void free_xml_fn(crm_data_t *a_node); #if 1 # define free_xml(xml_obj) free_xml_fn(xml_obj); xml_obj = NULL #else # define free_xml(xml_obj) xml_obj = NULL #endif void free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node); #define zap_xml_from_parent(parent, xml_obj) free_xml_from_parent(parent, xml_obj); xml_obj = NULL /* * Create a node named "name" as a child of "parent" * If parent is NULL, creates an unconnected node. * * Returns the created node * */ extern crm_data_t *create_xml_node(crm_data_t *parent, const char *name); /* * Make a copy of name and value and use the copied memory to create * an attribute for node. * * If node, name or value are NULL, nothing is done. * * If name or value are an empty string, nothing is done. * * Returns FALSE on failure and TRUE on success. * */ extern const char *crm_xml_add( crm_data_t *node, const char *name, const char *value); extern const char *crm_xml_add_int( crm_data_t* node, const char *name, int value); /* * Unlink the node and set its doc pointer to NULL so free_xml() * will act appropriately */ extern void unlink_xml_node(crm_data_t *node); /* * Set a timestamp attribute on a_node */ extern void add_xml_tstamp(crm_data_t *a_node); /* * */ extern void purge_diff_markers(crm_data_t *a_node); /* * Returns a deep copy of src_node * */ extern crm_data_t *copy_xml(const crm_data_t *src_node); /* * Add a copy of xml_node to new_parent */ extern crm_data_t *add_node_copy( crm_data_t *new_parent, const crm_data_t *xml_node); /* * XML I/O Functions * * Whitespace between tags is discarded. */ extern crm_data_t *file2xml(FILE *input); extern crm_data_t *stdin2xml(void); extern crm_data_t *string2xml(const char *input); extern int write_xml_file(crm_data_t *xml_node, const char *filename); extern char *dump_xml_formatted(const crm_data_t *msg); extern char *dump_xml_unformatted(const crm_data_t *msg); extern void print_xml_formatted( int log_level, const char *function, const crm_data_t *an_xml_node, const char *text); /* * Diff related Functions */ extern crm_data_t *diff_xml_object( crm_data_t *left, crm_data_t *right, gboolean suppress); extern void log_xml_diff(unsigned int log_level, crm_data_t *diff, const char *function); extern gboolean apply_xml_diff( crm_data_t *old, crm_data_t *diff, crm_data_t **new); /* * Searching & Modifying */ extern crm_data_t *find_xml_node( crm_data_t *cib, const char * node_path, gboolean must_find); extern crm_data_t *find_entity( crm_data_t *parent, const char *node_name, const char *id); extern crm_data_t *subtract_xml_object( crm_data_t *left, crm_data_t *right, const char *marker); extern int add_xml_object( crm_data_t *parent, crm_data_t *target, const crm_data_t *update); extern void xml_remove_prop(crm_data_t *obj, const char *name); extern void crm_set_element_parent(crm_data_t *data, crm_data_t *parent); extern gboolean delete_xml_child( crm_data_t *parent, crm_data_t *child, crm_data_t *to_delete); extern gboolean update_xml_child(crm_data_t *child, crm_data_t *to_update); extern gboolean find_xml_child(crm_data_t *child, const char *tag, const char *id); /* * */ extern const char *crm_element_value(const crm_data_t *data, const char *name); extern char *crm_element_value_copy(const crm_data_t *data, const char *name); extern const char *crm_element_name(const crm_data_t *data); extern void xml_validate(const crm_data_t *root); extern void crm_update_parents(crm_data_t *root); extern gboolean xml_has_children(crm_data_t *root); -extern char *calculate_xml_digest(crm_data_t *local_cib); +extern char *calculate_xml_digest(crm_data_t *local_cib, gboolean sort); #if XML_PARANOIA_CHECKS # define crm_validate_data(obj) xml_validate(obj) #else # define crm_validate_data(obj) CRM_DEV_ASSERT(obj != NULL) #endif #define xmlGetNodePath(data) crm_element_value(data, XML_ATTR_TAGNAME) #define xml_child_iter(parent, child, loop_code) \ if(parent != NULL) { \ int __counter = 0; \ crm_data_t *child = NULL; \ crm_validate_data(parent); \ for (__counter = 0; __counter < parent->nfields; __counter++) { \ if(parent->types[__counter] != FT_STRUCT \ && parent->types[__counter] != FT_UNCOMPRESS) { \ continue; \ } \ child = parent->values[__counter]; \ if(child == NULL) { \ crm_debug_4("Skipping %s == NULL", \ parent->names[__counter]); \ } else { \ loop_code; \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } #define xml_child_iter_filter(parent, child, filter, loop_code) \ if(parent != NULL) { \ int __counter = 0; \ crm_data_t *child = NULL; \ crm_validate_data(parent); \ for (__counter = 0; __counter < parent->nfields; __counter++) { \ if(parent->types[__counter] != FT_STRUCT \ && parent->types[__counter] != FT_UNCOMPRESS) { \ continue; \ } \ child = parent->values[__counter]; \ if(child == NULL) { \ crm_debug_4("Skipping %s == NULL", \ parent->names[__counter]); \ } else if(filter == NULL/*constant condition*/ \ || safe_str_eq(filter, parent->names[__counter])) { \ loop_code; \ } else { \ crm_debug_4("Skipping <%s../>", \ parent->names[__counter]); \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } #define xml_prop_iter(parent, prop_name, prop_value, code) if(parent != NULL) { \ const char *prop_name = NULL; \ const char *prop_value = NULL; \ int __counter = 0; \ crm_validate_data(parent); \ crm_debug_5("Searching %d fields", parent->nfields); \ for (__counter = 0; __counter < parent->nfields; __counter++) { \ crm_debug_5("Searching field %d", __counter); \ if(parent->types[__counter] != FT_STRING) { \ continue; \ } else if(safe_str_eq(parent->names[__counter], F_XML_TAGNAME)) { \ continue; \ } else if(safe_str_eq(parent->names[__counter], F_XML_PARENT)) { \ continue; \ } \ prop_name = parent->names[__counter]; \ prop_value = parent->values[__counter]; \ code; \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } #endif diff --git a/lib/crm/common/xml.c b/lib/crm/common/xml.c index 7f77404113..4f77fb5d90 100644 --- a/lib/crm/common/xml.c +++ b/lib/crm/common/xml.c @@ -1,2390 +1,2397 @@ -/* $Id: xml.c,v 1.62 2006/03/31 11:50:24 andrew Exp $ */ +/* $Id: xml.c,v 1.63 2006/03/31 12:45:02 andrew Exp $ */ /* * 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 int is_comment_start(const char *input); int is_comment_end(const char *input); gboolean drop_comments(const char *input, int *offset); void dump_array( int log_level, const char *message, const char **array, int depth); int print_spaces(char *buffer, int spaces); int log_data_element(const char *function, const char *prefix, int log_level, int depth, const crm_data_t *data, gboolean formatted); int dump_data_element( int depth, char **buffer, const crm_data_t *data, gboolean formatted); crm_data_t *parse_xml(const char *input, int *offset); int get_tag_name(const char *input); int get_attr_name(const char *input); int get_attr_value(const char *input); gboolean can_prune_leaf(crm_data_t *xml_node); void diff_filter_context(int context, int upper_bound, int lower_bound, crm_data_t *xml_node, crm_data_t *parent); int in_upper_context(int depth, int context, crm_data_t *xml_node); crm_data_t * find_xml_node(crm_data_t *root, const char * search_path, gboolean must_find) { if(must_find || root != NULL) { crm_validate_data(root); } if(search_path == NULL) { crm_warn("Will never find "); return NULL; } xml_child_iter_filter( root, a_child, search_path, /* crm_debug_5("returning node (%s).", xmlGetNodePath(a_child)); */ crm_log_xml(LOG_DEBUG_5, "found:", a_child); crm_log_xml(LOG_DEBUG_6, "in:", root); crm_validate_data(a_child); return a_child; ); if(must_find) { crm_warn("Could not find %s in %s.", search_path, xmlGetNodePath(root)); } else if(root != NULL) { crm_debug_3("Could not find %s in %s.", search_path, xmlGetNodePath(root)); } else { crm_debug_3("Could not find %s in .", search_path); } return NULL; } crm_data_t* find_xml_node_nested(crm_data_t *root, const char **search_path, int len) { int j; gboolean is_found = TRUE; crm_data_t *match = NULL; crm_data_t *lastMatch = root; crm_validate_data(root); if(search_path == NULL || search_path[0] == NULL) { crm_warn("Will never find NULL"); return NULL; } dump_array(LOG_DEBUG_5, "Looking for.", search_path, len); for (j=0; j < len; ++j) { if (search_path[j] == NULL) { /* a NULL also means stop searching */ break; } match = find_xml_node(lastMatch, search_path[j], FALSE); if(match == NULL) { is_found = FALSE; break; } else { lastMatch = match; } } if (is_found) { crm_debug_5("returning node (%s).", xmlGetNodePath(lastMatch)); crm_log_xml_debug_5(lastMatch, "found\t%s"); crm_log_xml_debug_5(root, "in \t%s"); crm_validate_data(lastMatch); return lastMatch; } dump_array(LOG_DEBUG_2, "Could not find the full path to the node you specified.", search_path, len); crm_debug_2("Closest point was node (%s) starting from %s.", xmlGetNodePath(lastMatch), crm_element_name(root)); return NULL; } const char * get_xml_attr_nested(crm_data_t *parent, const char **node_path, int length, const char *attr_name, gboolean error) { const char *attr_value = NULL; crm_data_t *attr_parent = NULL; if(error || parent != NULL) { crm_validate_data(parent); } if(parent == NULL) { crm_debug_3("Can not find attribute %s in NULL parent",attr_name); return NULL; } if(attr_name == NULL || strlen(attr_name) == 0) { crm_err("Can not find attribute with no name in %s", xmlGetNodePath(parent)); return NULL; } if(length == 0) { attr_parent = parent; } else { attr_parent = find_xml_node_nested(parent, node_path, length); if(attr_parent == NULL && error) { crm_err("No node at the path you specified."); return NULL; } } attr_value = crm_element_value(attr_parent, attr_name); if((attr_value == NULL || strlen(attr_value) == 0) && error) { crm_err("No value present for %s at %s", attr_name, xmlGetNodePath(attr_parent)); return NULL; } return attr_value; } crm_data_t* find_entity(crm_data_t *parent, const char *node_name, const char *id) { crm_validate_data(parent); xml_child_iter_filter( parent, a_child, node_name, if(id == NULL || safe_str_eq(id, ID(a_child))) { crm_debug_4("returning node (%s).", xmlGetNodePath(a_child)); return a_child; } ); crm_debug_3("node <%s id=%s> not found in %s.", node_name, id, xmlGetNodePath(parent)); return NULL; } void copy_in_properties(crm_data_t* target, const crm_data_t *src) { int value_len = 0; char *incr_value = NULL; char *new_value = NULL; crm_validate_data(src); crm_validate_data(target); if(src == NULL) { crm_warn("No node to copy properties from"); } else if (target == NULL) { crm_err("No node to copy properties into"); } else { xml_prop_iter( src, local_prop_name, local_prop_value, /* if the value is name followed by "++" we need * to increment the existing value */ new_value = NULL; incr_value = NULL; /* do a quick check to decide if its worth * constructing the various strings and * performing string comparisions */ value_len = strlen(local_prop_value); if(value_len > 2 && local_prop_value[0] == local_prop_value[0] && local_prop_value[value_len-1] == '+' && local_prop_value[value_len-2] == '+') { int old_int = 0; const char *old_value = NULL; incr_value=crm_concat(local_prop_name,"+",'+'); if(safe_str_eq(local_prop_value, incr_value)) { old_value = crm_element_value( target, local_prop_name); old_int = crm_parse_int(old_value, "0"); new_value = crm_itoa(old_int + 1); local_prop_value = new_value; } } crm_xml_add(target, local_prop_name, local_prop_value); crm_free(incr_value); crm_free(new_value); ); crm_validate_data(target); } return; } crm_data_t* add_node_copy(crm_data_t *parent, const crm_data_t *src_node) { const char *name = NULL; crm_data_t *child = NULL; CRM_CHECK(src_node != NULL, return NULL); crm_validate_data(src_node); name = crm_element_name(src_node); CRM_CHECK(name != NULL, return NULL); child = create_xml_node(parent, name); copy_in_properties(child, src_node); xml_child_iter(src_node, src_child, add_node_copy(child, src_child); ); crm_set_element_parent(child, parent); return child; } const char * crm_xml_add(crm_data_t* node, const char *name, const char *value) { const char *parent_name = NULL; if(node != NULL) { parent_name = crm_element_name(node); } crm_debug_5("[%s] Setting %s to %s", crm_str(parent_name), name, value); if (name == NULL || strlen(name) <= 0) { } else if(node == NULL) { } else if(parent_name == NULL && strcmp(name, F_XML_TAGNAME) != 0) { } else if (value == NULL || strlen(value) <= 0) { xml_remove_prop(node, name); return NULL; } else { const char *new_value = NULL; crm_validate_data(node); ha_msg_mod(node, name, value); new_value = crm_element_value(node, name); return new_value; } return NULL; } const char * crm_xml_add_int(crm_data_t* node, const char *name, int value) { const char *parent_name = NULL; if(node != NULL) { parent_name = crm_element_name(node); } crm_debug_5("[%s] Setting %s to %d", crm_str(parent_name), name, value); if (name == NULL || strlen(name) <= 0) { } else if(node == NULL) { } else if(parent_name == NULL && strcmp(name, F_XML_TAGNAME) != 0) { } else { crm_validate_data(node); ha_msg_mod_int(node, name, value); return crm_element_value(node, name); } return NULL; } crm_data_t* create_xml_node(crm_data_t *parent, const char *name) { const char *local_name = NULL; const char *parent_name = NULL; crm_data_t *ret_value = NULL; if (name == NULL || strlen(name) < 1) { ret_value = NULL; } else { local_name = name; ret_value = ha_msg_new(1); CRM_CHECK(ret_value != NULL, return NULL); crm_xml_add(ret_value, F_XML_TAGNAME, name); crm_set_element_parent(ret_value, parent); crm_validate_data(ret_value); if(parent) { crm_validate_data(parent); parent_name = crm_element_name(parent); crm_debug_5("Attaching %s to parent %s", local_name, parent_name); CRM_CHECK(HA_OK == ha_msg_addstruct( parent, name, ret_value), return NULL); crm_msg_del(ret_value); crm_validate_data(parent); ret_value = parent->values[parent->nfields-1]; } } crm_debug_5("Created node [%s [%s]]", crm_str(parent_name), crm_str(local_name)); return ret_value; } void free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node) { CRM_CHECK(parent != NULL, return); CRM_CHECK(a_node != NULL, return); crm_validate_data(parent); cl_msg_remove_value(parent, a_node); crm_validate_data(parent); } void free_xml_fn(crm_data_t *a_node) { if(a_node == NULL) { ; /* nothing to do */ } else { int has_parent = 0; crm_validate_data(a_node); ha_msg_value_int(a_node, F_XML_PARENT, &has_parent); /* there is no way in hell we should be deleting anything * with a parent and without the parent knowning */ CRM_CHECK(has_parent == 0, return); if(has_parent == 0) { crm_validate_data(a_node); crm_msg_del(a_node); } } return; } void add_xml_tstamp(crm_data_t *a_node) { char *since_epoch = NULL; time_t a_time = time(NULL); crm_validate_data(a_node); if(a_time == (time_t)-1) { cl_perror("set_node_tstamp(): Invalid time returned"); return; } crm_malloc0(since_epoch, 128*(sizeof(char))); if(since_epoch != NULL) { sprintf(since_epoch, "%ld", (unsigned long)a_time); ha_msg_mod(a_node, XML_ATTR_TSTAMP, since_epoch); crm_validate_data(a_node); crm_free(since_epoch); } } crm_data_t* copy_xml(const crm_data_t *src_node) { return add_node_copy(NULL, src_node); } crm_data_t* string2xml(const char *input) { crm_data_t *output = parse_xml(input, NULL); if(output != NULL) { crm_update_parents(output); crm_validate_data(output); } return output; } crm_data_t * stdin2xml(void) { int lpc = 0; int MAX_XML_BUFFER = 20000; int ch = 0; gboolean more = TRUE; gboolean inTag = FALSE; FILE *input = stdin; char *xml_buffer = NULL; crm_data_t *xml_obj = NULL; crm_malloc0(xml_buffer, sizeof(char)*(MAX_XML_BUFFER+1)); while (more && lpc < MAX_XML_BUFFER) { ch = fgetc(input); /* crm_debug_3("Got [%c]", ch); */ switch(ch) { case EOF: case 0: ch = 0; more = FALSE; xml_buffer[lpc++] = ch; break; case '>': case '<': inTag = TRUE; if(ch == '>') { inTag = FALSE; } xml_buffer[lpc++] = ch; break; case '\n': case '\r': case '\t': case ' ': ch = ' '; if(inTag) { xml_buffer[lpc++] = ch; } break; default: xml_buffer[lpc++] = ch; break; } } xml_buffer[MAX_XML_BUFFER] = 0; xml_obj = string2xml(xml_buffer); crm_free(xml_buffer); crm_log_xml_debug_3(xml_obj, "Created fragment"); return xml_obj; } crm_data_t* file2xml(FILE *input) { char *buffer = NULL; crm_data_t *new_obj = NULL; int start = 0, length = 0, read_len = 0; if(input == NULL) { crm_err("No file to read"); return NULL; } /* see how big the file is */ start = ftell(input); fseek(input, 0L, SEEK_END); length = ftell(input); fseek(input, 0L, start); CRM_ASSERT(start == ftell(input)); crm_debug_3("Reading %d bytes from file", length); crm_malloc0(buffer, sizeof(char) * (length+1)); read_len = fread(buffer, sizeof(char), length, input); if(read_len != length) { crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len); } else if(length > 0) { new_obj = string2xml(buffer); } else { crm_warn("File contained no XML"); } crm_free(buffer); return new_obj; } void dump_array(int log_level, const char *message, const char **array, int depth) { int j; if(message != NULL) { do_crm_log(log_level, __FILE__, __FUNCTION__, "%s", message); } do_crm_log(log_level, __FILE__, __FUNCTION__, "Contents of the array:"); if(array == NULL || array[0] == NULL || depth == 0) { do_crm_log(log_level, __FILE__, __FUNCTION__, "\t"); return; } for (j=0; j < depth && array[j] != NULL; j++) { if (array[j] == NULL) { break; } do_crm_log(log_level, __FILE__, __FUNCTION__, "\t--> (%s).", array[j]); } } int write_xml_file(crm_data_t *xml_node, const char *filename) { int res = 0; char *now_str = NULL; time_t now; CRM_CHECK(filename != NULL, return -1); crm_debug_3("Writing XML out to %s", filename); crm_validate_data(xml_node); if (xml_node == NULL) { crm_err("Cannot write NULL to %s", filename); return -1; } crm_validate_data(xml_node); crm_log_xml_debug_4(xml_node, "Writing out"); crm_validate_data(xml_node); now = time(NULL); now_str = ctime(&now); now_str[24] = EOS; /* replace the newline */ crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str); crm_validate_data(xml_node); { FILE *file_output_strm = fopen(filename, "w"); if(file_output_strm == NULL) { res = -1; cl_perror("Cannot write to %s", filename); } else { char *buffer = dump_xml_formatted(xml_node); CRM_CHECK(buffer != NULL && strlen(buffer) > 0, return -1); if(buffer != NULL && strlen(buffer) > 0) { res = fprintf(file_output_strm, "%s", buffer); if(res < 0) { cl_perror("Cannot write output to %s", filename); } } fflush(file_output_strm); fclose(file_output_strm); crm_free(buffer); } } crm_debug_3("Saved %d bytes to the Cib as XML", res); return res; } void print_xml_formatted(int log_level, const char *function, const crm_data_t *msg, const char *text) { if(msg == NULL) { do_crm_log(log_level,NULL,function, "%s: NULL", crm_str(text)); return; } crm_validate_data(msg); log_data_element(function, text, log_level, 0, msg, TRUE); return; } crm_data_t * get_message_xml(HA_Message *msg, const char *field) { crm_data_t *xml_node = NULL; crm_data_t *tmp_node = NULL; crm_validate_data(msg); tmp_node = cl_get_struct(msg, field); if(tmp_node != NULL) { xml_node = copy_xml(tmp_node); } return xml_node; } gboolean add_message_xml(HA_Message *msg, const char *field, const crm_data_t *xml) { crm_validate_data(xml); crm_validate_data(msg); CRM_CHECK(field != NULL, return FALSE); ha_msg_addstruct_compress(msg, field, xml); crm_update_parents(msg); return TRUE; } char * dump_xml_formatted(const crm_data_t *an_xml_node) { char *buffer = NULL; char *mutable_ptr = NULL; if(an_xml_node == NULL) { return NULL; } crm_malloc0(buffer, 3*get_stringlen(an_xml_node)); mutable_ptr = buffer; crm_validate_data(an_xml_node); CRM_CHECK(dump_data_element( 0, &mutable_ptr, an_xml_node, TRUE) >= 0, crm_crit("Could not dump the whole message")); crm_debug_4("Dumped: %s", buffer); return buffer; } char * dump_xml_unformatted(const crm_data_t *an_xml_node) { char *buffer = NULL; char *mutable_ptr = NULL; crm_malloc0(buffer, 2*get_stringlen(an_xml_node)); mutable_ptr = buffer; crm_validate_data(an_xml_node); CRM_CHECK(dump_data_element( 0, &mutable_ptr, an_xml_node, TRUE) >= 0, crm_crit("Could not dump the whole message")); crm_debug_4("Dumped: %s", buffer); return buffer; } #define update_buffer_head(buffer, len) if(len < 0) { \ (*buffer) = EOS; return -1; \ } else { \ buffer += len; \ } int print_spaces(char *buffer, int depth) { int lpc = 0; int spaces = 2*depth; /* <= so that we always print 1 space - prevents problems with syslog */ for(lpc = 0; lpc <= spaces; lpc++) { if(sprintf(buffer, "%c", ' ') < 1) { return -1; } buffer += 1; } return lpc; } int log_data_element( const char *function, const char *prefix, int log_level, int depth, const crm_data_t *data, gboolean formatted) { int printed = 0; int child_result = 0; int has_children = 0; char print_buffer[1000]; char *buffer = print_buffer; const char *name = crm_element_name(data); crm_debug_5("Dumping %s...", name); crm_validate_data(data); if(data == NULL) { crm_warn("No data to dump as XML"); return 0; } else if(name == NULL && depth == 0) { xml_child_iter( data, a_child, child_result = log_data_element( function, prefix, log_level, depth, a_child, formatted); if(child_result < 0) { return child_result; } ); return 0; } else if(name == NULL) { crm_err("Cannot dump NULL element at depth %d", depth); return -1; } if(formatted) { printed = print_spaces(buffer, depth); update_buffer_head(buffer, printed); } printed = sprintf(buffer, "<%s", name); update_buffer_head(buffer, printed); xml_prop_iter( data, prop_name, prop_value, if(safe_str_eq(F_XML_TAGNAME, prop_name)) { continue; } else if(safe_str_eq(F_XML_PARENT, prop_name)) { continue; } crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value); update_buffer_head(buffer, printed); ); xml_child_iter( data, child, if(child != NULL) { has_children++; break; } ); printed = sprintf(buffer, "%s>", has_children==0?"/":""); update_buffer_head(buffer, printed); do_crm_log(log_level, function, NULL, "%s%s", prefix?prefix:"", print_buffer); buffer = print_buffer; if(has_children == 0) { return 0; } xml_child_iter( data, a_child, child_result = log_data_element( function, prefix, log_level, depth+1, a_child, formatted); if(child_result < 0) { return -1; } ); if(formatted) { printed = print_spaces(buffer, depth); update_buffer_head(buffer, printed); } do_crm_log(log_level, function, NULL, "%s%s", prefix?prefix:"", print_buffer, name); crm_debug_5("Dumped %s...", name); return has_children; } int dump_data_element( int depth, char **buffer, const crm_data_t *data, gboolean formatted) { int printed = 0; int child_result = 0; int has_children = 0; const char *name = crm_element_name(data); crm_debug_5("Dumping %s...", name); crm_validate_data(data); if(buffer == NULL || *buffer == NULL) { crm_err("No buffer supplied to dump XML into"); return -1; } else if(data == NULL) { crm_warn("No data to dump as XML"); (*buffer)[0] = EOS; return 0; } else if(name == NULL && depth == 0) { xml_child_iter( data, a_child, child_result = dump_data_element( depth, buffer, a_child, formatted); if(child_result < 0) { return child_result; } ); return 0; } else if(name == NULL) { crm_err("Cannot dump NULL element at depth %d", depth); return -1; } if(formatted) { printed = print_spaces(*buffer, depth); update_buffer_head(*buffer, printed); } printed = sprintf(*buffer, "<%s", name); update_buffer_head(*buffer, printed); xml_prop_iter(data, prop_name, prop_value, if(safe_str_eq(F_XML_TAGNAME, prop_name)) { continue; } else if(safe_str_eq(F_XML_PARENT, prop_name)) { continue; } crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = sprintf(*buffer, " %s=\"%s\"", prop_name, prop_value); update_buffer_head(*buffer, printed); ); xml_child_iter( data, child, if(child != NULL) { has_children++; break; } ); printed = sprintf(*buffer, "%s>%s", has_children==0?"/":"", formatted?"\n":""); update_buffer_head(*buffer, printed); if(has_children == 0) { return 0; } xml_child_iter( data, child, child_result = dump_data_element( depth+1, buffer, child, formatted); if(child_result < 0) { return -1; } ); if(formatted) { printed = print_spaces(*buffer, depth); update_buffer_head(*buffer, printed); } printed = sprintf(*buffer, "%s", name, formatted?"\n":""); update_buffer_head(*buffer, printed); crm_debug_5("Dumped %s...", name); return has_children; } gboolean xml_has_children(crm_data_t *xml_root) { crm_validate_data(xml_root); xml_child_iter( xml_root, a_child, return TRUE; ); return FALSE; } void xml_validate(const crm_data_t *xml_root) { int lpc = 0; CRM_ASSERT(xml_root != NULL); CRM_ASSERT(cl_is_allocated(xml_root) == 1); CRM_ASSERT(xml_root->nfields < 500); for (lpc = 0; lpc < xml_root->nfields; lpc++) { void *child = xml_root->values[lpc]; CRM_ASSERT(cl_is_allocated(xml_root->names[lpc]) == 1); if(child == NULL) { } else if(xml_root->types[lpc] == FT_STRUCT || xml_root->types[lpc] == FT_UNCOMPRESS) { crm_validate_data(child); } else if(xml_root->types[lpc] == FT_STRING) { CRM_ASSERT(cl_is_allocated(child) == 1); /* } else { */ /* CRM_CHECK(FALSE); */ } } } void crm_set_element_parent(crm_data_t *data, crm_data_t *parent) { crm_validate_data(data); if(parent != NULL) { ha_msg_mod_int(data, F_XML_PARENT, 1); } else { ha_msg_mod_int(data, F_XML_PARENT, 0); } } const char * crm_element_value(const crm_data_t *data, const char *name) { const char *value = NULL; crm_validate_data(data); value = cl_get_string(data, name); #if XML_PARANOIA_CHECKS CRM_CHECK(value == NULL || cl_is_allocated(value) == 1, return NULL); #endif return value; } char * crm_element_value_copy(const crm_data_t *data, const char *name) { const char *value = NULL; char *value_copy = NULL; crm_validate_data(data); value = cl_get_string(data, name); #if XML_PARANOIA_CHECKS CRM_CHECK(cl_is_allocated(value) == 1, return NULL); #endif if(value != NULL) { value_copy = crm_strdup(value); } return value_copy; } const char * crm_element_name(const crm_data_t *data) { #if CRM_DEV_BUILD crm_validate_data(data); #endif return cl_get_string(data, F_XML_TAGNAME); } void xml_remove_prop(crm_data_t *obj, const char *name) { if(crm_element_value(obj, name) != NULL) { cl_msg_remove(obj, name); } } void crm_update_parents(crm_data_t *xml_root) { crm_validate_data(xml_root); xml_child_iter( xml_root, a_child, crm_set_element_parent(a_child, xml_root); crm_update_parents(a_child); ); } int get_tag_name(const char *input) { int lpc = 0; char ch = 0; const char *error = NULL; gboolean do_special = FALSE; for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c [%d]", ch, lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '?': if(lpc == 0) { /* weird xml tag that we dont care about */ do_special = TRUE; } else { return lpc; } break; case '/': case '>': case '\t': case '\n': case ' ': if(!do_special) { return lpc; } break; default: if(do_special) { } else if('a' <= ch && ch <= 'z') { } else if('A' <= ch && ch <= 'Z') { } else if(ch == '_') { } else if(ch == '-') { } else { error = "bad character, not in [a-zA-Z_-]"; } break; } } crm_err("Error parsing token near %.15s: %s", input, crm_str(error)); return -1; } int get_attr_name(const char *input) { int lpc = 0; char ch = 0; const char *error = NULL; for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c[%d]", ch, lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '\t': case '\n': case ' ': error = "unexpected whitespace"; break; case '=': return lpc; default: if('a' <= ch && ch <= 'z') { } else if('A' <= ch && ch <= 'Z') { } else if(isdigit((int) ch)) { } else if(ch == '_') { } else if(ch == '-') { } else { error = "bad character, not in [a-zA-Z0-9_-]"; } break; } } crm_err("Error parsing token near %.15s: %s", input, crm_str(error)); return -1; } int get_attr_value(const char *input) { int lpc = 0; char ch = 0; const char *error = NULL; for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c [%d]", ch, lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '\\': if(input[lpc+1] == '"') { /* skip over the next char */ lpc++; break; } /*fall through*/ case '"': return lpc; default: break; } } crm_err("Error parsing token near %.15s: %s", input, crm_str(error)); return -1; } int is_comment_start(const char *input) { CRM_CHECK(input != NULL, return 0); if(strlen(input) > 4 && input[0] == '<' && input[1] == '!' && input[2] == '-' && input[3] == '-') { crm_debug_6("Found comment start: "); return 3; } else if(strlen(input) > 1 && input[0] == '?' && input[1] == '>') { crm_debug_6("Found comment end: ?>"); return 2; } if(strlen(input) > 2) { crm_debug_6("Not comment end: %c%c%c", input[0], input[1], input[2]); } else { crm_debug_6("Not comment end"); } return 0; } gboolean drop_comments(const char *input, int *offset) { gboolean more = TRUE; gboolean in_directive = FALSE; int in_comment = FALSE; const char *our_input = input; int len = 0, lpc = 0; int tag_len = 0; char ch = 0; if(input == NULL) { return FALSE; } if(offset != NULL) { our_input = input + (*offset); } len = strlen(our_input); while(lpc < len && more) { ch = our_input[lpc]; crm_debug_6("Processing char %c[%d]", ch, lpc); switch(ch) { case 0: if(in_comment == FALSE) { more = FALSE; } else { crm_err("unexpected EOS"); crm_warn("Parsing error at or before: %s", our_input); } break; case '<': tag_len = is_comment_start(our_input + lpc); if(tag_len > 0) { if(in_comment) { crm_err("Nested XML comments are not supported!"); crm_warn("Parsing error at or before: %s", our_input); crm_warn("Netsed comment found at: %s", our_input + lpc + tag_len); } in_comment = TRUE; lpc+=tag_len; if(tag_len == 2) { #if 1 if(our_input[lpc-1] == '!') { in_directive = TRUE; } #else tag_len = get_tag_name(our_input + lpc); crm_debug_2("Tag length: %d", len); if(strncmp("DOCTYPE", our_input+lpc, tag_len) == 0) { in_directive = TRUE; } #endif } } else if(in_comment == FALSE){ more = FALSE; } else { lpc++; crm_debug_6("Skipping comment char %c", our_input[lpc]); } break; case '>': lpc++; if(in_directive) { in_directive = FALSE; in_comment = FALSE; } break; case '-': case '?': tag_len = is_comment_end(our_input + lpc); if(tag_len > 0) { lpc+=tag_len; in_comment = FALSE; } else { lpc++; crm_debug_6("Skipping comment char %c", our_input[lpc]); } break; case ' ': case '\t': case '\n': case '\r': lpc++; crm_debug_6("Skipping whitespace char %d", our_input[lpc]); break; default: lpc++; crm_debug_6("Skipping comment char %c", our_input[lpc]); break; } } crm_debug_4("Finished processing comments"); if(offset != NULL) { (*offset) += lpc; } if(lpc > 0) { crm_debug_5("Skipped %d comment chars", lpc); return TRUE; } return FALSE; } crm_data_t* parse_xml(const char *input, int *offset) { int len = 0, lpc = 0; char ch = 0; char *tag_name = NULL; char *attr_name = NULL; char *attr_value = NULL; gboolean more = TRUE; gboolean were_comments = TRUE; const char *error = NULL; const char *our_input = input; crm_data_t *new_obj = NULL; if(input == NULL) { return NULL; } if(offset != NULL) { our_input = input + (*offset); } len = strlen(our_input); while(lpc < len && were_comments) { were_comments = drop_comments(our_input, &lpc); } CRM_CHECK(our_input[lpc] == '<', return NULL); lpc++; len = get_tag_name(our_input + lpc); crm_debug_5("Tag length: %d", len); if(len < 0) { return NULL; } crm_malloc0(tag_name, len+1); strncpy(tag_name, our_input + lpc, len+1); tag_name[len] = EOS; crm_debug_4("Processing tag %s", tag_name); new_obj = ha_msg_new(1); ha_msg_add(new_obj, F_XML_TAGNAME, tag_name); lpc += len; for(; more && error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = our_input[lpc]; crm_debug_5("Processing char %c[%d]", ch, lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '/': if(our_input[lpc+1] == '>') { more = FALSE; } break; case '<': if(our_input[lpc+1] != '/') { crm_data_t *child = NULL; gboolean any_comments = FALSE; do { were_comments = drop_comments(our_input, &lpc); any_comments = any_comments || were_comments; } while(lpc < len && were_comments); if(any_comments) { lpc--; break; } crm_debug_4("Start parsing child..."); child = parse_xml(our_input, &lpc); if(child == NULL) { error = "error parsing child"; } else { ha_msg_addstruct_compress( new_obj, crm_element_name(child), child); crm_debug_4("Finished parsing child: %s", crm_element_name(child)); ha_msg_del(child); if(our_input[lpc] == '<') { /* lpc is about to get incrimented * make sure we process the '<' that * we're currently looking at */ lpc--; } /* lpc++; /\* > *\/ */ } } else { lpc += 2; /* *\/ */ if(our_input[lpc] != '>') { error = "clase tag cannot contain attrs"; } crm_debug_4("Finished parsing ourselves: %s", crm_element_name(new_obj)); } else { error = "Mismatching close tag"; crm_err("Expected: %s", tag_name); } } break; case '=': lpc++; /* = */ /*fall through*/ case '"': lpc++; /* " */ len = get_attr_value(our_input+lpc); if(len < 0) { error = "couldnt find attr_value"; } else { crm_malloc0(attr_value, len+1); strncpy(attr_value, our_input+lpc, len+1); attr_value[len] = EOS; lpc += len; /* lpc++; /\* " *\/ */ crm_debug_4("creating nvpair: <%s %s=\"%s\"...", tag_name, crm_str(attr_name), crm_str(attr_value)); ha_msg_add(new_obj, attr_name, attr_value); crm_free(attr_name); crm_free(attr_value); } break; case '>': case ' ': case '\t': case '\n': case '\r': break; default: len = get_attr_name(our_input+lpc); if(len < 0) { error = "couldnt find attr_name"; } else { crm_malloc0(attr_name, len+1); strncpy(attr_name, our_input+lpc, len+1); attr_name[len] = EOS; lpc += len; crm_debug_4("found attr name: %s", attr_name); lpc--; /* make sure the '=' is seen next time around */ } break; } } if(error) { crm_err("Error parsing token: %s", error); crm_err("Error at or before: %s", our_input+lpc-3); return NULL; } crm_debug_4("Finished processing %s tag", tag_name); crm_free(tag_name); if(offset != NULL) { (*offset) += lpc; } return new_obj; } void log_xml_diff(unsigned int log_level, crm_data_t *diff, const char *function) { crm_data_t *added = find_xml_node(diff, "diff-added", FALSE); crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE); gboolean is_first = TRUE; xml_child_iter( removed, child, log_data_element(function, "-", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { crm_log_maybe(log_level, " --- "); } ); /* crm_log_maybe(log_level, " === "); */ is_first = TRUE; xml_child_iter( added, child, log_data_element(function, "+", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { crm_log_maybe(log_level, " --- "); } ); } void purge_diff_markers(crm_data_t *a_node) { CRM_CHECK(a_node != NULL, return); xml_remove_prop(a_node, XML_DIFF_MARKER); xml_child_iter(a_node, child, purge_diff_markers(child); ); } gboolean apply_xml_diff(crm_data_t *old, crm_data_t *diff, crm_data_t **new) { gboolean result = TRUE; crm_data_t *added = find_xml_node(diff, "diff-added", FALSE); crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE); int root_nodes_seen = 0; CRM_CHECK(new != NULL, return FALSE); crm_debug_2("Substraction Phase"); xml_child_iter(removed, child_diff, CRM_CHECK(root_nodes_seen == 0, result = FALSE); if(root_nodes_seen == 0) { *new = subtract_xml_object(old, child_diff, NULL); } root_nodes_seen++; ); if(root_nodes_seen == 0) { *new = copy_xml(old); } else if(root_nodes_seen > 1) { crm_err("(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen); result = FALSE; } root_nodes_seen = 0; crm_debug_2("Addition Phase"); if(result) { xml_child_iter(added, child_diff, CRM_CHECK(root_nodes_seen == 0, result = FALSE); if(root_nodes_seen == 0) { add_xml_object(NULL, *new, child_diff); } root_nodes_seen++; ); } if(root_nodes_seen > 1) { crm_err("(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen); result = FALSE; #if CRM_DEV_BUILD } else if(result) { crm_data_t *intermediate = NULL; crm_data_t *diff_of_diff = NULL; crm_debug_2("Verification Phase"); intermediate = diff_xml_object(old, *new, FALSE); diff_of_diff = diff_xml_object(intermediate, diff, TRUE); if(diff_of_diff != NULL) { crm_warn("Diff application failed!"); log_xml_diff(LOG_DEBUG, diff_of_diff, "diff:diff_of_diff"); log_xml_diff(LOG_INFO, intermediate, "diff:actual_diff"); result = FALSE; } free_xml(diff_of_diff); free_xml(intermediate); diff_of_diff = NULL; intermediate = NULL; #endif } if(result == FALSE) { log_xml_diff(LOG_INFO, diff, "diff:input_diff"); log_data_element("diff:input", NULL, LOG_DEBUG_2, 0, old, TRUE); /* CRM_CHECK(diff_of_diff != NULL); */ result = FALSE; } else { purge_diff_markers(*new); } return result; } crm_data_t * diff_xml_object(crm_data_t *old, crm_data_t *new, gboolean suppress) { crm_data_t *diff = NULL; crm_data_t *tmp1 = NULL; crm_data_t *added = NULL; crm_data_t *removed = NULL; tmp1 = subtract_xml_object(old, new, "removed:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { ha_msg_del(tmp1); tmp1 = NULL; } else { diff = create_xml_node(NULL, "diff"); removed = create_xml_node(diff, "diff-removed"); added = create_xml_node(diff, "diff-added"); add_node_copy(removed, tmp1); } free_xml(tmp1); } tmp1 = subtract_xml_object(new, old, "added:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { ha_msg_del(tmp1); return diff; } if(diff == NULL) { diff = create_xml_node(NULL, "diff"); } if(removed == NULL) { removed = create_xml_node(diff, "diff-removed"); } if(added == NULL) { added = create_xml_node(diff, "diff-added"); } add_node_copy(added, tmp1); free_xml(tmp1); } return diff; } gboolean can_prune_leaf(crm_data_t *xml_node) { gboolean can_prune = TRUE; /* return FALSE; */ xml_prop_iter(xml_node, prop_name, prop_value, if(safe_str_eq(prop_name, XML_ATTR_ID)) { continue; } can_prune = FALSE; ); xml_child_iter(xml_node, child, if(can_prune_leaf(child)) { cl_msg_remove_value(xml_node, child); __counter--; } else { can_prune = FALSE; } ); return can_prune; } void diff_filter_context(int context, int upper_bound, int lower_bound, crm_data_t *xml_node, crm_data_t *parent) { crm_data_t *us = NULL; crm_data_t *new_parent = parent; const char *name = crm_element_name(xml_node); CRM_CHECK(xml_node != NULL && name != NULL, return); us = create_xml_node(parent, name); xml_prop_iter(xml_node, prop_name, prop_value, lower_bound = context; crm_xml_add(us, prop_name, prop_value); ); if(lower_bound >= 0 || upper_bound >= 0) { crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); new_parent = us; } else { upper_bound = in_upper_context(0, context, xml_node); if(upper_bound >= 0) { crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); new_parent = us; } else { free_xml(us); us = NULL; } } xml_child_iter(us, child, diff_filter_context( context, upper_bound-1, lower_bound-1, child, new_parent); ); } int in_upper_context(int depth, int context, crm_data_t *xml_node) { gboolean has_attributes = FALSE; if(context == 0) { return 0; } xml_prop_iter(xml_node, prop_name, prop_value, has_attributes = TRUE; break; ); if(has_attributes) { return depth; } else if(depth < context) { xml_child_iter(xml_node, child, if(in_upper_context(depth+1, context, child)) { return depth; } ); } return 0; } crm_data_t * subtract_xml_object(crm_data_t *left, crm_data_t *right, const char *marker) { gboolean skip = FALSE; gboolean differences = FALSE; crm_data_t *diff = NULL; crm_data_t *child_diff = NULL; crm_data_t *right_child = NULL; const char *right_val = NULL; const char *name = NULL; int lpc = 0; const char *filter[] = { XML_ATTR_ORIGIN, XML_DIFF_MARKER, XML_CIB_ATTR_WRITTEN, }; crm_log_xml(LOG_DEBUG_5, "left:", left); crm_log_xml(LOG_DEBUG_5, "right:", right); if(left == NULL) { return NULL; } else if(right == NULL) { crm_data_t *deleted = NULL; crm_debug_5("Processing <%s id=%s> (complete copy)", crm_element_name(left), ID(left)); deleted = copy_xml(left); crm_xml_add(deleted, XML_DIFF_MARKER, marker); return deleted; } name = crm_element_name(left); /* sanity checks */ CRM_CHECK(name != NULL, return NULL); CRM_CHECK(safe_str_eq(crm_element_name(left), crm_element_name(right)), return NULL); CRM_CHECK(safe_str_eq(ID(left), ID(right)), return NULL); diff = create_xml_node(NULL, name); /* changes to name/value pairs */ crm_debug_5("Processing <%s id=%s>", crm_str(name), ID(left)); xml_prop_iter(left, prop_name, left_value, if(safe_str_eq(prop_name, XML_ATTR_ID)) { continue; } skip = FALSE; for(lpc = 0; skip == FALSE && lpc < DIMOF(filter); lpc++){ if(safe_str_eq(prop_name, filter[lpc])) { skip = TRUE; } } if(skip) { continue; } right_val = crm_element_value(right, prop_name); if(right_val == NULL) { differences = TRUE; crm_xml_add(diff, prop_name, left_value); crm_debug_6("\t%s: %s", crm_str(prop_name), crm_str(left_value)); } else if(safe_str_eq(left_value, right_val)) { crm_debug_5("\t%s: %s (removed)", crm_str(prop_name), crm_str(left_value)); } else { differences = TRUE; crm_xml_add(diff, prop_name, left_value); crm_debug_5("\t%s: %s->%s", crm_str(prop_name), crm_str(left_value), right_val); } ); /* changes to child objects */ crm_debug_3("Processing children of <%s id=%s>",crm_str(name),ID(left)); xml_child_iter( left, left_child, right_child = find_entity( right, crm_element_name(left_child), ID(left_child)); child_diff = subtract_xml_object( left_child, right_child, marker); if(child_diff != NULL) { differences = TRUE; add_node_copy(diff, child_diff); free_xml(child_diff); } ); if(differences == FALSE) { /* check for XML_DIFF_MARKER in a child */ xml_child_iter( right, right_child, if(crm_element_value(right_child, XML_DIFF_MARKER)) { crm_debug_2("Found the root of the deletion: %s", name); differences = TRUE; break; } ); } if(differences == FALSE) { free_xml(diff); crm_debug_5("\tNo changes to <%s id=%s>", crm_str(name), ID(left)); return NULL; } crm_xml_add(diff, XML_ATTR_ID, ID(left)); return diff; } int add_xml_object(crm_data_t *parent, crm_data_t *target, const crm_data_t *update) { const char *object_id = NULL; const char *object_name = NULL; const char *right_val = NULL; int result = 0; crm_log_xml(LOG_DEBUG_5, "update:", update); crm_log_xml(LOG_DEBUG_5, "target:", target); CRM_CHECK(update != NULL, return 0); object_name = crm_element_name(update); object_id = ID(update); CRM_CHECK(object_name != NULL, return 0); if(target == NULL && object_id == NULL) { /* placeholder object */ target = find_xml_node(parent, object_name, FALSE); } else if(target == NULL) { target = find_entity(parent, object_name, object_id); } if(target == NULL) { target = create_xml_node(parent, object_name); crm_debug_2("Added <%s%s%s/>", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); CRM_CHECK(target != NULL, return 0); } else { crm_debug_2("Found node <%s%s%s/> to update", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); } xml_prop_iter(update, prop_name, left_value, right_val = crm_element_value(target, prop_name); if(safe_str_eq(prop_name, XML_DIFF_MARKER)) { continue; } else if(right_val == NULL) { crm_xml_add(target, prop_name, left_value); crm_debug_2("\t%s: %s (added)", crm_str(prop_name), crm_str(left_value)); } else if(safe_str_neq(left_value, right_val)) { crm_xml_add(target, prop_name, left_value); crm_debug_2("\t%s: %s->%s", crm_str(prop_name), crm_str(left_value), right_val); } ); crm_debug_3("Processing children of <%s id=%s>", crm_str(object_name), crm_str(object_id)); xml_child_iter( update, a_child, int tmp_result = 0; crm_debug_3("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); tmp_result = add_xml_object(target, NULL, a_child); if(tmp_result < 0) { crm_err("Error updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); /* only the first error is likely to be interesting */ if(result >= 0) { result = tmp_result; } } ); crm_debug_3("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id)); return result; } gboolean update_xml_child(crm_data_t *child, crm_data_t *to_update) { gboolean can_update = TRUE; CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(to_update != NULL, return FALSE); if(safe_str_neq(crm_element_name(to_update), crm_element_name(child))) { can_update = FALSE; } else if(safe_str_neq(ID(to_update), ID(child))) { can_update = FALSE; } else if(can_update) { crm_log_xml_debug(child, "Update match found..."); copy_in_properties(child, to_update); } xml_child_iter( child, child_of_child, /* only update the first one */ if(can_update) { break; } can_update = update_xml_child(child_of_child, to_update); ); return can_update; } gboolean find_xml_child(crm_data_t *child, const char *tag, const char *id) { gboolean match_found = TRUE; CRM_CHECK(child != NULL, return FALSE); if(safe_str_neq(tag, crm_element_name(child))) { match_found = FALSE; } else if(safe_str_neq(id, ID(child))) { match_found = FALSE; } else if(match_found) { crm_err("Update match found for <%s id=%s.../>", tag, id); crm_log_xml_debug(child, __FUNCTION__); } xml_child_iter( child, child_of_child, /* only find the first one */ if(match_found) { break; } match_found = find_xml_child(child_of_child, tag, id); ); return match_found; } gboolean delete_xml_child(crm_data_t *parent, crm_data_t *child, crm_data_t *to_delete) { gboolean can_delete = FALSE; const char *right_val = NULL; CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(to_delete != NULL, return FALSE); if(safe_str_eq(crm_element_name(to_delete), crm_element_name(child))) { can_delete = TRUE; } xml_prop_iter(to_delete, prop_name, left_value, if(can_delete == FALSE) { break; } right_val = crm_element_value(child, prop_name); if(safe_str_neq(left_value, right_val)) { can_delete = FALSE; } ); if(can_delete && parent != NULL) { crm_log_xml_debug(child, "Delete match found..."); cl_msg_remove_value(parent, child); child = NULL; } else if(can_delete) { crm_log_xml_debug(child, "Cannot delete the search root"); } xml_child_iter( child, child_of_child, /* only delete the first one */ if(can_delete) { break; } can_delete = delete_xml_child(child, child_of_child, to_delete); ); return can_delete; } void hash2nvpair(gpointer key, gpointer value, gpointer user_data) { const char *name = key; const char *s_value = value; crm_data_t *xml_node = user_data; crm_data_t *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR); crm_xml_add(xml_child, XML_ATTR_ID, name); crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name); crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value); crm_debug_3("dumped: name=%s value=%s", name, s_value); } void hash2field(gpointer key, gpointer value, gpointer user_data) { const char *name = key; const char *s_value = value; crm_data_t *xml_node = user_data; if(crm_element_value(xml_node, name) == NULL) { crm_xml_add(xml_node, name, s_value); crm_debug_3("dumped: %s=%s", name, s_value); } else { crm_debug_2("duplicate: %s=%s", name, s_value); } } #if CRM_DEPRECATED_SINCE_2_0_3 GHashTable * xml2list_202(crm_data_t *parent) { crm_data_t *nvpair_list = NULL; GHashTable *nvpair_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); CRM_CHECK(parent != NULL, return nvpair_hash); nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); if(nvpair_list == NULL) { crm_debug("No attributes in %s", crm_element_name(parent)); crm_log_xml_debug_2( parent,"No attributes for resource op"); } xml_child_iter_filter( nvpair_list, node_iter, XML_CIB_TAG_NVPAIR, const char *key = crm_element_value( node_iter, XML_NVPAIR_ATTR_NAME); const char *value = crm_element_value( node_iter, XML_NVPAIR_ATTR_VALUE); crm_debug_2("Added %s=%s", key, value); g_hash_table_insert( nvpair_hash, crm_strdup(key), crm_strdup(value)); ); return nvpair_hash; } #endif GHashTable * xml2list(crm_data_t *parent) { crm_data_t *nvpair_list = NULL; GHashTable *nvpair_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); CRM_CHECK(parent != NULL, return nvpair_hash); nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); if(nvpair_list == NULL) { crm_debug_2("No attributes in %s", crm_element_name(parent)); crm_log_xml_debug_2( parent,"No attributes for resource op"); } crm_log_xml_debug_3(nvpair_list, "Unpacking"); xml_prop_iter( nvpair_list, key, value, crm_debug_4("Added %s=%s", key, value); g_hash_table_insert( nvpair_hash, crm_strdup(key), crm_strdup(value)); ); return nvpair_hash; } static void assign_uuid(crm_data_t *xml_obj) { cl_uuid_t new_uuid; char *new_uuid_s = NULL; const char *tag_name = crm_element_name(xml_obj); const char *tag_id = ID(xml_obj); crm_malloc0(new_uuid_s, sizeof(char)*38); cl_uuid_generate(&new_uuid); cl_uuid_unparse(&new_uuid, new_uuid_s); crm_warn("Updating object from <%s id=%s/> to <%s id=%s/>", tag_name, tag_id?tag_id:"__empty__", tag_name, new_uuid_s); crm_xml_add(xml_obj, XML_ATTR_ID, new_uuid_s); crm_log_xml_debug(xml_obj, "Updated object"); crm_free(new_uuid_s); } gboolean do_id_check(crm_data_t *xml_obj, GHashTable *id_hash) { int lpc = 0; char *lookup_id = NULL; gboolean modified = FALSE; const char *tag_id = NULL; const char *tag_name = NULL; const char *lookup_value = NULL; gboolean created_hash = FALSE; const char *allowed_list[] = { XML_TAG_CIB, XML_TAG_FRAGMENT, XML_CIB_TAG_NODES, XML_CIB_TAG_RESOURCES, XML_CIB_TAG_CONSTRAINTS, XML_CIB_TAG_STATUS, XML_CIB_TAG_LRM, XML_LRM_TAG_RESOURCES, XML_TAG_PARAMS, "operations", }; const char *non_unique[] = { XML_LRM_TAG_RESOURCE, XML_LRM_TAG_RSC_OP, }; if(xml_obj == NULL) { return FALSE; } else if(id_hash == NULL) { created_hash = TRUE; id_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); } xml_child_iter( xml_obj, xml_child, if(do_id_check(xml_child, id_hash)) { modified = TRUE; } ); tag_id = ID(xml_obj); tag_name = TYPE(xml_obj); xml_prop_iter( xml_obj, local_prop_name, local_prop_value, if(safe_str_eq(local_prop_name, XML_DIFF_MARKER)) { crm_err("Detected "XML_DIFF_MARKER" attribute"); continue; } lookup_id = NULL; if(ID(xml_obj) != NULL) { for(lpc = 0; lpc < DIMOF(non_unique); lpc++) { if(safe_str_eq(tag_name, non_unique[lpc])) { /* this tag can have a non-unique ID */ break; } } if(lpc < DIMOF(non_unique)) { break; } lookup_id = crm_concat(tag_name, tag_id, '-'); lookup_value = g_hash_table_lookup(id_hash, lookup_id); if(lookup_value != NULL) { char *old_id = crm_strdup(tag_id); modified = TRUE; assign_uuid(xml_obj); tag_id = ID(xml_obj); crm_err("\"id\" collision detected..." " Multiple '%s' entries with id=\"%s\"," " assigned id=\"%s\"", tag_name, old_id, tag_id); } g_hash_table_insert( id_hash, lookup_id, crm_strdup(tag_id)); break; } for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) { if(safe_str_eq(tag_name, allowed_list[lpc])) { /* this tag is never meant to have an ID */ break; } } if(lpc < DIMOF(allowed_list)) { break; } modified = TRUE; crm_err("%s object with attributes but no ID field detected.", tag_name); assign_uuid(xml_obj); break; ); if(created_hash) { g_hash_table_destroy(id_hash); } return modified; } typedef struct name_value_s { const char *name; - const char *value; + const void *value; } name_value_t; static gint sort_pairs(gconstpointer a, gconstpointer b) { const name_value_t *pair_a = a; const name_value_t *pair_b = b; if(a == NULL && b == NULL) { return 0; } else if(a == NULL) { return 1; } else if(b == NULL) { return -1; } if(pair_a->name == NULL && pair_b->name == NULL) { return 0; } else if(pair_a->name == NULL) { return 1; } else if(pair_b->name == NULL) { return -1; } return strcmp(pair_a->name, pair_b->name); } static void dump_pair(gpointer data, gpointer user_data) { name_value_t *pair = data; crm_data_t *parent = user_data; crm_xml_add(parent, pair->name, pair->value); } static void free_pair(gpointer data, gpointer user_data) { name_value_t *pair = data; crm_free(pair); } static crm_data_t * sorted_xml(const crm_data_t *input) { GListPtr sorted = NULL; GListPtr unsorted = NULL; name_value_t *pair = NULL; crm_data_t *result = NULL; const char *name = crm_element_name(input); CRM_CHECK(input != NULL, return NULL); name = crm_element_name(input); CRM_CHECK(name != NULL, return NULL); result = create_xml_node(NULL, name); xml_prop_iter(input, p_name, p_value, crm_malloc0(pair, sizeof(name_value_t)); pair->name = p_name; pair->value = p_value; unsorted = g_list_prepend(unsorted, pair) ); sorted = g_list_sort(unsorted, sort_pairs); g_list_foreach(sorted, dump_pair, result); g_list_foreach(sorted, free_pair, NULL); g_list_free(sorted); return result; } /* "c048eae664dba840e1d2060f00299e9d" */ char * -calculate_xml_digest(crm_data_t *input) +calculate_xml_digest(crm_data_t *input, gboolean sort) { int i = 0; int digest_len = 16; char *digest = NULL; unsigned char *raw_digest = NULL; - crm_data_t *sorted = sorted_xml(input); - char *buffer = dump_xml_formatted(sorted); + crm_data_t *sorted = NULL; + char *buffer = NULL; + + if(sort) { + sorted = sorted_xml(input); + } else { + sorted = copy_xml(input); + } + buffer = dump_xml_formatted(sorted); CRM_CHECK(buffer != NULL && strlen(buffer) > 0, free_xml(sorted); return NULL); crm_malloc0(digest, sizeof(char) * (2 * digest_len + 1)); crm_malloc0(raw_digest, sizeof(char) * (digest_len + 1)); MD5((unsigned char *)buffer, strlen(buffer), raw_digest); for(i = 0; i < digest_len; i++) { sprintf(digest+(2*i), "%02x", raw_digest[i]); } crm_debug_2("Digest is: %s\n", digest); crm_log_xml(LOG_DEBUG_3, "digest:source", sorted); crm_free(buffer); crm_free(raw_digest); free_xml(sorted); return digest; }