diff --git a/cib/common.c b/cib/common.c index 86cd98267c..0341aaa8be 100644 --- a/cib/common.c +++ b/cib/common.c @@ -1,346 +1,349 @@ /* * Copyright (C) 2008 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 #include #include #include "common.h" extern gboolean cib_is_master; extern const char* cib_root; gboolean stand_alone = FALSE; extern enum cib_errors cib_status; extern gboolean can_write(int flags); extern enum cib_errors cib_perform_command( xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged); static xmlNode * cib_prepare_common(xmlNode *root, const char *section) { xmlNode *data = NULL; /* extract the CIB from the fragment */ if(root == NULL) { return NULL; } else if(safe_str_eq(crm_element_name(root), XML_TAG_FRAGMENT) || safe_str_eq(crm_element_name(root), F_CIB_CALLDATA)) { - data = find_xml_node(root, XML_TAG_CIB, TRUE); + data = first_named_child(root, XML_TAG_CIB); +#if 0 if(data != NULL) { crm_debug_3("Extracted CIB from %s", TYPE(root)); } else { crm_log_xml_debug_4(root, "No CIB"); } - +#endif } else { data = root; } /* grab the section specified for the command */ if(section != NULL && data != NULL - && safe_str_eq(crm_element_name(data), XML_TAG_CIB)){ + && crm_str_eq(crm_element_name(data), XML_TAG_CIB, TRUE)){ data = get_object_root(section, data); +#if 0 if(data != NULL) { crm_debug_3("Extracted %s from CIB", section); } else { crm_log_xml_debug_4(root, "No Section"); } +#endif } - crm_log_xml_debug_4(root, "cib:input"); + /* crm_log_xml_debug_4(root, "cib:input"); */ return data; } static enum cib_errors cib_prepare_none(xmlNode *request, xmlNode **data, const char **section) { *data = NULL; *section = crm_element_value(request, F_CIB_SECTION); return cib_ok; } static enum cib_errors cib_prepare_data(xmlNode *request, xmlNode **data, const char **section) { xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA); *section = crm_element_value(request, F_CIB_SECTION); *data = cib_prepare_common(input_fragment, *section); /* crm_log_xml_debug(*data, "data"); */ return cib_ok; } static enum cib_errors cib_prepare_sync(xmlNode *request, xmlNode **data, const char **section) { *data = NULL; *section = crm_element_value(request, F_CIB_SECTION); return cib_ok; } static enum cib_errors cib_prepare_diff(xmlNode *request, xmlNode **data, const char **section) { xmlNode *input_fragment = NULL; - const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); + const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); *data = NULL; *section = NULL; if(crm_is_true(update)) { input_fragment = get_message_xml(request,F_CIB_UPDATE_DIFF); } else { input_fragment = get_message_xml(request, F_CIB_CALLDATA); } CRM_CHECK(input_fragment != NULL,crm_log_xml(LOG_WARNING, "no input", request)); *data = cib_prepare_common(input_fragment, NULL); return cib_ok; } static enum cib_errors cib_cleanup_query(const char *op, xmlNode **data, xmlNode **output) { CRM_DEV_ASSERT(*data == NULL); return cib_ok; } static enum cib_errors cib_cleanup_data(const char *op, xmlNode **data, xmlNode **output) { free_xml(*output); *data = NULL; return cib_ok; } static enum cib_errors cib_cleanup_output(const char *op, xmlNode **data, xmlNode **output) { free_xml(*output); return cib_ok; } static enum cib_errors cib_cleanup_none(const char *op, xmlNode **data, xmlNode **output) { CRM_DEV_ASSERT(*data == NULL); CRM_DEV_ASSERT(*output == NULL); return cib_ok; } static enum cib_errors cib_cleanup_sync(const char *op, xmlNode **data, xmlNode **output) { /* data is non-NULL but doesnt need to be free'd */ CRM_DEV_ASSERT(*data == NULL); CRM_DEV_ASSERT(*output == NULL); return cib_ok; } /* typedef struct cib_operation_s { const char* operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_quorum; enum cib_errors (*prepare)(xmlNode *, xmlNode**, const char **); enum cib_errors (*cleanup)(xmlNode**, xmlNode**); enum cib_errors (*fn)( const char *, int, const char *, xmlNode*, xmlNode*, xmlNode**, xmlNode**); } cib_operation_t; */ /* technically bump does modify the cib... * but we want to split the "bump" from the "sync" */ static cib_operation_t cib_server_ops[] = { {NULL, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default}, {CIB_OP_QUERY, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_query, cib_process_query}, {CIB_OP_MODIFY, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_modify}, {CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_server_process_diff}, {CIB_OP_REPLACE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_replace_svr}, {CIB_OP_CREATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_create}, {CIB_OP_DELETE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete}, {CIB_OP_SYNC, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync}, {CIB_OP_BUMP, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_bump}, {CIB_OP_ERASE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_erase}, {CRM_OP_NOOP, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default}, {CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete_absolute}, {CIB_OP_UPGRADE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_upgrade}, {CIB_OP_SLAVE, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {CIB_OP_SLAVEALL, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {CIB_OP_SYNC_ONE, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync_one}, {CIB_OP_MASTER, TRUE, TRUE, FALSE, cib_prepare_data, cib_cleanup_data, cib_process_readwrite}, {CIB_OP_ISMASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {"cib_shutdown_req",FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_shutdown_req}, {CRM_OP_QUIT, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_quit}, {CRM_OP_PING, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_output, cib_process_ping}, }; enum cib_errors cib_get_operation_id(const char *op, int *operation) { int lpc = 0; static int max_msg_types = DIMOF(cib_server_ops); if(op != NULL) { for (lpc = 1; lpc < max_msg_types; lpc++) { if (strcmp(op, cib_server_ops[lpc].operation) == 0) { *operation = lpc; return cib_ok; } } } crm_err("Operation %s is not valid", op); *operation = -1; return cib_operation; } xmlNode * cib_msg_copy(xmlNode *msg, gboolean with_data) { int lpc = 0; const char *field = NULL; const char *value = NULL; xmlNode *value_struct = NULL; static const char *field_list[] = { F_XML_TAGNAME , F_TYPE , F_CIB_CLIENTID , F_CIB_CALLOPTS , F_CIB_CALLID , F_CIB_OPERATION , F_CIB_ISREPLY , F_CIB_SECTION , F_CIB_HOST , F_CIB_RC , F_CIB_DELEGATED , F_CIB_OBJID , F_CIB_OBJTYPE , F_CIB_EXISTING , F_CIB_SEENCOUNT , F_CIB_TIMEOUT , F_CIB_CALLBACK_TOKEN , F_CIB_GLOBAL_UPDATE , F_CIB_CLIENTNAME , F_CIB_NOTIFY_TYPE , F_CIB_NOTIFY_ACTIVATE }; static const char *data_list[] = { F_CIB_CALLDATA , F_CIB_UPDATE , F_CIB_UPDATE_RESULT }; xmlNode *copy = create_xml_node(NULL, "copy"); CRM_ASSERT(copy != NULL); for(lpc = 0; lpc < DIMOF(field_list); lpc++) { field = field_list[lpc]; value = crm_element_value(msg, field); if(value != NULL) { crm_xml_add(copy, field, value); } } for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) { field = data_list[lpc]; value_struct = get_message_xml(msg, field); if(value_struct != NULL) { add_message_xml(copy, field, value_struct); } } return copy; } cib_op_t *cib_op_func(int call_type) { return &(cib_server_ops[call_type].fn); } gboolean cib_op_modifies(int call_type) { return cib_server_ops[call_type].modifies_cib; } int cib_op_can_run( int call_type, int call_options, gboolean privileged, gboolean global_update) { int rc = cib_ok; if(rc == cib_ok && cib_server_ops[call_type].needs_privileges && privileged == FALSE) { /* abort */ return cib_not_authorized; } #if 0 if(rc == cib_ok && stand_alone == FALSE && global_update == FALSE && (call_options & cib_quorum_override) == 0 && cib_server_ops[call_type].needs_quorum) { return cib_no_quorum; } #endif return cib_ok; } int cib_op_prepare( int call_type, xmlNode *request, xmlNode **input, const char **section) { return cib_server_ops[call_type].prepare(request, input, section); } int cib_op_cleanup( int call_type, const char *op, xmlNode **input, xmlNode **output) { return cib_server_ops[call_type].cleanup(op, input, output); } diff --git a/cib/primatives.c b/cib/primatives.c index 8c681e98b0..d4b6bfa865 100644 --- a/cib/primatives.c +++ b/cib/primatives.c @@ -1,260 +1,255 @@ /* * 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 /* * In case of confusion, this is the memory management policy for * all functions in this file. * * All add/modify functions use copies of supplied data. * It is therefore appropriate that the callers free the supplied data * at some point after the function has finished. * * All delete functions will handle the freeing of deleted data * but not the function arguments. */ int delete_cib_object(xmlNode *parent, xmlNode *delete_spec) { const char *object_name = NULL; const char *object_id = NULL; xmlNode *equiv_node = NULL; int result = cib_ok; if(delete_spec != NULL) { object_name = crm_element_name(delete_spec); } object_id = crm_element_value(delete_spec, XML_ATTR_ID); crm_debug_3("Processing: <%s id=%s>", crm_str(object_name), crm_str(object_id)); if(delete_spec == NULL) { result = cib_NOOBJECT; } else if(parent == NULL) { result = cib_NOPARENT; } else if(object_id == NULL) { /* placeholder object */ equiv_node = find_xml_node(parent, object_name, FALSE); } else { equiv_node = find_entity(parent, object_name, object_id); } if(result != cib_ok) { ; /* nothing */ } else if(equiv_node == NULL) { result = cib_ok; } else if(xml_has_children(delete_spec) == FALSE) { /* only leaves are deleted */ crm_debug("Removing leaf: <%s id=%s>", crm_str(object_name), crm_str(object_id)); zap_xml_from_parent(parent, equiv_node); } else { xml_child_iter( delete_spec, child, int tmp_result = delete_cib_object(equiv_node, child); /* only the first error is likely to be interesting */ if(tmp_result != cib_ok && result == cib_ok) { result = tmp_result; } ); } return result; } int add_cib_object(xmlNode *parent, xmlNode *new_obj) { enum cib_errors result = cib_ok; const char *object_name = NULL; const char *object_id = NULL; xmlNode *equiv_node = NULL; if(new_obj != NULL) { object_name = crm_element_name(new_obj); } object_id = crm_element_value(new_obj, XML_ATTR_ID); crm_debug_3("Processing: <%s id=%s>", crm_str(object_name), crm_str(object_id)); if(new_obj == NULL || object_name == NULL) { result = cib_NOOBJECT; } else if(parent == NULL) { result = cib_NOPARENT; } else if(object_id == NULL) { /* placeholder object */ equiv_node = find_xml_node(parent, object_name, FALSE); } else { equiv_node = find_entity(parent, object_name, object_id); } if(result != cib_ok) { ; /* do nothing */ } else if(equiv_node != NULL) { result = cib_EXISTS; } else { result = update_cib_object(parent, new_obj); } return result; } int update_cib_object(xmlNode *parent, xmlNode *update) { const char *replace = NULL; const char *object_name = NULL; const char *object_id = NULL; xmlNode *target = NULL; int result = cib_ok; - CRM_DEV_ASSERT(update != NULL); - if(crm_assert_failed) { return cib_NOOBJECT; } - - CRM_DEV_ASSERT(parent != NULL); - if(crm_assert_failed) { return cib_NOPARENT; } + CRM_CHECK(update != NULL, return cib_NOOBJECT); + CRM_CHECK(parent != NULL, return cib_NOPARENT); object_name = crm_element_name(update); - object_id = ID(update); - - CRM_DEV_ASSERT(object_name != NULL); - if(crm_assert_failed) { return cib_NOOBJECT; } + CRM_CHECK(object_name != NULL, return cib_NOOBJECT); + object_id = ID(update); crm_debug_3("Processing: <%s id=%s>", crm_str(object_name), crm_str(object_id)); if(object_id == NULL) { /* placeholder object */ target = find_xml_node(parent, object_name, FALSE); } else { target = find_entity(parent, object_name, object_id); } if(target == NULL) { target = create_xml_node(parent, object_name); } crm_debug_2("Found node <%s id=%s> to update", crm_str(object_name), crm_str(object_id)); replace = crm_element_value(update, XML_CIB_ATTR_REPLACE); if(replace != NULL) { xmlNode *remove = NULL; int last = 0, lpc = 0, len = 0; len = strlen(replace); while(lpc <= len) { if(replace[lpc] == ',' || replace[lpc] == 0) { char *replace_item = NULL; if ( last == lpc ) { /* nothing to do */ last = lpc+1; goto incr; } crm_malloc0(replace_item, lpc - last + 1); strncpy(replace_item, replace+last, lpc-last); remove = find_xml_node(target, replace_item, FALSE); if(remove != NULL) { crm_debug_3("Replacing node <%s> in <%s>", replace_item, crm_element_name(target)); zap_xml_from_parent(target, remove); } crm_free(replace_item); last = lpc+1; } incr: lpc++; } xml_remove_prop(update, XML_CIB_ATTR_REPLACE); xml_remove_prop(target, XML_CIB_ATTR_REPLACE); } copy_in_properties(target, update); 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 = update_cib_object(target, a_child); /* only the first error is likely to be interesting */ if(tmp_result != cib_ok) { crm_err("Error updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); if(result == cib_ok) { result = tmp_result; } } ); crm_debug_3("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id)); return result; } diff --git a/crmd/election.c b/crmd/election.c index 2aee436ba6..6fbc464090 100644 --- a/crmd/election.c +++ b/crmd/election.c @@ -1,443 +1,443 @@ /* * 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 GHashTable *voted = NULL; uint highest_born_on = -1; static int current_election_id = 1; const char *get_hg_version(void); const char *get_hg_version(void) { /* limit this #define's use to a single file to avoid rebuilding more than necessary */ return HA_HG_VERSION; } /* A_ELECTION_VOTE */ void do_election_vote(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) { gboolean not_voting = FALSE; xmlNode *vote = NULL; /* don't vote if we're in one of these states or wanting to shut down */ switch(cur_state) { case S_RECOVERY: case S_STOPPING: case S_TERMINATE: crm_warn("Not voting in election, we're in state %s", fsa_state2string(cur_state)); not_voting = TRUE; break; default: break; } if(not_voting == FALSE) { if(is_set(fsa_input_register, R_STARTING)) { not_voting = TRUE; } } if(not_voting) { if(AM_I_DC) { register_fsa_input(C_FSA_INTERNAL, I_RELEASE_DC, NULL); } else { register_fsa_input(C_FSA_INTERNAL, I_PENDING, NULL); } return; } vote = create_request( CRM_OP_VOTE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL); current_election_id++; crm_xml_add(vote, F_CRM_ELECTION_OWNER, fsa_our_uuid); crm_xml_add_int(vote, F_CRM_ELECTION_ID, current_election_id); send_request(vote, NULL); crm_debug("Destroying voted hash"); g_hash_table_destroy(voted); free_xml(vote); voted = NULL; if(cur_state == S_ELECTION || cur_state == S_RELEASE_DC) { crm_timer_start(election_timeout); } else if(cur_state != S_INTEGRATION) { crm_err("Broken? Voting in state %s", fsa_state2string(cur_state)); } return; } char *dc_hb_msg = NULL; int beat_num = 0; gboolean do_dc_heartbeat(gpointer data) { return TRUE; } struct election_data_s { const char *winning_uname; unsigned int winning_bornon; }; static void log_member_uname(gpointer key, gpointer value, gpointer user_data) { if(crm_is_member_active(value)) { crm_err("%s: %s", (char*)user_data, (char*)key); } } static void log_node(gpointer key, gpointer value, gpointer user_data) { crm_err("%s: %s", (char*)user_data, (char*)key); } void do_election_check(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 voted_size = g_hash_table_size(voted); int num_members = crm_active_members(); /* in the case of #voted > #members, it is better to * wait for the timeout and give the cluster time to * stabilize */ if(fsa_state != S_ELECTION) { crm_debug("Ignore election check: we not in an election"); } else if(voted_size >= num_members) { /* we won and everyone has voted */ crm_timer_stop(election_timeout); register_fsa_input(C_FSA_INTERNAL, I_ELECTION_DC, NULL); if(voted_size > num_members) { char *data = NULL; data = crm_strdup("member"); g_hash_table_foreach(crm_peer_cache, log_member_uname, data); crm_free(data); data = crm_strdup("voted"); g_hash_table_foreach(voted, log_node, data); crm_free(data); } crm_debug("Destroying voted hash"); g_hash_table_destroy(voted); voted = NULL; } else { crm_info("Still waiting on %d non-votes (%d total)", num_members - voted_size, num_members); } return; } /* A_ELECTION_COUNT */ void do_election_count_vote(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 election_id = -1; gboolean we_loose = FALSE; + const char *op = NULL; + const char *vote_from = NULL; + const char *your_version = NULL; + const char *election_owner = NULL; + const char *reason = "unknown"; static time_t last_election_loss = 0; enum crmd_fsa_input election_result = I_NULL; crm_node_t *our_node = NULL, *your_node = NULL; ha_msg_input_t *vote = fsa_typed_data(fsa_dt_ha_msg); - const char *op = NULL; - const char *vote_from = NULL; - const char *your_version = NULL; - const char *election_owner = NULL; - CRM_CHECK(msg_data != NULL, return); - CRM_CHECK(vote != NULL, crm_err("Bogus data from %s", msg_data->origin); return); - - op = crm_element_value(vote->msg, F_CRM_TASK); - vote_from = crm_element_value(vote->msg, F_CRM_HOST_FROM); - your_version = crm_element_value(vote->msg, F_CRM_VERSION); - election_owner = crm_element_value(vote->msg, F_CRM_ELECTION_OWNER); - /* if the membership copy is NULL we REALLY shouldnt be voting * the question is how we managed to get here. */ - - CRM_CHECK(vote->msg != NULL, crm_err("Bogus data from %s", msg_data->origin); return); + + CRM_CHECK(msg_data != NULL, return); CRM_CHECK(crm_peer_cache != NULL, return); - CRM_CHECK(vote_from != NULL, vote_from = fsa_our_uname); + CRM_CHECK(vote != NULL, crm_err("Bogus data from %s", msg_data->origin); return); + CRM_CHECK(vote->msg != NULL, crm_err("Bogus data from %s", msg_data->origin); return); - our_node = g_hash_table_lookup(crm_peer_cache, fsa_our_uname); - your_node = g_hash_table_lookup(crm_peer_cache, vote_from); + vote_from = crm_element_value(vote->msg, F_CRM_HOST_FROM); + CRM_CHECK(vote_from != NULL, vote_from = fsa_our_uname); + your_node = g_hash_table_lookup(crm_peer_cache, vote_from); if(your_node == NULL) { crm_debug("Election ignore: The other side doesn't exist in CCM: %s", vote_from); return; } if(voted == NULL) { crm_debug("Created voted hash"); voted = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); } + op = crm_element_value(vote->msg, F_CRM_TASK); + election_owner = crm_element_value(vote->msg, F_CRM_ELECTION_OWNER); + crm_element_value_int(vote->msg, F_CRM_ELECTION_ID, &election_id); + + our_node = g_hash_table_lookup(crm_peer_cache, fsa_our_uname); crm_debug("Election %d, owner: %s", election_id, election_owner); /* update the list of nodes that have voted */ if(crm_str_eq(fsa_our_uuid, election_owner, TRUE) || crm_str_eq(fsa_our_uname, election_owner, TRUE)) { if(election_id == current_election_id) { char *uname_copy = NULL; char *op_copy = crm_strdup(op); uname_copy = crm_strdup(your_node->uname); g_hash_table_replace(voted, uname_copy, op_copy); crm_info("Updated voted hash for %s to %s", your_node->uname, op); } else { crm_debug("Ignore old '%s' from %s: %d vs. %d", op, your_node->uname, election_id, current_election_id); return; } } else { CRM_CHECK(safe_str_neq(op, CRM_OP_NOVOTE), return); } if(vote_from == NULL || crm_str_eq(vote_from, fsa_our_uname, TRUE)) { /* don't count our own vote */ crm_info("Election ignore: our %s (%s)", op,crm_str(vote_from)); return; } else if(crm_str_eq(op, CRM_OP_NOVOTE, TRUE)) { crm_info("Election ignore: no-vote from %s", vote_from); return; } crm_info("Election check: %s from %s", op, vote_from); + your_version = crm_element_value(vote->msg, F_CRM_VERSION); + if(our_node == NULL || safe_str_neq(our_node->state, CRM_NODE_MEMBER)) { - crm_info("Election fail: we don't exist in CCM"); + reason = "we don't exist in CCM"; we_loose = TRUE; } else if(compare_version(your_version, CRM_FEATURE_SET) < 0) { - crm_info("Election fail: version"); + reason = "version"; we_loose = TRUE; } else if(compare_version(your_version, CRM_FEATURE_SET) > 0) { - crm_info("Election pass: version"); + reason = "version"; } else if(is_heartbeat_cluster() && your_node->born < our_node->born) { - crm_debug("Election fail: born_on"); + reason = "born_on"; we_loose = TRUE; } else if(is_heartbeat_cluster() && your_node->born > our_node->born) { - crm_debug("Election pass: born_on"); + reason = "born_on"; } else if(fsa_our_uname == NULL || strcasecmp(fsa_our_uname, vote_from) > 0) { - crm_debug("Election fail: uname"); + reason = "uname"; we_loose = TRUE; } else { - CRM_CHECK(strcasecmp(fsa_our_uname, vote_from) != 0, ;); + CRM_CHECK(strcmp(fsa_our_uname, vote_from) != 0, ;); crm_debug("Them: %s (born=%llu) Us: %s (born=%llu)", vote_from, (unsigned long long)your_node->born, fsa_our_uname, (unsigned long long)our_node->born); /* cant happen... * } else if(strcasecmp(fsa_our_uname, vote_from) == 0) { * * default... * } else { // strcasecmp(fsa_our_uname, vote_from) < 0 * we win */ } if(we_loose) { - gboolean vote_sent = FALSE; xmlNode *novote = create_request( CRM_OP_NOVOTE, NULL, vote_from, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL); update_dc(NULL, FALSE); crm_timer_stop(election_timeout); - crm_debug("Election lost to %s (%d)", vote_from, election_id); + crm_debug("Election lost to %s (%d): %s", vote_from, election_id, reason); if(fsa_input_register & R_THE_DC) { crm_debug_3("Give up the DC to %s", vote_from); election_result = I_RELEASE_DC; } else { crm_debug_3("We werent the DC anyway"); election_result = I_PENDING; } crm_xml_add(novote, F_CRM_ELECTION_OWNER, election_owner); crm_xml_add_int(novote, F_CRM_ELECTION_ID, election_id); - vote_sent = send_request(novote, NULL); - CRM_DEV_ASSERT(vote_sent); + CRM_CHECK(send_request(novote, NULL),;); free_xml(novote); fsa_cib_conn->cmds->set_slave(fsa_cib_conn, cib_scope_local); last_election_loss = time(NULL); } else { int dampen = 2; time_t tm_now = time(NULL); if(tm_now - last_election_loss < (time_t)dampen) { crm_debug("Election ignore: We already lost an election less than %ds ago", dampen); return; } last_election_loss = 0; election_result = I_ELECTION; - crm_info("Election won over %s", vote_from); + crm_info("Election won over %s: %s", vote_from, reason); g_hash_table_destroy(voted); voted = NULL; } register_fsa_input(C_FSA_INTERNAL, election_result, NULL); } /* A_ELECT_TIMER_START, A_ELECTION_TIMEOUT */ /* we won */ void do_election_timer_ctrl(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) { } static void feature_update_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { if(rc != cib_ok) { fsa_data_t *msg_data = NULL; register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); } } /* A_DC_TAKEOVER */ void do_dc_takeover(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 rc = cib_ok; xmlNode *cib = NULL; crm_info("Taking over DC status for this partition"); set_bit_inplace(fsa_input_register, R_THE_DC); if(voted != NULL) { crm_debug_2("Destroying voted hash"); g_hash_table_destroy(voted); voted = NULL; } set_bit_inplace(fsa_input_register, R_JOIN_OK); set_bit_inplace(fsa_input_register, R_INVOKE_PE); fsa_cib_conn->cmds->set_slave_all(fsa_cib_conn, cib_none); fsa_cib_conn->cmds->set_master(fsa_cib_conn, cib_none); cib = create_xml_node(NULL, XML_TAG_CIB); crm_xml_add(cib, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); fsa_cib_update(XML_TAG_CIB, cib, cib_quorum_override, rc); add_cib_op_callback(fsa_cib_conn, rc, FALSE, NULL, feature_update_callback); update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, "dc-version", VERSION"-"HA_HG_VERSION, FALSE); free_xml(cib); } /* A_DC_RELEASE */ void do_dc_release(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) { if(action & A_DC_RELEASE) { crm_debug("Releasing the role of DC"); clear_bit_inplace(fsa_input_register, R_THE_DC); } else if (action & A_DC_RELEASED) { crm_info("DC role released"); #if 0 if( are there errors ) { /* we cant stay up if not healthy */ /* or perhaps I_ERROR and go to S_RECOVER? */ result = I_SHUTDOWN; } #endif register_fsa_input(C_FSA_INTERNAL, I_RELEASE_SUCCESS, NULL); } else { crm_err("Unknown action %s", fsa_action2string(action)); } crm_debug_2("Am I still the DC? %s", AM_I_DC?XML_BOOLEAN_YES:XML_BOOLEAN_NO); } diff --git a/crmd/fsa.c b/crmd/fsa.c index 629855e8d0..dedba63581 100644 --- a/crmd/fsa.c +++ b/crmd/fsa.c @@ -1,771 +1,749 @@ /* * 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 longclock_t action_start = 0; longclock_t action_stop = 0; longclock_t action_diff = 0; unsigned int action_diff_ms = 0; char *fsa_our_dc = NULL; cib_t *fsa_cib_conn = NULL; char *fsa_our_dc_version = NULL; ll_lrm_t *fsa_lrm_conn; char *fsa_our_uuid = NULL; char *fsa_our_uname = NULL; #if SUPPORT_HEARTBEAT ll_cluster_t *fsa_cluster_conn; #endif fsa_timer_t *wait_timer = NULL; fsa_timer_t *recheck_timer = NULL; fsa_timer_t *election_trigger = NULL; fsa_timer_t *election_timeout = NULL; fsa_timer_t *integration_timer = NULL; fsa_timer_t *finalization_timer = NULL; fsa_timer_t *shutdown_escalation_timer = NULL; volatile gboolean do_fsa_stall = FALSE; volatile long long fsa_input_register = 0; volatile long long fsa_actions = A_NOTHING; volatile enum crmd_fsa_state fsa_state = S_STARTING; extern uint highest_born_on; extern uint num_join_invites; extern GHashTable *welcomed_nodes; extern GHashTable *finalized_nodes; extern GHashTable *confirmed_nodes; extern GHashTable *integrated_nodes; extern void initialize_join(gboolean before); #define DOT_PREFIX "actions:trace: " #define do_dot_log(fmt, args...) do_crm_log(LOG_DEBUG_2, fmt, ##args) long long do_state_transition(long long actions, enum crmd_fsa_state cur_state, enum crmd_fsa_state next_state, fsa_data_t *msg_data); inline long long clear_flags(long long actions, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input); void dump_rsc_info(void); void dump_rsc_info_callback(const xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); void ghash_print_node(gpointer key, gpointer value, gpointer user_data); void s_crmd_fsa_actions(fsa_data_t *fsa_data); void log_fsa_input(fsa_data_t *stored_msg); void init_dotfile(void); void init_dotfile(void) { do_dot_log(DOT_PREFIX"digraph \"g\" {"); do_dot_log(DOT_PREFIX" size = \"30,30\""); do_dot_log(DOT_PREFIX" graph ["); do_dot_log(DOT_PREFIX" fontsize = \"12\""); do_dot_log(DOT_PREFIX" fontname = \"Times-Roman\""); do_dot_log(DOT_PREFIX" fontcolor = \"black\""); do_dot_log(DOT_PREFIX" bb = \"0,0,398.922306,478.927856\""); do_dot_log(DOT_PREFIX" color = \"black\""); do_dot_log(DOT_PREFIX" ]"); do_dot_log(DOT_PREFIX" node ["); do_dot_log(DOT_PREFIX" fontsize = \"12\""); do_dot_log(DOT_PREFIX" fontname = \"Times-Roman\""); do_dot_log(DOT_PREFIX" fontcolor = \"black\""); do_dot_log(DOT_PREFIX" shape = \"ellipse\""); do_dot_log(DOT_PREFIX" color = \"black\""); do_dot_log(DOT_PREFIX" ]"); do_dot_log(DOT_PREFIX" edge ["); do_dot_log(DOT_PREFIX" fontsize = \"12\""); do_dot_log(DOT_PREFIX" fontname = \"Times-Roman\""); do_dot_log(DOT_PREFIX" fontcolor = \"black\""); do_dot_log(DOT_PREFIX" color = \"black\""); do_dot_log(DOT_PREFIX" ]"); do_dot_log(DOT_PREFIX"// special nodes"); do_dot_log(DOT_PREFIX" \"S_PENDING\" "); do_dot_log(DOT_PREFIX" ["); do_dot_log(DOT_PREFIX" color = \"blue\""); do_dot_log(DOT_PREFIX" fontcolor = \"blue\""); do_dot_log(DOT_PREFIX" ]"); do_dot_log(DOT_PREFIX" \"S_TERMINATE\" "); do_dot_log(DOT_PREFIX" ["); do_dot_log(DOT_PREFIX" color = \"red\""); do_dot_log(DOT_PREFIX" fontcolor = \"red\""); do_dot_log(DOT_PREFIX" ]"); do_dot_log(DOT_PREFIX"// DC only nodes"); do_dot_log(DOT_PREFIX" \"S_INTEGRATION\" [ fontcolor = \"green\" ]"); do_dot_log(DOT_PREFIX" \"S_POLICY_ENGINE\" [ fontcolor = \"green\" ]"); do_dot_log(DOT_PREFIX" \"S_TRANSITION_ENGINE\" [ fontcolor = \"green\" ]"); do_dot_log(DOT_PREFIX" \"S_RELEASE_DC\" [ fontcolor = \"green\" ]"); do_dot_log(DOT_PREFIX" \"S_IDLE\" [ fontcolor = \"green\" ]"); } static void do_fsa_action(fsa_data_t *fsa_data, long long an_action, void (*function)(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)) { - int action_log_level = LOG_DEBUG; gboolean do_time_check = TRUE; - - if(is_set(fsa_actions, an_action) == FALSE) { - crm_err("Action %s (%.16llx) was not requestsed", - fsa_action2string(an_action), an_action); - return; - } + int action_log_level = LOG_DEBUG; if(an_action & A_MSG_ROUTE) { action_log_level = LOG_DEBUG_2; } else if(an_action & A_CIB_START) { do_time_check = FALSE; } fsa_actions &= ~an_action; - crm_debug_3("Invoking action %s (%.16llx)", - fsa_action2string(an_action), an_action); if(do_time_check) { action_start = time_longclock(); } do_crm_log(action_log_level, DOT_PREFIX"\t// %s", fsa_action2string(an_action)); - function(an_action, fsa_data->fsa_cause, fsa_state, - fsa_data->fsa_input, fsa_data); - crm_debug_3("Action complete: %s (%.16llx)", - fsa_action2string(an_action), an_action); + function(an_action, fsa_data->fsa_cause, fsa_state, fsa_data->fsa_input, fsa_data); if(do_time_check) { + const char *action_string = fsa_action2string(an_action); action_stop = time_longclock(); action_diff = sub_longclock(action_stop, action_start); action_diff_ms = longclockto_ms(action_diff); if(action_diff_ms > action_diff_max_ms) { crm_err("Action %s took %dms to complete", - fsa_action2string(an_action), - action_diff_ms); + action_string, action_diff_ms); } else if(action_diff_ms > action_diff_warn_ms) { crm_warn("Action %s took %dms to complete", - fsa_action2string(an_action), - action_diff_ms); + action_string, action_diff_ms); } } } enum crmd_fsa_state s_crmd_fsa(enum crmd_fsa_cause cause) { fsa_data_t *fsa_data = NULL; long long register_copy = fsa_input_register; long long new_actions = A_NOTHING; enum crmd_fsa_state last_state = fsa_state; crm_debug_2("FSA invoked with Cause: %s\tState: %s", fsa_cause2string(cause), fsa_state2string(fsa_state)); do_fsa_stall = FALSE; if(is_message() == FALSE && fsa_actions != A_NOTHING) { /* fake the first message so we can get into the loop */ crm_malloc0(fsa_data, sizeof(fsa_data_t)); fsa_data->fsa_input = I_NULL; fsa_data->fsa_cause = C_FSA_INTERNAL; fsa_data->origin = __FUNCTION__; fsa_data->data_type = fsa_dt_none; fsa_message_queue = g_list_append(fsa_message_queue, fsa_data); fsa_data = NULL; } while(is_message() && do_fsa_stall == FALSE) { crm_debug_2("Checking messages (%d remaining)", g_list_length(fsa_message_queue)); fsa_data = get_message(); - CRM_DEV_ASSERT(fsa_data != NULL); - if(crm_assert_failed) { - continue; - } + CRM_CHECK(fsa_data != NULL, continue); log_fsa_input(fsa_data); /* add any actions back to the queue */ fsa_actions |= fsa_data->actions; /* get the next batch of actions */ new_actions = crmd_fsa_actions[fsa_data->fsa_input][fsa_state]; fsa_actions |= new_actions; if(fsa_data->fsa_input != I_NULL && fsa_data->fsa_input != I_ROUTER) { crm_debug("Processing %s: [ state=%s cause=%s origin=%s ]", fsa_input2string(fsa_data->fsa_input), fsa_state2string(fsa_state), fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin); } -/* - if(fsa_actions & A_SHUTDOWN) { - crm_log_level = LOG_DEBUG_2; - } -*/ + #ifdef FSA_TRACE if(new_actions != A_NOTHING) { crm_debug_2("Adding FSA actions %.16llx for %s/%s", new_actions, fsa_input2string(fsa_data->fsa_input), fsa_state2string(fsa_state)); fsa_dump_actions(new_actions, "\tFSA scheduled"); } else if(fsa_data->fsa_input != I_NULL && new_actions == A_NOTHING) { crm_debug("No action specified for input,state (%s,%s)", fsa_input2string(fsa_data->fsa_input), fsa_state2string(fsa_state)); } if(fsa_data->actions != A_NOTHING) { crm_debug_2("Adding input actions %.16llx for %s/%s", new_actions, fsa_input2string(fsa_data->fsa_input), fsa_state2string(fsa_state)); fsa_dump_actions(fsa_data->actions,"\tInput scheduled"); } #endif /* logging : *before* the state is changed */ if(is_set(fsa_actions, A_ERROR)) { do_fsa_action(fsa_data, A_ERROR, do_log); } if(is_set(fsa_actions, A_WARN)) { do_fsa_action(fsa_data, A_WARN, do_log); } if(is_set(fsa_actions, A_LOG)) { do_fsa_action(fsa_data, A_LOG, do_log); } /* update state variables */ last_state = fsa_state; fsa_state = crmd_fsa_state[fsa_data->fsa_input][fsa_state]; /* * Hook to allow actions to removed due to certain inputs */ fsa_actions = clear_flags( fsa_actions, cause, fsa_state, fsa_data->fsa_input); /* * Hook for change of state. * Allows actions to be added or removed when entering a state */ if(last_state != fsa_state){ fsa_actions = do_state_transition( fsa_actions, last_state, fsa_state, fsa_data); } else { do_dot_log(DOT_PREFIX"\t// FSA input: State=%s \tCause=%s" " \tInput=%s \tOrigin=%s() \tid=%d", fsa_state2string(fsa_state), fsa_cause2string(fsa_data->fsa_cause), fsa_input2string(fsa_data->fsa_input), fsa_data->origin, fsa_data->id); } /* start doing things... */ s_crmd_fsa_actions(fsa_data); delete_fsa_input(fsa_data); fsa_data = NULL; } if(g_list_length(fsa_message_queue) > 0 || fsa_actions != A_NOTHING || do_fsa_stall) { crm_debug("Exiting the FSA: queue=%d, fsa_actions=0x%llx, stalled=%s", g_list_length(fsa_message_queue), fsa_actions, do_fsa_stall?"true":"false"); } else { crm_debug_2("Exiting the FSA"); } /* cleanup inputs? */ if(register_copy != fsa_input_register) { long long same = register_copy & fsa_input_register; fsa_dump_inputs(LOG_DEBUG, "Added input:", fsa_input_register ^ same); fsa_dump_inputs(LOG_DEBUG, "Removed input:", register_copy ^ same); } fsa_dump_queue(LOG_DEBUG); return fsa_state; } void s_crmd_fsa_actions(fsa_data_t *fsa_data) { /* * Process actions in order of priority but do only one * action at a time to avoid complicating the ordering. */ while(fsa_actions != A_NOTHING && do_fsa_stall == FALSE) { msg_queue_helper(); - CRM_DEV_ASSERT(fsa_data != NULL); - if(crm_assert_failed) { - return; - } + CRM_CHECK(fsa_data != NULL, return); /* regular action processing in order of action priority * * Make sure all actions that connect to required systems * are performed first */ if(is_set(fsa_actions, A_ERROR)) { do_fsa_action(fsa_data, A_ERROR, do_log); } else if(is_set(fsa_actions, A_WARN)) { do_fsa_action(fsa_data, A_WARN, do_log); } else if(is_set(fsa_actions, A_LOG)) { do_fsa_action(fsa_data, A_LOG, do_log); /* get out of here NOW! before anything worse happens */ } else if(is_set(fsa_actions, A_EXIT_1)) { do_fsa_action(fsa_data, A_EXIT_1, do_exit); /* essential start tasks */ } else if(is_set(fsa_actions, A_STARTUP)) { do_fsa_action(fsa_data, A_STARTUP, do_startup); } else if(is_set(fsa_actions, A_CIB_START)) { do_fsa_action(fsa_data, A_CIB_START, do_cib_control); } else if(is_set(fsa_actions, A_HA_CONNECT)) { do_fsa_action(fsa_data, A_HA_CONNECT, do_ha_control); } else if(is_set(fsa_actions, A_READCONFIG)) { do_fsa_action(fsa_data, A_READCONFIG, do_read_config); /* sub-system start/connect */ } else if(is_set(fsa_actions, A_LRM_CONNECT)) { do_fsa_action(fsa_data, A_LRM_CONNECT, do_lrm_control); } else if(is_set(fsa_actions, A_CCM_CONNECT)) { do_fsa_action(fsa_data, A_CCM_CONNECT, do_ccm_control); } else if(is_set(fsa_actions, A_TE_START)) { do_fsa_action(fsa_data, A_TE_START, do_te_control); } else if(is_set(fsa_actions, A_PE_START)) { do_fsa_action(fsa_data, A_PE_START, do_pe_control); /* sub-system restart */ } else if(is_set(fsa_actions, O_CIB_RESTART)) { do_fsa_action(fsa_data, O_CIB_RESTART, do_cib_control); } else if(is_set(fsa_actions, O_PE_RESTART)) { do_fsa_action(fsa_data, O_PE_RESTART, do_pe_control); } else if(is_set(fsa_actions, O_TE_RESTART)) { do_fsa_action(fsa_data, O_TE_RESTART, do_te_control); /* Timers */ /* else if(is_set(fsa_actions, O_DC_TIMER_RESTART)) { do_fsa_action(fsa_data, O_DC_TIMER_RESTART, do_timer_control) */; } else if(is_set(fsa_actions, A_DC_TIMER_STOP)) { do_fsa_action(fsa_data, A_DC_TIMER_STOP, do_timer_control); } else if(is_set(fsa_actions, A_INTEGRATE_TIMER_STOP)) { do_fsa_action(fsa_data, A_INTEGRATE_TIMER_STOP, do_timer_control); } else if(is_set(fsa_actions, A_INTEGRATE_TIMER_START)) { do_fsa_action(fsa_data, A_INTEGRATE_TIMER_START,do_timer_control); } else if(is_set(fsa_actions, A_FINALIZE_TIMER_STOP)) { do_fsa_action(fsa_data, A_FINALIZE_TIMER_STOP, do_timer_control); } else if(is_set(fsa_actions, A_FINALIZE_TIMER_START)) { do_fsa_action(fsa_data, A_FINALIZE_TIMER_START, do_timer_control); /* * Highest priority actions */ } else if(is_set(fsa_actions, A_MSG_ROUTE)) { do_fsa_action(fsa_data, A_MSG_ROUTE, do_msg_route); } else if(is_set(fsa_actions, A_RECOVER)) { do_fsa_action(fsa_data, A_RECOVER, do_recover); } else if(is_set(fsa_actions, A_CL_JOIN_RESULT)) { do_fsa_action(fsa_data, A_CL_JOIN_RESULT, do_cl_join_finalize_respond); } else if(is_set(fsa_actions, A_CL_JOIN_REQUEST)) { do_fsa_action(fsa_data, A_CL_JOIN_REQUEST, do_cl_join_offer_respond); } else if(is_set(fsa_actions, A_SHUTDOWN_REQ)) { do_fsa_action(fsa_data, A_SHUTDOWN_REQ, do_shutdown_req); } else if(is_set(fsa_actions, A_ELECTION_VOTE)) { do_fsa_action(fsa_data, A_ELECTION_VOTE, do_election_vote); } else if(is_set(fsa_actions, A_ELECTION_COUNT)) { do_fsa_action(fsa_data, A_ELECTION_COUNT, do_election_count_vote); } else if(is_set(fsa_actions, A_LRM_EVENT)) { do_fsa_action(fsa_data, A_LRM_EVENT, do_lrm_event); /* * High priority actions */ } else if(is_set(fsa_actions, A_STARTED)) { do_fsa_action(fsa_data, A_STARTED, do_started); } else if(is_set(fsa_actions, A_CL_JOIN_QUERY)) { do_fsa_action(fsa_data, A_CL_JOIN_QUERY, do_cl_join_query); } else if(is_set(fsa_actions, A_DC_TIMER_START)) { do_fsa_action(fsa_data, A_DC_TIMER_START, do_timer_control); /* * Medium priority actions */ } else if(is_set(fsa_actions, A_DC_TAKEOVER)) { do_fsa_action(fsa_data, A_DC_TAKEOVER, do_dc_takeover); } else if(is_set(fsa_actions, A_DC_RELEASE)) { do_fsa_action(fsa_data, A_DC_RELEASE, do_dc_release); } else if(is_set(fsa_actions, A_ELECTION_CHECK)) { do_fsa_action(fsa_data, A_ELECTION_CHECK, do_election_check); } else if(is_set(fsa_actions, A_ELECTION_START)) { do_fsa_action(fsa_data, A_ELECTION_START, do_election_vote); } else if(is_set(fsa_actions, A_TE_HALT)) { do_fsa_action(fsa_data, A_TE_HALT, do_te_invoke); } else if(is_set(fsa_actions, A_TE_CANCEL)) { do_fsa_action(fsa_data, A_TE_CANCEL, do_te_invoke); } else if(is_set(fsa_actions, A_DC_JOIN_OFFER_ALL)) { do_fsa_action(fsa_data, A_DC_JOIN_OFFER_ALL, do_dc_join_offer_all); } else if(is_set(fsa_actions, A_DC_JOIN_OFFER_ONE)) { do_fsa_action(fsa_data, A_DC_JOIN_OFFER_ONE, do_dc_join_offer_one); } else if(is_set(fsa_actions, A_DC_JOIN_PROCESS_REQ)) { do_fsa_action(fsa_data, A_DC_JOIN_PROCESS_REQ, do_dc_join_filter_offer); } else if(is_set(fsa_actions, A_DC_JOIN_PROCESS_ACK)) { do_fsa_action(fsa_data, A_DC_JOIN_PROCESS_ACK, do_dc_join_ack); /* * Low(er) priority actions * Make sure the CIB is always updated before invoking the * PE, and the PE before the TE */ } else if(is_set(fsa_actions, A_DC_JOIN_FINALIZE)) { do_fsa_action(fsa_data, A_DC_JOIN_FINALIZE, do_dc_join_finalize); } else if(is_set(fsa_actions, A_LRM_INVOKE)) { do_fsa_action(fsa_data, A_LRM_INVOKE, do_lrm_invoke); } else if(is_set(fsa_actions, A_PE_INVOKE)) { do_fsa_action(fsa_data, A_PE_INVOKE, do_pe_invoke); } else if(is_set(fsa_actions, A_TE_INVOKE)) { do_fsa_action(fsa_data, A_TE_INVOKE, do_te_invoke); } else if(is_set(fsa_actions, A_CL_JOIN_ANNOUNCE)) { do_fsa_action(fsa_data, A_CL_JOIN_ANNOUNCE, do_cl_join_announce); /* sub-system stop */ } else if(is_set(fsa_actions, A_DC_RELEASED)) { do_fsa_action(fsa_data, A_DC_RELEASED, do_dc_release); } else if(is_set(fsa_actions, A_PE_STOP)) { do_fsa_action(fsa_data, A_PE_STOP, do_pe_control); } else if(is_set(fsa_actions, A_TE_STOP)) { do_fsa_action(fsa_data, A_TE_STOP, do_te_control); } else if(is_set(fsa_actions, A_SHUTDOWN)) { do_fsa_action(fsa_data, A_SHUTDOWN, do_shutdown); } else if(is_set(fsa_actions, A_LRM_DISCONNECT)) { do_fsa_action(fsa_data, A_LRM_DISCONNECT, do_lrm_control); } else if(is_set(fsa_actions, A_CCM_DISCONNECT)) { do_fsa_action(fsa_data, A_CCM_DISCONNECT, do_ccm_control); } else if(is_set(fsa_actions, A_HA_DISCONNECT)) { do_fsa_action(fsa_data, A_HA_DISCONNECT, do_ha_control); } else if(is_set(fsa_actions, A_CIB_STOP)) { do_fsa_action(fsa_data, A_CIB_STOP, do_cib_control); } else if(is_set(fsa_actions, A_STOP)) { do_fsa_action(fsa_data, A_STOP, do_stop); /* exit gracefully */ } else if(is_set(fsa_actions, A_EXIT_0)) { do_fsa_action(fsa_data, A_EXIT_0, do_exit); /* Error checking and reporting */ } else { crm_err("Action %s (0x%llx) not supported ", fsa_action2string(fsa_actions), fsa_actions); register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, fsa_data, NULL, __FUNCTION__); } } } void log_fsa_input(fsa_data_t *stored_msg) { crm_debug_2("Processing queued input %d", stored_msg->id); if(stored_msg->fsa_cause == C_CCM_CALLBACK) { crm_debug_3("FSA processing CCM callback from %s", stored_msg->origin); } else if(stored_msg->fsa_cause == C_LRM_OP_CALLBACK) { crm_debug_3("FSA processing LRM callback from %s", stored_msg->origin); } else if(stored_msg->data == NULL) { crm_debug_3("FSA processing input from %s", stored_msg->origin); } else { ha_msg_input_t *ha_input = fsa_typed_data_adv( stored_msg, fsa_dt_ha_msg, __FUNCTION__); crm_debug_3("FSA processing XML message from %s", stored_msg->origin); crm_log_xml(LOG_MSG, "FSA message data", ha_input->xml); } } long long do_state_transition(long long actions, enum crmd_fsa_state cur_state, enum crmd_fsa_state next_state, fsa_data_t *msg_data) { long long tmp = actions; gboolean clear_recovery_bit = TRUE; enum crmd_fsa_cause cause = msg_data->fsa_cause; enum crmd_fsa_input current_input = msg_data->fsa_input; const char *state_from = fsa_state2string(cur_state); const char *state_to = fsa_state2string(next_state); const char *input = fsa_input2string(current_input); CRM_DEV_ASSERT(cur_state != next_state); do_dot_log(DOT_PREFIX"\t%s -> %s [ label=%s cause=%s origin=%s ]", state_from, state_to, input, fsa_cause2string(cause), msg_data->origin); crm_info("State transition %s -> %s [ input=%s cause=%s origin=%s ]", state_from, state_to, input, fsa_cause2string(cause), msg_data->origin); /* the last two clauses might cause trouble later */ if(election_timeout != NULL && next_state != S_ELECTION && cur_state != S_RELEASE_DC) { crm_timer_stop(election_timeout); /* } else { */ /* crm_timer_start(election_timeout); */ } #if 0 if(is_set(fsa_input_register, R_SHUTDOWN)){ set_bit_inplace(tmp, A_DC_TIMER_STOP); } #endif if(next_state == S_INTEGRATION) { set_bit_inplace(tmp, A_INTEGRATE_TIMER_START); } else { set_bit_inplace(tmp, A_INTEGRATE_TIMER_STOP); } if(next_state == S_FINALIZE_JOIN) { set_bit_inplace(tmp, A_FINALIZE_TIMER_START); } else { set_bit_inplace(tmp, A_FINALIZE_TIMER_STOP); } if(next_state != S_PENDING) { set_bit_inplace(tmp, A_DC_TIMER_STOP); } if(next_state != S_ELECTION) { highest_born_on = 0; } if(next_state != S_IDLE) { crm_timer_stop(recheck_timer); } if(cur_state == S_FINALIZE_JOIN && next_state == S_POLICY_ENGINE) { populate_cib_nodes(FALSE); do_update_cib_nodes(TRUE, __FUNCTION__); } switch(next_state) { case S_PENDING: fsa_cib_conn->cmds->set_slave(fsa_cib_conn, cib_scope_local); /* fall through */ case S_ELECTION: crm_debug_2("Resetting our DC to NULL on transition to %s", fsa_state2string(next_state)); update_dc(NULL, FALSE); break; case S_NOT_DC: if(is_set(fsa_input_register, R_SHUTDOWN)){ crm_info("(Re)Issuing shutdown request now" " that we have a new DC"); set_bit_inplace(tmp, A_SHUTDOWN_REQ); } CRM_DEV_ASSERT(fsa_our_dc != NULL); if(fsa_our_dc == NULL) { crm_err("Reached S_NOT_DC without a DC" " being recorded"); } break; case S_RECOVERY: clear_recovery_bit = FALSE; break; case S_FINALIZE_JOIN: CRM_DEV_ASSERT(AM_I_DC); if(cause == C_TIMER_POPPED) { crm_warn("Progressed to state %s after %s", fsa_state2string(next_state), fsa_cause2string(cause)); } if(g_hash_table_size(welcomed_nodes) > 0) { char *msg = crm_strdup( " Welcome reply not received from"); crm_warn("%u cluster nodes failed to respond" " to the join offer.", g_hash_table_size(welcomed_nodes)); g_hash_table_foreach( welcomed_nodes, ghash_print_node, msg); crm_free(msg); } else { crm_info("All %d cluster nodes " "responded to the join offer.", g_hash_table_size(integrated_nodes)); } break; case S_POLICY_ENGINE: CRM_DEV_ASSERT(AM_I_DC); if(cause == C_TIMER_POPPED) { crm_warn("Progressed to state %s after %s", fsa_state2string(next_state), fsa_cause2string(cause)); } if(g_hash_table_size(finalized_nodes) > 0) { char *msg = crm_strdup( " Confirm not received from"); crm_err("%u cluster nodes failed to confirm" " their join.", g_hash_table_size(finalized_nodes)); g_hash_table_foreach( finalized_nodes, ghash_print_node, msg); crm_free(msg); } else if(g_hash_table_size(confirmed_nodes) == crm_active_members()) { crm_info("All %u cluster nodes are" " eligible to run resources.", crm_active_members()); } else if(g_hash_table_size(confirmed_nodes) > crm_active_members()) { crm_err("We have more confirmed nodes than our membership does"); register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL); } else if(saved_ccm_membership_id != crm_peer_seq) { crm_info("Membership changed: %llu -> %llu - join restart", saved_ccm_membership_id, crm_peer_seq); register_fsa_input_before(C_FSA_INTERNAL, I_NODE_JOIN, NULL); } else { crm_warn("Only %u of %u cluster " "nodes are eligible to run resources - continue %d", g_hash_table_size(confirmed_nodes), crm_active_members(), g_hash_table_size(welcomed_nodes)); } /* initialize_join(FALSE); */ break; case S_STOPPING: case S_TERMINATE: /* possibly redundant */ set_bit_inplace(fsa_input_register, R_SHUTDOWN); break; case S_IDLE: CRM_DEV_ASSERT(AM_I_DC); dump_rsc_info(); if(is_set(fsa_input_register, R_SHUTDOWN)){ crm_info("(Re)Issuing shutdown request now" " that we are the DC"); set_bit_inplace(tmp, A_SHUTDOWN_REQ); } if(recheck_timer->period_ms > 0) { crm_timer_start(recheck_timer); } break; default: break; } if(clear_recovery_bit && next_state != S_PENDING) { tmp &= ~A_RECOVER; } else if(clear_recovery_bit == FALSE) { tmp |= A_RECOVER; } if(tmp != actions) { - fsa_dump_actions(actions ^ tmp, "New actions"); + /* fsa_dump_actions(actions ^ tmp, "New actions"); */ actions = tmp; } return actions; } inline long long clear_flags(long long actions, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input) { static long long startup_actions = A_STARTUP|A_CIB_START|A_LRM_CONNECT|A_CCM_CONNECT|A_HA_CONNECT|A_READCONFIG|A_STARTED|A_CL_JOIN_QUERY; if(cur_state == S_STOPPING || ((fsa_input_register & R_SHUTDOWN) == R_SHUTDOWN)) { clear_bit_inplace(actions, startup_actions); } /* fsa_dump_actions(actions ^ saved_actions, "Cleared Actions"); */ return actions; } void dump_rsc_info(void) { } void ghash_print_node(gpointer key, gpointer value, gpointer user_data) { const char *text = user_data; const char *uname = key; const char *value_s = value; crm_info("%s: %s %s", text, uname, value_s); } diff --git a/crmd/join_client.c b/crmd/join_client.c index 9dc689680e..a7c19bf044 100644 --- a/crmd/join_client.c +++ b/crmd/join_client.c @@ -1,274 +1,273 @@ /* * 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 int reannounce_count = 0; void join_query_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); extern ha_msg_input_t *copy_ha_msg_input(ha_msg_input_t *orig); /* A_CL_JOIN_QUERY */ /* is there a DC out there? */ void do_cl_join_query(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) { xmlNode *req = create_request(CRM_OP_JOIN_ANNOUNCE, NULL, NULL, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL); sleep(1); /* give the CCM time to propogate to the DC */ crm_debug("Querying for a DC"); send_msg_via_ha(req); free_xml(req); } /* A_CL_JOIN_ANNOUNCE */ /* this is kind of a workaround for the fact that we may not be around * or are otherwise unable to reply when the DC sends out A_WELCOME_ALL */ void do_cl_join_announce(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) { /* Once we hear from the DC, we can stop the timer * * This timer was started either on startup or when a node * left the CCM list */ /* dont announce if we're in one of these states */ if(cur_state != S_PENDING) { crm_warn("Do not announce ourselves in state %s", fsa_state2string(cur_state)); return; } if(AM_I_OPERATIONAL) { /* send as a broadcast */ xmlNode *req = create_request( CRM_OP_JOIN_ANNOUNCE, NULL, NULL, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL); crm_debug("Announcing availability"); update_dc(NULL, FALSE); send_msg_via_ha(req); free_xml(req); } else { /* Delay announce until we have finished local startup */ crm_warn("Delaying announce until local startup is complete"); return; } } static int query_call_id = 0; /* A_CL_JOIN_REQUEST */ /* aka. accept the welcome offer */ void do_cl_join_offer_respond(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) { ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); const char *welcome_from = crm_element_value(input->msg, F_CRM_HOST_FROM); const char *join_id = crm_element_value(input->msg, F_CRM_JOIN_ID); #if 0 if(we are sick) { log error ; /* save the request for later? */ return; } #endif crm_debug_2("Accepting join offer: join-%s", crm_element_value(input->msg, F_CRM_JOIN_ID)); /* we only ever want the last one */ if(query_call_id > 0) { crm_debug_3("Cancelling previous join query: %d", query_call_id); remove_cib_op_callback(query_call_id, FALSE); query_call_id = 0; } update_dc(input->msg, FALSE); if(safe_str_neq(welcome_from, fsa_our_dc)) { /* dont do anything until DC's sort themselves out */ crm_err("Expected a welcome from %s, but %s replied", fsa_our_dc, welcome_from); return; } CRM_DEV_ASSERT(input != NULL); query_call_id = fsa_cib_conn->cmds->query( fsa_cib_conn, NULL, NULL, cib_scope_local); add_cib_op_callback( fsa_cib_conn, query_call_id, FALSE, crm_strdup(join_id), join_query_callback); crm_debug_2("Registered join query callback: %d", query_call_id); register_fsa_action(A_DC_TIMER_STOP); } void join_query_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { xmlNode *local_cib = NULL; char *join_id = user_data; xmlNode *generation = create_xml_node( NULL, XML_CIB_TAG_GENERATION_TUPPLE); CRM_DEV_ASSERT(join_id != NULL); query_call_id = 0; if(rc == cib_ok) { #if CRM_DEPRECATED_SINCE_2_0_4 if(safe_str_eq(crm_element_name(output), XML_TAG_CIB)) { local_cib = output; } else { local_cib = find_xml_node(output, XML_TAG_CIB, TRUE); } #else local_cib = output; CRM_DEV_ASSERT(safe_str_eq(crm_element_name(local_cib), XML_TAG_CIB)); #endif } if(local_cib != NULL) { xmlNode *reply = NULL; crm_debug("Respond to join offer join-%s", join_id); crm_debug("Acknowledging %s as our DC", fsa_our_dc); copy_in_properties(generation, local_cib); reply = create_request( CRM_OP_JOIN_REQUEST, generation, fsa_our_dc, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL); crm_xml_add(reply, F_CRM_JOIN_ID, join_id); send_msg_via_ha(reply); free_xml(reply); } else { crm_err("Could not retrieve Generation to attach to our" " join acknowledgement: %s", cib_error2string(rc)); register_fsa_error_adv( C_FSA_INTERNAL, I_ERROR, NULL, NULL, __FUNCTION__); } crm_free(join_id); free_xml(generation); } /* A_CL_JOIN_RESULT */ /* aka. this is notification that we have (or have not) been accepted */ void do_cl_join_finalize_respond(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) { xmlNode *tmp1 = NULL; gboolean was_nack = TRUE; ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); int join_id = -1; const char *op = crm_element_value(input->msg,F_CRM_TASK); const char *ack_nack = crm_element_value(input->msg,CRM_OP_JOIN_ACKNAK); const char *welcome_from = crm_element_value(input->msg,F_CRM_HOST_FROM); if(safe_str_neq(op, CRM_OP_JOIN_ACKNAK)) { crm_debug_2("Ignoring op=%s message", op); return; } /* calculate if it was an ack or a nack */ if(crm_is_true(ack_nack)) { was_nack = FALSE; } crm_element_value_int(input->msg, F_CRM_JOIN_ID, &join_id); if(was_nack) { crm_err("Join join-%d with %s failed. NACK'd", join_id, welcome_from); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return; } if(AM_I_DC == FALSE && safe_str_eq(welcome_from, fsa_our_uname)) { crm_warn("Discarding our own welcome - we're no longer the DC"); return; } update_dc(input->msg, TRUE); /* send our status section to the DC */ crm_debug("Confirming join join-%d: %s", join_id, crm_element_value(input->msg, F_CRM_TASK)); - crm_debug_2("Discovering local LRM status"); tmp1 = do_lrm_query(TRUE); if(tmp1 != NULL) { xmlNode *reply = create_request( CRM_OP_JOIN_CONFIRM, tmp1, fsa_our_dc, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL); crm_xml_add_int(reply, F_CRM_JOIN_ID, join_id); crm_debug("join-%d: Join complete." " Sending local LRM status to %s", join_id, fsa_our_dc); send_msg_via_ha(reply); free_xml(reply); if(AM_I_DC == FALSE) { register_fsa_input_adv( cause, I_NOT_DC, NULL, A_NOTHING, TRUE, __FUNCTION__); } free_xml(tmp1); } else { - crm_err("Could send our LRM state to the DC"); + crm_err("Could not send our LRM state to the DC"); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); } } diff --git a/crmd/join_dc.c b/crmd/join_dc.c index 4fef5be4f9..f22e82156e 100644 --- a/crmd/join_dc.c +++ b/crmd/join_dc.c @@ -1,718 +1,726 @@ /* * 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 GHashTable *welcomed_nodes = NULL; GHashTable *integrated_nodes = NULL; GHashTable *finalized_nodes = NULL; GHashTable *confirmed_nodes = NULL; char *max_epoch = NULL; char *max_generation_from = NULL; xmlNode *max_generation_xml = NULL; void initialize_join(gboolean before); gboolean finalize_join_for(gpointer key, gpointer value, gpointer user_data); void finalize_sync_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source); void finalize_join(const char *caller); static int current_join_id = 0; unsigned long long saved_ccm_membership_id = 0; static void update_attrd(void) { static IPC_Channel *attrd = NULL; if(attrd == NULL) { crm_info("Connecting to attrd..."); attrd = init_client_ipc_comms_nodispatch(T_ATTRD); } if(attrd != NULL) { xmlNode *update = create_xml_node(NULL, __FUNCTION__); crm_xml_add(update, F_TYPE, T_ATTRD); crm_xml_add(update, F_ORIG, "crmd"); crm_xml_add(update, "task", "refresh"); if(send_ipc_message(attrd, update) == FALSE) { crm_err("attrd refresh failed"); attrd = NULL; } else { crm_debug("sent attrd refresh"); } free_xml(update); } else { crm_info("Couldn't connect to attrd this time"); } } void initialize_join(gboolean before) { /* clear out/reset a bunch of stuff */ crm_debug("join-%d: Initializing join data (flag=%s)", current_join_id, before?"true":"false"); g_hash_table_destroy(welcomed_nodes); g_hash_table_destroy(integrated_nodes); g_hash_table_destroy(finalized_nodes); g_hash_table_destroy(confirmed_nodes); if(before) { if(max_generation_from != NULL) { crm_free(max_generation_from); max_generation_from = NULL; } if(max_generation_xml != NULL) { free_xml(max_generation_xml); max_generation_xml = NULL; } clear_bit_inplace(fsa_input_register, R_HAVE_CIB); clear_bit_inplace(fsa_input_register, R_CIB_ASKED); } welcomed_nodes = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); integrated_nodes = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); finalized_nodes = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); confirmed_nodes = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); } void erase_node_from_join(const char *uname) { gboolean w = FALSE, i = FALSE, f = FALSE, c = FALSE; if(uname == NULL) { return; } if(welcomed_nodes != NULL) { w = g_hash_table_remove(welcomed_nodes, uname); } if(integrated_nodes != NULL) { i = g_hash_table_remove(integrated_nodes, uname); } if(finalized_nodes != NULL) { f = g_hash_table_remove(finalized_nodes, uname); } if(confirmed_nodes != NULL) { c = g_hash_table_remove(confirmed_nodes, uname); } if(w || i || f || c) { crm_info("Removed node %s from join calculations:" " welcomed=%d itegrated=%d finalized=%d confirmed=%d", uname, w, i, f, c); } } static void join_make_offer(gpointer key, gpointer value, gpointer user_data) { const char *join_to = NULL; const crm_node_t *member = value; CRM_ASSERT(member != NULL); if(crm_is_member_active(member) == FALSE) { return; } join_to = member->uname; if(join_to == NULL) { crm_err("No recipient for welcome message"); return; } erase_node_from_join(join_to); if(saved_ccm_membership_id != crm_peer_seq) { saved_ccm_membership_id = crm_peer_seq; crm_info("Making join offers based on membership %llu", crm_peer_seq); } if(member->processes & crm_proc_crmd) { xmlNode *offer = create_request( CRM_OP_JOIN_OFFER, NULL, join_to, CRM_SYSTEM_CRMD, CRM_SYSTEM_DC, NULL); char *join_offered = crm_itoa(current_join_id); crm_xml_add_int(offer, F_CRM_JOIN_ID, current_join_id); /* send the welcome */ crm_debug("join-%d: Sending offer to %s", current_join_id, join_to); send_msg_via_ha(offer); free_xml(offer); g_hash_table_insert( welcomed_nodes, crm_strdup(join_to), join_offered); } else { crm_info("Peer process on %s is not active (yet?): %.8lx %d", join_to, (long)member->processes, g_hash_table_size(crm_peer_cache)); } } /* A_DC_JOIN_OFFER_ALL */ void do_dc_join_offer_all(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) { /* reset everyones status back to down or in_ccm in the CIB * * any nodes that are active in the CIB but not in the CCM list * will be seen as offline by the PE anyway */ current_join_id++; initialize_join(TRUE); /* do_update_cib_nodes(TRUE, __FUNCTION__); */ update_dc(NULL, FALSE); if(cause == C_HA_MESSAGE && current_input == I_NODE_JOIN) { crm_info("A new node joined the cluster"); } g_hash_table_foreach(crm_peer_cache, join_make_offer, NULL); /* dont waste time by invoking the PE yet; */ crm_info("join-%d: Waiting on %d outstanding join acks", current_join_id, g_hash_table_size(welcomed_nodes)); } /* A_DC_JOIN_OFFER_ONE */ void do_dc_join_offer_one(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) { crm_node_t *member; - ha_msg_input_t *welcome = fsa_typed_data(fsa_dt_ha_msg); + ha_msg_input_t *welcome = NULL; const char *op = NULL; const char *join_to = NULL; + if(msg_data->data) { + welcome = fsa_typed_data(fsa_dt_ha_msg); + + } else { + crm_info("A new node joined - wait until it contacts us"); + return; + } + if(welcome == NULL) { crm_err("Attempt to send welcome message " "without a message to reply to!"); return; } join_to = crm_element_value(welcome->msg, F_CRM_HOST_FROM); if(join_to == NULL) { crm_err("Attempt to send welcome message " "without a host to reply to!"); return; } op = crm_element_value(welcome->msg, F_CRM_TASK); if(join_to != NULL && (cur_state == S_INTEGRATION || cur_state == S_FINALIZE_JOIN)) { /* note: it _is_ possible that a node will have been * sick or starting up when the original offer was made. * however, it will either re-announce itself in due course * _or_ we can re-store the original offer on the client. */ crm_debug("(Re-)offering membership to %s...", join_to); } crm_info("join-%d: Processing %s request from %s in state %s", current_join_id, op, join_to, fsa_state2string(cur_state)); /* always offer to the DC (ourselves) * this ensures the correct value for max_generation_from */ member = g_hash_table_lookup(crm_peer_cache, fsa_our_uname); join_make_offer(NULL, member, NULL); member = g_hash_table_lookup(crm_peer_cache, join_to); join_make_offer(NULL, member, NULL); /* this was a genuine join request, cancel any existing * transition and invoke the PE */ start_transition(fsa_state); /* dont waste time by invoking the pe yet; */ crm_debug("Waiting on %d outstanding join acks for join-%d", g_hash_table_size(welcomed_nodes), current_join_id); } static int compare_int_fields(xmlNode *left, xmlNode *right, const char *field) { const char *elem_l = crm_element_value(left, field); const char *elem_r = crm_element_value(right, field); int int_elem_l = crm_parse_int(elem_l, "-1"); int int_elem_r = crm_parse_int(elem_r, "-1"); if(int_elem_l < int_elem_r) { return -1; } else if(int_elem_l > int_elem_r) { return 1; } return 0; } /* A_DC_JOIN_PROCESS_REQ */ void do_dc_join_filter_offer(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) { xmlNode *generation = NULL; int cmp = 0; int join_id = -1; gboolean ack_nack_bool = TRUE; const char *ack_nack = CRMD_JOINSTATE_MEMBER; ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg); const char *join_from = crm_element_value(join_ack->msg, F_CRM_HOST_FROM); const char *ref = crm_element_value(join_ack->msg, XML_ATTR_REFERENCE); gpointer join_node = g_hash_table_lookup( crm_peer_cache, join_from); crm_debug("Processing req from %s", join_from); generation = join_ack->xml; crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id); if(max_generation_xml != NULL && generation != NULL) { int lpc = 0; const char *attributes[] = { XML_ATTR_GENERATION_ADMIN, XML_ATTR_GENERATION, XML_ATTR_NUMUPDATES, }; for(lpc = 0; cmp == 0 && lpc < DIMOF(attributes); lpc++) { cmp = compare_int_fields(max_generation_xml, generation, attributes[lpc]); } } if(join_node == NULL) { crm_err("Node %s is not a member", join_from); ack_nack_bool = FALSE; } else if(generation == NULL) { crm_err("Generation was NULL"); ack_nack_bool = FALSE; } else if(join_id != current_join_id) { crm_debug("Invalid response from %s: join-%d vs. join-%d", join_from, join_id, current_join_id); check_join_state(cur_state, __FUNCTION__); return; } else if(max_generation_xml == NULL) { max_generation_xml = copy_xml(generation); max_generation_from = crm_strdup(join_from); } else if(cmp < 0 || (cmp == 0 && safe_str_eq(join_from, fsa_our_uname))) { crm_debug("%s has a better generation number than" " the current max %s", join_from, max_generation_from); if(max_generation_xml) { crm_log_xml_debug(max_generation_xml, "Max generation"); } crm_log_xml_debug(generation, "Their generation"); crm_free(max_generation_from); free_xml(max_generation_xml); max_generation_from = crm_strdup(join_from); max_generation_xml = copy_xml(join_ack->xml); } if(ack_nack_bool == FALSE) { /* NACK this client */ ack_nack = CRMD_JOINSTATE_NACK; crm_err("join-%d: NACK'ing node %s (ref %s)", join_id, join_from, ref); } else { crm_debug("join-%d: Welcoming node %s (ref %s)", join_id, join_from, ref); } /* add them to our list of CRMD_STATE_ACTIVE nodes */ g_hash_table_insert( integrated_nodes, crm_strdup(join_from), crm_strdup(ack_nack)); crm_debug("%u nodes have been integrated into join-%d", g_hash_table_size(integrated_nodes), join_id); g_hash_table_remove(welcomed_nodes, join_from); if(check_join_state(cur_state, __FUNCTION__) == FALSE) { /* dont waste time by invoking the PE yet; */ crm_debug("join-%d: Still waiting on %d outstanding offers", join_id, g_hash_table_size(welcomed_nodes)); } } /* A_DC_JOIN_FINALIZE */ void do_dc_join_finalize(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) { enum cib_errors rc = cib_ok; /* This we can do straight away and avoid clients timing us out * while we compute the latest CIB */ crm_debug("Finializing join-%d for %d clients", current_join_id, g_hash_table_size(integrated_nodes)); if(g_hash_table_size(integrated_nodes) == 0) { return; } clear_bit_inplace(fsa_input_register, R_HAVE_CIB); if(max_generation_from == NULL || safe_str_eq(max_generation_from, fsa_our_uname)){ set_bit_inplace(fsa_input_register, R_HAVE_CIB); } if(is_set(fsa_input_register, R_IN_TRANSITION)) { crm_warn("join-%d: We are still in a transition." " Delaying until the TE completes.", current_join_id); crmd_fsa_stall(NULL); return; } if(is_set(fsa_input_register, R_HAVE_CIB) == FALSE) { /* ask for the agreed best CIB */ crm_info("join-%d: Asking %s for its copy of the CIB", current_join_id, crm_str(max_generation_from)); crm_log_xml_debug(max_generation_xml, "Requesting version"); set_bit_inplace(fsa_input_register, R_CIB_ASKED); rc = fsa_cib_conn->cmds->sync_from( fsa_cib_conn, max_generation_from, NULL, cib_quorum_override); fsa_cib_conn->cmds->register_callback( fsa_cib_conn, rc, 60, FALSE, crm_strdup(max_generation_from), "finalize_sync_callback", finalize_sync_callback); return; } else { /* Send _our_ CIB out to everyone */ fsa_cib_conn->cmds->sync_from( fsa_cib_conn, fsa_our_uname, NULL,cib_quorum_override); update_attrd(); } finalize_join(__FUNCTION__); } void finalize_sync_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { CRM_DEV_ASSERT(cib_not_master != rc); clear_bit_inplace(fsa_input_register, R_CIB_ASKED); if(rc != cib_ok) { do_crm_log((rc==cib_old_data?LOG_WARNING:LOG_ERR), "Sync from %s resulted in an error: %s", (char*)user_data, cib_error2string(rc)); /* restart the whole join process */ register_fsa_error_adv( C_FSA_INTERNAL, I_ELECTION_DC,NULL,NULL,__FUNCTION__); } else if(AM_I_DC && fsa_state == S_FINALIZE_JOIN) { finalize_join(__FUNCTION__); update_attrd(); } else { crm_debug("No longer the DC in S_FINALIZE_JOIN: %s/%s", AM_I_DC?"DC":"CRMd", fsa_state2string(fsa_state)); } crm_free(user_data); } void finalize_join(const char *caller) { xmlNode *cib = create_xml_node(NULL, XML_TAG_CIB); set_bit_inplace(fsa_input_register, R_HAVE_CIB); clear_bit_inplace(fsa_input_register, R_CIB_ASKED); set_uuid(cib, XML_ATTR_DC_UUID, fsa_our_uname); crm_debug_3("Update %s in the CIB to our uuid: %s", XML_ATTR_DC_UUID, crm_element_value(cib, XML_ATTR_DC_UUID)); fsa_cib_anon_update(NULL, cib, cib_quorum_override); free_xml(cib); crm_debug_3("Syncing to %d clients", g_hash_table_size(finalized_nodes)); /* make sure dc_uuid is re-set to us */ if(check_join_state(fsa_state, caller) == FALSE) { crm_debug("Notifying %d clients of join-%d results", g_hash_table_size(integrated_nodes), current_join_id); g_hash_table_foreach_remove( integrated_nodes, finalize_join_for, NULL); } } static void join_update_complete_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { fsa_data_t *msg_data = NULL; if(rc == cib_ok) { crm_debug("Join update %d complete", call_id); check_join_state(fsa_state, __FUNCTION__); } else { crm_err("Join update %d failed", call_id); crm_log_xml(LOG_DEBUG, "failed", msg); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); } } /* A_DC_JOIN_PROCESS_ACK */ void do_dc_join_ack(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 join_id = -1; int call_id = 0; ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg); const char *join_id_s = NULL; const char *join_state = NULL; const char *op = crm_element_value(join_ack->msg, F_CRM_TASK); const char *join_from = crm_element_value(join_ack->msg, F_CRM_HOST_FROM); if(safe_str_neq(op, CRM_OP_JOIN_CONFIRM)) { crm_debug("Ignoring op=%s message from %s", op, join_from); return; } crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id); join_id_s = crm_element_value(join_ack->msg, F_CRM_JOIN_ID); /* now update them to "member" */ crm_debug_2("Processing ack from %s", join_from); join_state = (const char *) g_hash_table_lookup(finalized_nodes, join_from); if(join_state == NULL) { crm_err("Join not in progress: ignoring join-%d from %s", join_id, join_from); return; } else if(safe_str_neq(join_state, CRMD_JOINSTATE_MEMBER)) { crm_err("Node %s wasnt invited to join the cluster",join_from); g_hash_table_remove(finalized_nodes, join_from); return; } else if(join_id != current_join_id) { crm_err("Invalid response from %s: join-%d vs. join-%d", join_from, join_id, current_join_id); g_hash_table_remove(finalized_nodes, join_from); return; } g_hash_table_remove(finalized_nodes, join_from); if(g_hash_table_lookup(confirmed_nodes, join_from) != NULL) { crm_err("join-%d: hash already contains confirmation from %s", join_id, join_from); } g_hash_table_insert( confirmed_nodes, crm_strdup(join_from), crm_strdup(join_id_s)); crm_info("join-%d: Updating node state to %s for %s", join_id, CRMD_JOINSTATE_MEMBER, join_from); /* update CIB with the current LRM status from the node * We dont need to notify the TE of these updates, a transition will * be started in due time */ erase_status_tag(join_from, XML_CIB_TAG_LRM); fsa_cib_update(XML_CIB_TAG_STATUS, join_ack->xml, cib_scope_local|cib_quorum_override|cib_can_create, call_id); add_cib_op_callback( fsa_cib_conn, call_id, FALSE, NULL, join_update_complete_callback); crm_debug("join-%d: Registered callback for LRM update %d", join_id, call_id); } gboolean finalize_join_for(gpointer key, gpointer value, gpointer user_data) { const char *join_to = NULL; const char *join_state = NULL; xmlNode *acknak = NULL; if(key == NULL || value == NULL) { return TRUE; } join_to = (const char *)key; join_state = (const char *)value; /* make sure the node exists in the config section */ create_node_entry(join_to, join_to, NORMALNODE); /* send the ack/nack to the node */ acknak = create_request( CRM_OP_JOIN_ACKNAK, NULL, join_to, CRM_SYSTEM_CRMD, CRM_SYSTEM_DC, NULL); crm_xml_add_int(acknak, F_CRM_JOIN_ID, current_join_id); /* set the ack/nack */ if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { crm_debug("join-%d: ACK'ing join request from %s, state %s", current_join_id, join_to, join_state); crm_xml_add(acknak, CRM_OP_JOIN_ACKNAK, XML_BOOLEAN_TRUE); g_hash_table_insert( finalized_nodes, crm_strdup(join_to), crm_strdup(CRMD_JOINSTATE_MEMBER)); } else { crm_warn("join-%d: NACK'ing join request from %s, state %s", current_join_id, join_to, join_state); crm_xml_add(acknak, CRM_OP_JOIN_ACKNAK, XML_BOOLEAN_FALSE); } send_msg_via_ha(acknak); free_xml(acknak); return TRUE; } void ghash_print_node(gpointer key, gpointer value, gpointer user_data); gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source) { crm_debug("Invoked by %s in state: %s", source, fsa_state2string(cur_state)); if(saved_ccm_membership_id != crm_peer_seq) { crm_info("%s: Membership changed since join started: %llu -> %llu", source, saved_ccm_membership_id, crm_peer_seq); register_fsa_input_before(C_FSA_INTERNAL, I_NODE_JOIN, NULL); } else if(cur_state == S_INTEGRATION) { if(g_hash_table_size(welcomed_nodes) == 0) { crm_debug("join-%d: Integration of %d peers complete: %s", current_join_id, g_hash_table_size(integrated_nodes), source); register_fsa_input_before( C_FSA_INTERNAL, I_INTEGRATED, NULL); return TRUE; } } else if(cur_state == S_FINALIZE_JOIN) { if(is_set(fsa_input_register, R_HAVE_CIB) == FALSE) { crm_debug("join-%d: Delaying I_FINALIZED until we have the CIB", current_join_id); return TRUE; } else if(g_hash_table_size(integrated_nodes) == 0 && g_hash_table_size(finalized_nodes) == 0) { crm_debug("join-%d complete: %s", current_join_id, source); register_fsa_input_later( C_FSA_INTERNAL, I_FINALIZED, NULL); } else if(g_hash_table_size(integrated_nodes) != 0 && g_hash_table_size(finalized_nodes) != 0) { char *msg = NULL; crm_err("join-%d: Waiting on %d integrated nodes" " AND %d finalized nodes", current_join_id, g_hash_table_size(integrated_nodes), g_hash_table_size(finalized_nodes)); msg = crm_strdup("Integrated node"); g_hash_table_foreach(integrated_nodes, ghash_print_node, msg); crm_free(msg); msg = crm_strdup("Finalized node"); g_hash_table_foreach(finalized_nodes, ghash_print_node, msg); crm_free(msg); } else if(g_hash_table_size(integrated_nodes) != 0) { crm_debug("join-%d: Still waiting on %d integrated nodes", current_join_id, g_hash_table_size(integrated_nodes)); } else if(g_hash_table_size(finalized_nodes) != 0) { crm_debug("join-%d: Still waiting on %d finalized nodes", current_join_id, g_hash_table_size(finalized_nodes)); } } return FALSE; } diff --git a/crmd/lrm.c b/crmd/lrm.c index 2d52d2eaa1..fd02d58dc4 100644 --- a/crmd/lrm.c +++ b/crmd/lrm.c @@ -1,1922 +1,1892 @@ /* * 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 struct recurring_op_s { char *rsc_id; char *op_key; int call_id; int interval; gboolean remove; gboolean cancelled; }; char *make_stop_id(const char *rsc, int call_id); void cib_rsc_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); gboolean build_operation_update( xmlNode *rsc_list, lrm_rsc_t *rsc, lrm_op_t *op, const char *src, int lpc, int level); gboolean build_active_RAs(xmlNode *rsc_list); gboolean is_rsc_active(const char *rsc_id); int do_update_resource(lrm_op_t *op); gboolean process_lrm_event(lrm_op_t *op); void do_lrm_rsc_op(lrm_rsc_t *rsc, const char *operation, xmlNode *msg, xmlNode *request); lrm_op_t *construct_op( xmlNode *rsc_op, const char *rsc_id, const char *operation); void send_direct_ack(const char *to_host, const char *to_sys, lrm_rsc_t *rsc, lrm_op_t* op, const char *rsc_id); void free_recurring_op(gpointer value); GHashTable *resources = NULL; GHashTable *pending_ops = NULL; GCHSource *lrm_source = NULL; int num_lrm_register_fails = 0; int max_lrm_register_fails = 30; void lrm_connection_destroy(gpointer user_data) { if(is_set(fsa_input_register, R_LRM_CONNECTED)) { crm_crit("LRM Connection failed"); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); } else { crm_info("LRM Connection disconnected"); } lrm_source = NULL; } /* A_LRM_CONNECT */ void 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) { if(fsa_lrm_conn == NULL) { register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return; } if(action & A_LRM_DISCONNECT) { if(verify_stopped(cur_state, LOG_INFO) == FALSE) { crmd_fsa_stall(NULL); return; } if(is_set(fsa_input_register, R_LRM_CONNECTED)) { clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); fsa_lrm_conn->lrm_ops->signoff(fsa_lrm_conn); crm_info("Disconnected from the LRM"); } /* TODO: Clean up the hashtable */ } if(action & A_LRM_CONNECT) { int ret = HA_OK; pending_ops = 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); 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; } } 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; } /* 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, lrm_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__); } } static void ghash_print_pending(gpointer key, gpointer value, gpointer user_data) { const char *stop_id = key; int *log_level = user_data; struct recurring_op_s *pending = value; do_crm_log(*log_level, "Pending action: %s (%s)", stop_id, pending->op_key); } static void ghash_print_pending_for_rsc(gpointer key, gpointer value, gpointer user_data) { const char *stop_id = key; char *rsc = user_data; struct recurring_op_s *pending = value; if(safe_str_eq(rsc, pending->rsc_id)) { do_crm_log(LOG_NOTICE, "%sction %s (%s) incomplete at shutdown", pending->interval==0?"A":"Recurring a", stop_id, pending->op_key); } } static void ghash_count_pending(gpointer key, gpointer value, gpointer user_data) { int *counter = user_data; struct recurring_op_s *pending = value; if(pending->interval > 0) { /* Ignore recurring actions in the shutdown calculations */ return; } (*counter)++; } gboolean verify_stopped(enum crmd_fsa_state cur_state, int log_level) { int counter = 0; gboolean rc = TRUE; GListPtr lrm_list = NULL; crm_debug("Checking for active resources before exit"); if(cur_state == S_TERMINATE) { log_level = LOG_ERR; } g_hash_table_foreach(pending_ops, ghash_count_pending, &counter); if(counter > 0) { rc = FALSE; do_crm_log(log_level, "%d pending LRM operations at shutdown%s", g_hash_table_size(pending_ops), cur_state == S_TERMINATE?"":"... waiting"); if(cur_state == S_TERMINATE || !is_set(fsa_input_register, R_SENT_RSC_STOP)) { g_hash_table_foreach( pending_ops, ghash_print_pending, &log_level); } goto bail; } if(is_set(fsa_input_register, R_LRM_CONNECTED)) { 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) == FALSE) { continue; } crm_err("Resource %s was active at shutdown." " You may ignore this error if it is unmanaged.", rsc_id); g_hash_table_foreach( pending_ops, ghash_print_pending_for_rsc, rsc_id); ); bail: set_bit_inplace(fsa_input_register, R_SENT_RSC_STOP); if(cur_state == S_TERMINATE) { rc = TRUE; } return rc; } static char * get_rsc_metadata(const char *type, const char *class, const char *provider) { char *metadata = NULL; CRM_CHECK(type != NULL, return NULL); CRM_CHECK(class != NULL, return NULL); if(provider == NULL) { provider = "heartbeat"; } crm_debug_2("Retreiving metadata for %s::%s:%s", type, class, provider); metadata = fsa_lrm_conn->lrm_ops->get_rsc_type_metadata( fsa_lrm_conn, class, type, provider); if(metadata) { /* copy the metadata because the LRM likes using * g_alloc instead of cl_malloc */ char *m_copy = crm_strdup(metadata); g_free(metadata); metadata = m_copy; } else { crm_warn("No metadata found for %s::%s:%s", type, class, provider); } return metadata; } typedef struct reload_data_s { char *key; char *metadata; gboolean can_reload; GListPtr restart_list; } reload_data_t; static void g_hash_destroy_reload(gpointer data) { reload_data_t *reload = data; crm_free(reload->key); crm_free(reload->metadata); slist_destroy(char, child, reload->restart_list, crm_free(child)); } GHashTable *reload_hash = NULL; static GListPtr get_rsc_restart_list(lrm_rsc_t *rsc, lrm_op_t *op) { int len = 0; char *key = NULL; char *copy = NULL; const char *value = NULL; const char *provider = NULL; xmlNode *params = NULL; xmlNode *actions = NULL; xmlNode *metadata = NULL; reload_data_t *reload = NULL; if(reload_hash == NULL) { reload_hash = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, g_hash_destroy_reload); } provider = rsc->provider; if(provider == NULL) { provider = "heartbeat"; } len = strlen(rsc->type) + strlen(rsc->class) + strlen(provider) + 4; crm_malloc(key, len); snprintf(key, len, "%s::%s:%s", rsc->type, rsc->class, provider); reload = g_hash_table_lookup(reload_hash, key); if(reload == NULL) { crm_malloc0(reload, sizeof(reload_data_t)); g_hash_table_insert(reload_hash, key, reload); reload->key = key; key = NULL; reload->metadata = get_rsc_metadata(rsc->type, rsc->class, provider); metadata = string2xml(reload->metadata); if(metadata == NULL) { crm_err("Metadata for %s::%s:%s is not valid XML", rsc->provider, rsc->class, rsc->type); goto cleanup; } actions = find_xml_node(metadata, "actions", TRUE); xml_child_iter_filter( actions, action, "action", value = crm_element_value(action, "name"); if(safe_str_eq("reload", value)) { reload->can_reload = TRUE; break; } ); if(reload->can_reload == FALSE) { goto cleanup; } params = find_xml_node(metadata, "parameters", TRUE); xml_child_iter_filter( params, param, "parameter", value = crm_element_value(param, "unique"); if(crm_is_true(value)) { value = crm_element_value(param, "name"); if(value == NULL) { crm_err("%s: NULL param", key); continue; } crm_debug("Attr %s is not reloadable", value); copy = crm_strdup(value); CRM_CHECK(copy != NULL, continue); reload->restart_list = g_list_append(reload->restart_list, copy); } ); } cleanup: crm_free(key); free_xml(metadata); return reload?reload->restart_list:NULL; } static void append_digest(lrm_rsc_t *rsc, lrm_op_t *op, xmlNode *update, const char *version, const char *magic, int level) { /* this will enable us to later determine that the * resource's parameters have changed and we should force * a restart */ char *digest = NULL; xmlNode *args_xml = create_xml_node(NULL, XML_TAG_PARAMS); g_hash_table_foreach(op->params, hash2field, args_xml); filter_action_parameters(args_xml, version); digest = calculate_xml_digest(args_xml, TRUE, FALSE); +#if 0 if(level < crm_log_level && op->interval == 0 && crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) { char *digest_source = dump_xml_unformatted(args_xml); do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n", digest, ID(update), magic, digest_source); crm_free(digest_source); } - +#endif crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest); free_xml(args_xml); crm_free(digest); } static void append_restart_list(lrm_rsc_t *rsc, lrm_op_t *op, xmlNode *update, const char *version) { int len = 0; char *list = NULL; char *digest = NULL; const char *value = NULL; gboolean non_empty = FALSE; xmlNode *restart = NULL; GListPtr restart_list = NULL; if(op->interval > 0) { /* monitors are not reloadable */ return; } else if(op->params) { crm_info("%s has no parameters", ID(update)); return; } else if(rsc == NULL) { - crm_info("Resource %s no longer in the LRM", op->rsc_id); return; - } else if(safe_str_neq(CRMD_ACTION_START, op->op_type)) { + } else if(crm_str_eq(CRMD_ACTION_START, op->op_type, TRUE) == FALSE) { /* only starts are potentially reloadable */ return; } else if(compare_version("1.0.8", version) > 0) { - crm_debug("Caller version %s does not support reloads", version); + /* Caller version does not support reloads */ return; } restart_list = get_rsc_restart_list(rsc, op); if(restart_list == NULL) { - crm_debug_2("Resource %s does not support reloads", op->rsc_id); + /* Resource does not support reloads */ return; } restart = create_xml_node(NULL, XML_TAG_PARAMS); slist_iter(param, const char, restart_list, lpc, int start = len; - if(param == NULL) { - crm_err("%s/%d: NULL param!", rsc->id, lpc); - continue; - } + CRM_CHECK(param != NULL, continue); value = g_hash_table_lookup(op->params, param); if(value != NULL) { non_empty = TRUE; crm_xml_add(restart, param, value); } len += strlen(param) + 2; crm_realloc(list, len+1); sprintf(list+start, " %s ", param); ); digest = calculate_xml_digest(restart, TRUE, FALSE); crm_xml_add(update, XML_LRM_ATTR_OP_RESTART, list); crm_xml_add(update, XML_LRM_ATTR_RESTART_DIGEST, digest); +#if 0 crm_debug("%s : %s", digest, list); if(non_empty) { crm_log_xml_debug(restart, "restart digest source"); } - slist_destroy(char, child, restart_list, - crm_free(child)); +#endif + + slist_destroy(char, child, restart_list, crm_free(child)); free_xml(restart); crm_free(digest); crm_free(list); } gboolean build_operation_update( xmlNode *xml_rsc, lrm_rsc_t *rsc, lrm_op_t *op, const char *src, int lpc, int level) { char *magic = NULL; const char *task = NULL; xmlNode *xml_op = NULL; char *op_id = NULL; char *local_user_data = NULL; const char *caller_version = NULL; - CRM_DEV_ASSERT(op != NULL); - if(crm_assert_failed) { - return FALSE; - } - + CRM_CHECK(op != NULL, 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(AM_I_DC) { caller_version = CRM_FEATURE_SET; } else if(fsa_our_dc_version != NULL) { caller_version = fsa_our_dc_version; } else { /* there is a small risk in formerly mixed clusters that * it will be sub-optimal. * however with our upgrade policy, the update we send * should still be completely supported anyway */ caller_version = g_hash_table_lookup( op->params, XML_ATTR_CRM_VERSION); crm_warn("Falling back to operation originator version: %s", caller_version); } crm_debug_3("DC version: %s", caller_version); task = op->op_type; /* remap the task name under various scenarios * this makes life easier for the PE when its trying determin the current state */ if(crm_str_eq(task, "reload", TRUE)) { if(op->op_status == LRM_OP_DONE) { task = CRMD_ACTION_START; } else { task = CRMD_ACTION_STATUS; } } else if(crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) { /* if the migrate_from fails it will have enough info to do the right thing */ if(op->op_status == LRM_OP_DONE) { task = CRMD_ACTION_STOP; } else { task = CRMD_ACTION_STATUS; } } else if(op->op_status == LRM_OP_DONE && crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) { task = CRMD_ACTION_START; - } - if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { + } else if(crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) { const char *n_type = g_hash_table_lookup( op->params, crm_meta_name("notify_type")); const char *n_task = g_hash_table_lookup( op->params, crm_meta_name("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 { + } + + if (op_id == NULL) { op_id = generate_op_key(op->rsc_id, task, op->interval); } xml_op = find_entity(xml_rsc, XML_LRM_TAG_RSC_OP, op_id); if(xml_op != NULL) { crm_log_xml(LOG_DEBUG, "Replacing existing entry", xml_op); } 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, task); - crm_xml_add(xml_op, XML_ATTR_ORIGIN, src); if(op->user_data == NULL) { char *id = crm_itoa(op->call_id); crm_debug("Generating fake transition key for:" " %s_%s_%d %d from %s", op->rsc_id, op->op_type, op->interval, op->call_id, op->app_name); local_user_data = generate_transition_key(-1, 0, 0, id); op->user_data = local_user_data; crm_free(id); } magic = generate_transition_magic(op->user_data, op->op_status, op->rc); - crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data); - crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic); - - 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_2("Resource action %s/%s %s: %d", - op->rsc_id, task, - op_status2text(op->op_status), op->rc); - break; - case LRM_OP_DONE: - break; - } - - crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id); - - /* set these on 'xml_rsc' too to make life easy for the PE */ - crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version); - crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc); - crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status); - crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval); + crm_xml_add(xml_op, XML_ATTR_ID, op_id); + crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task); + crm_xml_add(xml_op, XML_ATTR_ORIGIN, src); + crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version); + crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data); + crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic); + + crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id); + crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc); + crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status); + crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval); if(compare_version("2.1", caller_version) <= 0) { if(op->t_run || op->t_rcchange || op->exec_time || op->queue_time) { crm_debug_2("Timing data (%s_%s_%d): last=%lu change=%lu exec=%lu queue=%lu", op->rsc_id, op->op_type, op->interval, op->t_run, op->t_rcchange, op->exec_time, op->queue_time); crm_xml_add_int(xml_op, "last-run", op->t_run); crm_xml_add_int(xml_op, "last-rc-change", op->t_rcchange); crm_xml_add_int(xml_op, "exec-time", op->exec_time); crm_xml_add_int(xml_op, "queue-time", op->queue_time); } } append_digest(rsc, op, xml_op, caller_version, magic, level); append_restart_list(rsc, op, xml_op, caller_version); if(op->op_status != LRM_OP_DONE && crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) { const char *host = g_hash_table_lookup( op->params, crm_meta_name("migrate_source_uuid")); crm_xml_add(xml_op, CRMD_ACTION_MIGRATED, host); } if(local_user_data) { crm_free(local_user_data); op->user_data = NULL; } crm_free(magic); + crm_free(op_id); 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_3("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_%d (%d) for %s (status=%d, rc=%d)", op->op_type, op->interval, op->call_id, the_rsc->id, op->op_status, op->rc); CRM_ASSERT(max_call_id <= op->call_id); if(op->rc == EXECRA_OK && safe_str_eq(op->op_type, CRMD_ACTION_STOP)) { active = FALSE; } else if(op->rc == EXECRA_OK && safe_str_eq(op->op_type, CRMD_ACTION_MIGRATE)) { /* a stricter check is too complex... * leave that to the PE */ 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(xmlNode *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, int max_call_id = -1; xmlNode *xml_rsc = NULL; lrm_rsc_t *the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM: %s", rid); continue; } xml_rsc = create_xml_node(rsc_list, XML_LRM_TAG_RESOURCE); 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); slist_iter( op, lrm_op_t, op_list, llpc, if(max_call_id < op->call_id) { build_operation_update( xml_rsc, the_rsc, op, __FUNCTION__, llpc, LOG_DEBUG_2); } 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; } xmlNode* do_lrm_query(gboolean is_replace) { gboolean shut_down = FALSE; xmlNode *xml_result= NULL; xmlNode *xml_state = NULL; xmlNode *xml_data = NULL; xmlNode *rsc_list = NULL; const char *exp_state = CRMD_STATE_ACTIVE; 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); crm_xml_add(xml_data, XML_ATTR_ID, fsa_our_uuid); 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); xml_result = create_cib_fragment(xml_state, XML_CIB_TAG_STATUS); free_xml(xml_state); crm_log_xml_debug_3(xml_state, "Current state of the LRM"); return xml_result; } /* * Remove the rsc from the CIB * * Avoids refreshing the entire LRM section of this host */ #define rsc_template "//"XML_CIB_TAG_STATE"[@uname=\"%s\"]//"XML_LRM_TAG_RESOURCE"[@id=\"%s\"]" static void delete_rsc_entry(const char *rsc_id) { int max = 0; char *rsc_xpath = NULL; CRM_CHECK(rsc_id != NULL, return); max = strlen(rsc_template) + strlen(rsc_id) + strlen(fsa_our_uname) + 1; crm_malloc0(rsc_xpath, max); snprintf(rsc_xpath, max, rsc_template, fsa_our_uname, rsc_id); CRM_CHECK(rsc_id != NULL, return); crm_debug("sync: Sending delete op for %s", rsc_id); fsa_cib_conn->cmds->delete( fsa_cib_conn, rsc_xpath, NULL, cib_quorum_override|cib_xpath); crm_free(rsc_xpath); } /* * Remove the op from the CIB * * Avoids refreshing the entire LRM section of this host */ #define op_template "//"XML_CIB_TAG_STATE"[@uname=\"%s\"]//"XML_LRM_TAG_RESOURCE"[@id=\"%s\"]/"XML_LRM_TAG_RSC_OP"[@id=\"%s\"]" #define op_call_template "//"XML_CIB_TAG_STATE"[@uname=\"%s\"]//"XML_LRM_TAG_RESOURCE"[@id=\"%s\"]/"XML_LRM_TAG_RSC_OP"[@id=\"%s\" and @"XML_LRM_ATTR_CALLID"=\"%d\"]" static void delete_op_entry(lrm_op_t *op, const char *rsc_id, const char *key, int call_id) { xmlNode *xml_top = NULL; if(op != NULL) { xml_top = create_xml_node(NULL, XML_LRM_TAG_RSC_OP); crm_xml_add_int(xml_top, XML_LRM_ATTR_CALLID, op->call_id); crm_xml_add(xml_top, XML_ATTR_TRANSITION_KEY, op->user_data); crm_debug("async: Sending delete op for %s_%s_%d (call=%d)", op->rsc_id, op->op_type, op->interval, op->call_id); fsa_cib_conn->cmds->delete( fsa_cib_conn, XML_CIB_TAG_STATUS, xml_top, cib_quorum_override); } else if (rsc_id != NULL && key != NULL) { int max = 0; char *op_xpath = NULL; if(call_id > 0) { max = strlen(op_call_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) + 10; crm_malloc0(op_xpath, max); snprintf(op_xpath, max, op_call_template, fsa_our_uname, rsc_id, key, call_id); } else { max = strlen(op_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) + 1; crm_malloc0(op_xpath, max); snprintf(op_xpath, max, op_template, fsa_our_uname, rsc_id, key); } crm_debug("sync: Sending delete op for %s (call=%d)", rsc_id, call_id); fsa_cib_conn->cmds->delete( fsa_cib_conn, op_xpath, NULL, cib_quorum_override|cib_xpath); crm_free(op_xpath); } else { crm_err("Not enough information to delete op entry: rsc=%p key=%p", rsc_id, key); return; } crm_log_xml_debug_2(xml_top, "op:cancel"); free_xml(xml_top); } static gboolean cancel_op(lrm_rsc_t *rsc, const char *key, int op, gboolean remove) { int rc = HA_OK; struct recurring_op_s *pending = NULL; CRM_CHECK(op != 0, return FALSE); CRM_CHECK(rsc != NULL, return FALSE); if(key == NULL) { key = make_stop_id(rsc->id, op); } pending = g_hash_table_lookup(pending_ops, key); if(pending) { if(remove && pending->remove == FALSE) { pending->remove = TRUE; crm_debug("Scheduling %s for removal", key); } if(pending->cancelled) { crm_debug("Operation %s already cancelled", key); return TRUE; } pending->cancelled = TRUE; } else { crm_info("No pending op found for %s", key); } crm_debug("Cancelling op %d for %s (%s)", op, rsc->id, key); rc = rsc->ops->cancel_op(rsc, op); if(rc != HA_OK) { crm_debug("Op %d for %s (%s): Nothing to cancel", op, rsc->id, key); /* The caller needs to make sure the entry is * removed from the pending_ops list * * Usually by returning TRUE inside the worker function * supplied to g_hash_table_foreach_remove() * * Not removing the entry from pending_ops will block * the node from shutting down */ return FALSE; } return TRUE; } struct cancel_data { gboolean done; gboolean remove; const char *key; lrm_rsc_t *rsc; }; static gboolean cancel_action_by_key(gpointer key, gpointer value, gpointer user_data) { struct cancel_data *data = user_data; struct recurring_op_s *op = (struct recurring_op_s*)value; if(safe_str_eq(op->op_key, data->key)) { data->done = TRUE; if (cancel_op(data->rsc, key, op->call_id, data->remove) == FALSE) { return TRUE; } } return FALSE; } static gboolean cancel_op_key(lrm_rsc_t *rsc, const char *key, gboolean remove) { struct cancel_data data; CRM_CHECK(rsc != NULL, return FALSE); CRM_CHECK(key != NULL, return FALSE); data.key = key; data.rsc = rsc; data.done = FALSE; data.remove = remove; g_hash_table_foreach_remove(pending_ops, cancel_action_by_key, &data); return data.done; } static lrm_rsc_t * get_lrm_resource(xmlNode *resource, xmlNode *op_msg, gboolean do_create) { char rid[64]; lrm_rsc_t *rsc = NULL; const char *short_id = ID(resource); const char *long_id = crm_element_value(resource, XML_ATTR_ID_LONG); crm_debug_2("Retrieving %s from the LRM.", short_id); CRM_CHECK(short_id != NULL, return NULL); if(rsc == NULL) { /* check if its already there (short name) */ strncpy(rid, short_id, 64); rid[63] = 0; rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); } if(rsc == NULL && long_id != NULL) { /* try the long name instead */ strncpy(rid, long_id, 64); rid[63] = 0; rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); } if(rsc == NULL && do_create) { /* add it to the LRM */ const char *type = crm_element_value(resource, XML_ATTR_TYPE); const char *class = crm_element_value(resource, XML_AGENT_ATTR_CLASS); const char *provider = crm_element_value(resource, XML_AGENT_ATTR_PROVIDER); GHashTable *params = xml2list(op_msg); CRM_CHECK(class != NULL, return NULL); CRM_CHECK(type != NULL, return NULL); crm_debug_2("Adding rsc %s before operation", short_id); strncpy(rid, short_id, 64); rid[63] = 0; if(g_hash_table_size(params) == 0) { crm_log_xml_warn(op_msg, "EmptyParams"); } 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) { fsa_data_t *msg_data = NULL; crm_err("Could not add resource %s to LRM", rid); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); } } return rsc; } static gboolean lrm_remove_deleted_op( gpointer key, gpointer value, gpointer user_data) { const char *rsc = user_data; struct recurring_op_s *pending = value; if(safe_str_eq(rsc, pending->rsc_id)) { crm_info("Removing op %s:%d for deleted resource %s", pending->op_key, pending->call_id, rsc); return TRUE; } return FALSE; } /* A_LRM_INVOKE */ void 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) { gboolean done = FALSE; gboolean create_rsc = TRUE; const char *crm_op = NULL; const char *from_sys = NULL; const char *from_host = NULL; const char *operation = NULL; ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); crm_op = crm_element_value(input->msg, F_CRM_TASK); from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM); if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) { from_host = crm_element_value(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(safe_str_eq(crm_op, CRM_OP_LRM_FAIL)) { #if HAVE_LRM_ASYNC_FAIL lrm_rsc_t *rsc = NULL; xmlNode *xml_rsc = find_xml_node( input->xml, XML_CIB_TAG_RESOURCE, TRUE); CRM_CHECK(xml_rsc != NULL, return); rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc); if(rsc) { int rc = HA_OK; crm_info("Failing resource %s...", rsc->id); rc = fsa_lrm_conn->lrm_ops->fail_rsc(fsa_lrm_conn, rsc->id, 1, "do_lrm_invoke: Async failure"); if(rc != HA_OK) { crm_err("Could not initiate an asynchronous failure for %s (%d)", rsc->id, rc); } lrm_free_rsc(rsc); } else { crm_info("Cannot find/create resource in order to fail it..."); crm_log_xml_warn(input->msg, "bad input"); } #else crm_info("Failing resource..."); operation = "fail"; #endif } else if(input->xml != NULL) { operation = crm_element_value(input->xml, XML_LRM_ATTR_TASK); } if(safe_str_eq(crm_op, CRM_OP_LRM_REFRESH)) { enum cib_errors rc = cib_ok; xmlNode *fragment = do_lrm_query(TRUE); crm_info("Forcing a local LRM refresh"); fsa_cib_update(XML_CIB_TAG_STATUS, fragment, cib_quorum_override, rc); free_xml(fragment); } else if(safe_str_eq(crm_op, CRM_OP_LRM_QUERY)) { xmlNode *data = do_lrm_query(FALSE); xmlNode *reply = create_reply(input->msg, data); if(relay_message(reply, TRUE) == FALSE) { crm_err("Unable to route reply"); crm_log_xml(LOG_ERR, "reply", reply); } free_xml(reply); free_xml(data); } else if(safe_str_eq(operation, CRM_OP_PROBED) || safe_str_eq(crm_op, CRM_OP_REPROBE)) { int cib_options = cib_inhibit_notify; const char *probed = XML_BOOLEAN_TRUE; if(safe_str_eq(crm_op, CRM_OP_REPROBE)) { cib_options = cib_none; probed = XML_BOOLEAN_FALSE; } update_attr(fsa_cib_conn, cib_inhibit_notify, XML_CIB_TAG_STATUS, fsa_our_uuid, NULL, NULL, CRM_OP_PROBED, probed, FALSE); } else if(operation != NULL) { lrm_rsc_t *rsc = NULL; xmlNode *params = NULL; xmlNode *xml_rsc = find_xml_node( input->xml, XML_CIB_TAG_RESOURCE, TRUE); CRM_CHECK(xml_rsc != NULL, return); /* only the first 16 chars are used by the LRM */ params = find_xml_node(input->xml, XML_TAG_ATTRS, TRUE); if(safe_str_eq(operation, CRMD_ACTION_DELETE)) { create_rsc = FALSE; } rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc); if(rsc == NULL && create_rsc) { crm_err("Invalid resource definition"); crm_log_xml_warn(input->msg, "bad input"); } else if(rsc == NULL) { lrm_op_t* op = NULL; crm_err("Not creating resource for a %s event: %s", operation, ID(input->xml)); crm_log_xml_warn(input->msg, "bad input"); op = construct_op(input->xml, ID(xml_rsc), operation); op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; CRM_ASSERT(op != NULL); send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc)); free_lrm_op(op); } else if(safe_str_eq(operation, CRMD_ACTION_CANCEL)) { lrm_op_t* op = NULL; char *op_key = NULL; int call = 0; const char *call_id = NULL; const char *op_task = NULL; const char *op_interval = NULL; CRM_CHECK(params != NULL, crm_log_xml_warn(input->xml, "Bad command"); return); op_interval = crm_element_value(params, crm_meta_name("interval")); op_task = crm_element_value(params, crm_meta_name(XML_LRM_ATTR_TASK)); call_id = crm_element_value(params, crm_meta_name(XML_LRM_ATTR_CALLID)); #if CRM_DEPRECATED_SINCE_2_0_5 if(op_interval == NULL) { op_interval = crm_element_value(params, "interval"); } if(op_task == NULL) { op_task = crm_element_value(params, XML_LRM_ATTR_TASK); if(op_task == NULL) { op_task = crm_element_value(params, "task"); } } #endif CRM_CHECK(op_task != NULL, crm_log_xml_warn(input->xml, "Bad command"); return); CRM_CHECK(op_interval != NULL, crm_log_xml_warn(input->xml, "Bad command"); return); op = construct_op(input->xml, rsc->id, op_task); CRM_ASSERT(op != NULL); op_key = generate_op_key( rsc->id,op_task,crm_parse_int(op_interval,"0")); crm_debug("PE requested op %s (call=%s) be cancelled", op_key, call_id?call_id:"NA"); call = crm_parse_int(call_id, "0"); if(call == 0) { /* the normal case when the PE cancels a recurring op */ done = cancel_op_key(rsc, op_key, TRUE); } else { /* the normal case when the PE cancels an orphan op */ done = cancel_op(rsc, NULL, call, TRUE); } if(done == FALSE) { crm_debug("Nothing known about operation %d for %s", call, op_key); delete_op_entry(NULL, rsc->id, op_key, call); /* needed?? surely not otherwise the cancel_op_(_key) wouldn't * have failed in the first place */ g_hash_table_remove(pending_ops, op_key); } op->rc = EXECRA_OK; op->op_status = LRM_OP_DONE; send_direct_ack(from_host, from_sys, rsc, op, rsc->id); crm_free(op_key); free_lrm_op(op); } else if(safe_str_eq(operation, CRMD_ACTION_DELETE)) { int rc = HA_OK; lrm_op_t* op = NULL; CRM_ASSERT(rsc != NULL); op = construct_op(input->xml, rsc->id, operation); CRM_ASSERT(op != NULL); op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; crm_info("Removing resource %s from the LRM", rsc->id); rc = fsa_lrm_conn->lrm_ops->delete_rsc(fsa_lrm_conn, rsc->id); if(rc != HA_OK) { crm_err("Failed to remove resource %s", rsc->id); op->op_status = LRM_OP_ERROR; op->rc = EXECRA_UNKNOWN_ERROR; } delete_rsc_entry(rsc->id); send_direct_ack(from_host, from_sys, rsc, op, rsc->id); free_lrm_op(op); g_hash_table_foreach_remove(pending_ops, lrm_remove_deleted_op, rsc->id); if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) { /* this isn't expected - trigger a new transition */ time_t now = time(NULL); char *now_s = crm_itoa(now); crm_debug("Triggering a refresh after %s deleted %s from the LRM", from_sys, rsc->id); update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, "last-lrm-refresh", now_s, FALSE); crm_free(now_s); } } else if(rsc != NULL) { do_lrm_rsc_op(rsc, 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); } } lrm_op_t * construct_op(xmlNode *rsc_op, const char *rsc_id, const char *operation) { lrm_op_t *op = NULL; const char *op_delay = NULL; const char *op_timeout = NULL; const char *op_interval = 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->copyparams = 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(op->params == NULL) { CRM_DEV_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation)); } op_delay = g_hash_table_lookup(op->params, crm_meta_name("start_delay")); op_timeout = g_hash_table_lookup(op->params, crm_meta_name("timeout")); op_interval = g_hash_table_lookup(op->params, crm_meta_name("interval")); #if CRM_DEPRECATED_SINCE_2_0_5 if(op_delay == NULL) { op_delay = g_hash_table_lookup(op->params, "start_delay"); } if(op_timeout == NULL) { op_timeout = g_hash_table_lookup(op->params, "timeout"); } if(op_interval == NULL) { op_interval = g_hash_table_lookup(op->params, "interval"); } #endif op->interval = crm_parse_int(op_interval, "0"); op->timeout = crm_parse_int(op_timeout, "0"); op->start_delay = crm_parse_int(op_delay, "0"); /* sanity */ if(op->interval < 0) { op->interval = 0; } if(op->timeout < 0) { op->timeout = 0; } if(op->start_delay < 0) { op->start_delay = 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(op->interval != 0) { if(safe_str_eq(operation, CRMD_ACTION_START) || safe_str_eq(operation, CRMD_ACTION_STOP)) { crm_err("Start and Stop actions cannot have an interval"); op->interval = 0; } } /* reset the resource's parameters? */ if(op->interval == 0) { if(safe_str_eq(CRMD_ACTION_START, operation) || safe_str_eq(CRMD_ACTION_STATUS, operation)) { op->copyparams = 1; } } 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_rsc_t *rsc, lrm_op_t* op, const char *rsc_id) { xmlNode *reply = NULL; xmlNode *update, *iter; xmlNode *fragment; - CRM_DEV_ASSERT(op != NULL); - if(crm_assert_failed) { - return; - } + CRM_CHECK(op != NULL, 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; } update = create_node_state( fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__); iter = create_xml_node(update, XML_CIB_TAG_LRM); crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid); 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, rsc, op, __FUNCTION__, 0, LOG_DEBUG); 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_log_xml_debug_2(update, "ACK Update"); crm_info("ACK'ing resource op %s_%s_%d from %s: %s", op->rsc_id, op->op_type, op->interval, op->user_data, crm_element_value(reply, XML_ATTR_REFERENCE)); if(relay_message(reply, TRUE) == FALSE) { crm_log_xml(LOG_ERR, "Unable to route reply", reply); } free_xml(fragment); free_xml(update); free_xml(reply); } static gboolean stop_recurring_action_by_rsc(gpointer key, gpointer value, gpointer user_data) { lrm_rsc_t *rsc = user_data; struct recurring_op_s *op = (struct recurring_op_s*)value; if(op->interval != 0 && safe_str_eq(op->rsc_id, rsc->id)) { if (cancel_op(rsc, key, op->call_id, FALSE) == FALSE) { return TRUE; } } return FALSE; } void do_lrm_rsc_op(lrm_rsc_t *rsc, const char *operation, xmlNode *msg, xmlNode *request) { int call_id = 0; char *op_id = NULL; lrm_op_t* op = NULL; fsa_data_t *msg_data = NULL; const char *transition = NULL; CRM_CHECK(rsc != NULL, return); if(msg != NULL) { transition = crm_element_value(msg, XML_ATTR_TRANSITION_KEY); if(transition == NULL) { crm_log_xml_err(msg, "Missing transition number"); } } op = construct_op(msg, rsc->id, operation); /* stop the monitor before stopping the resource */ if(crm_str_eq(operation, CRMD_ACTION_STOP, TRUE) || crm_str_eq(operation, CRMD_ACTION_DEMOTE, TRUE) || crm_str_eq(operation, CRMD_ACTION_PROMOTE, TRUE) || crm_str_eq(operation, CRMD_ACTION_MIGRATE, TRUE)) { g_hash_table_foreach_remove(pending_ops, stop_recurring_action_by_rsc, rsc); } /* now do the op */ crm_info("Performing key=%s op=%s_%s_%d )", transition, rsc->id, operation, op->interval); if(fsa_state != S_NOT_DC && fsa_state != S_TRANSITION_ENGINE) { if(safe_str_neq(operation, "fail") && safe_str_neq(operation, CRMD_ACTION_STOP)) { crm_info("Discarding attempt to perform action %s on %s" " in state %s", operation, rsc->id, fsa_state2string(fsa_state)); op->rc = 99; op->op_status = LRM_OP_ERROR; send_direct_ack(NULL, NULL, rsc, op, rsc->id); free_lrm_op(op); crm_free(op_id); return; } } op_id = generate_op_key(rsc->id, op->op_type, op->interval); if(op->interval > 0) { /* cancel it so we can then restart it without conflict */ cancel_op_key(rsc, op_id, FALSE); 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, rsc->id, call_id); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); } else if(op->interval > 0 && op->start_delay > 5 * 60 * 1000) { char *uuid = NULL; int dummy = 0, target_rc = 0; crm_info("Faking confirmation of %s: execution postponed for over 5 minutes", op_id); decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &target_rc); crm_free(uuid); op->rc = target_rc; op->op_status = LRM_OP_DONE; send_direct_ack(NULL, NULL, rsc, op, rsc->id); } else { /* record all operations so we can wait * for them to complete during shutdown */ char *call_id_s = make_stop_id(rsc->id, call_id); struct recurring_op_s *pending = NULL; crm_malloc0(pending, sizeof(struct recurring_op_s)); crm_debug_2("Recording pending op: %d - %s %s", call_id, op_id, call_id_s); pending->call_id = call_id; pending->interval = op->interval; pending->op_key = crm_strdup(op_id); pending->rsc_id = crm_strdup(rsc->id); g_hash_table_replace(pending_ops, call_id_s, pending); } crm_free(op_id); free_lrm_op(op); return; } void free_recurring_op(gpointer value) { struct recurring_op_s *op = (struct recurring_op_s*)value; crm_free(op->rsc_id); crm_free(op->op_key); 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_CHECK(op != NULL, return NULL); + CRM_CHECK(op->rsc_id != NULL, return 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 cib_rsc_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { switch(rc) { case cib_ok: case cib_diff_failed: case cib_diff_resync: crm_debug_2("Resource update %d complete: rc=%d", call_id, rc); break; default: crm_err("Resource update %d failed: (rc=%d) %s", call_id, rc, cib_error2string(rc)); } } int do_update_resource(lrm_op_t* op) { /* */ int rc = cib_ok; lrm_rsc_t *rsc = NULL; xmlNode *update, *iter; CRM_CHECK(op != NULL, return 0); update = create_node_state( fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__); iter = create_xml_node(update, XML_CIB_TAG_LRM); crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid); 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); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, op->rsc_id); CRM_CHECK(rsc->type != NULL, crm_err("Resource %s has no value for type", op->rsc_id)); CRM_CHECK(rsc->class != NULL, crm_err("Resource %s has no value for class", 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); build_operation_update(iter, rsc, op, __FUNCTION__, 0, LOG_DEBUG); lrm_free_rsc(rsc); /* 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 */ fsa_cib_update(XML_CIB_TAG_STATUS, update, cib_quorum_override, rc); /* the return code is a call number, not an error code */ crm_debug_2("Sent resource state update message: %d", rc); fsa_cib_conn->cmds->register_callback( fsa_cib_conn, rc, 60, FALSE, NULL, "cib_rsc_callback", cib_rsc_callback); free_xml(update); return rc; } void 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) { CRM_CHECK(FALSE, return); } gboolean process_lrm_event(lrm_op_t *op) { char *op_id = NULL; char *op_key = NULL; int update_id = 0; int log_level = LOG_ERR; gboolean removed = FALSE; struct recurring_op_s *pending = NULL; CRM_CHECK(op != NULL, return FALSE); CRM_CHECK(op->rsc_id != NULL, return FALSE); op_key = generate_op_key(op->rsc_id, op->op_type, op->interval); switch(op->op_status) { case LRM_OP_ERROR: case LRM_OP_PENDING: case LRM_OP_NOTSUPPORTED: break; case LRM_OP_CANCELLED: log_level = LOG_INFO; break; case LRM_OP_DONE: log_level = LOG_INFO; break; case LRM_OP_TIMEOUT: log_level = LOG_DEBUG_3; crm_err("LRM operation %s (%d) %s (timeout=%dms)", op_key, op->call_id, op_status2text(op->op_status), op->timeout); break; default: crm_err("Mapping unknown status (%d) to ERROR", op->op_status); op->op_status = LRM_OP_ERROR; } if(op->op_status == LRM_OP_ERROR && (op->rc == EXECRA_RUNNING_MASTER || op->rc == EXECRA_NOT_RUNNING)) { /* Leave it up to the TE/PE to decide if this is an error */ op->op_status = LRM_OP_DONE; log_level = LOG_INFO; } op_id = make_stop_id(op->rsc_id, op->call_id); pending = g_hash_table_lookup(pending_ops, op_id); if(op->op_status != LRM_OP_CANCELLED) { update_id = do_update_resource(op); if(op->interval != 0) { goto out; } } else if(op->interval == 0) { /* no known valid reason for this to happen */ crm_err("Op %s (call=%d): Cancelled", op_key, op->call_id); } else if(pending == NULL) { crm_err("Op %s (call=%d): No 'pending' entry", op_key, op->call_id); } else if(op->user_data == NULL) { crm_err("Op %s (call=%d): No user data", op_key, op->call_id); } else if(pending->remove) { delete_op_entry(op, op->rsc_id, op_key, op->call_id); } else { crm_debug("Op %s (call=%d): no delete event required", op_key, op->call_id); } if(g_hash_table_remove(pending_ops, op_id)) { removed = TRUE; crm_debug_2("Op %s (call=%d, stop-id=%s): Confirmed", op_key, op->call_id, op_id); } out: do_crm_log(log_level, "LRM operation %s (call=%d, rc=%d, cib-update=%d, confirmed=%s) %s %s", op_key, op->call_id, op->rc, update_id, removed?"true":"false", op_status2text(op->op_status), execra_code2string(op->rc)); if(op->rc != 0 && op->output != NULL) { crm_info("Result: %s", op->output); } else if(op->output != NULL) { crm_debug("Result: %s", op->output); } crm_free(op_key); crm_free(op_id); return TRUE; } 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; } diff --git a/crmd/messages.c b/crmd/messages.c index 7fd805295f..47b8917087 100644 --- a/crmd/messages.c +++ b/crmd/messages.c @@ -1,1113 +1,1111 @@ /* * 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 GListPtr fsa_message_queue = NULL; extern void crm_shutdown(int nsig); enum crmd_fsa_input handle_request(xmlNode *stored_msg); enum crmd_fsa_input handle_response(xmlNode *stored_msg); enum crmd_fsa_input handle_shutdown_request(xmlNode *stored_msg); ha_msg_input_t *copy_ha_msg_input(ha_msg_input_t *orig); gboolean ipc_queue_helper(gpointer key, gpointer value, gpointer user_data); #ifdef MSG_LOG # define ROUTER_RESULT(x) crm_debug_3("Router result: %s", x); \ crm_log_xml(LOG_MSG, "router.log", msg); #else # define ROUTER_RESULT(x) crm_debug_3("Router result: %s", x) #endif /* debug only, can wrap all it likes */ int last_data_id = 0; void register_fsa_error_adv( enum crmd_fsa_cause cause, enum crmd_fsa_input input, fsa_data_t *cur_data, void *new_data, const char *raised_from) { /* save the current actions if any */ if(fsa_actions != A_NOTHING) { register_fsa_input_adv( cur_data?cur_data->fsa_cause:C_FSA_INTERNAL, I_NULL, cur_data?cur_data->data:NULL, fsa_actions, TRUE, __FUNCTION__); } /* reset the action list */ fsa_actions = A_NOTHING; /* register the error */ register_fsa_input_adv( cause, input, new_data, A_NOTHING, TRUE, raised_from); } int register_fsa_input_adv( enum crmd_fsa_cause cause, enum crmd_fsa_input input, void *data, long long with_actions, gboolean prepend, const char *raised_from) { unsigned old_len = g_list_length(fsa_message_queue); fsa_data_t *fsa_data = NULL; last_data_id++; CRM_CHECK(raised_from != NULL, raised_from = ""); crm_debug_2("%s %s FSA input %d (%s) (cause=%s) %s data", raised_from, prepend?"prepended":"appended",last_data_id, fsa_input2string(input), fsa_cause2string(cause), data?"with":"without"); if(input == I_WAIT_FOR_EVENT) { do_fsa_stall = TRUE; crm_debug("Stalling the FSA pending further input: cause=%s", fsa_cause2string(cause)); if(old_len > 0) { crm_warn("%s stalled the FSA with pending inputs", raised_from); fsa_dump_queue(LOG_DEBUG); } if(data == NULL) { set_bit_inplace(fsa_actions, with_actions); with_actions = A_NOTHING; return 0; } crm_err("%s stalled the FSA with data - this may be broken", raised_from); } if(input == I_NULL && with_actions == A_NOTHING /* && data == NULL */){ /* no point doing anything */ crm_err("Cannot add entry to queue: no input and no action"); return 0; } crm_malloc0(fsa_data, sizeof(fsa_data_t)); fsa_data->id = last_data_id; fsa_data->fsa_input = input; fsa_data->fsa_cause = cause; fsa_data->origin = raised_from; fsa_data->data = NULL; fsa_data->data_type = fsa_dt_none; fsa_data->actions = with_actions; if(with_actions != A_NOTHING) { crm_debug_3("Adding actions %.16llx to input", with_actions); } if(data != NULL) { switch(cause) { case C_FSA_INTERNAL: case C_CRMD_STATUS_CALLBACK: case C_IPC_MESSAGE: case C_HA_MESSAGE: crm_debug_3("Copying %s data from %s as a HA msg", fsa_cause2string(cause), raised_from); CRM_CHECK(((ha_msg_input_t*)data)->msg != NULL, crm_err("Bogus data from %s", raised_from)); fsa_data->data = copy_ha_msg_input(data); fsa_data->data_type = fsa_dt_ha_msg; break; case C_LRM_OP_CALLBACK: crm_debug_3("Copying %s data from %s as lrm_op_t", fsa_cause2string(cause), raised_from); fsa_data->data = copy_lrm_op((lrm_op_t*)data); fsa_data->data_type = fsa_dt_lrm; break; case C_CCM_CALLBACK: case C_SUBSYSTEM_CONNECT: case C_LRM_MONITOR_CALLBACK: case C_TIMER_POPPED: case C_SHUTDOWN: case C_HEARTBEAT_FAILED: case C_HA_DISCONNECT: case C_ILLEGAL: case C_UNKNOWN: case C_STARTUP: crm_err("Copying %s data (from %s)" " not yet implemented", fsa_cause2string(cause), raised_from); exit(1); break; } crm_debug_4("%s data copied", fsa_cause2string(fsa_data->fsa_cause)); } /* make sure to free it properly later */ if(prepend) { crm_debug_2("Prepending input"); fsa_message_queue = g_list_prepend(fsa_message_queue, fsa_data); } else { fsa_message_queue = g_list_append(fsa_message_queue, fsa_data); } crm_debug_2("Queue len: %d", g_list_length(fsa_message_queue)); fsa_dump_queue(LOG_DEBUG_2); if(old_len == g_list_length(fsa_message_queue)){ crm_err("Couldnt add message to the queue"); } if(fsa_source) { crm_debug_3("Triggering FSA: %s", __FUNCTION__); G_main_set_trigger(fsa_source); } return last_data_id; } void fsa_dump_queue(int log_level) { if(log_level < (int)crm_log_level) { return; } slist_iter( data, fsa_data_t, fsa_message_queue, lpc, do_crm_log(log_level, "queue[%d(%d)]: input %s raised by %s()\t(cause=%s)", lpc, data->id, fsa_input2string(data->fsa_input), data->origin, fsa_cause2string(data->fsa_cause)); ); } ha_msg_input_t * copy_ha_msg_input(ha_msg_input_t *orig) { ha_msg_input_t *copy = NULL; xmlNodePtr data = NULL; if(orig != NULL) { crm_debug_4("Copy msg"); data = copy_xml(orig->msg); } else { crm_debug_3("No message to copy"); } copy = new_ha_msg_input(data); if(orig->msg != NULL) { CRM_CHECK(copy->msg != NULL, crm_err("copy failed")); } return copy; } void delete_fsa_input(fsa_data_t *fsa_data) { lrm_op_t *op = NULL; xmlNode *foo = NULL; if(fsa_data == NULL) { return; } crm_debug_4("About to free %s data", fsa_cause2string(fsa_data->fsa_cause)); if(fsa_data->data != NULL) { switch(fsa_data->data_type) { case fsa_dt_ha_msg: delete_ha_msg_input(fsa_data->data); break; case fsa_dt_xml: foo = fsa_data->data; free_xml(foo); break; case fsa_dt_lrm: op = (lrm_op_t*)fsa_data->data; free_lrm_op(op); break; case fsa_dt_none: if(fsa_data->data != NULL) { crm_err("Dont know how to free %s data from %s", fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin); exit(1); } break; } crm_debug_4("%s data freed", fsa_cause2string(fsa_data->fsa_cause)); } crm_free(fsa_data); } /* returns the next message */ fsa_data_t * get_message(void) { fsa_data_t* message = g_list_nth_data(fsa_message_queue, 0); fsa_message_queue = g_list_remove(fsa_message_queue, message); crm_debug_2("Processing input %d", message->id); return message; } /* returns the current head of the FIFO queue */ gboolean is_message(void) { return (g_list_length(fsa_message_queue) > 0); } void * fsa_typed_data_adv( fsa_data_t *fsa_data, enum fsa_data_type a_type, const char *caller) { void *ret_val = NULL; if(fsa_data == NULL) { do_crm_log(LOG_ERR, "%s: No FSA data available", caller); } else if(fsa_data->data == NULL) { do_crm_log(LOG_ERR, "%s: No message data available. Origin: %s", caller, fsa_data->origin); } else if(fsa_data->data_type != a_type) { do_crm_log(LOG_CRIT, "%s: Message data was the wrong type! %d vs. requested=%d." " Origin: %s", caller, fsa_data->data_type, a_type, fsa_data->origin); CRM_ASSERT(fsa_data->data_type == a_type); } else { ret_val = fsa_data->data; } return ret_val; } /* A_MSG_ROUTE */ void do_msg_route(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) { ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); route_message(msg_data->fsa_cause, input->msg); } void route_message(enum crmd_fsa_cause cause, xmlNode *input) { ha_msg_input_t fsa_input; enum crmd_fsa_input result = I_NULL; fsa_input.msg = input; CRM_CHECK(cause == C_IPC_MESSAGE || cause == C_HA_MESSAGE, return); /* try passing the buck first */ crm_debug_4("Attempting to route message"); if(relay_message(input, cause==C_IPC_MESSAGE)) { crm_debug_4("Message routed..."); return; } crm_debug_4("Message wasn't routed... try handling locally"); /* calculate defer */ result = handle_message(input); switch(result) { case I_NULL: crm_debug_4("Message processed"); break; case I_CIB_OP: break; case I_ROUTER: break; case I_NODE_JOIN: case I_JOIN_REQUEST: case I_JOIN_RESULT: break; default: crm_debug_4("Defering local processing of message"); register_fsa_input_later(cause, result, &fsa_input); result = I_NULL; break; } if(result != I_NULL) { /* add to the front of the queue */ register_fsa_input(cause, result, &fsa_input); } } gboolean send_request(xmlNode *msg, char **msg_reference) { if(msg_reference != NULL) { *msg_reference = crm_strdup( crm_element_value(msg, XML_ATTR_REFERENCE)); } if(relay_message(msg, TRUE) == FALSE) { ha_msg_input_t fsa_input; fsa_input.msg = msg; register_fsa_input(C_IPC_MESSAGE, I_ROUTER, &fsa_input); return FALSE; } return TRUE; } gboolean relay_message(xmlNode *msg, gboolean originated_locally) { int is_for_dc = 0; int is_for_dcib = 0; int is_for_te = 0; int is_for_crm = 0; int is_for_cib = 0; int is_local = 0; gboolean processing_complete = FALSE; const char *host_to = crm_element_value(msg, F_CRM_HOST_TO); const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO); const char *sys_from= crm_element_value(msg, F_CRM_SYS_FROM); const char *type = crm_element_value(msg, F_TYPE); const char *msg_error = NULL; crm_debug_3("Routing message %s", crm_element_value(msg, XML_ATTR_REFERENCE)); if(msg == NULL) { msg_error = "Cannot route empty message"; } else if(safe_str_eq(CRM_OP_HELLO, crm_element_value(msg, F_CRM_TASK))){ /* quietly ignore */ processing_complete = TRUE; } else if(safe_str_neq(type, T_CRM)) { msg_error = "Bad message type"; } else if(sys_to == NULL) { msg_error = "Bad message destination: no subsystem"; } if(msg_error != NULL) { processing_complete = TRUE; crm_err("%s", msg_error); crm_log_xml(LOG_WARNING, "bad msg", msg); } if(processing_complete) { return TRUE; } processing_complete = TRUE; is_for_dc = (strcasecmp(CRM_SYSTEM_DC, sys_to) == 0); is_for_dcib = (strcasecmp(CRM_SYSTEM_DCIB, sys_to) == 0); is_for_te = (strcasecmp(CRM_SYSTEM_TENGINE, sys_to) == 0); is_for_cib = (strcasecmp(CRM_SYSTEM_CIB, sys_to) == 0); is_for_crm = (strcasecmp(CRM_SYSTEM_CRMD, sys_to) == 0); is_local = 0; if(host_to == NULL || strlen(host_to) == 0) { if(is_for_dc || is_for_te) { is_local = 0; } else if(is_for_crm && originated_locally) { is_local = 0; } else { is_local = 1; } } else if(safe_str_eq(fsa_our_uname, host_to)) { is_local=1; } if(is_for_dc || is_for_dcib || is_for_te) { if(AM_I_DC && is_for_te) { ROUTER_RESULT("Message result: Local relay"); send_msg_via_ipc(msg, sys_to); } else if(AM_I_DC) { ROUTER_RESULT("Message result: DC/CRMd process"); processing_complete = FALSE; /* more to be done by caller */ } else if(originated_locally && safe_str_neq(sys_from, CRM_SYSTEM_PENGINE) && safe_str_neq(sys_from, CRM_SYSTEM_TENGINE)) { /* Neither the TE or PE should be sending messages * to DC's on other nodes * * By definition, if we are no longer the DC, then * the PE or TE's data should be discarded */ ROUTER_RESULT("Message result: External relay to DC"); send_msg_via_ha(msg); } else { /* discard */ ROUTER_RESULT("Message result: Discard, not DC"); } } else if(is_local && (is_for_crm || is_for_cib)) { ROUTER_RESULT("Message result: CRMd process"); processing_complete = FALSE; /* more to be done by caller */ } else if(is_local) { ROUTER_RESULT("Message result: Local relay"); send_msg_via_ipc(msg, sys_to); } else { ROUTER_RESULT("Message result: External relay"); send_msg_via_ha(msg); } return processing_complete; } gboolean crmd_authorize_message(xmlNode *client_msg, crmd_client_t *curr_client) { /* check the best case first */ const char *sys_from = crm_element_value(client_msg, F_CRM_SYS_FROM); char *uuid = NULL; char *client_name = NULL; char *major_version = NULL; char *minor_version = NULL; const char *filtered_from; gpointer table_key = NULL; gboolean auth_result = FALSE; struct crm_subsystem_s *the_subsystem = NULL; gboolean can_reply = FALSE; /* no-one has registered with this id */ xmlNode *xml = NULL; const char *op = crm_element_value(client_msg, F_CRM_TASK); if (safe_str_neq(CRM_OP_HELLO, op)) { if(sys_from == NULL) { crm_warn("Message [%s] was had no value for %s... discarding", crm_element_value(client_msg, XML_ATTR_REFERENCE), F_CRM_SYS_FROM); return FALSE; } filtered_from = sys_from; /* The CIB can have two names on the DC */ if(strcasecmp(sys_from, CRM_SYSTEM_DCIB) == 0) filtered_from = CRM_SYSTEM_CIB; if (g_hash_table_lookup (ipc_clients, filtered_from) != NULL) { can_reply = TRUE; /* reply can be routed */ } crm_debug_2("Message reply can%s be routed from %s.", can_reply?"":" not", sys_from); if(can_reply == FALSE) { crm_warn("Message [%s] not authorized", crm_element_value(client_msg, XML_ATTR_REFERENCE)); } return can_reply; } crm_debug_3("received client join msg"); crm_log_xml(LOG_MSG, "join", client_msg); xml = get_message_xml(client_msg, F_CRM_DATA); auth_result = process_hello_message( xml, &uuid, &client_name, &major_version, &minor_version); if (auth_result == TRUE) { if(client_name == NULL || uuid == NULL) { crm_err("Bad client details (client_name=%s, uuid=%s)", crm_str(client_name), crm_str(uuid)); auth_result = FALSE; } } if (auth_result == TRUE) { /* check version */ int mav = atoi(major_version); int miv = atoi(minor_version); crm_debug_3("Checking client version number"); if (mav < 0 || miv < 0) { crm_err("Client version (%d:%d) is not acceptable", mav, miv); auth_result = FALSE; } crm_free(major_version); crm_free(minor_version); } if (safe_str_eq(CRM_SYSTEM_PENGINE, client_name)) { the_subsystem = pe_subsystem; } else if (safe_str_eq(CRM_SYSTEM_TENGINE, client_name)) { the_subsystem = te_subsystem; } if (auth_result == TRUE && the_subsystem != NULL) { /* if we already have one of those clients * only applies to te, pe etc. not admin clients */ crm_debug_3("Checking if %s is required/already connected", client_name); table_key = (gpointer)crm_strdup(client_name); if(is_set(fsa_input_register, the_subsystem->flag_connected)) { auth_result = FALSE; crm_free(table_key); table_key = NULL; crm_warn("Bit\t%.16llx set in %.16llx", the_subsystem->flag_connected, fsa_input_register); crm_err("Client %s is already connected", client_name); } else if(FALSE == is_set(fsa_input_register, the_subsystem->flag_required)) { crm_warn("Bit\t%.16llx not set in %.16llx", the_subsystem->flag_connected, fsa_input_register); crm_warn("Client %s joined but we dont need it", client_name); stop_subsystem(the_subsystem, TRUE); } else { the_subsystem->ipc = curr_client->client_channel; set_bit_inplace(fsa_input_register, the_subsystem->flag_connected); } } else { table_key = (gpointer)generate_hash_key(client_name, uuid); } if (auth_result == TRUE) { crm_debug_2("Accepted client %s", crm_str(table_key)); curr_client->table_key = table_key; curr_client->sub_sys = crm_strdup(client_name); curr_client->uuid = crm_strdup(uuid); g_hash_table_insert (ipc_clients, table_key, curr_client->client_channel); send_hello_message(curr_client->client_channel, "n/a", CRM_SYSTEM_CRMD, "0", "1"); crm_debug_3("Updated client list with %s", crm_str(table_key)); crm_debug_3("Triggering FSA: %s", __FUNCTION__); G_main_set_trigger(fsa_source); if(the_subsystem != NULL) { CRM_CHECK(the_subsystem->client == NULL, process_client_disconnect(the_subsystem->client)); the_subsystem->client = curr_client; } } else { crm_free(table_key); crm_warn("Rejected client logon request"); curr_client->client_channel->ch_status = IPC_DISC_PENDING; } if(uuid != NULL) crm_free(uuid); if(minor_version != NULL) crm_free(minor_version); if(major_version != NULL) crm_free(major_version); if(client_name != NULL) crm_free(client_name); /* hello messages should never be processed further */ return FALSE; } enum crmd_fsa_input handle_message(xmlNode *stored_msg) { enum crmd_fsa_input next_input = I_NULL; const char *type = NULL; if(stored_msg == NULL) { crm_err("No message to handle"); return I_NULL; } type = crm_element_value(stored_msg, F_CRM_MSG_TYPE); if(safe_str_eq(type, XML_ATTR_REQUEST)) { next_input = handle_request(stored_msg); } else if(safe_str_eq(type, XML_ATTR_RESPONSE)) { next_input = handle_response(stored_msg); } else { crm_err("Unknown message type: %s", type); } /* crm_debug_2("%s: Next input is %s", __FUNCTION__, */ /* fsa_input2string(next_input)); */ return next_input; } enum crmd_fsa_input handle_request(xmlNode *stored_msg) { xmlNode *msg = NULL; enum crmd_fsa_input next_input = I_NULL; const char *op = crm_element_value(stored_msg, F_CRM_TASK); const char *sys_to = crm_element_value(stored_msg, F_CRM_SYS_TO); const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM); crm_debug_2("Received %s "XML_ATTR_REQUEST" from %s in state %s", op, host_from, fsa_state2string(fsa_state)); if(op == NULL) { crm_log_xml(LOG_ERR, "Bad message", stored_msg); /*========== common actions ==========*/ - } else if(strcasecmp(op, CRM_OP_NOOP) == 0) { + } else if(strcmp(op, CRM_OP_NOOP) == 0) { crm_debug_2("no-op from %s", crm_str(host_from)); - } else if(strcasecmp(op, CRM_OP_NOVOTE) == 0) { + } else if(strcmp(op, CRM_OP_NOVOTE) == 0) { ha_msg_input_t fsa_input; fsa_input.msg = stored_msg; register_fsa_input_adv(C_HA_MESSAGE, I_NULL, &fsa_input, A_ELECTION_COUNT|A_ELECTION_CHECK, FALSE, __FUNCTION__); - } else if(strcasecmp(op, CRM_OP_VOTE) == 0) { + } else if(strcmp(op, CRM_OP_VOTE) == 0) { /* count the vote and decide what to do after that */ ha_msg_input_t fsa_input; fsa_input.msg = stored_msg; register_fsa_input_adv(C_HA_MESSAGE, I_NULL, &fsa_input, A_ELECTION_COUNT|A_ELECTION_CHECK, FALSE, __FUNCTION__); /* Sometimes we _must_ go into S_ELECTION */ if(fsa_state == S_HALT) { crm_debug("Forcing an election from S_HALT"); next_input = I_ELECTION; #if 0 } else if(AM_I_DC) { /* This is the old way of doing things but what is gained? */ next_input = I_ELECTION; #endif } - } else if(strcasecmp(op, CRM_OP_LOCAL_SHUTDOWN) == 0) { + } else if(strcmp(op, CRM_OP_LOCAL_SHUTDOWN) == 0) { crm_shutdown(SIGTERM); /*next_input = I_SHUTDOWN; */ next_input = I_NULL; - } else if(strcasecmp(op, CRM_OP_PING) == 0) { + } else if(strcmp(op, CRM_OP_PING) == 0) { /* eventually do some stuff to figure out * if we /are/ ok */ xmlNode *ping = createPingAnswerFragment(sys_to, "ok"); crm_xml_add(ping, "crmd_state", fsa_state2string(fsa_state)); crm_info("Current ping state: %s", fsa_state2string(fsa_state)); msg = create_reply(stored_msg, ping); relay_message(msg, TRUE); free_xml(ping); free_xml(msg); /* probably better to do this via signals on the * local node */ - } else if(strcasecmp(op, CRM_OP_DEBUG_UP) == 0) { + } else if(strcmp(op, CRM_OP_DEBUG_UP) == 0) { alter_debug(DEBUG_INC); crm_info("Debug set to %d", get_crm_log_level()); - } else if(strcasecmp(op, CRM_OP_DEBUG_DOWN) == 0) { + } else if(strcmp(op, CRM_OP_DEBUG_DOWN) == 0) { alter_debug(DEBUG_DEC); crm_info("Debug set to %d", get_crm_log_level()); - } else if(strcasecmp(op, CRM_OP_JOIN_OFFER) == 0) { + } else if(strcmp(op, CRM_OP_JOIN_OFFER) == 0) { next_input = I_JOIN_OFFER; crm_debug("Raising I_JOIN_OFFER: join-%s", crm_element_value(stored_msg, F_CRM_JOIN_ID)); - } else if(strcasecmp(op, CRM_OP_JOIN_ACKNAK) == 0) { + } else if(strcmp(op, CRM_OP_JOIN_ACKNAK) == 0) { next_input = I_JOIN_RESULT; crm_debug("Raising I_JOIN_RESULT: join-%s", crm_element_value(stored_msg, F_CRM_JOIN_ID)); - } else if(strcasecmp(op, CRM_OP_LRM_DELETE) == 0 - || strcasecmp(op, CRM_OP_LRM_FAIL) == 0 - || strcasecmp(op, CRM_OP_LRM_REFRESH) == 0 - || strcasecmp(op, CRM_OP_REPROBE) == 0) { + } else if(strcmp(op, CRM_OP_LRM_DELETE) == 0 + || strcmp(op, CRM_OP_LRM_FAIL) == 0 + || strcmp(op, CRM_OP_LRM_REFRESH) == 0 + || strcmp(op, CRM_OP_REPROBE) == 0) { crm_xml_add(stored_msg, F_CRM_SYS_TO, CRM_SYSTEM_LRMD); next_input = I_ROUTER; /* this functionality should only be enabled * if this is a development build */ - } else if(CRM_DEV_BUILD && strcasecmp(op, CRM_OP_DIE) == 0/*constant condition*/) { + } else if(CRM_DEV_BUILD && strcmp(op, CRM_OP_DIE) == 0/*constant condition*/) { crm_warn("Test-only code: Killing the CRM without mercy"); crm_warn("Inhibiting respawns"); exit(100); /*========== (NOT_DC)-Only Actions ==========*/ } else if(AM_I_DC == FALSE){ gboolean dc_match = safe_str_eq(host_from, fsa_our_dc); if(dc_match || fsa_our_dc == NULL) { - if(strcasecmp(op, CRM_OP_HBEAT) == 0) { + if(strcmp(op, CRM_OP_HBEAT) == 0) { crm_debug_3("Received DC heartbeat from %s", host_from); next_input = I_DC_HEARTBEAT; } else if(fsa_our_dc == NULL) { crm_warn("CRMd discarding request: %s" " (DC: %s, from: %s)", op, crm_str(fsa_our_dc), host_from); crm_log_xml(LOG_WARNING, "Ignored Request", stored_msg); - } else if(strcasecmp(op, CRM_OP_SHUTDOWN) == 0) { + } else if(strcmp(op, CRM_OP_SHUTDOWN) == 0) { next_input = I_STOP; } else { crm_err("CRMd didnt expect request: %s", op); crm_log_xml(LOG_ERR, "bad request", stored_msg); } } else { crm_warn("Discarding %s op from %s", op, host_from); } /*========== DC-Only Actions ==========*/ } else if(AM_I_DC) { - if(strcasecmp(op, CRM_OP_JOIN_ANNOUNCE) == 0) { + if(strcmp(op, CRM_OP_JOIN_ANNOUNCE) == 0) { next_input = I_NODE_JOIN; - } else if(strcasecmp(op, CRM_OP_JOIN_REQUEST) == 0) { + } else if(strcmp(op, CRM_OP_JOIN_REQUEST) == 0) { next_input = I_JOIN_REQUEST; - } else if(strcasecmp(op, CRM_OP_JOIN_CONFIRM) == 0) { + } else if(strcmp(op, CRM_OP_JOIN_CONFIRM) == 0) { next_input = I_JOIN_RESULT; - } else if(strcasecmp(op, CRM_OP_SHUTDOWN) == 0) { + } else if(strcmp(op, CRM_OP_SHUTDOWN) == 0) { gboolean dc_match = safe_str_eq(host_from, fsa_our_dc); if(is_set(fsa_input_register, R_SHUTDOWN)) { crm_info("Shutting ourselves down (DC)"); next_input = I_STOP; } else if(dc_match) { crm_err("We didnt ask to be shut down, yet our" " TE is telling us too." " Better get out now!"); next_input = I_TERMINATE; } else if(fsa_state != S_STOPPING) { crm_err("Another node is asking us to shutdown" " but we think we're ok."); next_input = I_ELECTION; } - } else if(strcasecmp(op, CRM_OP_SHUTDOWN_REQ) == 0) { + } else if(strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0) { /* a slave wants to shut down */ /* create cib fragment and add to message */ next_input = handle_shutdown_request(stored_msg); } else { crm_err("Unexpected request (%s) sent to the DC", op); crm_log_xml(LOG_ERR, "Unexpected", stored_msg); } } return next_input; } enum crmd_fsa_input handle_response(xmlNode *stored_msg) { enum crmd_fsa_input next_input = I_NULL; const char *op = crm_element_value(stored_msg, F_CRM_TASK); const char *sys_from = crm_element_value(stored_msg, F_CRM_SYS_FROM); const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM); const char *msg_ref = crm_element_value(stored_msg, XML_ATTR_REFERENCE); crm_debug_2("Received %s "XML_ATTR_RESPONSE" from %s in state %s", op, host_from, fsa_state2string(fsa_state)); if(op == NULL) { crm_log_xml(LOG_ERR, "Bad message", stored_msg); - } else if(AM_I_DC && strcasecmp(op, CRM_OP_PECALC) == 0) { + } else if(AM_I_DC && strcmp(op, CRM_OP_PECALC) == 0) { crm_debug_2("Processing %s reply %s (fsa=%s)", sys_from, msg_ref, crm_str(fsa_pe_ref)); if(msg_ref != NULL && safe_str_eq(msg_ref, fsa_pe_ref)) { next_input = I_PE_SUCCESS; crm_debug_2("Completed: %s...", fsa_pe_ref); crm_free(fsa_pe_ref); fsa_pe_ref = NULL; } else { crm_debug_2("Skipping superceeded reply from %s", sys_from); } - } else if(strcasecmp(op, CRM_OP_VOTE) == 0 - || strcasecmp(op, CRM_OP_HBEAT) == 0 - || strcasecmp(op, CRM_OP_SHUTDOWN_REQ) == 0 - || strcasecmp(op, CRM_OP_SHUTDOWN) == 0) { + } else if(strcmp(op, CRM_OP_VOTE) == 0 + || strcmp(op, CRM_OP_HBEAT) == 0 + || strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0 + || strcmp(op, CRM_OP_SHUTDOWN) == 0) { crm_debug_2("Ignoring %s from %s in %s", op, host_from, fsa_state2string(fsa_state)); next_input = I_NULL; } else { crm_err("Unexpected response (op=%s) sent to the %s", op, AM_I_DC?"DC":"CRMd"); next_input = I_NULL; } return next_input; } enum crmd_fsa_input handle_shutdown_request(xmlNode *stored_msg) { /* handle here to avoid potential version issues * where the shutdown message/proceedure may have * been changed in later versions. * * This way the DC is always in control of the shutdown */ time_t now = time(NULL); xmlNode *node_state = NULL; const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM); if(host_from == NULL) { /* we're shutting down and the DC */ host_from = fsa_our_uname; } crm_info("Creating shutdown request for %s (state=%s)", host_from, fsa_state2string(fsa_state)); crm_log_xml(LOG_MSG, "message", stored_msg); node_state = create_node_state( host_from, NULL, NULL, NULL, NULL, CRMD_STATE_INACTIVE, FALSE, __FUNCTION__); crm_xml_add_int(node_state, XML_CIB_ATTR_SHUTDOWN, (int)now); fsa_cib_anon_update(XML_CIB_TAG_STATUS, node_state, cib_quorum_override); crm_log_xml_debug_2(node_state, "Shutdown update"); free_xml(node_state); /* will be picked up by the TE as long as its running */ start_transition(fsa_state); return I_NULL; } /* frees msg upon completion */ gboolean send_msg_via_ha(xmlNode *msg) { int log_level = LOG_DEBUG_3; gboolean broadcast = FALSE; gboolean all_is_good = TRUE; const char *op = crm_element_value(msg, F_CRM_TASK); const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO); const char *host_to = crm_element_value(msg, F_CRM_HOST_TO); enum crm_ais_msg_types dest = 0; if(is_openais_cluster()) { dest = 1; #if SUPPORT_AIS dest = text2msg_type(sys_to); #endif } if (msg == NULL) { crm_err("Attempt to send NULL Message via HA failed."); all_is_good = FALSE; } else { crm_debug_4("Relaying message to (%s) via HA", host_to); } if (all_is_good) { if (sys_to == NULL || strlen(sys_to) == 0) { crm_err("You did not specify a destination sub-system" " for this message."); all_is_good = FALSE; } } /* There are a number of messages may not need to be ordered. * At a later point perhaps we should detect them and send them * as unordered messages. */ if (all_is_good) { if (host_to == NULL || strlen(host_to) == 0 || safe_str_eq(sys_to, CRM_SYSTEM_DC)) { broadcast = TRUE; all_is_good = send_cluster_message(NULL, dest, msg, FALSE); } else { all_is_good = send_cluster_message(host_to, dest, msg, FALSE); } } if(all_is_good == FALSE) { log_level = LOG_WARNING; } if(log_level == LOG_WARNING || (safe_str_neq(op, CRM_OP_HBEAT))) { do_crm_log(log_level, "Sending %sHA message (ref=%s) to %s@%s %s.", broadcast?"broadcast ":"directed ", crm_element_value(msg, XML_ATTR_REFERENCE), crm_str(sys_to), host_to==NULL?"":host_to, all_is_good?"succeeded":"failed"); } return all_is_good; } /* msg is deleted by the time this returns */ extern gboolean process_te_message(xmlNode *msg, xmlNode *xml_data); gboolean send_msg_via_ipc(xmlNode *msg, const char *sys) { gboolean send_ok = TRUE; IPC_Channel *client_channel; crm_debug_4("relaying msg to sub_sys=%s via IPC", sys); client_channel = (IPC_Channel*)g_hash_table_lookup(ipc_clients, sys); if(crm_element_value(msg, F_CRM_HOST_FROM) == NULL) { crm_xml_add(msg, F_CRM_HOST_FROM, fsa_our_uname); } if (client_channel != NULL) { crm_debug_3("Sending message via channel %s.", sys); send_ok = send_ipc_message(client_channel, msg); - } else if(sys != NULL && strcasecmp(sys, CRM_SYSTEM_TENGINE) == 0) { + } else if(sys != NULL && strcmp(sys, CRM_SYSTEM_TENGINE) == 0) { xmlNode *data = get_message_xml(msg, F_CRM_DATA); process_te_message(msg, data); - } else if(sys != NULL && strcasecmp(sys, CRM_SYSTEM_LRMD) == 0) { + } else if(sys != NULL && strcmp(sys, CRM_SYSTEM_LRMD) == 0) { fsa_data_t *fsa_data = NULL; ha_msg_input_t *msg_copy = new_ha_msg_input(msg); crm_malloc0(fsa_data, sizeof(fsa_data_t)); fsa_data->fsa_input = I_MESSAGE; fsa_data->fsa_cause = C_IPC_MESSAGE; fsa_data->data = msg_copy; fsa_data->origin = __FUNCTION__; fsa_data->data_type = fsa_dt_ha_msg; #ifdef FSA_TRACE - crm_debug_2("Invoking action %s (%.16llx)", - fsa_action2string(A_LRM_INVOKE), - A_LRM_INVOKE); + crm_debug_2("Invoking action A_LRM_INVOKE (%.16llx)", A_LRM_INVOKE); #endif do_lrm_invoke(A_LRM_INVOKE, C_IPC_MESSAGE, fsa_state, I_MESSAGE, fsa_data); crm_free(msg_copy); crm_free(fsa_data); } else { crm_err("Unknown Sub-system (%s)... discarding message.", crm_str(sys)); send_ok = FALSE; } return send_ok; } void msg_queue_helper(void) { #if SUPPORT_HEARTBEAT IPC_Channel *ipc = NULL; if(fsa_cluster_conn != NULL) { ipc = fsa_cluster_conn->llc_ops->ipcchan( fsa_cluster_conn); } if(ipc != NULL) { ipc->ops->resume_io(ipc); } /* g_hash_table_foreach_remove(ipc_clients, ipc_queue_helper, NULL); */ #endif } gboolean ipc_queue_helper(gpointer key, gpointer value, gpointer user_data) { crmd_client_t *ipc_client = value; if(ipc_client->client_channel != NULL) { ipc_client->client_channel->ops->is_message_pending(ipc_client->client_channel); } return FALSE; } diff --git a/crmd/te_utils.c b/crmd/te_utils.c index a948e5704a..388e4e7f9b 100644 --- a/crmd/te_utils.c +++ b/crmd/te_utils.c @@ -1,229 +1,243 @@ /* * 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 GCHSource *stonith_src = NULL; GTRIGSource *stonith_reconnect = NULL; gboolean te_connect_stonith(gpointer user_data) { int lpc = 0; int rc = ST_OK; IPC_Channel *fence_ch = NULL; if(stonith_src != NULL) { crm_debug("Still connected"); return TRUE; } for(lpc = 0; lpc < 30; lpc++) { crm_info("Attempting connection to fencing daemon..."); sleep(1); rc = stonithd_signon("tengine"); if(rc == ST_OK) { break; } if(user_data != NULL) { crm_err("Sign-in failed: triggered a retry"); G_main_set_trigger(stonith_reconnect); return TRUE; } crm_err("Sign-in failed: pausing and trying again in 2s..."); sleep(1); } CRM_ASSERT(rc == ST_OK); /* If not, we failed 30 times... just get out */ CRM_ASSERT(stonithd_set_stonith_ops_callback( tengine_stonith_callback) == ST_OK); crm_debug_2("Grabbing IPC channel"); fence_ch = stonithd_input_IPC_channel(); CRM_ASSERT(fence_ch != NULL); crm_debug_2("Attaching to mainloop"); stonith_src = G_main_add_IPC_Channel( G_PRIORITY_LOW, fence_ch, FALSE, tengine_stonith_dispatch, NULL, tengine_stonith_connection_destroy); CRM_ASSERT(stonith_src != NULL); crm_info("Connected"); return TRUE; } gboolean start_global_timer(crm_action_timer_t *timer, int timeout) { CRM_ASSERT(timer != NULL); CRM_CHECK(timer > 0, return FALSE); CRM_CHECK(timer->source_id == 0, return FALSE); if(timeout <= 0) { crm_err("Tried to start timer with period: %d", timeout); } else if(timer->source_id == 0) { crm_debug("Starting abort timer: %dms", timeout); timer->timeout = timeout; timer->source_id = Gmain_timeout_add( timeout, global_timer_callback, (void*)timer); CRM_ASSERT(timer->source_id != 0); return TRUE; } else { crm_err("Timer is already active with period: %d", timer->timeout); } return FALSE; } gboolean stop_te_timer(crm_action_timer_t *timer) { const char *timer_desc = "action timer"; if(timer == NULL) { return FALSE; } if(timer->reason == timeout_abort) { timer_desc = "global timer"; crm_debug_2("Stopping %s", timer_desc); } if(timer->source_id != 0) { crm_debug_2("Stopping %s", timer_desc); Gmain_timeout_remove(timer->source_id); timer->source_id = 0; } else { crm_debug_2("%s was already stopped", timer_desc); return FALSE; } return TRUE; } gboolean te_graph_trigger(gpointer user_data) { int timeout = 0; enum transition_status graph_rc = -1; crm_debug_2("Invoking graph %d in state %s", transition_graph->id, fsa_state2string(fsa_state)); switch(fsa_state) { case S_STARTING: case S_PENDING: case S_NOT_DC: case S_HALT: case S_ILLEGAL: case S_STOPPING: case S_TERMINATE: return TRUE; break; default: break; } if(transition_graph->complete == FALSE) { graph_rc = run_graph(transition_graph); timeout = transition_graph->transition_timeout; print_graph(LOG_DEBUG_3, transition_graph); if(graph_rc == transition_active) { crm_debug_3("Transition not yet complete"); stop_te_timer(transition_timer); start_global_timer(transition_timer, timeout); return TRUE; } else if(graph_rc == transition_pending) { crm_debug_3("Transition not yet complete - no actions fired"); return TRUE; } if(graph_rc != transition_complete) { crm_err("Transition failed: %s", transition_status(graph_rc)); print_graph(LOG_WARNING, transition_graph); } } crm_info("Transition %d is now complete", transition_graph->id); transition_graph->complete = TRUE; notify_crmd(transition_graph); return TRUE; } void trigger_graph_processing(const char *fn, int line) { G_main_set_trigger(transition_trigger); crm_debug_2("%s:%d - Triggered graph processing", fn, line); } void abort_transition_graph( int abort_priority, enum transition_action abort_action, const char *abort_text, xmlNode *reason, const char *fn, int line) { int log_level = LOG_DEBUG; /* if(abort_priority >= INFINITY) { log_level = LOG_INFO; } */ do_crm_log(log_level, "%s:%d - Triggered graph processing (complete=%d) : %s", fn, line, transition_graph->complete, abort_text); + switch(fsa_state) { + case S_STARTING: + case S_PENDING: + case S_NOT_DC: + case S_HALT: + case S_ILLEGAL: + case S_STOPPING: + case S_TERMINATE: + /* swallow these - we'll pick up the changes in due course */ + return; + default: + break; + } + if(transition_graph && transition_graph->complete) { register_fsa_input(C_FSA_INTERNAL, I_PE_CALC, NULL); return; } - + update_abort_priority( transition_graph, abort_priority, abort_action, abort_text); if(reason != NULL) { const char *magic = crm_element_value(reason, XML_ATTR_TRANSITION_MAGIC); if(magic) { do_crm_log(log_level, "Caused by update to %s: %s", ID(reason), magic); } else { crm_log_xml(log_level, "Cause", reason); } } G_main_set_trigger(transition_trigger); } diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h index b94b46ef28..2ada0e70d7 100644 --- a/include/crm/common/xml.h +++ b/include/crm/common/xml.h @@ -1,353 +1,351 @@ /* * 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 #include typedef xmlNode crm_data_t; #define CRM_BZ2_BLOCKS 4 #define CRM_BZ2_WORK 20 -#define CRM_BZ2_THRESHOLD 10240 +#define CRM_BZ2_THRESHOLD 512 * 1024 #define XML_PARANOIA_CHECKS 0 extern gboolean add_message_xml( xmlNode *msg, const char *field, xmlNode *xml); extern xmlNode *get_message_xml(xmlNode *msg, const char *field); extern GHashTable *xml2list(xmlNode *parent); #if CRM_DEPRECATED_SINCE_2_0_3 extern GHashTable *xml2list_202(xmlNode *parent); #endif extern void hash2nvpair(gpointer key, gpointer value, gpointer user_data); extern void hash2field(gpointer key, gpointer value, gpointer user_data); extern void hash2metafield(gpointer key, gpointer value, gpointer user_data); extern gboolean do_id_check(xmlNode *xml_obj, GHashTable *id_hash, gboolean silent_add, gboolean silent_rename); /* * 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(xmlNode *target, xmlNode *src); extern void expand_plus_plus(xmlNode* target, const char *name, const char *value); extern void fix_plus_plus_recursive(xmlNode* target); /* * 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 xmlNode *find_xml_node_nested( xmlNode *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(xmlNode *parent, const char **node_path, int length, const char *attr_name, gboolean error); void free_xml_from_parent(xmlNode *parent, xmlNode *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 xmlNode *create_xml_node(xmlNode *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( xmlNode *node, const char *name, const char *value); extern const char *crm_xml_replace( xmlNode *node, const char *name, const char *value); extern const char *crm_xml_add_int( xmlNode* 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(xmlNode *node); /* * */ extern void purge_diff_markers(xmlNode *a_node); /* * Returns a deep copy of src_node * */ extern xmlNode *copy_xml(xmlNode *src_node); /* * Add a copy of xml_node to new_parent */ extern xmlNode *add_node_copy( xmlNode *new_parent, xmlNode *xml_node); extern int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child); /* * XML I/O Functions * * Whitespace between tags is discarded. */ extern xmlNode *file2xml(FILE *input, gboolean compressed); extern xmlNode *stdin2xml(void); extern xmlNode *string2xml(const char *input); extern int write_xml_file( xmlNode *xml_node, const char *filename, gboolean compress); extern char *dump_xml_formatted(xmlNode *msg); extern char *dump_xml_unformatted(xmlNode *msg); extern void print_xml_formatted( int log_level, const char *function, xmlNode *an_xml_node, const char *text); /* * Diff related Functions */ extern xmlNode *diff_xml_object( xmlNode *left, xmlNode *right, gboolean suppress); extern void log_xml_diff(unsigned int log_level, xmlNode *diff, const char *function); extern gboolean apply_xml_diff( xmlNode *old, xmlNode *diff, xmlNode **new); /* * Searching & Modifying */ extern xmlNode *find_xml_node( xmlNode *cib, const char * node_path, gboolean must_find); extern xmlNode *find_entity( xmlNode *parent, const char *node_name, const char *id); extern xmlNode *subtract_xml_object( xmlNode *left, xmlNode *right, const char *marker); extern int add_xml_object( xmlNode *parent, xmlNode *target, xmlNode *update); extern void xml_remove_prop(xmlNode *obj, const char *name); extern gboolean replace_xml_child( xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only); extern gboolean update_xml_child(xmlNode *child, xmlNode *to_update); extern int find_xml_children( xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches); extern int crm_element_value_int(xmlNode *data, const char *name, int *dest); extern char *crm_element_value_copy(xmlNode *data, const char *name); extern const char *crm_element_value_const(const xmlNode *data, const char *name); static inline const char *crm_element_name(const xmlNode *data) { return (data ? (const char *)data->name : NULL); } static inline const char * crm_element_value(xmlNode *data, const char *name) { xmlAttr *attr = NULL; if(data == NULL) { crm_err("Couldn't find %s in NULL", name?name:""); return NULL; - } - if(name == NULL) { + + } else if(name == NULL) { crm_err("Couldn't find NULL in %s", crm_element_name(data)); return NULL; } attr = xmlHasProp(data, (const xmlChar*)name); - if(attr && attr->children) { - return (const char*)attr->children->content; + if(attr == NULL || attr->children == NULL) { + return NULL; } - return NULL; + return (const char*)attr->children->content; } extern void xml_validate(const xmlNode *root); extern gboolean xml_has_children(const xmlNode *root); extern char *calculate_xml_digest(xmlNode *local_cib, gboolean sort, gboolean do_filter); extern gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs); extern int update_validation(xmlNode **xml_blob, gboolean transform, gboolean to_logs); extern int get_schema_version(const char *name); extern const char *get_schema_name(int version); #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 xml_child_iter(parent, child, code) do { \ if(parent != NULL) { \ xmlNode *child = NULL; \ xmlNode *__crm_xml_iter = parent->children; \ while(__crm_xml_iter != NULL) { \ child = __crm_xml_iter; \ __crm_xml_iter = __crm_xml_iter->next; \ if(child) { \ code; \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } \ } while(0) # define xml_child_iter_filter(parent, child, filter, code) do { \ if(parent != NULL) { \ xmlNode *child = NULL; \ xmlNode *__crm_xml_iter = parent->children; \ while(__crm_xml_iter != NULL) { \ child = __crm_xml_iter; \ __crm_xml_iter = __crm_xml_iter->next; \ if(filter == NULL \ - || safe_str_eq(filter, (const char *)child->name)) { \ + || crm_str_eq(filter, (const char *)child->name, TRUE)) { \ code; \ - } else { \ - crm_debug_4("Skipping <%s../>", child->name); \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } \ } while(0) # define xml_prop_iter(parent, prop_name, prop_value, code) do { \ if(parent != NULL) { \ xmlAttrPtr prop_iter = parent->properties; \ const char *prop_name = NULL; \ const char *prop_value = NULL; \ while(prop_iter != NULL) { \ prop_name = (const char *)prop_iter->name; \ prop_value = crm_element_value(parent, prop_name);\ prop_iter = prop_iter->next; \ if(prop_name) { \ code; \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } \ } while(0) # define free_xml(a_node) do { \ if((a_node) != NULL) { \ xmlNode *top = NULL; \ xmlDoc *a_doc = (a_node)->doc; \ if (a_doc != NULL) { \ top = xmlDocGetRootElement(a_doc); \ } \ if(a_doc != NULL && top == (a_node)) { \ xmlFreeDoc(a_doc); \ } else { \ if(a_doc != NULL) { \ crm_err("Attempted to free XML from within an xmlDocPtr"); \ } \ /* make sure the node is unlinked first */ \ xmlUnlinkNode(a_node); \ xmlFreeNode(a_node); \ } \ } \ } while(0) extern xmlNode *first_named_child(xmlNode *parent, const char *name); extern xmlNode *convert_ipc_message(IPC_Message *msg, const char *field); extern xmlNode *convert_ha_message(xmlNode *parent, HA_Message *msg, const char *field); extern HA_Message *convert_xml_message(xmlNode *msg); extern xmlNode *sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive); extern xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path); extern gboolean cli_config_update(xmlNode **xml); extern xmlNode *expand_idref(xmlNode *input); #endif diff --git a/include/crm/crm.h b/include/crm/crm.h index 61ad592ed0..ab30605d11 100644 --- a/include/crm/crm.h +++ b/include/crm/crm.h @@ -1,330 +1,333 @@ /* * 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__H #define CRM__H #include #include #include #undef MIN #undef MAX #include #include #include #ifdef MCHECK #include #endif #include #define CRM_FEATURE_SET "3.0" #define MINIMUM_SCHEMA_VERSION "pacemaker-0.7" #define LATEST_SCHEMA_VERSION "pacemaker-"DTD_VERSION #define EOS '\0' #define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) ) #define HAURL(url) HA_URLBASE url #ifndef CRM_DEV_BUILD # define CRM_DEV_BUILD 0 #endif #define CRM_DEPRECATED_SINCE_2_0_1 0 #define CRM_DEPRECATED_SINCE_2_0_2 0 #define CRM_DEPRECATED_SINCE_2_0_3 0 #define CRM_DEPRECATED_SINCE_2_0_4 0 #define CRM_DEPRECATED_SINCE_2_0_5 0 #define CRM_DEPRECATED_SINCE_2_0_6 1 #define CRM_DEPRECATED_SINCE_2_0_7 1 #define CRM_DEPRECATED_SINCE_2_0_8 1 #define CRM_DEPRECATED_SINCE_2_1_0 1 #define CRM_META "CRM_meta" #define crm_meta_name(field) CRM_META"_"field #define ipc_call_diff_max_ms 5000 #define action_diff_warn_ms 5000 #define action_diff_max_ms 20000 #define fsa_diff_warn_ms 10000 #define fsa_diff_max_ms 30000 -#define CRM_ASSERT(expr) if((expr) == FALSE) { \ - crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, TRUE, FALSE); \ - } - -extern gboolean crm_assert_failed; +#define CRM_ASSERT(expr) do { \ + if((expr) == FALSE) { \ + crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, TRUE, FALSE); \ + } \ + } while(0) -#define CRM_DEV_ASSERT(expr) \ - crm_assert_failed = FALSE; \ +#define CRM_DEV_ASSERT(expr) do { \ if((expr) == FALSE) { \ - crm_assert_failed = TRUE; \ crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \ - } + } \ + } while(0) -#define CRM_CHECK(expr, failure_action) if((expr) == FALSE) { \ - crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \ - failure_action; \ - } +#define CRM_CHECK(expr, failure_action) do { \ + if((expr) == FALSE) { \ + crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \ + failure_action; \ + } \ + } while(0) -#define CRM_CHECK_AND_STORE(expr, failure_action) if((expr) == FALSE) { \ - crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, TRUE, TRUE); \ - failure_action; \ - } +#define CRM_CHECK_AND_STORE(expr, failure_action) do { \ + if((expr) == FALSE) { \ + crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, TRUE, TRUE); \ + failure_action; \ + } \ + } while(0) extern const char *crm_system_name; /* Clean these up at some point, some probably should be runtime options */ #define WORKING_DIR HA_VARLIBDIR"/heartbeat/crm" #define CRM_SOCK_DIR HA_VARRUNDIR"/heartbeat/crm" #define BIN_DIR HA_LIBDIR"/heartbeat" #define SOCKET_LEN 1024 #define APPNAME_LEN 256 #define MAX_IPC_FAIL 5 #define CIB_FILENAME WORKING_DIR"/cib.xml" #define CIB_BACKUP WORKING_DIR"/cib_backup.xml" #define MSG_LOG 1 #define DOT_FSA_ACTIONS 1 #define DOT_ALL_FSA_INPUTS 1 /* #define FSA_TRACE 1 */ #define INFINITY_S "INFINITY" #define MINUS_INFINITY_S "-INFINITY" #define INFINITY 1000000 /* Sub-systems */ #define CRM_SYSTEM_DC "dc" #define CRM_SYSTEM_DCIB "dcib" /* The master CIB */ #define CRM_SYSTEM_CIB "cib" #define CRM_SYSTEM_CRMD "crmd" #define CRM_SYSTEM_LRMD "lrmd" #define CRM_SYSTEM_PENGINE "pengine" #define CRM_SYSTEM_TENGINE "tengine" #define CRM_SYSTEM_STONITHD "stonithd" /* Valid operations */ #define CRM_OP_NOOP "noop" #define CRM_OP_JOIN_ANNOUNCE "join_announce" #define CRM_OP_JOIN_OFFER "join_offer" #define CRM_OP_JOIN_REQUEST "join_request" #define CRM_OP_JOIN_ACKNAK "join_ack_nack" #define CRM_OP_JOIN_CONFIRM "join_confirm" #define CRM_OP_DIE "die_no_respawn" #define CRM_OP_RETRIVE_CIB "retrieve_cib" #define CRM_OP_PING "ping" #define CRM_OP_VOTE "vote" #define CRM_OP_NOVOTE "no-vote" #define CRM_OP_HELLO "hello" #define CRM_OP_HBEAT "dc_beat" #define CRM_OP_PECALC "pe_calc" #define CRM_OP_ABORT "abort" #define CRM_OP_QUIT "quit" #define CRM_OP_LOCAL_SHUTDOWN "start_shutdown" #define CRM_OP_SHUTDOWN_REQ "req_shutdown" #define CRM_OP_SHUTDOWN "do_shutdown" #define CRM_OP_FENCE "stonith" #define CRM_OP_EVENTCC "event_cc" #define CRM_OP_TEABORT "te_abort" #define CRM_OP_TEABORTED "te_abort_confirmed" /* we asked */ #define CRM_OP_TE_HALT "te_halt" #define CRM_OP_TECOMPLETE "te_complete" #define CRM_OP_TETIMEOUT "te_timeout" #define CRM_OP_TRANSITION "transition" #define CRM_OP_REGISTER "register" #define CRM_OP_DEBUG_UP "debug_inc" #define CRM_OP_DEBUG_DOWN "debug_dec" #define CRM_OP_INVOKE_LRM "lrm_invoke" #define CRM_OP_LRM_REFRESH "lrm_refresh" #define CRM_OP_LRM_QUERY "lrm_query" #define CRM_OP_LRM_DELETE "lrm_delete" #define CRM_OP_LRM_FAIL "lrm_fail" #define CRM_OP_PROBED "probe_complete" #define CRM_OP_REPROBE "probe_again" #define CRMD_STATE_ACTIVE "member" #define CRMD_STATE_INACTIVE "down" #define CRMD_JOINSTATE_DOWN CRMD_STATE_INACTIVE #define CRMD_JOINSTATE_PENDING "pending" #define CRMD_JOINSTATE_MEMBER CRMD_STATE_ACTIVE #define CRMD_JOINSTATE_NACK "banned" #define CRMD_ACTION_DELETE "delete" #define CRMD_ACTION_CANCEL "cancel" #define CRMD_ACTION_MIGRATE "migrate_to" #define CRMD_ACTION_MIGRATED "migrate_from" #define CRMD_ACTION_START "start" #define CRMD_ACTION_STARTED "running" #define CRMD_ACTION_STOP "stop" #define CRMD_ACTION_STOPPED "stopped" #define CRMD_ACTION_PROMOTE "promote" #define CRMD_ACTION_PROMOTED "promoted" #define CRMD_ACTION_DEMOTE "demote" #define CRMD_ACTION_DEMOTED "demoted" #define CRMD_ACTION_NOTIFY "notify" #define CRMD_ACTION_NOTIFIED "notified" #define CRMD_ACTION_STATUS "monitor" /* short names */ #define RSC_DELETE CRMD_ACTION_DELETE #define RSC_CANCEL CRMD_ACTION_CANCEL #define RSC_MIGRATE CRMD_ACTION_MIGRATE #define RSC_MIGRATED CRMD_ACTION_MIGRATED #define RSC_START CRMD_ACTION_START #define RSC_STARTED CRMD_ACTION_STARTED #define RSC_STOP CRMD_ACTION_STOP #define RSC_STOPPED CRMD_ACTION_STOPPED #define RSC_PROMOTE CRMD_ACTION_PROMOTE #define RSC_PROMOTED CRMD_ACTION_PROMOTED #define RSC_DEMOTE CRMD_ACTION_DEMOTE #define RSC_DEMOTED CRMD_ACTION_DEMOTED #define RSC_NOTIFY CRMD_ACTION_NOTIFY #define RSC_NOTIFIED CRMD_ACTION_NOTIFIED #define RSC_STATUS CRMD_ACTION_STATUS typedef GList* GListPtr; #define slist_destroy(child_type, child, parent, a) \ { \ GListPtr __crm_iter_head = parent; \ child_type *child = NULL; \ while(__crm_iter_head != NULL) { \ child = (child_type *) __crm_iter_head->data; \ __crm_iter_head = __crm_iter_head->next; \ { a; } \ } \ g_list_free(parent); \ } #define slist_iter(child, child_type, parent, counter, a) \ { \ GListPtr __crm_iter_head = parent; \ child_type *child = NULL; \ int counter = 0; \ for(; __crm_iter_head != NULL; counter++) { \ child = (child_type *) __crm_iter_head->data; \ __crm_iter_head = __crm_iter_head->next; \ { a; } \ } \ } #define LOG_DEBUG_2 LOG_DEBUG+1 #define LOG_DEBUG_3 LOG_DEBUG+2 #define LOG_DEBUG_4 LOG_DEBUG+3 #define LOG_DEBUG_5 LOG_DEBUG+4 #define LOG_DEBUG_6 LOG_DEBUG+5 #define LOG_MSG LOG_DEBUG_3 /* * Throughout the macros below, note the leading, pre-comma, space in the * various ' , ##args' occurences to aid portability across versions of 'gcc'. * http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros */ #define do_crm_log(level, fmt, args...) do { \ if(crm_log_level < (level)) { \ continue; \ } else if((level) > LOG_DEBUG) { \ cl_log(LOG_DEBUG, "debug%d: %s: " fmt, \ level-LOG_INFO, __PRETTY_FUNCTION__ , ##args); \ } else { \ cl_log(level, "%s: " fmt, \ __PRETTY_FUNCTION__ , ##args); \ } \ } while(0) #define do_crm_log_always(level, fmt, args...) cl_log(level, "%s: " fmt, __PRETTY_FUNCTION__ , ##args) #define crm_crit(fmt, args...) do_crm_log_always(LOG_CRIT, fmt , ##args) #define crm_err(fmt, args...) do_crm_log_always(LOG_ERR, fmt , ##args) #define crm_warn(fmt, args...) do_crm_log_always(LOG_WARNING, fmt , ##args) #define crm_notice(fmt, args...) do_crm_log_always(LOG_NOTICE, fmt , ##args) #define crm_info(fmt, args...) do_crm_log_always(LOG_INFO, fmt , ##args) #define crm_debug(fmt, args...) do_crm_log(LOG_DEBUG, fmt , ##args) #define crm_debug_2(fmt, args...) do_crm_log(LOG_DEBUG_2, fmt , ##args) #define crm_debug_3(fmt, args...) do_crm_log(LOG_DEBUG_3, fmt , ##args) #define crm_debug_4(fmt, args...) do_crm_log(LOG_DEBUG_4, fmt , ##args) #define crm_debug_5(fmt, args...) do_crm_log(LOG_DEBUG_5, fmt , ##args) #define crm_debug_6(fmt, args...) do_crm_log(LOG_DEBUG_6, fmt , ##args) #include extern void crm_log_message_adv( int level, const char *alt_debugfile, const HA_Message *msg); #define crm_log_xml(level, text, xml) if(crm_log_level >= (level)) { \ print_xml_formatted(level, __PRETTY_FUNCTION__, xml, text); \ } #define crm_log_xml_crit(xml, text) crm_log_xml(LOG_CRIT, text, xml) #define crm_log_xml_err(xml, text) crm_log_xml(LOG_ERR, text, xml) #define crm_log_xml_warn(xml, text) crm_log_xml(LOG_WARNING, text, xml) #define crm_log_xml_notice(xml, text) crm_log_xml(LOG_NOTICE, text, xml) #define crm_log_xml_info(xml, text) crm_log_xml(LOG_INFO, text, xml) #define crm_log_xml_debug(xml, text) crm_log_xml(LOG_DEBUG, text, xml) #define crm_log_xml_debug_2(xml, text) crm_log_xml(LOG_DEBUG_2, text, xml) #define crm_log_xml_debug_3(xml, text) crm_log_xml(LOG_DEBUG_3, text, xml) #define crm_log_xml_debug_4(xml, text) crm_log_xml(LOG_DEBUG_4, text, xml) #define crm_log_xml_debug_5(xml, text) crm_log_xml(LOG_DEBUG_5, text, xml) #define crm_str(x) (const char*)(x?x:"") #define crm_malloc0(malloc_obj, length) do { \ malloc_obj = cl_malloc(length); \ if(malloc_obj == NULL) { \ crm_err("Failed allocation of %lu bytes", (unsigned long)length); \ CRM_ASSERT(malloc_obj != NULL); \ } \ memset(malloc_obj, 0, length); \ } while(0) #define crm_malloc(malloc_obj, length) do { \ malloc_obj = cl_malloc(length); \ if(malloc_obj == NULL) { \ crm_err("Failed allocation of %lu bytes", (unsigned long)length); \ CRM_ASSERT(malloc_obj != NULL); \ } \ } while(0) #define crm_realloc(realloc_obj, length) do { \ realloc_obj = cl_realloc(realloc_obj, length); \ CRM_ASSERT(realloc_obj != NULL); \ } while(0) #define crm_free(free_obj) do { if(free_obj) { cl_free(free_obj); free_obj=NULL; } } while(0) #define crm_msg_del(msg) do { if(msg != NULL) { ha_msg_del(msg); msg = NULL; } } while(0) #define crm_strdup(str) crm_strdup_fn(str, __FILE__, __PRETTY_FUNCTION__, __LINE__) #endif diff --git a/lib/crm/common/utils.c b/lib/crm/common/utils.c index 4370abed74..b19acd93b2 100644 --- a/lib/crm/common/utils.c +++ b/lib/crm/common/utils.c @@ -1,1636 +1,1635 @@ /* * 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 #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MAXLINE # define MAXLINE 512 #endif static uint ref_counter = 0; -gboolean crm_assert_failed = FALSE; unsigned int crm_log_level = LOG_INFO; gboolean crm_config_error = FALSE; gboolean crm_config_warning = FALSE; const char *crm_system_name = "unknown"; void crm_set_env_options(void); gboolean check_time(const char *value) { if(crm_get_msec(value) < 5000) { return FALSE; } return TRUE; } gboolean check_timer(const char *value) { if(crm_get_msec(value) < 0) { return FALSE; } return TRUE; } gboolean check_boolean(const char *value) { int tmp = FALSE; if(crm_str_to_boolean(value, &tmp) != 1) { return FALSE; } return TRUE; } gboolean check_number(const char *value) { errno = 0; if(value == NULL) { return FALSE; } else if(safe_str_eq(value, MINUS_INFINITY_S)) { } else if(safe_str_eq(value, INFINITY_S)) { } else { crm_int_helper(value, NULL); } if(errno != 0) { return FALSE; } return TRUE; } int char2score(const char *score) { int score_f = 0; if(score == NULL) { } else if(safe_str_eq(score, MINUS_INFINITY_S)) { score_f = -INFINITY; } else if(safe_str_eq(score, INFINITY_S)) { score_f = INFINITY; } else if(safe_str_eq(score, "+"INFINITY_S)) { score_f = INFINITY; } else { score_f = crm_parse_int(score, NULL); if(score_f > 0 && score_f > INFINITY) { score_f = INFINITY; } else if(score_f < 0 && score_f < -INFINITY) { score_f = -INFINITY; } } return score_f; } char * score2char(int score) { if(score >= INFINITY) { return crm_strdup("+"INFINITY_S); } else if(score <= -INFINITY) { return crm_strdup("-"INFINITY_S); } return crm_itoa(score); } const char * cluster_option(GHashTable* options, gboolean(*validate)(const char*), const char *name, const char *old_name, const char *def_value) { const char *value = NULL; CRM_ASSERT(name != NULL); if(options != NULL) { value = g_hash_table_lookup(options, name); } if(value == NULL && old_name && options != NULL) { value = g_hash_table_lookup(options, old_name); if(value != NULL) { crm_config_warn("Using deprecated name '%s' for" " cluster option '%s'", old_name, name); g_hash_table_insert( options, crm_strdup(name), crm_strdup(value)); value = g_hash_table_lookup(options, old_name); } } if(value == NULL) { crm_debug_2("Using default value '%s' for cluster option '%s'", def_value, name); if(options == NULL) { return def_value; } g_hash_table_insert( options, crm_strdup(name), crm_strdup(def_value)); value = g_hash_table_lookup(options, name); } if(validate && validate(value) == FALSE) { crm_config_err("Value '%s' for cluster option '%s' is invalid." " Defaulting to %s", value, name, def_value); g_hash_table_replace(options, crm_strdup(name), crm_strdup(def_value)); value = g_hash_table_lookup(options, name); } return value; } const char * get_cluster_pref(GHashTable *options, pe_cluster_option *option_list, int len, const char *name) { int lpc = 0; const char *value = NULL; gboolean found = FALSE; for(lpc = 0; lpc < len; lpc++) { if(safe_str_eq(name, option_list[lpc].name)) { found = TRUE; value = cluster_option(options, option_list[lpc].is_valid, option_list[lpc].name, option_list[lpc].alt_name, option_list[lpc].default_value); } } CRM_CHECK(found, crm_err("No option named: %s", name)); CRM_ASSERT(value != NULL); return value; } void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option *option_list, int len) { int lpc = 0; fprintf(stdout, "" "\n" "\n" " %s\n" " %s\n" " %s\n" " \n", name, version, desc_long, desc_short); for(lpc = 0; lpc < len; lpc++) { if(option_list[lpc].description_long == NULL && option_list[lpc].description_short == NULL) { continue; } fprintf(stdout, " \n" " %s\n" " \n" " %s%s%s\n" " \n", option_list[lpc].name, option_list[lpc].description_short, option_list[lpc].type, option_list[lpc].default_value, option_list[lpc].description_long?option_list[lpc].description_long:option_list[lpc].description_short, option_list[lpc].values?" Allowed values: ":"", option_list[lpc].values?option_list[lpc].values:""); } fprintf(stdout, " \n\n"); } void verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len) { int lpc = 0; for(lpc = 0; lpc < len; lpc++) { cluster_option(options, option_list[lpc].is_valid, option_list[lpc].name, option_list[lpc].alt_name, option_list[lpc].default_value); } } char * generateReference(const char *custom1, const char *custom2) { const char *local_cust1 = custom1; const char *local_cust2 = custom2; int reference_len = 4; char *since_epoch = NULL; reference_len += 20; /* too big */ reference_len += 40; /* too big */ if(local_cust1 == NULL) { local_cust1 = "_empty_"; } reference_len += strlen(local_cust1); if(local_cust2 == NULL) { local_cust2 = "_empty_"; } reference_len += strlen(local_cust2); crm_malloc0(since_epoch, reference_len); if(since_epoch != NULL) { sprintf(since_epoch, "%s-%s-%ld-%u", local_cust1, local_cust2, (unsigned long)time(NULL), ref_counter++); } return since_epoch; } gboolean decodeNVpair(const char *srcstring, char separator, char **name, char **value) { int lpc = 0; int len = 0; const char *temp = NULL; CRM_ASSERT(name != NULL && value != NULL); *name = NULL; *value = NULL; crm_debug_4("Attempting to decode: [%s]", srcstring); if (srcstring != NULL) { len = strlen(srcstring); while(lpc <= len) { if (srcstring[lpc] == separator) { crm_malloc0(*name, lpc+1); if(*name == NULL) { break; /* and return FALSE */ } strncpy(*name, srcstring, lpc); (*name)[lpc] = '\0'; /* this sucks but as the strtok manpage says.. * it *is* a bug */ len = len-lpc; len--; if(len <= 0) { *value = NULL; } else { crm_malloc0(*value, len+1); if(*value == NULL) { crm_free(*name); break; /* and return FALSE */ } temp = srcstring+lpc+1; strncpy(*value, temp, len); (*value)[len] = '\0'; } return TRUE; } lpc++; } } if(*name != NULL) { crm_free(*name); } *name = NULL; *value = NULL; return FALSE; } char * crm_concat(const char *prefix, const char *suffix, char join) { int len = 0; char *new_str = NULL; CRM_ASSERT(prefix != NULL); CRM_ASSERT(suffix != NULL); len = strlen(prefix) + strlen(suffix) + 2; crm_malloc0(new_str, (len)); sprintf(new_str, "%s%c%s", prefix, join, suffix); new_str[len-1] = 0; return new_str; } char * generate_hash_key(const char *crm_msg_reference, const char *sys) { char *hash_key = crm_concat(sys?sys:"none", crm_msg_reference, '_'); crm_debug_3("created hash key: (%s)", hash_key); return hash_key; } char * generate_hash_value(const char *src_node, const char *src_subsys) { char *hash_value = NULL; if (src_node == NULL || src_subsys == NULL) { return NULL; } if (strcasecmp(CRM_SYSTEM_DC, src_subsys) == 0) { hash_value = crm_strdup(src_subsys); if (!hash_value) { crm_err("memory allocation failed in " "generate_hash_value()"); } return hash_value; } hash_value = crm_concat(src_node, src_subsys, '_'); crm_info("created hash value: (%s)", hash_value); return hash_value; } char * crm_itoa(int an_int) { int len = 32; char *buffer = NULL; crm_malloc0(buffer, (len+1)); if(buffer != NULL) { snprintf(buffer, len, "%d", an_int); } return buffer; } extern int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str); gboolean crm_log_init( const char *entity, int level, gboolean coredir, gboolean to_stderr, int argc, char **argv) { /* const char *test = "Testing log daemon connection"; */ /* Redirect messages from glib functions to our handler */ /* cl_malloc_forced_for_glib(); */ g_log_set_handler(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, cl_glib_msg_handler, NULL); /* and for good measure... - this enum is a bit field (!) */ g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/ crm_system_name = entity; cl_log_set_entity(entity); cl_log_set_facility(HA_LOG_FACILITY); if(coredir) { cl_set_corerootdir(HA_COREDIR); cl_cdtocoredir(); } set_crm_log_level(level); crm_set_env_options(); cl_log_args(argc, argv); cl_log_enable_stderr(to_stderr); CL_SIGNAL(DEBUG_INC, alter_debug); CL_SIGNAL(DEBUG_DEC, alter_debug); return TRUE; } /* returns the old value */ unsigned int set_crm_log_level(unsigned int level) { unsigned int old = crm_log_level; while(crm_log_level < 100 && crm_log_level < level) { alter_debug(DEBUG_INC); } while(crm_log_level > 0 && crm_log_level > level) { alter_debug(DEBUG_DEC); } return old; } unsigned int get_crm_log_level(void) { return crm_log_level; } void crm_log_message_adv(int level, const char *prefix, const HA_Message *msg) { if((int)crm_log_level >= level) { do_crm_log(level, "#========= %s message start ==========#", prefix?prefix:""); if(level > LOG_DEBUG) { cl_log_message(LOG_DEBUG, msg); } else { cl_log_message(level, msg); } } } static int crm_version_helper(const char *text, char **end_text) { int atoi_result = -1; CRM_ASSERT(end_text != NULL); errno = 0; if(text != NULL && text[0] != 0) { atoi_result = (int)strtol(text, end_text, 10); if(errno == EINVAL) { crm_err("Conversion of '%s' %c failed", text, text[0]); atoi_result = -1; } } return atoi_result; } /* * version1 < version2 : -1 * version1 = version2 : 0 * version1 > version2 : 1 */ int compare_version(const char *version1, const char *version2) { int rc = 0; int lpc = 0; char *ver1_copy = NULL, *ver2_copy = NULL; char *rest1 = NULL, *rest2 = NULL; if(version1 == NULL && version2 == NULL) { return 0; } else if(version1 == NULL) { return -1; } else if(version2 == NULL) { return 1; } ver1_copy = crm_strdup(version1); ver2_copy = crm_strdup(version2); rest1 = ver1_copy; rest2 = ver2_copy; while(1) { int digit1 = 0; int digit2 = 0; lpc++; if(rest1 == rest2) { break; } if(rest1 != NULL) { digit1 = crm_version_helper(rest1, &rest1); } if(rest2 != NULL) { digit2 = crm_version_helper(rest2, &rest2); } if(digit1 < digit2){ rc = -1; crm_debug_5("%d < %d", digit1, digit2); break; } else if (digit1 > digit2){ rc = 1; crm_debug_5("%d > %d", digit1, digit2); break; } if(rest1 != NULL && rest1[0] == '.') { rest1++; } if(rest1 != NULL && rest1[0] == 0) { rest1 = NULL; } if(rest2 != NULL && rest2[0] == '.') { rest2++; } if(rest2 != NULL && rest2[0] == 0) { rest2 = NULL; } } crm_free(ver1_copy); crm_free(ver2_copy); if(rc == 0) { crm_debug_3("%s == %s (%d)", version1, version2, lpc); } else if(rc < 0) { crm_debug_3("%s < %s (%d)", version1, version2, lpc); } else if(rc > 0) { crm_debug_3("%s > %s (%d)", version1, version2, lpc); } return rc; } gboolean do_stderr = FALSE; void alter_debug(int nsig) { CL_SIGNAL(DEBUG_INC, alter_debug); CL_SIGNAL(DEBUG_DEC, alter_debug); switch(nsig) { case DEBUG_INC: if (crm_log_level < 100) { crm_log_level++; } break; case DEBUG_DEC: if (crm_log_level > 0) { crm_log_level--; } break; default: fprintf(stderr, "Unknown signal %d\n", nsig); cl_log(LOG_ERR, "Unknown signal %d", nsig); break; } } void g_hash_destroy_str(gpointer data) { crm_free(data); } #include #include #include long crm_int_helper(const char *text, char **end_text) { long atoi_result = -1; char *local_end_text = NULL; errno = 0; if(text != NULL) { if(end_text != NULL) { atoi_result = strtoul(text, end_text, 10); } else { atoi_result = strtoul(text, &local_end_text, 10); } /* CRM_CHECK(errno != EINVAL); */ if(errno == EINVAL) { crm_err("Conversion of %s failed", text); atoi_result = -1; } else { if(errno == ERANGE) { crm_err("Conversion of %s was clipped: %ld", text, atoi_result); } if(end_text == NULL && local_end_text[0] != '\0') { crm_err("Characters left over after parsing " "\"%s\": \"%s\"", text, local_end_text); } } } return atoi_result; } int crm_parse_int(const char *text, const char *default_text) { int atoi_result = -1; if(text != NULL) { atoi_result = crm_int_helper(text, NULL); if(errno == 0) { return atoi_result; } } if(default_text != NULL) { atoi_result = crm_int_helper(default_text, NULL); if(errno == 0) { return atoi_result; } } else { crm_err("No default conversion value supplied"); } return -1; } gboolean safe_str_neq(const char *a, const char *b) { if(a == b) { return FALSE; } else if(a==NULL || b==NULL) { return TRUE; } else if(strcasecmp(a, b) == 0) { return FALSE; } return TRUE; } char * crm_strdup_fn(const char *src, const char *file, const char *fn, int line) { char *dup = NULL; CRM_CHECK(src != NULL, return NULL); crm_malloc0(dup, strlen(src) + 1); return strcpy(dup, src); } #define ENV_PREFIX "HA_" void crm_set_env_options(void) { cl_inherit_logging_environment(500); cl_log_set_logd_channel_source(NULL, NULL); if(debug_level > 0 && (debug_level+LOG_INFO) > (int)crm_log_level) { set_crm_log_level(LOG_INFO + debug_level); } } gboolean crm_is_true(const char * s) { gboolean ret = FALSE; if(s != NULL) { cl_str_to_boolean(s, &ret); } return ret; } int crm_str_to_boolean(const char * s, int * ret) { if(s == NULL) { return -1; } else if (strcasecmp(s, "true") == 0 || strcasecmp(s, "on") == 0 || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0){ *ret = TRUE; return 1; } else if (strcasecmp(s, "false") == 0 || strcasecmp(s, "off") == 0 || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0){ *ret = FALSE; return 1; } return -1; } #ifndef NUMCHARS # define NUMCHARS "0123456789." #endif #ifndef WHITESPACE # define WHITESPACE " \t\n\r\f" #endif unsigned long long crm_get_interval(const char * input) { ha_time_t *interval = NULL; char *input_copy = crm_strdup(input); char *input_copy_mutable = input_copy; unsigned long long msec = 0; if(input == NULL) { return 0; } else if(input[0] != 'P') { return crm_get_msec(input); } interval = parse_time_duration(&input_copy_mutable); msec = date_in_seconds(interval); free_ha_date(interval); crm_free(input_copy); return msec * 1000; } unsigned long long crm_get_msec(const char * input) { const char * cp = input; const char * units; long multiplier = 1000; long divisor = 1; long ret = -1; double dret; if(input == NULL) { return 0; } cp += strspn(cp, WHITESPACE); units = cp + strspn(cp, NUMCHARS); units += strspn(units, WHITESPACE); if (strchr(NUMCHARS, *cp) == NULL) { return ret; } if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "msec", 4) == 0) { multiplier = 1; divisor = 1; }else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) { multiplier = 1; divisor = 1000; }else if (strncasecmp(units, "s", 1) == 0 || strncasecmp(units, "sec", 3) == 0) { multiplier = 1000; divisor = 1; }else if (strncasecmp(units, "m", 1) == 0 || strncasecmp(units, "min", 3) == 0) { multiplier = 60*1000; divisor = 1; }else if (strncasecmp(units, "h", 1) == 0 || strncasecmp(units, "hr", 2) == 0) { multiplier = 60*60*1000; divisor = 1; }else if (*units != EOS && *units != '\n' && *units != '\r') { return ret; } dret = atof(cp); dret *= (double)multiplier; dret /= (double)divisor; dret += 0.5; ret = (long)dret; return(ret); } const char * op_status2text(op_status_t status) { switch(status) { case LRM_OP_PENDING: return "pending"; break; case LRM_OP_DONE: return "complete"; break; case LRM_OP_ERROR: return "Error"; break; case LRM_OP_TIMEOUT: return "Timed Out"; break; case LRM_OP_NOTSUPPORTED: return "NOT SUPPORTED"; break; case LRM_OP_CANCELLED: return "Cancelled"; break; } CRM_CHECK(status >= LRM_OP_PENDING && status <= LRM_OP_CANCELLED, crm_err("Unknown status: %d", status)); return "UNKNOWN!"; } char * generate_op_key(const char *rsc_id, const char *op_type, int interval) { int len = 35; char *op_id = NULL; CRM_CHECK(rsc_id != NULL, return NULL); CRM_CHECK(op_type != NULL, return NULL); len += strlen(op_type); len += strlen(rsc_id); crm_malloc0(op_id, len); CRM_CHECK(op_id != NULL, return NULL); sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval); return op_id; } gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval) { char *mutable_key = NULL; char *mutable_key_ptr = NULL; int len = 0, offset = 0, ch = 0; CRM_CHECK(key != NULL, return FALSE); *interval = 0; len = strlen(key); offset = len-1; crm_debug_3("Source: %s", key); while(offset > 0 && isdigit(key[offset])) { int digits = len-offset; ch = key[offset] - '0'; CRM_CHECK(ch < 10, return FALSE); CRM_CHECK(ch >= 0, return FALSE); while(digits > 1) { digits--; ch = ch * 10; } *interval += ch; offset--; } crm_debug_3(" Interval: %d", *interval); CRM_CHECK(key[offset] == '_', return FALSE); mutable_key = crm_strdup(key); mutable_key_ptr = mutable_key_ptr; mutable_key[offset] = 0; offset--; while(offset > 0 && key[offset] != '_') { offset--; } CRM_CHECK(key[offset] == '_', crm_free(mutable_key); return FALSE); mutable_key_ptr = mutable_key+offset+1; crm_debug_3(" Action: %s", mutable_key_ptr); *op_type = crm_strdup(mutable_key_ptr); mutable_key[offset] = 0; offset--; CRM_CHECK(mutable_key != mutable_key_ptr, crm_free(mutable_key); return FALSE); crm_debug_3(" Resource: %s", mutable_key); *rsc_id = crm_strdup(mutable_key); crm_free(mutable_key); return TRUE; } char * generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type) { int len = 12; char *op_id = NULL; CRM_CHECK(rsc_id != NULL, return NULL); CRM_CHECK(op_type != NULL, return NULL); CRM_CHECK(notify_type != NULL, return NULL); len += strlen(op_type); len += strlen(rsc_id); len += strlen(notify_type); crm_malloc0(op_id, len); if(op_id != NULL) { sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type); } return op_id; } char * generate_transition_magic_v202(const char *transition_key, int op_status) { int len = 80; char *fail_state = NULL; CRM_CHECK(transition_key != NULL, return NULL); len += strlen(transition_key); crm_malloc0(fail_state, len); if(fail_state != NULL) { snprintf(fail_state, len, "%d:%s", op_status,transition_key); } return fail_state; } char * generate_transition_magic(const char *transition_key, int op_status, int op_rc) { int len = 80; char *fail_state = NULL; CRM_CHECK(transition_key != NULL, return NULL); len += strlen(transition_key); crm_malloc0(fail_state, len); if(fail_state != NULL) { snprintf(fail_state, len, "%d:%d;%s", op_status, op_rc, transition_key); } return fail_state; } gboolean decode_transition_magic( const char *magic, char **uuid, int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc) { int res = 0; char *key = NULL; gboolean result = TRUE; CRM_CHECK(magic != NULL, return FALSE); CRM_CHECK(op_rc != NULL, return FALSE); CRM_CHECK(op_status != NULL, return FALSE); crm_malloc0(key, strlen(magic)); res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key); if(res != 3) { crm_crit("Only found %d items in: %s", res, magic); return FALSE; } CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc), result = FALSE; goto bail; ); bail: crm_free(key); return result; } char * generate_transition_key(int transition_id, int action_id, int target_rc, const char *node) { int len = 40; char *fail_state = NULL; CRM_CHECK(node != NULL, return NULL); len += strlen(node); crm_malloc0(fail_state, len); if(fail_state != NULL) { snprintf(fail_state, len, "%d:%d:%d:%s", action_id, transition_id, target_rc, node); } return fail_state; } gboolean decode_transition_key( const char *key, char **uuid, int *transition_id, int *action_id, int *target_rc) { int res = 0; gboolean done = FALSE; CRM_CHECK(uuid != NULL, return FALSE); CRM_CHECK(target_rc != NULL, return FALSE); CRM_CHECK(action_id != NULL, return FALSE); CRM_CHECK(transition_id != NULL, return FALSE); crm_malloc0(*uuid, strlen(key)); res = sscanf(key, "%d:%d:%d:%s", action_id, transition_id, target_rc, *uuid); switch(res) { case 4: /* Post Pacemaker 0.6 */ done = TRUE; break; case 3: case 2: /* this can be tricky - the UUID might start with an integer */ /* Until Pacemaker 0.6 */ done = TRUE; *target_rc = -1; res = sscanf(key, "%d:%d:%s", action_id, transition_id, *uuid); if(res == 2) { *action_id = -1; res = sscanf(key, "%d:%s", transition_id, *uuid); CRM_CHECK(res == 2, done = FALSE); } else if(res != 3) { CRM_CHECK(res == 3, done = FALSE); } break; case 1: /* Prior to Heartbeat 2.0.8 */ done = TRUE; *action_id = -1; *target_rc = -1; res = sscanf(key, "%d:%s", transition_id, *uuid); CRM_CHECK(res == 2, done = FALSE); break; default: crm_crit("Unhandled sscanf result (%d) for %s", res, key); } if(strlen(*uuid) != 36) { crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key); } if(done == FALSE) { crm_err("Cannot decode '%s' rc=%d", key, res); crm_free(*uuid); *uuid = NULL; *target_rc = -1; *action_id = -1; *transition_id = -1; } return done; } void filter_action_parameters(xmlNode *param_set, const char *version) { char *timeout = NULL; char *interval = NULL; #if CRM_DEPRECATED_SINCE_2_0_5 const char *filter_205[] = { XML_ATTR_TE_TARGET_RC, XML_ATTR_LRM_PROBE, XML_RSC_ATTR_START, XML_RSC_ATTR_NOTIFY, XML_RSC_ATTR_UNIQUE, XML_RSC_ATTR_MANAGED, XML_RSC_ATTR_PRIORITY, XML_RSC_ATTR_MULTIPLE, XML_RSC_ATTR_STICKINESS, XML_RSC_ATTR_FAIL_STICKINESS, XML_RSC_ATTR_TARGET_ROLE, /* ignore clone fields */ XML_RSC_ATTR_INCARNATION, XML_RSC_ATTR_INCARNATION_MAX, XML_RSC_ATTR_INCARNATION_NODEMAX, XML_RSC_ATTR_MASTER_MAX, XML_RSC_ATTR_MASTER_NODEMAX, /* old field names */ "role", "crm_role", "te-target-rc", /* ignore notify fields */ "notify_stop_resource", "notify_stop_uname", "notify_start_resource", "notify_start_uname", "notify_active_resource", "notify_active_uname", "notify_inactive_resource", "notify_inactive_uname", "notify_promote_resource", "notify_promote_uname", "notify_demote_resource", "notify_demote_uname", "notify_master_resource", "notify_master_uname", "notify_slave_resource", "notify_slave_uname" }; #endif const char *attr_filter[] = { XML_ATTR_ID, XML_ATTR_CRM_VERSION, XML_LRM_ATTR_OP_DIGEST, }; gboolean do_delete = FALSE; int lpc = 0; static int meta_len = 0; if(meta_len == 0) { meta_len = strlen(CRM_META); } if(param_set == NULL) { return; } #if CRM_DEPRECATED_SINCE_2_0_5 if(version == NULL || compare_version("1.0.5", version) > 0) { for(lpc = 0; lpc < DIMOF(filter_205); lpc++) { xml_remove_prop(param_set, filter_205[lpc]); } } #endif for(lpc = 0; lpc < DIMOF(attr_filter); lpc++) { xml_remove_prop(param_set, attr_filter[lpc]); } timeout = crm_element_value_copy(param_set, CRM_META"_timeout"); interval = crm_element_value_copy(param_set, CRM_META"_interval"); xml_prop_iter(param_set, prop_name, prop_value, do_delete = FALSE; if(strncasecmp(prop_name, CRM_META, meta_len) == 0) { do_delete = TRUE; } if(do_delete) { xml_remove_prop(param_set, prop_name); } ); if(crm_get_msec(interval) && compare_version(version, "1.0.8") > 0) { /* Re-instate the operation's timeout value */ if(timeout != NULL) { crm_xml_add(param_set, CRM_META"_timeout", timeout); } } crm_free(interval); crm_free(timeout); } void filter_reload_parameters(xmlNode *param_set, const char *restart_string) { int len = 0; char *name = NULL; char *match = NULL; if(param_set == NULL) { return; } xml_prop_iter(param_set, prop_name, prop_value, name = NULL; len = strlen(prop_name) + 3; crm_malloc0(name, len); sprintf(name, " %s ", prop_name); name[len-1] = 0; match = strstr(restart_string, name); if(match == NULL) { crm_debug_3("%s not found in %s", prop_name, restart_string); xml_remove_prop(param_set, prop_name); } crm_free(name); ); } void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork) { int rc = 0; int pid = 0; int status = 0; if(do_core == FALSE) { do_crm_log(LOG_ERR, "%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition); return; } else if(do_fork) { do_crm_log(LOG_ERR, "%s: Triggered non-fatal assert at %s:%d : %s", function, file, line, assert_condition); pid=fork(); } else { do_crm_log(LOG_ERR, "%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition); } switch(pid) { case -1: crm_err("Cannot fork!"); return; default: /* Parent */ do_crm_log(LOG_ERR, "%s: Forked child %d to record non-fatal assert at %s:%d : %s", function, pid, file, line, assert_condition); do { rc = waitpid(pid, &status, 0); if(rc < 0 && errno != EINTR) { cl_perror("%s: Cannot wait on forked child %d", function, pid); } } while(rc < 0 && errno == EINTR); return; case 0: /* Child */ abort(); break; } } char * generate_series_filename( const char *directory, const char *series, int sequence, gboolean bzip) { int len = 40; char *filename = NULL; const char *ext = "raw"; CRM_CHECK(directory != NULL, return NULL); CRM_CHECK(series != NULL, return NULL); len += strlen(directory); len += strlen(series); crm_malloc0(filename, len); CRM_CHECK(filename != NULL, return NULL); if(bzip) { ext = "bz2"; } sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext); return filename; } int get_last_sequence(const char *directory, const char *series) { FILE *file_strm = NULL; int start = 0, length = 0, read_len = 0; char *series_file = NULL; char *buffer = NULL; int seq = 0; int len = 36; CRM_CHECK(directory != NULL, return 0); CRM_CHECK(series != NULL, return 0); len += strlen(directory); len += strlen(series); crm_malloc0(series_file, len); CRM_CHECK(series_file != NULL, return 0); sprintf(series_file, "%s/%s.last", directory, series); file_strm = fopen(series_file, "r"); if(file_strm == NULL) { crm_debug("Series file %s does not exist", series_file); crm_free(series_file); return 0; } /* see how big the file is */ start = ftell(file_strm); fseek(file_strm, 0L, SEEK_END); length = ftell(file_strm); fseek(file_strm, 0L, start); CRM_ASSERT(start == ftell(file_strm)); crm_debug_3("Reading %d bytes from file", length); crm_malloc0(buffer, (length+1)); read_len = fread(buffer, 1, length, file_strm); if(read_len != length) { crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len); crm_free(buffer); buffer = NULL; } else if(length <= 0) { crm_info("%s was not valid", series_file); crm_free(buffer); buffer = NULL; } crm_free(series_file); seq = crm_parse_int(buffer, "0"); crm_free(buffer); fclose(file_strm); return seq; } void write_last_sequence( const char *directory, const char *series, int sequence, int max) { int rc = 0; int len = 36; char *buffer = NULL; FILE *file_strm = NULL; char *series_file = NULL; CRM_CHECK(directory != NULL, return); CRM_CHECK(series != NULL, return); if(max == 0) { return; } while(max > 0 && sequence > max) { sequence -= max; } buffer = crm_itoa(sequence); len += strlen(directory); len += strlen(series); crm_malloc0(series_file, len); sprintf(series_file, "%s/%s.last", directory, series); file_strm = fopen(series_file, "w"); if(file_strm == NULL) { crm_err("Cannout open series file %s for writing", series_file); goto bail; } rc = fprintf(file_strm, "%s", buffer); if(rc < 0) { cl_perror("Cannot write to series file %s", series_file); } bail: if(file_strm != NULL) { fflush(file_strm); fclose(file_strm); } crm_free(series_file); crm_free(buffer); } void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile) { long pid; const char *devnull = "/dev/null"; if(daemonize == FALSE) { return; } pid = fork(); if (pid < 0) { fprintf(stderr, "%s: could not start daemon\n", name); cl_perror("fork"); exit(LSB_EXIT_GENERIC); } else if (pid > 0) { exit(LSB_EXIT_OK); } if (cl_lock_pidfile(pidfile) < 0 ) { pid = cl_read_pidfile_no_checking(pidfile); crm_warn("%s: already running [pid %ld] (%s).\n", name, pid, pidfile); exit(LSB_EXIT_OK); } umask(022); close(STDIN_FILENO); (void)open(devnull, O_RDONLY); /* Stdin: fd 0 */ close(STDOUT_FILENO); (void)open(devnull, O_WRONLY); /* Stdout: fd 1 */ close(STDERR_FILENO); (void)open(devnull, O_WRONLY); /* Stderr: fd 2 */ } gboolean crm_is_writable(const char *dir, const char *file, const char *user, const char *group, gboolean need_both) { int s_res = -1; struct stat buf; char *full_file = NULL; const char *target = NULL; gboolean pass = TRUE; gboolean readwritable = FALSE; CRM_ASSERT(dir != NULL); if(file != NULL) { full_file = crm_concat(dir, file, '/'); target = full_file; s_res = stat(full_file, &buf); if( s_res == 0 && S_ISREG(buf.st_mode) == FALSE ) { crm_err("%s must be a regular file", target); pass = FALSE; goto out; } } if (s_res != 0) { target = dir; s_res = stat(dir, &buf); if(s_res != 0) { crm_err("%s must exist and be a directory", dir); pass = FALSE; goto out; } else if( S_ISDIR(buf.st_mode) == FALSE ) { crm_err("%s must be a directory", dir); pass = FALSE; } } if(user) { struct passwd *sys_user = NULL; sys_user = getpwnam(user); readwritable = (sys_user != NULL && buf.st_uid == sys_user->pw_uid && (buf.st_mode & (S_IRUSR|S_IWUSR))); if(readwritable == FALSE) { crm_err("%s must be owned and r/w by user %s", target, user); if(need_both) { pass = FALSE; } } } if(group) { struct group *sys_grp = getgrnam(group); readwritable = ( sys_grp != NULL && buf.st_gid == sys_grp->gr_gid && (buf.st_mode & (S_IRGRP|S_IWGRP))); if(readwritable == FALSE) { if(need_both || user == NULL) { pass = FALSE; crm_err("%s must be owned and r/w by group %s", target, group); } else { crm_warn("%s should be owned and r/w by group %s", target, group); } } } out: crm_free(full_file); return pass; } static unsigned long long crm_bit_filter = 0; /* 0x00000002ULL; */ static unsigned int bit_log_level = LOG_DEBUG_5; long long crm_clear_bit(const char *function, long long word, long long bit) { unsigned int level = bit_log_level; if(bit & crm_bit_filter) { level = LOG_ERR; } do_crm_log(level, "Bit 0x%.16llx cleared by %s", bit, function); word &= ~bit; return word; } long long crm_set_bit(const char *function, long long word, long long bit) { unsigned int level = bit_log_level; if(bit & crm_bit_filter) { level = LOG_ERR; } do_crm_log(level, "Bit 0x%.16llx set by %s", bit, function); word |= bit; return word; } gboolean is_not_set(long long word, long long bit) { crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word); return ((word & bit) == 0); } gboolean is_set(long long word, long long bit) { crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word); return ((word & bit) == bit); } gboolean is_set_any(long long word, long long bit) { crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word); return ((word & bit) != 0); } gboolean is_openais_cluster(void) { static const char *cluster_type = NULL; if(cluster_type == NULL) { cluster_type = getenv("HA_cluster_type"); } if(safe_str_eq("openais", cluster_type)) { #if SUPPORT_AIS return TRUE; #else CRM_ASSERT(safe_str_eq("openais", cluster_type) == FALSE); #endif } return FALSE; } gboolean is_heartbeat_cluster(void) { #if SUPPORT_HEARTBEAT return !is_openais_cluster(); #else CRM_ASSERT(is_openais_cluster()); return FALSE; #endif } diff --git a/lib/crm/common/xml.c b/lib/crm/common/xml.c index d056b9a80d..fd68246360 100644 --- a/lib/crm/common/xml.c +++ b/lib/crm/common/xml.c @@ -1,3262 +1,3260 @@ /* * 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 #if HAVE_BZLIB_H # include #endif #define XML_BUFFER_SIZE 4096 #define XML_PARSER_DEBUG 0 struct schema_s { int type; const char *name; const char *location; const char *transform; int after_transform; }; struct schema_s known_schemas[] = { { 0, "none", NULL, NULL, 1 }, { 1, "pacemaker-0.6", DTD_DIRECTORY"/crm.dtd", DTD_DIRECTORY"/upgrade.xsl", 3 }, { 1, "transitional-0.6", DTD_DIRECTORY"/crm-transitional.dtd", DTD_DIRECTORY"/upgrade.xsl", 3 }, { 2, "pacemaker-0.7", DTD_DIRECTORY"/pacemaker-0.7.rng", NULL, 0 }, #if 0 { 2, LATEST_SCHEMA_VERSION, DTD_DIRECTORY"/"LATEST_SCHEMA_VERSION".rng", NULL, 0 }, /* Just in case I forget */ #endif }; static const char *filter[] = { XML_ATTR_ORIGIN, XML_DIFF_MARKER, XML_CIB_ATTR_WRITTEN, }; static void add_ha_nocopy(HA_Message *parent, HA_Message *child, const char *field) { int next = parent->nfields; if (parent->nfields >= parent->nalloc && ha_msg_expand(parent) != HA_OK ) { crm_err("Parent expansion failed"); return; } parent->names[next] = crm_strdup(field); parent->nlens[next] = strlen(field); parent->values[next] = child; parent->vlens[next] = sizeof(HA_Message); parent->types[next] = FT_UNCOMPRESS; parent->nfields++; } int is_comment_start(const char *input, size_t offset, size_t max); int is_comment_end(const char *input, size_t offset, size_t max); gboolean drop_comments(const char *input, size_t *offset, size_t max); void dump_array( int log_level, const char *message, const char **array, int depth); int print_spaces(char *buffer, int spaces, int max); int log_data_element(const char *function, const char *prefix, int log_level, int depth, xmlNode *data, gboolean formatted); int dump_data_element( int depth, char **buffer, int *max, int *offset, xmlNode *data, gboolean formatted); xmlNode *parse_xml(const char *input, size_t *offset); int get_tag_name(const char *input, size_t offset, size_t max); int get_attr_name(const char *input, size_t offset, size_t max); int get_attr_value(const char *input, size_t offset, size_t max); gboolean can_prune_leaf(xmlNode *xml_node); void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent); int in_upper_context(int depth, int context, xmlNode *xml_node); xmlNode * find_xml_node(xmlNode *root, const char * search_path, gboolean must_find) { const char *name = "NULL"; if(must_find || root != NULL) { crm_validate_data(root); } if(root != NULL) { name = crm_element_name(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).", crm_element_name(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, name); } else if(root != NULL) { crm_debug_3("Could not find %s in %s.", search_path, name); } else { crm_debug_3("Could not find %s in .", search_path); } return NULL; } xmlNode* find_xml_node_nested(xmlNode *root, const char **search_path, int len) { int j; gboolean is_found = TRUE; xmlNode *match = NULL; xmlNode *lastMatch = root; crm_validate_data(root); if(search_path == NULL || search_path[0] == 0) { 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).", crm_element_name(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.", crm_element_name(lastMatch), crm_element_name(root)); return NULL; } const char * get_xml_attr_nested(xmlNode *parent, const char **node_path, int length, const char *attr_name, gboolean error) { const char *attr_value = NULL; xmlNode *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 || attr_name[0] == 0) { crm_err("Can not find attribute with no name in %s", crm_element_name(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 || attr_value[0] == 0) && error) { crm_err("No value present for %s at %s", attr_name, crm_element_name(attr_parent)); return NULL; } return attr_value; } xmlNode* find_entity(xmlNode *parent, const char *node_name, const char *id) { crm_validate_data(parent); xml_child_iter_filter( parent, a_child, node_name, if(id == NULL || crm_str_eq(id, ID(a_child), TRUE)) { crm_debug_4("returning node (%s).", crm_element_name(a_child)); return a_child; } ); crm_debug_3("node <%s id=%s> not found in %s.", node_name, id, crm_element_name(parent)); return NULL; } void copy_in_properties(xmlNode* target, xmlNode *src) { 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, expand_plus_plus(target, local_prop_name, local_prop_value) ); crm_validate_data(target); } return; } void fix_plus_plus_recursive(xmlNode* target) { xml_prop_iter(target, name, value, expand_plus_plus(target, name, value)); xml_child_iter(target, child, fix_plus_plus_recursive(child)); } void expand_plus_plus(xmlNode* target, const char *name, const char *value) { int offset = 1; int name_len = 0; int int_value = 0; int value_len = 0; const char *old_value = NULL; if(value == NULL || name == NULL) { return; } old_value = crm_element_value(target, name); if(old_value == NULL) { /* if no previous value, set unexpanded */ goto set_unexpanded; } else if(strstr(value, name) != value) { goto set_unexpanded; } name_len = strlen(name); value_len = strlen(value); if(value_len < (name_len + 2) || value[name_len] != '+' || (value[name_len+1] != '+' && value[name_len+1] != '=')) { goto set_unexpanded; } int_value = char2score(old_value); if(value[name_len+1] != '+') { const char *offset_s = value+(name_len+2); offset = char2score(offset_s); } int_value += offset; if(int_value > INFINITY) { int_value = INFINITY; } crm_xml_add_int(target, name, int_value); return; set_unexpanded: if(old_value == value) { /* the old value is already set, nothing to do */ return; } crm_xml_add(target, name, value); return; } xmlNode* add_node_copy(xmlNode *parent, xmlNode *src_node) { const char *name = NULL; xmlNode *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 = copy_xml(src_node); xmlAddChild(parent, child); return child; } int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child) { int next = 0; crm_validate_data(parent); crm_validate_data(child); if(name == NULL) { name = crm_element_name(child); } if(name == NULL || name[0] == 0) { crm_err("Cannot add object with no name"); return HA_FAIL; } next = 0; xmlAddChild(parent, child); return HA_OK; } const char * crm_xml_add(xmlNode* node, const char *name, const char *value) { xmlAttr *attr = NULL; CRM_CHECK(node != NULL, return NULL); CRM_CHECK(name != NULL, return NULL); if(value == NULL) { return NULL; } #if XML_PARANOIA_CHECKS { const char *old_value = NULL; old_value = crm_element_value(node, name); /* Could be re-setting the same value */ CRM_CHECK_AND_STORE(old_value != value, crm_err("Cannot reset %s with crm_xml_add(%s)", name, value); return value); } #endif attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value); CRM_CHECK(attr && attr->children && attr->children->content, return NULL); return (char *)attr->children->content; } const char * crm_xml_replace(xmlNode* node, const char *name, const char *value) { xmlAttr *attr = NULL; const char *old_value = NULL; CRM_CHECK(node != NULL, return NULL); CRM_CHECK(name != NULL && name[0] != 0, return NULL); old_value = crm_element_value(node, name); /* Could be re-setting the same value */ CRM_CHECK_AND_STORE(old_value != value, return value); if (old_value != NULL && value == NULL) { xml_remove_prop(node, name); return NULL; } else if(value == NULL) { return NULL; } attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value); CRM_CHECK(attr && attr->children && attr->children->content, return NULL); return (char *)attr->children->content; } const char * crm_xml_add_int(xmlNode* node, const char *name, int value) { char *number = crm_itoa(value); const char *added = crm_xml_add(node, name, number); crm_free(number); return added; } xmlNode* create_xml_node(xmlNode *parent, const char *name) { xmlNode *ret_value = NULL; if (name == NULL || name[0] == 0) { ret_value = NULL; } else if(parent == NULL) { ret_value = xmlNewNode(NULL, (const xmlChar*)name); } else { ret_value = xmlNewChild(parent, NULL, (const xmlChar*)name, NULL); } return ret_value; } void free_xml_from_parent(xmlNode *parent, xmlNode *a_node) { CRM_CHECK(a_node != NULL, return); xmlUnlinkNode(a_node); a_node->doc = NULL; free_xml(a_node); } xmlNode* copy_xml(xmlNode *src_node) { return xmlCopyNode(src_node, 1); } xmlNode* string2xml(const char *input) { xmlNode *output = parse_xml(input, NULL); if(output != NULL) { crm_validate_data(output); } return output; } xmlNode * stdin2xml(void) { size_t data_length = 0; size_t read_chars = 0; char *xml_buffer = NULL; xmlNode *xml_obj = NULL; do { crm_realloc(xml_buffer, XML_BUFFER_SIZE + data_length + 1); read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin); data_length += read_chars; } while (read_chars > 0); if(data_length == 0) { crm_warn("No XML supplied on stdin"); return NULL; } xml_buffer[data_length] = '\0'; xml_obj = string2xml(xml_buffer); crm_free(xml_buffer); crm_log_xml_debug_3(xml_obj, "Created fragment"); return xml_obj; } xmlNode* file2xml(FILE *input, gboolean compressed) { char *buffer = NULL; gboolean work_done = FALSE; xmlNode *new_obj = NULL; size_t length = 0, read_len = 0; if(input == NULL) { /* Use perror here as we likely just called fopen() which return NULL */ cl_perror("File open failed, cannot read contents"); return NULL; } if(compressed) { #if HAVE_BZLIB_H int rc = 0; BZFILE *bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0); if ( rc != BZ_OK ) { BZ2_bzReadClose ( &rc, bz_file); return NULL; } rc = BZ_OK; while ( rc == BZ_OK ) { crm_realloc(buffer, XML_BUFFER_SIZE + length + 1); read_len = BZ2_bzRead ( &rc, bz_file, buffer + length, XML_BUFFER_SIZE); crm_debug_5("Read %ld bytes from file: %d", (long)read_len, rc); if ( rc == BZ_OK || rc == BZ_STREAM_END) { length += read_len; } } buffer[length] = '\0'; read_len = length; if ( rc != BZ_STREAM_END ) { crm_err("Couldnt read compressed xml from file"); crm_free(buffer); buffer = NULL; } BZ2_bzReadClose (&rc, bz_file); if(buffer == NULL) { return NULL; } work_done = TRUE; #else crm_err("Cannot read compressed files:" " bzlib was not available at compile time"); #endif } if(work_done == FALSE) { int start = 0; start = ftell(input); fseek(input, 0L, SEEK_END); length = ftell(input); fseek(input, 0L, start); CRM_ASSERT(start == ftell(input)); crm_debug_3("Reading %ld bytes from file", (long)length); crm_malloc0(buffer, (length+1)); read_len = fread(buffer, 1, length, input); } /* see how big the file is */ if(read_len != length) { crm_err("Calculated and read bytes differ: %ld vs. %ld", (long)length, (long)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, "%s", message); } do_crm_log(log_level, "Contents of the array:"); if(array == NULL || array[0] == NULL || depth == 0) { do_crm_log(log_level, "\t"); return; } for (j=0; j < depth && array[j] != NULL; j++) { if (array[j] == NULL) { break; } do_crm_log(log_level, "\t--> (%s).", array[j]); } } int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress) { int res = 0; time_t now; char *buffer = NULL; char *now_str = NULL; unsigned int out = 0; FILE *file_output_strm = NULL; static mode_t cib_mode = S_IRUSR|S_IWUSR; 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; } file_output_strm = fopen(filename, "w"); if(file_output_strm == NULL) { cl_perror("Cannot open %s for writing", filename); return -1; } /* establish the correct permissions */ fchmod(fileno(file_output_strm), cib_mode); crm_log_xml_debug_4(xml_node, "Writing out"); 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); buffer = dump_xml_formatted(xml_node); CRM_CHECK(buffer != NULL && strlen(buffer) > 0, crm_log_xml_warn(xml_node, "dump:failed"); goto bail); if(compress) { #if HAVE_BZLIB_H int rc = BZ_OK; unsigned int in = 0; BZFILE *bz_file = NULL; bz_file = BZ2_bzWriteOpen(&rc, file_output_strm, 5, 0, 30); if(rc != BZ_OK) { crm_err("bzWriteOpen failed: %d", rc); } else { BZ2_bzWrite(&rc,bz_file,buffer,strlen(buffer)); if(rc != BZ_OK) { crm_err("bzWrite() failed: %d", rc); } } if(rc == BZ_OK) { BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out); if(rc != BZ_OK) { crm_err("bzWriteClose() failed: %d",rc); out = -1; } else { crm_debug_2("%s: In: %d, out: %d", filename, in, out); } } #else crm_err("Cannot write compressed files:" " bzlib was not available at compile time"); #endif } if(out <= 0) { res = fprintf(file_output_strm, "%s", buffer); if(res < 0) { cl_perror("Cannot write output to %s", filename); goto bail; } } bail: if(fflush(file_output_strm) != 0) { cl_perror("fflush for %s failed:", filename); res = -1; } if(fsync(fileno(file_output_strm)) < 0) { cl_perror("fsync for %s failed:", filename); res = -1; } fclose(file_output_strm); crm_debug_3("Saved %d bytes to the Cib as XML", res); crm_free(buffer); return res; } void print_xml_formatted(int log_level, const char *function, xmlNode *msg, const char *text) { if(msg == NULL) { do_crm_log(log_level, "%s: %s: NULL", function, crm_str(text)); return; } crm_validate_data(msg); log_data_element(function, text, log_level, 0, msg, TRUE); return; } static HA_Message* convert_xml_message_struct(HA_Message *parent, xmlNode *src_node, const char *field) { xmlNode *child = NULL; xmlNode *__crm_xml_iter = src_node->children; xmlAttrPtr prop_iter = src_node->properties; const char *name = NULL; const char *value = NULL; HA_Message *result = ha_msg_new(3); ha_msg_add(result, F_XML_TAGNAME, (const char *)src_node->name); while(prop_iter != NULL) { name = (const char *)prop_iter->name; value = (const char *)xmlGetProp(src_node, prop_iter->name); prop_iter = prop_iter->next; ha_msg_add(result, name, value); } while(__crm_xml_iter != NULL) { child = __crm_xml_iter; __crm_xml_iter = __crm_xml_iter->next; convert_xml_message_struct(result, child, NULL); } if(parent == NULL) { return result; } if(field) { HA_Message *holder = holder = ha_msg_new(3); CRM_ASSERT(holder != NULL); ha_msg_add(holder, F_XML_TAGNAME, field); add_ha_nocopy(holder, result, (const char*)src_node->name); ha_msg_addstruct_compress(parent, field, holder); ha_msg_del(holder); } else { add_ha_nocopy(parent, result, (const char*)src_node->name); } return result; } static void convert_xml_child(HA_Message *msg, xmlNode *xml) { int orig = 0; int rc = BZ_OK; unsigned int len = 0; char *buffer = NULL; char *compressed = NULL; const char *name = NULL; name = (const char *)xml->name; buffer = dump_xml_unformatted(xml); orig = strlen(buffer); if(orig < CRM_BZ2_THRESHOLD) { ha_msg_add(msg, name, buffer); goto done; } len = (orig * 1.1) + 600; /* recomended size */ crm_malloc(compressed, len); rc = BZ2_bzBuffToBuffCompress(compressed, &len, buffer, orig, CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK); if(rc != BZ_OK) { crm_err("Compression failed: %d", rc); crm_free(compressed); convert_xml_message_struct(msg, xml, name); goto done; } crm_free(buffer); buffer = compressed; crm_debug_2("Compression details: %d -> %d", orig, len); ha_msg_addbin(msg, name, buffer, len); done: crm_free(buffer); # if 0 { unsigned int used = orig; char *uncompressed = NULL; crm_debug("Trying to decompress %d bytes", len); crm_malloc0(uncompressed, orig); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &used, compressed, len, 1, 0); CRM_CHECK(rc == BZ_OK, ;); CRM_CHECK(used == orig, ;); crm_debug("rc=%d, used=%d", rc, used); if(rc != BZ_OK) { exit(100); } crm_debug("Original %s, decompressed %s", buffer, uncompressed); crm_free(uncompressed); } # endif } HA_Message* convert_xml_message(xmlNode *xml) { HA_Message *result = NULL; result = ha_msg_new(3); ha_msg_add(result, F_XML_TAGNAME, (const char *)xml->name); xml_prop_iter(xml, name, value, ha_msg_add(result, name, value)); xml_child_iter(xml, child, convert_xml_child(result, child)); return result; } static void convert_ha_field(xmlNode *parent, HA_Message *msg, int lpc) { int type = 0; const char *name = NULL; const char *value = NULL; xmlNode *xml = NULL; int rc = BZ_OK; size_t orig_len = 0; unsigned int used = 0; char *uncompressed = NULL; char *compressed = NULL; int size = orig_len * 10; CRM_CHECK(parent != NULL, return); CRM_CHECK(msg != NULL, return); name = msg->names[lpc]; type = cl_get_type(msg, name); switch(type) { case FT_COMPRESS: case FT_STRUCT: convert_ha_message(parent, msg->values[lpc], name); break; case FT_UNCOMPRESS: convert_ha_message(parent, cl_get_struct(msg, name), name); break; case FT_STRING: value = cl_get_string(msg, name); if( value == NULL || value[0] != '<' ) { crm_xml_add(parent, name, value); break; } /* unpack xml string */ xml = string2xml(value); if(xml == NULL) { crm_xml_add(parent, name, value); break; } add_node_nocopy(parent, name, xml); break; case FT_BINARY: value = cl_get_binary(msg, name, &orig_len); size = orig_len * 10; if(orig_len < 3 || value[0] != 'B' || value[1] != 'Z' || value[2] != 'h') { if(strstr(name, "uuid") == NULL) { crm_err("Skipping non-bzip binary field: %s", name); } return; } crm_malloc0(compressed, orig_len); memcpy(compressed, value, orig_len); crm_debug_2("Trying to decompress %d bytes", (int)orig_len); retry: crm_realloc(uncompressed, size); memset(uncompressed, 0, size); used = size; rc = BZ2_bzBuffToBuffDecompress( uncompressed, &used, compressed, orig_len, 1, 0); if(rc == BZ_OUTBUFF_FULL) { size = size * 2; /* dont try to allocate more memory than we have */ if(size > 0) { goto retry; } } if(rc != BZ_OK) { crm_err("Decompression of %s (%d bytes) into %d failed: %d", name, (int)orig_len, size, rc); } else { xml = string2xml(uncompressed); } if(xml != NULL) { add_node_nocopy(parent, name, xml); } crm_free(uncompressed); crm_free(compressed); break; } } xmlNode * convert_ha_message(xmlNode *parent, HA_Message *msg, const char *field) { int lpc = 0; xmlNode *child = NULL; const char *tag = NULL; CRM_CHECK(msg != NULL, crm_err("Empty message for %s", field); return parent); tag = cl_get_string(msg, F_XML_TAGNAME); if(tag == NULL) { tag = field; } if(parent == NULL) { parent = create_xml_node(NULL, tag); child = parent; } else { child = create_xml_node(parent, tag); } for (lpc = 0; lpc < msg->nfields; lpc++) { convert_ha_field(child, msg, lpc); } return parent; } xmlNode *convert_ipc_message(IPC_Message *msg, const char *field) { HA_Message *hmsg = wirefmt2msg((char *)msg->msg_body, msg->msg_len, 0); xmlNode *xml = convert_ha_message(NULL, hmsg, __FUNCTION__); crm_msg_del(hmsg); return xml; } xmlNode * get_message_xml(xmlNode *msg, const char *field) { xmlNode *tmp = first_named_child(msg, field); return first_named_child(tmp, NULL); } gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml) { xmlNode *holder = create_xml_node(msg, field); add_node_copy(holder, xml); return TRUE; } char * dump_xml_formatted(xmlNode *an_xml_node) { int max = 1024, len = 0; char *buffer = NULL; if(an_xml_node == NULL) { return NULL; } crm_malloc0(buffer, max); crm_validate_data(an_xml_node); CRM_CHECK(dump_data_element( 0, &buffer, &max, &len, 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(xmlNode *an_xml_node) { int max = 1024, len = 0; char *buffer = NULL; if(an_xml_node == NULL) { return NULL; } crm_malloc0(buffer, max); crm_validate_data(an_xml_node); CRM_CHECK(dump_data_element( 0, &buffer, &max, &len, an_xml_node, FALSE) >= 0, crm_crit("Could not dump the whole message")); crm_debug_4("Dumped: %s", buffer); return buffer; } #define update_buffer_head_old(buffer, len) if(len < 0) { \ (*buffer) = EOS; return -1; \ } else { \ buffer += len; \ } #define update_buffer_head(len) do { \ int total = (*offset) + len + 1; \ if(total >= (*max)) { /* too late */ \ (*buffer) = EOS; return -1; \ } else if(((*max) - total) < 256) { \ (*max) *= 10; \ crm_realloc(*buffer, (*max)); \ } \ (*offset) += len; \ } while(0) int print_spaces(char *buffer, int depth, int max) { int lpc = 0; int spaces = 2*depth; max--; /* <= so that we always print 1 space - prevents problems with syslog */ for(lpc = 0; lpc <= spaces && lpc < max; 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, xmlNode *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); const char *hidden = NULL; 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, 100); update_buffer_head_old(buffer, printed); } printed = sprintf(buffer, "<%s", name); update_buffer_head_old(buffer, printed); hidden = crm_element_value(data, "hidden"); xml_prop_iter( data, prop_name, prop_value, if(prop_name == NULL || safe_str_eq(F_XML_TAGNAME, prop_name)) { continue; } else if(hidden != NULL && prop_name[0] != 0 && strstr(hidden, prop_name) != NULL) { prop_value = "*****"; } crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value); update_buffer_head_old(buffer, printed); ); xml_child_iter( data, child, if(child != NULL) { has_children++; break; } ); printed = sprintf(buffer, "%s>", has_children==0?"/":""); update_buffer_head_old(buffer, printed); do_crm_log(log_level, "%s: %s%s", function, 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, 100); update_buffer_head_old(buffer, printed); } do_crm_log(log_level, "%s: %s%s", function, prefix?prefix:"", print_buffer, name); crm_debug_5("Dumped %s...", name); return has_children; } #define bhead(buffer, offset) ((*buffer) + (*offset)) #define bremain(max, offset) ((*max) - (*offset)) int dump_data_element( int depth, char **buffer, int *max, int *offset, xmlNode *data, gboolean formatted) { int printed = 0; int has_children = 0; const char *name = NULL; CRM_CHECK(data != NULL, return 0); name = crm_element_name(data); CRM_CHECK(name != NULL, return 0); CRM_CHECK(buffer != NULL && *buffer != NULL, return 0); crm_debug_5("Dumping %s...", name); if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "<%s", name); update_buffer_head(printed); xml_prop_iter(data, prop_name, prop_value, crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = snprintf(bhead(buffer, offset), bremain(max, offset), " %s=\"%s\"", prop_name, prop_value); update_buffer_head(printed); ); has_children = xml_has_children(data); printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s>%s", has_children==0?"/":"", formatted?"\n":""); update_buffer_head(printed); if(has_children == 0) { return 0; } xml_child_iter(data, child, if(dump_data_element(depth+1, buffer, max, offset, child, formatted) < 0) { return -1; } ); if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", name, formatted?"\n":""); update_buffer_head(printed); crm_debug_5("Dumped %s...", name); return has_children; } gboolean xml_has_children(const xmlNode *xml_root) { if(xml_root != NULL && xml_root->children != NULL) { return TRUE; } return FALSE; } void xml_validate(const xmlNode *xml_root) { CRM_ASSERT(xml_root != NULL); } int crm_element_value_int(xmlNode *data, const char *name, int *dest) { const char *value = crm_element_value(data, name); CRM_CHECK(dest != NULL, return -1); if(value) { *dest = crm_parse_int(value, NULL); return 0; } return -1; } const char * crm_element_value_const(const xmlNode *data, const char *name) { return crm_element_value(data, name); } char * crm_element_value_copy(xmlNode *data, const char *name) { char *value_copy = NULL; const char *value = crm_element_value(data, name); if(value != NULL) { value_copy = crm_strdup(value); } return value_copy; } void xml_remove_prop(xmlNode *obj, const char *name) { xmlUnsetProp(obj, (const xmlChar*)name); } int get_tag_name(const char *input, size_t offset, size_t max) { char ch = 0; size_t lpc = offset; const char *error = NULL; gboolean do_special = FALSE; for(; error == NULL && lpc < max; lpc++) { ch = input[lpc]; #if XML_PARSER_DEBUG crm_debug_5("Processing char %c [%d]", ch, (int)lpc); #endif switch(ch) { case 0: error = "unexpected EOS"; break; case '?': if(lpc == 0) { /* weird xml tag that we dont care about */ do_special = TRUE; } else { goto out; } break; case '/': case '>': case '\t': case '\n': case ' ': if(!do_special) { goto out; } 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; out: CRM_ASSERT(lpc > offset); return lpc - offset; } int get_attr_name(const char *input, size_t offset, size_t max) { char ch = 0; size_t lpc = offset; const char *error = NULL; for(; error == NULL && lpc < max; lpc++) { ch = input[lpc]; #if XML_PARSER_DEBUG crm_debug_5("Processing char %c[%d]", ch, (int)lpc); #endif switch(ch) { case 0: error = "unexpected EOS"; break; case '\t': case '\n': case ' ': error = "unexpected whitespace"; break; case '=': return lpc - offset; default: if('a' <= ch && ch <= 'z') { } else if('A' <= ch && ch <= 'Z') { } else if('0' <= ch && ch <= '9') { } else if(ch == '_') { } else if(ch == '-') { } else { error = "bad character, not in [a-zA-Z0-9_-]"; } break; } } crm_err("Error parsing token near %.40s: (lpc=%d, ch='%c') %s", input+offset, (int)(lpc-offset), ch, crm_str(error)); return -1; } int get_attr_value(const char *input, size_t offset, size_t max) { char ch = 0; size_t lpc = offset; const char *error = NULL; for(; error == NULL && lpc < max; lpc++) { ch = input[lpc]; #if XML_PARSER_DEBUG crm_debug_5("Processing char %c [%d]", ch, (int)lpc); #endif switch(ch) { case 0: error = "unexpected EOS"; break; case '\\': if(input[lpc+1] == '"') { /* skip over the next char */ lpc++; } break; case '"': if(input[lpc+1] == '/' || input[lpc+1] == '>' || isspace(input[lpc+1])) { return lpc - offset; } /* else * An embedded quote - often used in XPath strings */ break; default: break; } } crm_err("Error parsing token near %.40s: %s", input+offset, crm_str(error)); return -1; } int is_comment_start(const char *input, size_t offset, size_t max) { size_t remaining = max - offset; CRM_CHECK(input != NULL, return 0); CRM_CHECK(offset < max, return 0); input += offset; if(remaining > 4 && input[0] == '<' && input[1] == '!' && input[2] == '-' && input[3] == '-') { #if XML_PARSER_DEBUG crm_debug_6("Found comment start: "); #endif return 3; } else if(remaining > 1 && input[0] == '?' && input[1] == '>') { #if XML_PARSER_DEBUG crm_debug_6("Found comment end: ?>"); #endif return 2; } #if XML_PARSER_DEBUG if(remaining > 2) { crm_debug_6("Not comment end: %c%c%c", input[0], input[1], input[2]); } else { crm_debug_6("Not comment end"); } #endif return 0; } static gboolean drop_whitespace(const char *input, size_t *offset, size_t max) { char ch = 0; size_t lpc = *offset; gboolean more = TRUE; const char *our_input = input; if(input == NULL) { return FALSE; } while(lpc < max && more) { ch = our_input[lpc]; #if XML_PARSER_DEBUG crm_debug_6("Processing char %c[%d]", ch, (int)lpc); #endif if(isspace(ch)) { lpc++; } else { more = FALSE; } } #if XML_PARSER_DEBUG crm_debug_4("Finished processing whitespace"); if(lpc > *offset) { crm_debug_5("Skipped %d whitespace chars", (int)(lpc - *offset)); } #endif (*offset) = lpc; return FALSE; } gboolean drop_comments(const char *input, size_t *offset, size_t max) { gboolean more = TRUE; gboolean in_directive = FALSE; int in_comment = FALSE; size_t lpc = 0; int tag_len = 0; char ch = 0; if(input == NULL) { return FALSE; } CRM_ASSERT(offset != NULL); lpc = *offset; while(lpc < max && more) { ch = input[lpc]; #if XML_PARSER_DEBUG crm_debug_6("Processing char [%d]: %c ", (int)lpc, ch); #endif switch(ch) { case 0: if(in_comment == FALSE) { more = FALSE; } else { crm_err("unexpected EOS"); crm_warn("Parsing error at or before: %s", input+lpc); } break; case '<': tag_len = is_comment_start(input, lpc, max); if(tag_len > 0) { if(in_comment) { crm_err("Nested XML comments are not supported!"); crm_warn("Parsing error at or before: %s", input+lpc); } in_comment = TRUE; lpc+=tag_len; if(tag_len == 2 && input[lpc-1] == '!') { in_directive = TRUE; } } else if(in_comment == FALSE){ more = FALSE; } else { lpc++; } break; case '>': lpc++; if(in_directive) { in_directive = FALSE; in_comment = FALSE; } break; case '-': case '?': tag_len = is_comment_end(input, lpc, max); if(tag_len > 0) { lpc+=tag_len; in_comment = FALSE; } else { lpc++; } break; case ' ': case '\t': case '\n': case '\r': lpc++; break; default: lpc++; break; } } #if XML_PARSER_DEBUG crm_debug_4("Finished processing comments"); crm_debug_5("Skipped %d comment chars", (int)(lpc - *offset)); #endif *offset = lpc; return FALSE; } xmlNode* parse_xml(const char *input, size_t *offset) { char ch = 0; int len = 0; size_t lpc = 0, max = 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; xmlNode *new_obj = NULL; if(input == NULL) { return NULL; } if(offset != NULL) { our_input = input + (*offset); } max = strlen(our_input); were_comments = drop_comments(our_input, &lpc, max); CRM_CHECK(our_input[lpc] == '<', return NULL); lpc++; len = get_tag_name(our_input, lpc, max); #if XML_PARSER_DEBUG crm_debug_5("Tag length: %d", (int)len); #endif CRM_CHECK(len > 0, return NULL); crm_malloc0(tag_name, len+1); strncpy(tag_name, our_input + lpc, len+1); tag_name[len] = EOS; #if XML_PARSER_DEBUG crm_debug_4("Processing tag %s", tag_name); #endif new_obj = xmlNewNode(NULL, (xmlChar*)tag_name); lpc += len; for(; more && error == NULL && lpc < max; lpc++) { ch = our_input[lpc]; #if XML_PARSER_DEBUG crm_debug_5("Processing char %c[%d]", ch, (int)lpc); #endif switch(ch) { case 0: error = "unexpected EOS"; break; case '/': if(our_input[lpc+1] == '>') { more = FALSE; } break; case '<': if(our_input[lpc+1] == '!') { lpc--; /* allow the '<' to be processed */ drop_comments(our_input, &lpc, max); lpc--; /* allow the '<' to be processed */ } else if(our_input[lpc+1] != '/') { xmlNode *child = NULL; #if XML_PARSER_DEBUG crm_debug_4("Start parsing child at %d...", (int)lpc); #endif lpc--; child = parse_xml(our_input, &lpc); if(child == NULL) { error = "error parsing child"; break; } add_node_nocopy(new_obj, NULL, child); /* ha_msg_addstruct_compress( */ /* new_obj, crm_element_name(child), child); */ #if XML_PARSER_DEBUG crm_debug_4("Finished parsing child: %s", crm_element_name(child)); #endif if(our_input[lpc] == '<') { lpc--; /* allow the '<' to be processed */ } } else { lpc += 2; /* ') { error = "clase tag cannot contain attrs"; } #if XML_PARSER_DEBUG crm_debug_4("Finished parsing ourselves: %s", crm_element_name(new_obj)); #endif } 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, max); 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; #if XML_PARSER_DEBUG crm_debug_4("creating nvpair: <%s %s=\"%s\"...", tag_name, attr_name, attr_value); #endif crm_xml_add(new_obj, attr_name, attr_value); crm_free(attr_name); crm_free(attr_value); } break; case '>': while(lpc < max && our_input[lpc+1] != '<') { lpc++; } break; case ' ': case '\t': case '\n': case '\r': break; default: len = get_attr_name(our_input, lpc, max); 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; #if XML_PARSER_DEBUG crm_debug_4("found attr name: %s", attr_name); #endif 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: %.40s", our_input+lpc-3); crm_free(tag_name); free_xml(new_obj); return NULL; } if(offset == NULL) { drop_comments(our_input, &lpc, max); drop_whitespace(our_input, &lpc, max); if(lpc < max) { if(crm_log_level < LOG_ERR) { fprintf(stderr, "%s: Ignoring trailing characters in XML input. Supply -V for more details.\n", __PRETTY_FUNCTION__); } else { cl_log(LOG_ERR, "%s: Ignoring trailing characters in XML input.", __PRETTY_FUNCTION__); } cl_log(LOG_ERR, "%s: Parsed %d characters of a possible %d. Trailing text was: '%d', \'%.40s\'...", __PRETTY_FUNCTION__, (int)lpc, (int)max, our_input[lpc], our_input+lpc); } } #if XML_PARSER_DEBUG crm_debug_4("Finished processing %s tag", tag_name); #endif crm_free(tag_name); if(offset != NULL) { (*offset) += lpc; } return new_obj; } void log_xml_diff(unsigned int log_level, xmlNode *diff, const char *function) { xmlNode *added = find_xml_node(diff, "diff-added", FALSE); xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); gboolean is_first = TRUE; if(crm_log_level < log_level) { /* nothing will ever be printed */ return; } xml_child_iter( removed, child, log_data_element(function, "-", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { do_crm_log(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 { do_crm_log(log_level, " --- "); } ); } void purge_diff_markers(xmlNode *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(xmlNode *old, xmlNode *diff, xmlNode **new) { gboolean result = TRUE; const char *digest = crm_element_value(diff, XML_ATTR_DIGEST); xmlNode *added = find_xml_node(diff, "diff-added", FALSE); xmlNode *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; } else if(result && digest) { char *new_digest = calculate_xml_digest(*new, FALSE, TRUE); if(safe_str_neq(new_digest, digest)) { crm_info("Digest mis-match: expected %s, calculated %s", digest, new_digest); result = FALSE; } else { crm_debug_2("Digest matched: expected %s, calculated %s", digest, new_digest); } } else if(result) { int lpc = 0; xmlNode *intermediate = NULL; xmlNode *diff_of_diff = NULL; xmlNode *calc_added = NULL; xmlNode *calc_removed = NULL; const char *value = NULL; const char *name = NULL; const char *version_attrs[] = { XML_ATTR_NUMUPDATES, XML_ATTR_GENERATION, XML_ATTR_GENERATION_ADMIN }; crm_debug_2("Verification Phase"); intermediate = diff_xml_object(old, *new, FALSE); calc_added = find_xml_node(intermediate, "diff-added", FALSE); calc_removed = find_xml_node(intermediate, "diff-removed", FALSE); /* add any version details to the diff so they match */ for(lpc = 0; lpc < DIMOF(version_attrs); lpc++) { name = version_attrs[lpc]; value = crm_element_value(added, name); crm_xml_add(calc_added, name, value); value = crm_element_value(removed, name); crm_xml_add(calc_removed, name, value); } diff_of_diff = diff_xml_object(intermediate, diff, TRUE); if(diff_of_diff != NULL) { crm_info("Diff application failed!"); crm_log_xml_debug(old, "diff:original"); crm_log_xml_debug(diff, "diff:input"); result = FALSE; } free_xml(diff_of_diff); free_xml(intermediate); diff_of_diff = NULL; intermediate = NULL; } if(result) { purge_diff_markers(*new); } return result; } xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress) { xmlNode *diff = NULL; xmlNode *tmp1 = NULL; xmlNode *added = NULL; xmlNode *removed = NULL; tmp1 = subtract_xml_object(old, new, "removed:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { free_xml(tmp1); } else { diff = create_xml_node(NULL, "diff"); removed = create_xml_node(diff, "diff-removed"); added = create_xml_node(diff, "diff-added"); add_node_nocopy(removed, NULL, tmp1); } } tmp1 = subtract_xml_object(new, old, "added:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { free_xml(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_nocopy(added, NULL, tmp1); } return diff; } gboolean can_prune_leaf(xmlNode *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)) { free_xml(child); } else { can_prune = FALSE; } ); return can_prune; } void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent) { xmlNode *us = NULL; xmlNode *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, xmlNode *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; } xmlNode * subtract_xml_object(xmlNode *left, xmlNode *right, const char *marker) { gboolean skip = FALSE; gboolean differences = FALSE; xmlNode *diff = NULL; xmlNode *child_diff = NULL; xmlNode *right_child = NULL; const char *id = NULL; const char *name = NULL; const char *value = NULL; const char *right_val = NULL; int lpc = 0; static int filter_len = DIMOF(filter); if(left == NULL) { return NULL; } id = ID(left); if(right == NULL) { xmlNode *deleted = NULL; crm_debug_5("Processing <%s id=%s> (complete copy)", crm_element_name(left), id); deleted = copy_xml(left); crm_xml_add(deleted, XML_DIFF_MARKER, marker); return deleted; } name = crm_element_name(left); CRM_CHECK(name != NULL, return NULL); diff = create_xml_node(NULL, name); /* changes to name/value pairs */ xml_prop_iter(left, prop_name, left_value, if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) { continue; } skip = FALSE; for(lpc = 0; skip == FALSE && lpc < filter_len; lpc++){ if(crm_str_eq(prop_name, filter[lpc], TRUE)) { skip = TRUE; } } if(skip) { continue; } right_val = crm_element_value(right, prop_name); if(right_val == NULL) { /* new */ differences = TRUE; crm_xml_add(diff, prop_name, left_value); } else if(strcmp(left_value, right_val) == 0) { /* unchanged */ } else { /* changed */ differences = TRUE; crm_xml_add(diff, prop_name, left_value); } ); /* changes to child objects */ 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_nocopy(diff, NULL, child_diff); } ); if(differences == FALSE) { /* check for XML_DIFF_MARKER in a child */ xml_child_iter( right, right_child, value = crm_element_value(right_child, XML_DIFF_MARKER); if(value != NULL && safe_str_eq(value, "removed:top")) { crm_debug_3("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); return NULL; } crm_xml_add(diff, XML_ATTR_ID, id); return diff; } int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update) { const char *object_id = NULL; const char *object_name = NULL; #if XML_PARSE_DEBUG crm_log_xml(LOG_DEBUG_5, "update:", update); crm_log_xml(LOG_DEBUG_5, "target:", target); #endif 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_CHECK(target != NULL, return 0); #if XML_PARSER_DEBUG crm_debug_2("Added <%s%s%s/>", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); } else { crm_debug_3("Found node <%s%s%s/> to update", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); #endif } copy_in_properties(target, update); xml_child_iter( update, a_child, #if XML_PARSER_DEBUG crm_debug_4("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); #endif add_xml_object(target, NULL, a_child); ); #if XML_PARSER_DEBUG crm_debug_3("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id)); #endif return 0; } gboolean update_xml_child(xmlNode *child, xmlNode *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) { #if XML_PARSER_DEBUG crm_log_xml_debug_2(child, "Update match found..."); #endif add_xml_object(NULL, 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; } int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches) { int match_found = 0; CRM_CHECK(root != NULL, return FALSE); CRM_CHECK(children != NULL, return FALSE); if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) { } else if(value != NULL && safe_str_neq(value, crm_element_value(root, field))) { } else { if(*children == NULL) { *children = create_xml_node(NULL, __FUNCTION__); } add_node_copy(*children, root); match_found = 1; } if(search_matches || match_found == 0) { xml_child_iter( root, child, match_found += find_xml_children( children, child, tag, field, value, search_matches); ); } return match_found; } gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only) { gboolean can_delete = FALSE; const char *up_id = NULL; const char *child_id = NULL; const char *right_val = NULL; CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(update != NULL, return FALSE); up_id = ID(update); child_id = ID(child); if(up_id == NULL || safe_str_eq(child_id, up_id)) { can_delete = TRUE; } if(safe_str_neq(crm_element_name(update), crm_element_name(child))) { can_delete = FALSE; } if(can_delete && delete_only) { xml_prop_iter(update, prop_name, left_value, 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_4(child, "Delete match found..."); if(delete_only || update == NULL) { free_xml_from_parent(NULL, child); } else { xmlNode *tmp = copy_xml(update); xmlNode *old = xmlReplaceNode(child, tmp); free_xml_from_parent(NULL, old); } child = NULL; return TRUE; } else if(can_delete) { crm_log_xml_debug(child, "Cannot delete the search root"); can_delete = FALSE; } xml_child_iter( child, child_of_child, /* only delete the first one */ if(can_delete) { break; } can_delete = replace_xml_child(child, child_of_child, update, delete_only); ); return can_delete; } void hash2nvpair(gpointer key, gpointer value, gpointer user_data) { const char *name = key; const char *s_value = value; xmlNode *xml_node = user_data; xmlNode *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; xmlNode *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); } } void hash2metafield(gpointer key, gpointer value, gpointer user_data) { int lpc = 0; int max = 0; char *crm_name = NULL; if(key == NULL || value == NULL) { return; } crm_name = crm_concat(CRM_META, key, '_'); /* Massage the names so they can be used as shell variables */ max = strlen(crm_name); for(; lpc < max; lpc++) { switch(crm_name[lpc]) { case '-': crm_name[lpc] = '_'; break; } } hash2field(crm_name, value, user_data); crm_free(crm_name); } GHashTable * xml2list(xmlNode *parent) { xmlNode *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(xmlNode *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, 38); cl_uuid_generate(&new_uuid); cl_uuid_unparse(&new_uuid, new_uuid_s); crm_info("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_2(xml_obj, "Updated object"); crm_free(new_uuid_s); } static gboolean tag_needs_id(const char *tag_name) { int lpc = 0; 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_LRM_TAG_RESOURCES, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_OPCONFIG, XML_CIB_TAG_RSCCONFIG, XML_CIB_TAG_CONFIGURATION, "attributes", "operations", "diff", "diff-added", "diff-removed", }; for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) { if(crm_str_eq(tag_name, allowed_list[lpc], TRUE)) { /* this tag is never meant to have an ID */ return FALSE; } } return TRUE; } static gboolean non_unique_allowed(const char *tag_name) { int lpc = 0; const char *non_unique[] = { XML_LRM_TAG_RESOURCE, XML_LRM_TAG_RSC_OP, }; 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 */ return TRUE; } } return FALSE; } gboolean do_id_check(xmlNode *xml_obj, GHashTable *id_hash, gboolean silent_add, gboolean silent_rename) { char *lookup_id = NULL; gboolean modified = FALSE; char *old_id = NULL; const char *tag_id = NULL; const char *tag_name = NULL; const char *lookup_value = NULL; gboolean created_hash = FALSE; 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, silent_add, silent_rename)) { modified = TRUE; } ); tag_id = ID(xml_obj); tag_name = TYPE(xml_obj); if(tag_needs_id(tag_name) == FALSE) { crm_debug_5("%s does not need an ID", tag_name); goto finish_id_check; } else if(tag_id != NULL && non_unique_allowed(tag_name)){ crm_debug_5("%s does not need top be unique", tag_name); goto finish_id_check; } lookup_id = NULL; if(tag_id != NULL) { lookup_id = crm_concat(tag_name, tag_id, '-'); lookup_value = g_hash_table_lookup(id_hash, lookup_id); if(lookup_value == NULL) { g_hash_table_insert(id_hash, lookup_id, crm_strdup(tag_id)); goto finish_id_check; } modified |= (!silent_rename); } else { modified |= (!silent_add); } if(tag_id != NULL) { old_id = crm_strdup(tag_id); } crm_free(lookup_id); assign_uuid(xml_obj); tag_id = ID(xml_obj); if(modified == FALSE) { /* nothing to report */ } else if(old_id != NULL && safe_str_neq(tag_id, old_id)) { crm_err("\"id\" collision detected... Multiple '%s' entries" " with id=\"%s\", assigned id=\"%s\"", tag_name, old_id, tag_id); } else if(old_id == NULL && tag_id != NULL) { crm_err("Detected <%s.../> object without an ID. Assigned: %s", tag_name, tag_id); } crm_free(old_id); finish_id_check: if(created_hash) { g_hash_table_destroy(id_hash); } return modified; } typedef struct name_value_s { const char *name; const void *value; } name_value_t; static gint sort_pairs(gconstpointer a, gconstpointer b) { int rc = 0; const name_value_t *pair_a = a; const name_value_t *pair_b = b; CRM_ASSERT(a != NULL); CRM_ASSERT(pair_a->name != NULL); CRM_ASSERT(b != NULL); CRM_ASSERT(pair_b->name != NULL); rc = strcmp(pair_a->name, pair_b->name); if(rc < 0) { return -1; } else if(rc > 0) { return 1; } return 0; } static void dump_pair(gpointer data, gpointer user_data) { name_value_t *pair = data; xmlNode *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); } xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive) { GListPtr sorted = NULL; GListPtr unsorted = NULL; name_value_t *pair = NULL; xmlNode *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(parent, 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); pair = NULL; ); 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); if(recursive) { xml_child_iter(input, child, sorted_xml(child, result, recursive)); } else { xml_child_iter(input, child, add_node_copy(result, child)); } return result; } static void filter_xml(xmlNode *data, const char **filter, int filter_len, gboolean recursive) { int lpc = 0; for(lpc = 0; lpc < filter_len; lpc++) { xml_remove_prop(data, filter[lpc]); } if(recursive == FALSE) { return; } xml_child_iter(data, child, filter_xml(child, filter, filter_len, recursive)); } /* "c048eae664dba840e1d2060f00299e9d" */ char * calculate_xml_digest(xmlNode *input, gboolean sort, gboolean do_filter) { int i = 0; int digest_len = 16; char *digest = NULL; unsigned char *raw_digest = NULL; xmlNode *sorted = NULL; char *buffer = NULL; size_t buffer_len = 0; if(sort || do_filter) { sorted = sorted_xml(input, NULL, TRUE); } else { sorted = copy_xml(input); } if(do_filter) { filter_xml(sorted, filter, DIMOF(filter), TRUE); } buffer = dump_xml_formatted(sorted); buffer_len = strlen(buffer); CRM_CHECK(buffer != NULL && buffer_len > 0, free_xml(sorted); return NULL); crm_malloc(digest, (2 * digest_len + 1)); crm_malloc(raw_digest, (digest_len + 1)); MD5((unsigned char *)buffer, buffer_len, raw_digest); for(i = 0; i < digest_len; i++) { sprintf(digest+(2*i), "%02x", raw_digest[i]); } digest[(2*digest_len)] = 0; crm_debug_2("Digest %s: %s\n", digest, buffer); crm_log_xml(LOG_DEBUG_3, "digest:source", sorted); crm_free(buffer); crm_free(raw_digest); free_xml(sorted); return digest; } #if HAVE_LIBXML2 # include # include # include # include # include #endif static gboolean validate_with_dtd( xmlDocPtr doc, gboolean to_logs, const char *dtd_file) { gboolean valid = TRUE; xmlDtdPtr dtd = NULL; xmlValidCtxtPtr cvp = NULL; CRM_CHECK(doc != NULL, return FALSE); CRM_CHECK(dtd_file != NULL, return FALSE); dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file); CRM_CHECK(dtd != NULL, goto cleanup); cvp = xmlNewValidCtxt(); CRM_CHECK(cvp != NULL, goto cleanup); if(to_logs) { cvp->userData = (void *) LOG_ERR; cvp->error = (xmlValidityErrorFunc) cl_log; cvp->warning = (xmlValidityWarningFunc) cl_log; } else { cvp->userData = (void *) stderr; cvp->error = (xmlValidityErrorFunc) fprintf; cvp->warning = (xmlValidityWarningFunc) fprintf; } if (!xmlValidateDtd(cvp, doc, dtd)) { valid = FALSE; } cleanup: if(cvp) { xmlFreeValidCtxt(cvp); } if(dtd) { xmlFreeDtd(dtd); } return valid; } xmlNode *first_named_child(xmlNode *parent, const char *name) { xml_child_iter_filter(parent, match, name, return match); return NULL; } #if 0 static void relaxng_invalid_stderr(void * userData, xmlErrorPtr error) { /* Structure xmlError struct _xmlError { int domain : What part of the library raised this er int code : The error code, e.g. an xmlParserError char * message : human-readable informative error messag xmlErrorLevel level : how consequent is the error char * file : the filename int line : the line number if available char * str1 : extra string information char * str2 : extra string information char * str3 : extra string information int int1 : extra number information int int2 : column number of the error or 0 if N/A void * ctxt : the parser context if available void * node : the node in the tree } */ crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message); } #endif static gboolean validate_with_relaxng( xmlDocPtr doc, gboolean to_logs, const char *relaxng_file) { gboolean valid = TRUE; #if HAVE_LIBXML2 int rc = 0; xmlRelaxNGPtr rng = NULL; xmlRelaxNGValidCtxtPtr valid_ctx = NULL; xmlRelaxNGParserCtxtPtr parser_ctx = NULL; CRM_CHECK(doc != NULL, return FALSE); CRM_CHECK(relaxng_file != NULL, return FALSE); xmlLoadExtDtdDefaultValue = 1; parser_ctx = xmlRelaxNGNewParserCtxt(relaxng_file); CRM_CHECK(parser_ctx != NULL, goto cleanup); if(to_logs) { xmlRelaxNGSetParserErrors(parser_ctx, (xmlRelaxNGValidityErrorFunc) cl_log, (xmlRelaxNGValidityWarningFunc) cl_log, GUINT_TO_POINTER(LOG_ERR)); } else { xmlRelaxNGSetParserErrors(parser_ctx, (xmlRelaxNGValidityErrorFunc) fprintf, (xmlRelaxNGValidityWarningFunc) fprintf, stderr); } rng = xmlRelaxNGParse(parser_ctx); CRM_CHECK(rng != NULL, goto cleanup); valid_ctx = xmlRelaxNGNewValidCtxt(rng); CRM_CHECK(valid_ctx != NULL, goto cleanup); if(to_logs) { xmlRelaxNGSetValidErrors(valid_ctx, (xmlRelaxNGValidityErrorFunc) cl_log, (xmlRelaxNGValidityWarningFunc) cl_log, GUINT_TO_POINTER(LOG_ERR)); } else { xmlRelaxNGSetValidErrors(valid_ctx, (xmlRelaxNGValidityErrorFunc) fprintf, (xmlRelaxNGValidityWarningFunc) fprintf, stderr); } /* xmlRelaxNGSetValidStructuredErrors( */ /* valid_ctx, relaxng_invalid_stderr, valid_ctx); */ xmlLineNumbersDefault(1); rc = xmlRelaxNGValidateDoc(valid_ctx, doc); if (rc > 0) { valid = FALSE; } else if (rc < 0) { crm_err("Internal libxml error during validation\n"); } cleanup: if(parser_ctx != NULL) { xmlRelaxNGFreeParserCtxt(parser_ctx); } if(valid_ctx != NULL) { xmlRelaxNGFreeValidCtxt(valid_ctx); } if (rng != NULL) { xmlRelaxNGFree(rng); } #endif return valid; } static int max_schemas = DIMOF(known_schemas); static gboolean validate_with(xmlNode *xml, int method, gboolean to_logs) { xmlDocPtr doc = NULL; gboolean valid = FALSE; int type = known_schemas[method].type; const char *file = known_schemas[method].location; CRM_CHECK(xml != NULL, return FALSE); doc = xml->doc; if(xml->doc == NULL) { doc = xmlNewDoc((const xmlChar *)"1.0"); xmlDocSetRootElement(doc, xml); } crm_debug_2("Validating with: %s (type=%d)", crm_str(file), type); switch(type) { case 0: valid = TRUE; break; case 1: valid = validate_with_dtd(doc, to_logs, file); break; case 2: valid = validate_with_relaxng(doc, to_logs, file); break; default: crm_err("Unknown validator type: %d", type); break; } return valid; } gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs) { int lpc = 0; if(validation == NULL) { validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION); } if(validation == NULL) { validation = crm_element_value(xml_blob, "ignore-dtd"); if(crm_is_true(validation)) { validation = "none"; } else { validation = "transitional-0.6"; } } if(safe_str_eq(validation, "none")) { return TRUE; } for(; lpc < max_schemas; lpc++) { if(safe_str_eq(validation, known_schemas[lpc].name)) { return validate_with(xml_blob, lpc, to_logs); } } crm_err("Unknown validator: %s", validation); return FALSE; } static xmlNode *apply_transformation(xmlNode *xml, const char *transform) { xmlNode *out = NULL; xmlDocPtr res = NULL; xmlDocPtr doc = NULL; xsltStylesheet *xslt = NULL; CRM_CHECK(xml != NULL, return FALSE); doc = xml->doc; if(doc == NULL) { doc = xmlNewDoc((const xmlChar *)"1.0"); xmlDocSetRootElement(doc, xml); } xmlLoadExtDtdDefaultValue = 1; xmlSubstituteEntitiesDefault(1); xslt = xsltParseStylesheetFile((const xmlChar *)transform); CRM_CHECK(xslt != NULL, goto cleanup); res = xsltApplyStylesheet(xslt, doc, NULL); CRM_CHECK(res != NULL, goto cleanup); out = xmlDocGetRootElement(res); cleanup: if(xslt) { xsltFreeStylesheet(xslt); } xsltCleanupGlobals(); xmlCleanupParser(); return out; } const char *get_schema_name(int version) { if(version < 0 || version >= max_schemas) { return "unknown"; } return known_schemas[version].name; } int get_schema_version(const char *name) { int lpc = 0; for(; lpc < max_schemas; lpc++) { if(safe_str_eq(name, known_schemas[lpc].name)) { return lpc; } } return -1; } /* set which validation to use */ int update_validation( xmlNode **xml_blob, gboolean transform, gboolean to_logs) { xmlNode *xml = NULL; char *value = NULL; int lpc = 0, match = -1, best = 0; CRM_CHECK(xml_blob != NULL, return -1); CRM_CHECK(*xml_blob != NULL, return -1); xml = *xml_blob; value = crm_element_value_copy(xml, XML_ATTR_VALIDATION); if(value != NULL) { match = get_schema_version(value); lpc = match; if(transform == FALSE) { lpc++; } } if(match == (max_schemas - 1)) { /* nothing to do */ crm_free(value); return match; } for(; lpc < max_schemas; lpc++) { gboolean valid = TRUE; crm_debug("Testing '%s' validation", known_schemas[lpc].name); valid = validate_with(xml, lpc, to_logs); if(valid) { best = lpc; } if(valid && transform && known_schemas[lpc].transform != NULL) { xmlNode *upgrade = NULL; int next = known_schemas[lpc].after_transform; if(next <= 0) { next = lpc+1; } crm_notice("Upgrading %s-style configuration to %s with %s", known_schemas[lpc].name, known_schemas[lpc+1].name, known_schemas[lpc].transform); upgrade = apply_transformation(xml, known_schemas[lpc].transform); if(upgrade == NULL) { crm_err("Transformation %s failed", known_schemas[lpc].transform); } else if(validate_with(upgrade, next, to_logs)) { crm_info("Transformation %s successful", known_schemas[lpc].transform); lpc = next; best = next; free_xml(xml); xml = upgrade; } else { crm_err("Transformation %s did not produce a valid configuration", known_schemas[lpc].transform); crm_log_xml_info(upgrade, "transform:bad"); free_xml(upgrade); } } } if(best > match) { crm_notice("Upgraded from %s to %s validation", value?value:"", known_schemas[best].name); crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[best].name); match = best; } *xml_blob = xml; crm_free(value); return match; } /* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */ xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path) { xmlDocPtr doc = NULL; xmlXPathObjectPtr xpathObj = NULL; xmlXPathContextPtr xpathCtx = NULL; const xmlChar *xpathExpr = (const xmlChar *)path; CRM_CHECK(path != NULL, return NULL); CRM_CHECK(xml_top != NULL, return NULL); CRM_CHECK(strlen(path) > 0, return NULL); doc = xml_top->doc; if(doc == NULL) { doc = xmlNewDoc((const xmlChar *)"1.0"); xmlDocSetRootElement(doc, xml_top); } crm_debug_2("Evaluating: %s", path); xpathCtx = xmlXPathNewContext(doc); CRM_ASSERT(xpathCtx != NULL); xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); xmlXPathFreeContext(xpathCtx); return xpathObj; } gboolean cli_config_update(xmlNode **xml) { gboolean rc = TRUE; const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION); if(safe_str_neq(value, LATEST_SCHEMA_VERSION)) { int schema_version = 0; int min_version = get_schema_version(MINIMUM_SCHEMA_VERSION); xmlNode *converted = NULL; converted = copy_xml(*xml); schema_version = update_validation(&converted, TRUE, FALSE); value = crm_element_value(converted, XML_ATTR_VALIDATION); if(schema_version < min_version) { fprintf(stderr, "Your current configuration could only be upgraded to %s... " "the minimum requirement is %s.\n", crm_str(value), MINIMUM_SCHEMA_VERSION); free_xml(converted); converted = NULL; rc = FALSE; } else { free_xml(*xml); *xml = converted; } } return rc; } xmlNode *expand_idref(xmlNode *input) { const char *tag = NULL; const char *ref = NULL; xmlNode *top = input; xmlNode *result = input; while(top->parent != NULL) { top = top->parent; } tag = crm_element_name(result); ref = crm_element_value(result, XML_ATTR_IDREF); if(ref != NULL) { char *xpath_string = NULL; int xpath_max = 512, offset = 0; xmlXPathObjectPtr xpathObj = NULL; crm_malloc0(xpath_string, 512); offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s[@id=\"%s\"]", tag, ref); xpathObj = xpath_search(top, xpath_string); if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) { crm_config_err("Referenced %s 'id=%s' not found", tag, ref); } else { CRM_CHECK(xpathObj->nodesetval->nodeNr == 1, crm_config_err("Too many matches (%d) for referenced %s 'id=%s'", xpathObj->nodesetval->nodeNr, tag, ref)); result = xpathObj->nodesetval->nodeTab[0]; CRM_CHECK(result->type == XML_ELEMENT_NODE, result = NULL); } if(xpathObj) { xmlXPathFreeObject(xpathObj); } crm_free(xpath_string); } return result; }