diff --git a/crm/crmd/messages.c b/crm/crmd/messages.c index 6df8b04287..7344dbe02b 100644 --- a/crm/crmd/messages.c +++ b/crm/crmd/messages.c @@ -1,1207 +1,1208 @@ /* * 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 GListPtr fsa_message_queue = NULL; extern void crm_shutdown(int nsig); enum crmd_fsa_input handle_request(ha_msg_input_t *stored_msg); enum crmd_fsa_input handle_response(ha_msg_input_t *stored_msg); enum crmd_fsa_input handle_shutdown_request(HA_Message *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_message_adv(LOG_MSG, "router.log", relay_message); #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 */ 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); } static gboolean last_was_vote = FALSE; void 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; crm_debug("%s raised FSA input %s (cause=%s) %s data", raised_from,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"); 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; } crm_err("%s stalled the FSA with data - this may be broken", raised_from); } if(old_len == 0) { last_was_vote = FALSE; } 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; } else if(data == NULL) { last_was_vote = FALSE; } else if(last_was_vote && cause == C_HA_MESSAGE && input == I_ROUTER) { const char *op = cl_get_string( ((ha_msg_input_t*)data)->msg, F_CRM_TASK); if(safe_str_eq(op, CRM_OP_VOTE)) { /* It is always safe to treat N successive votes as * a single one * * If all the discarded votes are more "loosing" than * the first then the result is accurate * (win or loose). * * If any of the discarded votes are less "loosing" * than the first then we will cast our vote and the * eventual winner will vote us down again (which * even in the case that N=2, is no worse than if we * had not disarded the vote). */ crm_debug_2("Vote compression: %d", old_len); return; } } else if (cause == C_HA_MESSAGE && input == I_ROUTER) { const char *op = cl_get_string( ((ha_msg_input_t*)data)->msg, F_CRM_TASK); if(safe_str_eq(op, CRM_OP_VOTE)) { last_was_vote = TRUE; crm_debug_3("Added vote: %d", old_len); } } else { last_was_vote = FALSE; } 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); 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: crm_debug_3("Copying %s data from %s as CCM data", fsa_cause2string(cause), raised_from); fsa_data->data = copy_ccm_data(data); fsa_data->data_type = fsa_dt_ccm; break; 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_4("Prepending input"); fsa_message_queue = g_list_prepend(fsa_message_queue, fsa_data); } else { crm_debug_4("Appending input"); fsa_message_queue = g_list_append(fsa_message_queue, fsa_data); } crm_debug("Queue len: %d -> %d", old_len, g_list_length(fsa_message_queue)); fsa_dump_queue(LOG_DEBUG); if(old_len == g_list_length(fsa_message_queue)){ crm_err("Couldnt add message to the queue"); } if(fsa_source) { G_main_set_trigger(fsa_source); } } 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, __FILE__, __FUNCTION__, "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 *input_copy = NULL; crm_malloc0(input_copy, sizeof(ha_msg_input_t)); if(orig != NULL) { crm_debug_4("Copy msg"); input_copy->msg = ha_msg_copy(orig->msg); if(orig->xml != NULL) { crm_debug_4("Copy xml"); input_copy->xml = copy_xml_node_recursive(orig->xml); } } else { crm_debug_3("No message to copy"); } return input_copy; } void delete_fsa_input(fsa_data_t *fsa_data) { lrm_op_t *op = NULL; crm_data_t *foo = NULL; struct crmd_ccm_data_s *ccm_input = 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; crm_free(op->user_data); crm_free(op->output); crm_free(op->rsc_id); crm_free(op->app_name); crm_free(op); break; case fsa_dt_ccm: ccm_input = (struct crmd_ccm_data_s *) fsa_data->data; crm_free(ccm_input->oc); crm_free(ccm_input); 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); 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, caller, NULL, "No FSA data available"); } else if(fsa_data->data == NULL) { do_crm_log(LOG_ERR, caller, NULL, "No message data available"); } else if(fsa_data->data_type != a_type) { do_crm_log(LOG_CRIT, caller, NULL, "Message data was the wrong type! %d vs. requested=%d." " Origin: %s", 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 */ enum crmd_fsa_input 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) { enum crmd_fsa_input result = I_NULL; ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); gboolean routed = FALSE; if(msg_data->fsa_cause != C_IPC_MESSAGE && msg_data->fsa_cause != C_HA_MESSAGE) { /* dont try and route these */ crm_warn("Can only process HA and IPC messages"); return I_NULL; } /* try passing the buck first */ crm_debug_4("Attempting to route message"); routed = relay_message(input->msg, cause==C_IPC_MESSAGE); if(routed == FALSE) { crm_debug_4("Message wasn't routed... try handling locally"); /* calculate defer */ result = handle_message(input); switch(result) { case I_NULL: break; case I_DC_HEARTBEAT: break; case I_CIB_OP: break; /* what else should go here? */ default: crm_debug_4("Defering local processing of message"); register_fsa_input_later( cause, result, msg_data->data); result = I_NULL; break; } if(result == I_NULL) { crm_debug_4("Message processed"); } else { register_fsa_input(cause, result, msg_data->data); } } else { crm_debug_4("Message routed..."); input->msg = NULL; } return I_NULL; } /* * This method frees msg */ gboolean send_request(HA_Message *msg, char **msg_reference) { gboolean was_sent = FALSE; /* crm_log_xml_debug_3(request, "Final request..."); */ if(msg_reference != NULL) { *msg_reference = crm_strdup( cl_get_string(msg, XML_ATTR_REFERENCE)); } was_sent = relay_message(msg, TRUE); if(was_sent == FALSE) { ha_msg_input_t *fsa_input = new_ha_msg_input(msg); register_fsa_input(C_IPC_MESSAGE, I_ROUTER, fsa_input); delete_ha_msg_input(fsa_input); crm_msg_del(msg); } return was_sent; } /* unless more processing is required, relay_message is freed */ gboolean relay_message(HA_Message *relay_message, gboolean originated_locally) { int is_for_dc = 0; int is_for_dcib = 0; int is_for_crm = 0; int is_for_cib = 0; int is_local = 0; gboolean processing_complete = FALSE; const char *host_to = cl_get_string(relay_message, F_CRM_HOST_TO); const char *sys_to = cl_get_string(relay_message, F_CRM_SYS_TO); const char *sys_from= cl_get_string(relay_message, F_CRM_SYS_FROM); const char *type = cl_get_string(relay_message, F_TYPE); const char *msg_error = NULL; crm_debug_3("Routing message %s", cl_get_string(relay_message, XML_ATTR_REFERENCE)); if(relay_message == NULL) { msg_error = "Cannot route empty message"; } else if(safe_str_eq(CRM_OP_HELLO, cl_get_string(relay_message, 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_message(LOG_WARNING, relay_message); } if(processing_complete) { crm_msg_del(relay_message); return TRUE; } processing_complete = TRUE; is_for_dc = (strcmp(CRM_SYSTEM_DC, sys_to) == 0); is_for_dcib = (strcmp(CRM_SYSTEM_DCIB, sys_to) == 0); is_for_cib = (strcmp(CRM_SYSTEM_CIB, sys_to) == 0); is_for_crm = (strcmp(CRM_SYSTEM_CRMD, sys_to) == 0); is_local = 0; if(host_to == NULL || strlen(host_to) == 0) { if(is_for_dc) { is_local = 0; } else if(is_for_crm && originated_locally) { is_local = 0; } else { is_local = 1; } } else if(strcmp(fsa_our_uname, host_to) == 0) { is_local=1; } if(is_for_dc || is_for_dcib) { 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(fsa_cluster_conn, relay_message); } else { /* discard */ ROUTER_RESULT("Message result: Discard, not DC"); crm_msg_del(relay_message); } } 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(relay_message, sys_to); } else { ROUTER_RESULT("Message result: External relay"); send_msg_via_ha(fsa_cluster_conn, relay_message); } return processing_complete; } gboolean crmd_authorize_message(ha_msg_input_t *client_msg, crmd_client_t *curr_client) { /* check the best case first */ const char *sys_from = cl_get_string(client_msg->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 */ const char *op = cl_get_string(client_msg->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", cl_get_string(client_msg->msg, XML_ATTR_REFERENCE), F_CRM_SYS_FROM); return FALSE; } filtered_from = sys_from; /* The CIB can have two names on the DC */ if(strcmp(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", cl_get_string(client_msg->msg, XML_ATTR_REFERENCE)); } return can_reply; } crm_debug_3("received client join msg"); crm_log_message(LOG_MSG, client_msg->msg); auth_result = process_hello_message( client_msg->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 (auth_result == TRUE) { /* if we already have one of those clients * only applies to te, pe etc. not admin clients */ if (strcmp(CRM_SYSTEM_PENGINE, client_name) == 0) { the_subsystem = pe_subsystem; } else if (strcmp(CRM_SYSTEM_TENGINE, client_name) == 0) { the_subsystem = te_subsystem; } if (the_subsystem != NULL) { /* do we already have one? */ crm_debug_3("Checking if %s is required/already connected", client_name); if(is_set(fsa_input_register, the_subsystem->flag_connected)) { auth_result = FALSE; 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)) { auth_result = FALSE; 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); } else { the_subsystem->ipc = curr_client->client_channel; } } else { table_key = (gpointer) generate_hash_key(client_name, uuid); } } if (auth_result == TRUE) { if(table_key == NULL) { table_key = (gpointer)crm_strdup(client_name); } 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)); if(the_subsystem != NULL) { set_bit_inplace(fsa_input_register, the_subsystem->flag_connected); } G_main_set_trigger(fsa_source); } else { 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(ha_msg_input_t *stored_msg) { enum crmd_fsa_input next_input = I_NULL; const char *type = NULL; if(stored_msg == NULL || stored_msg->msg == NULL) { crm_err("No message to handle"); return I_NULL; } type = cl_get_string(stored_msg->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(ha_msg_input_t *stored_msg) { HA_Message *msg = NULL; enum crmd_fsa_input next_input = I_NULL; const char *op = cl_get_string(stored_msg->msg, F_CRM_TASK); const char *sys_to = cl_get_string(stored_msg->msg, F_CRM_SYS_TO); const char *host_from = cl_get_string(stored_msg->msg, F_CRM_HOST_FROM); crm_debug_2("Received %s in state %s", op, fsa_state2string(fsa_state)); if(op == NULL) { crm_err("Bad message"); crm_log_message(LOG_ERR, stored_msg->msg); /*========== common actions ==========*/ } else if(strcmp(op, CRM_OP_NOOP) == 0) { crm_debug("no-op from %s", crm_str(host_from)); } else if(strcmp(op, CRM_OP_VOTE) == 0) { /* count the vote and decide what to do after that */ register_fsa_input_adv(C_HA_MESSAGE, I_NULL, stored_msg, A_ELECTION_COUNT, 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(strcmp(op, CRM_OP_LOCAL_SHUTDOWN) == 0) { crm_shutdown(SIGTERM); /*next_input = I_SHUTDOWN; */ next_input = I_NULL; } else if(strcmp(op, CRM_OP_PING) == 0) { /* eventually do some stuff to figure out * if we /are/ ok */ crm_data_t *ping = createPingAnswerFragment(sys_to, "ok"); set_xml_property_copy(ping, "crmd_state", fsa_state2string(fsa_state)); crm_info("Current state: %s", fsa_state2string(fsa_state)); msg = create_reply(stored_msg->msg, ping); if(relay_message(msg, TRUE) == FALSE) { crm_msg_del(msg); } /* probably better to do this via signals on the * local node */ } else if(strcmp(op, CRM_OP_DEBUG_UP) == 0) { int level = get_crm_log_level(); set_crm_log_level(level+1); crm_info("Debug set to %d (was %d)", get_crm_log_level(), level); } else if(strcmp(op, CRM_OP_DEBUG_DOWN) == 0) { int level = get_crm_log_level(); set_crm_log_level(level-1); crm_info("Debug set to %d (was %d)", get_crm_log_level(), level); } else if(strcmp(op, CRM_OP_JOIN_OFFER) == 0) { next_input = I_JOIN_OFFER; } else if(strcmp(op, CRM_OP_JOIN_ACKNAK) == 0) { next_input = I_JOIN_RESULT; /* this functionality should only be enabled * if this is a development build */ } 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(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_warn("Ignored Request"); crm_log_message(LOG_WARNING, stored_msg->msg); } else if(strcmp(op, CRM_OP_SHUTDOWN) == 0) { next_input = I_STOP; } else { crm_err("CRMd didnt expect request: %s", op); crm_log_message(LOG_ERR, stored_msg->msg); } } else { crm_warn("Discarding %s op from %s", op, host_from); } /*========== DC-Only Actions ==========*/ } else if(AM_I_DC){ if(safe_str_eq(op, CRM_OP_TEABORT)) { if(fsa_state == S_POLICY_ENGINE || fsa_state == S_TRANSITION_ENGINE || fsa_state == S_IDLE) { next_input = I_PE_CALC; } else { crm_debug("Filtering %s op in state %s", op, fsa_state2string(fsa_state)); } } else if(safe_str_eq(op, CRM_OP_TETIMEOUT)) { if(fsa_state == S_TRANSITION_ENGINE || fsa_state == S_POLICY_ENGINE) { next_input = I_PE_CALC; } else if(fsa_state == S_IDLE) { crm_err("Transition timed out in S_IDLE"); next_input = I_PE_CALC; } else { crm_err("Filtering %s op in state %s", op, fsa_state2string(fsa_state)); } } else if(strcmp(op, CRM_OP_TECOMPLETE) == 0) { if(fsa_state == S_TRANSITION_ENGINE) { next_input = I_TE_SUCCESS; } else { crm_debug("Filtering %s op in state %s", op, fsa_state2string(fsa_state)); } } else if(strcmp(op, CRM_OP_JOIN_ANNOUNCE) == 0) { next_input = I_NODE_JOIN; } else if(strcmp(op, CRM_OP_JOIN_REQUEST) == 0) { next_input = I_JOIN_REQUEST; } else if(strcmp(op, CRM_OP_JOIN_CONFIRM) == 0) { next_input = I_JOIN_RESULT; } else if(strcmp(op, CRM_OP_SHUTDOWN) == 0) { gboolean dc_match = safe_str_eq(host_from, fsa_our_dc); 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(is_set(fsa_input_register, R_SHUTDOWN)) { crm_err("We asked to be shut down, " " are still the DC, yet another node" " (DC) is askin us to shutdown!"); next_input = I_STOP; } 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(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->msg); } else { crm_err("Unexpected request (%s) sent to the DC", op); crm_log_message(LOG_ERR, stored_msg->msg); } } return next_input; } enum crmd_fsa_input handle_response(ha_msg_input_t *stored_msg) { enum crmd_fsa_input next_input = I_NULL; const char *op = cl_get_string(stored_msg->msg, F_CRM_TASK); const char *sys_from = cl_get_string(stored_msg->msg, F_CRM_SYS_FROM); const char *msg_ref = cl_get_string(stored_msg->msg, XML_ATTR_REFERENCE); crm_debug_2("Received %s %s in state %s", op, XML_ATTR_RESPONSE, fsa_state2string(fsa_state)); if(op == NULL) { crm_err("Bad message"); crm_log_message(LOG_ERR, stored_msg->msg); } else if(AM_I_DC && strcmp(op, CRM_OP_PECALC) == 0) { if(safe_str_eq(msg_ref, fsa_pe_ref)) { next_input = I_PE_SUCCESS; } else { crm_debug_2("Skipping superceeded reply from %s", sys_from); } } 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) { next_input = I_NULL; } else if(strcmp(op, CRM_OP_CIB_CREATE) == 0 || strcmp(op, CRM_OP_CIB_UPDATE) == 0 || strcmp(op, CRM_OP_CIB_DELETE) == 0 || strcmp(op, CRM_OP_CIB_REPLACE) == 0 || strcmp(op, CRM_OP_CIB_ERASE) == 0) { /* perhaps we should do somethign with these replies, * especially check that the actions passed */ } 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(HA_Message *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 */ crm_data_t *frag = NULL; time_t now = time(NULL); char *now_s = crm_itoa((int)now); crm_data_t *node_state = create_xml_node(NULL, XML_CIB_TAG_STATE); const char *host_from= cl_get_string(stored_msg, F_CRM_HOST_FROM); crm_info("Creating shutdown request for %s",host_from); crm_log_message(LOG_MSG, stored_msg); set_uuid(fsa_cluster_conn, node_state, XML_ATTR_UUID, host_from); set_xml_property_copy(node_state, XML_ATTR_UNAME, host_from); set_xml_property_copy(node_state, XML_CIB_ATTR_SHUTDOWN, now_s); set_xml_property_copy( node_state, XML_CIB_ATTR_EXPSTATE, CRMD_STATE_INACTIVE); frag = create_cib_fragment(node_state, NULL); /* cleanup intermediate steps */ free_xml(node_state); crm_free(now_s); fsa_cib_conn->cmds->modify( fsa_cib_conn, XML_CIB_TAG_STATUS, frag, NULL, cib_quorum_override); free_xml(frag); /* will be picked up by the TE as long as its running */ if(need_transition(fsa_state) && is_set(fsa_input_register, R_TE_CONNECTED) == FALSE) { register_fsa_action(A_TE_CANCEL); } return I_NULL; } /* frees msg upon completion */ gboolean send_msg_via_ha(ll_cluster_t *hb_fd, HA_Message *msg) { int log_level = LOG_DEBUG_3; gboolean broadcast = FALSE; gboolean all_is_good = TRUE; const char *op = cl_get_string(msg, F_CRM_TASK); const char *sys_to = cl_get_string(msg, F_CRM_SYS_TO); const char *host_to = cl_get_string(msg, F_CRM_HOST_TO); 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_ha_message(hb_fd, msg, NULL); } else { all_is_good = send_ha_message(hb_fd, msg, host_to); } } if(all_is_good == FALSE) { log_level = LOG_ERR; } if(log_level == LOG_ERR || (safe_str_neq(op, CRM_OP_HBEAT))) { do_crm_log(log_level, __FILE__, __FUNCTION__, "Sending %sHA message (ref=%s) to %s@%s %s.", broadcast?"broadcast ":"directed ", cl_get_string(msg, XML_ATTR_REFERENCE), crm_str(sys_to), host_to==NULL?"":host_to, all_is_good?"succeeded":"failed"); } crm_msg_del(msg); return all_is_good; } /* msg is deleted by the time this returns */ gboolean send_msg_via_ipc(HA_Message *msg, const char *sys) { gboolean send_ok = TRUE; IPC_Channel *client_channel; enum crmd_fsa_input next_input; crm_debug_4("relaying msg to sub_sys=%s via IPC", sys); client_channel = (IPC_Channel*)g_hash_table_lookup(ipc_clients, sys); if(cl_get_string(msg, F_CRM_HOST_FROM) == NULL) { ha_msg_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); msg = NULL; /* so the crm_msg_del() below doesnt fail */ } else if(sys != NULL && strcmp(sys, CRM_SYSTEM_CIB) == 0) { crm_err("Sub-system (%s) has been incorporated into the CRMd.", sys); crm_err("Change the way we handle this CIB message"); crm_log_message(LOG_ERR, msg); send_ok = FALSE; } 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); #endif next_input = do_lrm_invoke(A_LRM_INVOKE, C_IPC_MESSAGE, fsa_state, I_MESSAGE, fsa_data); delete_ha_msg_input(msg_copy); crm_free(fsa_data); /* todo: feed this back in for anything != I_NULL */ #ifdef FSA_TRACE crm_debug_2("Result of action %s was %s", fsa_action2string(A_LRM_INVOKE), fsa_input2string(next_input)); #endif } else { - crm_err("Unknown Sub-system (%s)... discarding message.", sys); + crm_err("Unknown Sub-system (%s)... discarding message.", + crm_str(sys)); send_ok = FALSE; } crm_msg_del(msg); return send_ok; } void msg_queue_helper(void) { 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); */ } 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/crm/crmd/subsystems.c b/crm/crmd/subsystems.c index ca98f4c5e8..baa372d389 100644 --- a/crm/crmd/subsystems.c +++ b/crm/crmd/subsystems.c @@ -1,215 +1,215 @@ /* * 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 /* for access */ #include #include #include #include /* for calls to open */ #include /* for calls to open */ #include /* for calls to open */ #include /* for getpwuid */ #include /* for initgroups */ #include /* for getrlimit */ #include /* for getrlimit */ #include #include #include #include #include #include #include #include static void crmdManagedChildRegistered(ProcTrack* p) { struct crm_subsystem_s *the_subsystem = p->privatedata; the_subsystem->pid = p->pid; } static void crmdManagedChildDied( ProcTrack* p, int status, int signo, int exitcode, int waslogged) { struct crm_subsystem_s *the_subsystem = p->privatedata; the_subsystem->pid = -1; the_subsystem->ipc = NULL; clear_bit_inplace(fsa_input_register, the_subsystem->flag_connected); if(is_set(fsa_input_register, the_subsystem->flag_required)) { /* this wasnt supposed to happen */ crm_err("The %s subsystem terminated unexpectedly", the_subsystem->name); register_fsa_input_before(C_IPC_MESSAGE, I_ERROR, NULL); } p->privatedata = NULL; } static const char * crmdManagedChildName(ProcTrack* p) { struct crm_subsystem_s *the_subsystem = p->privatedata; return the_subsystem->name; } static ProcTrack_ops crmd_managed_child_ops = { crmdManagedChildDied, crmdManagedChildRegistered, crmdManagedChildName }; gboolean stop_subsystem(struct crm_subsystem_s* the_subsystem) { IPC_Channel *client_channel = the_subsystem->ipc; crm_debug_2("Stopping sub-system \"%s\"", the_subsystem->name); clear_bit_inplace(fsa_input_register, the_subsystem->flag_required); if (the_subsystem->pid <= 0) { crm_debug_2("Client %s not running", the_subsystem->name); } else if(FALSE == is_set( fsa_input_register, the_subsystem->flag_connected)) { /* running but not yet connected */ crm_warn("Stopping %s before it had connected", the_subsystem->name); CL_KILL(the_subsystem->pid, -SIGKILL); the_subsystem->pid = -1; } else if(client_channel == NULL || client_channel->ops->get_chan_status( client_channel) != IPC_CONNECT) { crm_err("Client %s has already quit", the_subsystem->name); } else { HA_Message *quit = create_request( CRM_OP_QUIT, NULL, NULL, the_subsystem->name, AM_I_DC?CRM_SYSTEM_DC:CRM_SYSTEM_CRMD, NULL); crm_info("Sending quit message to %s.", the_subsystem->name); send_ipc_message(client_channel, quit); } return TRUE; } gboolean start_subsystem(struct crm_subsystem_s* the_subsystem) { pid_t pid; struct stat buf; int s_res; unsigned int j; struct rlimit oflimits; const char *devnull = "/dev/null"; char *args = NULL; crm_info("Starting sub-system \"%s\"", the_subsystem->name); set_bit_inplace(fsa_input_register, the_subsystem->flag_required); if (the_subsystem->pid > 0) { crm_warn("Client %s already running as pid %d", the_subsystem->name, (int) the_subsystem->pid); /* starting a started X is not an error */ return TRUE; } /* * We want to ensure that the exec will succeed before * we bother forking. */ if (access(the_subsystem->path, F_OK|X_OK) != 0) { cl_perror("Cannot (access) exec %s", the_subsystem->path); return FALSE; } s_res = stat(the_subsystem->command, &buf); if(s_res != 0) { cl_perror("Cannot (stat) exec %s", the_subsystem->command); return FALSE; } /* We need to fork so we can make child procs not real time */ switch(pid=fork()) { case -1: crm_err("Cannot fork."); return FALSE; default: /* Parent */ NewTrackedProc(pid, 0, PT_LOGNORMAL, the_subsystem, &crmd_managed_child_ops); the_subsystem->pid = pid; return TRUE; case 0: /* Child */ break; } crm_debug("Executing \"%s %s\" (pid %d)", the_subsystem->command, the_subsystem->args, (int) getpid()); /* A precautionary measure */ getrlimit(RLIMIT_NOFILE, &oflimits); for (j=0; j < oflimits.rlim_cur; ++j) { close(j); } (void)open(devnull, O_RDONLY); /* Stdin: fd 0 */ (void)open(devnull, O_WRONLY); /* Stdout: fd 1 */ (void)open(devnull, O_WRONLY); /* Stderr: fd 2 */ if(the_subsystem->args != NULL) { args = crm_strdup(the_subsystem->args); } { char* const start_args[] = { crm_strdup(the_subsystem->command), args, NULL }; (void)execvp(the_subsystem->command, start_args); } /* Should not happen */ cl_perror("FATAL: Cannot exec %s %s", - the_subsystem->command, the_subsystem->args); + the_subsystem->command, crm_str(the_subsystem->args)); exit(100); /* Suppress respawning */ return TRUE; /* never reached */ } diff --git a/crm/pengine/utils.c b/crm/pengine/utils.c index f2786b13d4..ac89167cb6 100644 --- a/crm/pengine/utils.c +++ b/crm/pengine/utils.c @@ -1,1342 +1,1342 @@ -/* $Id: utils.c,v 1.75 2005/05/18 20:15:58 andrew Exp $ */ +/* $Id: utils.c,v 1.76 2005/05/19 06:49:52 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include int action_id = 1; int color_id = 0; extern GListPtr global_action_list; void print_str_str(gpointer key, gpointer value, gpointer user_data); gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); void unpack_operation(crm_data_t *xml_obj, GHashTable *hash); /* only for rsc_colocation constraints */ rsc_colocation_t * invert_constraint(rsc_colocation_t *constraint) { rsc_colocation_t *inverted_con = NULL; crm_debug_2("Inverting constraint"); if(constraint == NULL) { pe_err("Cannot invert NULL constraint"); return NULL; } crm_malloc0(inverted_con, sizeof(rsc_colocation_t)); if(inverted_con == NULL) { return NULL; } inverted_con->id = crm_strdup(constraint->id); inverted_con->strength = constraint->strength; /* swap the direction */ inverted_con->rsc_lh = constraint->rsc_rh; inverted_con->rsc_rh = constraint->rsc_lh; crm_action_debug_3( print_rsc_colocation("Inverted constraint", inverted_con, FALSE)); return inverted_con; } /* are the contents of list1 and list2 equal * nodes with weight < 0 are ignored if filter == TRUE * * slow but linear * */ gboolean node_list_eq(GListPtr list1, GListPtr list2, gboolean filter) { node_t *other_node; GListPtr lhs = list1; GListPtr rhs = list2; slist_iter( node, node_t, lhs, lpc, if(node == NULL || (filter && node->weight < 0)) { continue; } other_node = (node_t*) pe_find_node(rhs, node->details->uname); if(other_node == NULL || other_node->weight < 0) { return FALSE; } ); lhs = list2; rhs = list1; slist_iter( node, node_t, lhs, lpc, if(node == NULL || (filter && node->weight < 0)) { continue; } other_node = (node_t*) pe_find_node(rhs, node->details->uname); if(other_node == NULL || other_node->weight < 0) { return FALSE; } ); return TRUE; } /* the intersection of list1 and list2 */ GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter) { GListPtr result = NULL; unsigned lpc = 0; for(lpc = 0; lpc < g_list_length(list1); lpc++) { node_t *node = (node_t*)g_list_nth_data(list1, lpc); node_t *other_node = pe_find_node(list2, node->details->uname); node_t *new_node = NULL; if(other_node != NULL) { new_node = node_copy(node); } if(new_node != NULL) { new_node->weight = merge_weights( new_node->weight, other_node->weight); if(filter && new_node->weight < 0) { crm_free(new_node); new_node = NULL; } } if(new_node != NULL) { result = g_list_append(result, new_node); } } return result; } /* list1 - list2 */ GListPtr node_list_minus(GListPtr list1, GListPtr list2, gboolean filter) { GListPtr result = NULL; slist_iter( node, node_t, list1, lpc, node_t *other_node = pe_find_node(list2, node->details->uname); node_t *new_node = NULL; if(node == NULL || other_node != NULL || (filter && node->weight < 0)) { continue; } new_node = node_copy(node); result = g_list_append(result, new_node); ); crm_debug_2("Minus result len: %d", g_list_length(result)); return result; } /* list1 + list2 - (intersection of list1 and list2) */ GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter) { GListPtr result = NULL; slist_iter( node, node_t, list1, lpc, node_t *new_node = NULL; node_t *other_node = (node_t*) pe_find_node(list2, node->details->uname); if(node == NULL || other_node != NULL || (filter && node->weight < 0)) { continue; } new_node = node_copy(node); result = g_list_append(result, new_node); ); slist_iter( node, node_t, list2, lpc, node_t *new_node = NULL; node_t *other_node = (node_t*) pe_find_node(list1, node->details->uname); if(node == NULL || other_node != NULL || (filter && node->weight < 0)) { continue; } new_node = node_copy(node); result = g_list_append(result, new_node); ); crm_debug_2("Xor result len: %d", g_list_length(result)); return result; } GListPtr node_list_or(GListPtr list1, GListPtr list2, gboolean filter) { node_t *other_node = NULL; GListPtr result = NULL; gboolean needs_filter = FALSE; result = node_list_dup(list1, filter); slist_iter( node, node_t, list2, lpc, if(node == NULL) { continue; } other_node = (node_t*)pe_find_node( result, node->details->uname); if(other_node != NULL) { other_node->weight = merge_weights( other_node->weight, node->weight); if(filter && node->weight < 0) { needs_filter = TRUE; } } else { node_t *new_node = node_copy(node); result = g_list_append(result, new_node); } ); /* not the neatest way, but the most expedient for now */ if(filter && needs_filter) { GListPtr old_result = result; result = node_list_dup(old_result, filter); pe_free_shallow_adv(old_result, TRUE); } return result; } GListPtr node_list_dup(GListPtr list1, gboolean filter) { GListPtr result = NULL; slist_iter( this_node, node_t, list1, lpc, node_t *new_node = NULL; if(filter && this_node->weight < 0) { continue; } new_node = node_copy(this_node); if(new_node != NULL) { result = g_list_append(result, new_node); } ); return result; } node_t * node_copy(node_t *this_node) { node_t *new_node = NULL; CRM_DEV_ASSERT(this_node != NULL); if(this_node == NULL) { pe_err("Failed copy of node."); return NULL; } crm_malloc0(new_node, sizeof(node_t)); CRM_DEV_ASSERT(new_node != NULL); if(new_node == NULL) { return NULL; } crm_debug_4("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node); new_node->weight = this_node->weight; new_node->fixed = this_node->fixed; new_node->details = this_node->details; return new_node; } /* * Create a new color with the contents of "nodes" as the list of * possible nodes that resources with this color can be run on. * * Typically, when creating a color you will provide the node list from * the resource you will first assign the color to. * * If "colors" != NULL, it will be added to that list * If "resources" != NULL, it will be added to every provisional resource * in that list */ color_t * create_color(GListPtr *colors, resource_t *resource, GListPtr node_list) { color_t *new_color = NULL; crm_debug_4("Creating color"); crm_malloc0(new_color, sizeof(color_t)); if(new_color == NULL) { return NULL; } new_color->id = color_id++; new_color->local_weight = 1.0; crm_debug_4("Creating color details"); crm_malloc0(new_color->details, sizeof(struct color_shared_s)); if(new_color->details == NULL) { crm_free(new_color); return NULL; } new_color->details->id = new_color->id; new_color->details->highest_priority = -1; new_color->details->chosen_node = NULL; new_color->details->candidate_nodes = NULL; new_color->details->allocated_resources = NULL; new_color->details->pending = TRUE; if(resource != NULL) { crm_debug_4("populating node list"); new_color->details->highest_priority = resource->priority; new_color->details->candidate_nodes = node_list_dup(node_list, TRUE); } crm_action_debug_3(print_color("Created color", new_color, TRUE)); if(colors != NULL) { *colors = g_list_append(*colors, new_color); } return new_color; } color_t * copy_color(color_t *a_color) { color_t *color_copy = NULL; if(a_color == NULL) { pe_err("Cannot copy NULL"); return NULL; } crm_malloc0(color_copy, sizeof(color_t)); if(color_copy != NULL) { color_copy->id = a_color->id; color_copy->details = a_color->details; color_copy->local_weight = 1.0; } return color_copy; } resource_t * pe_find_resource(GListPtr rsc_list, const char *id) { unsigned lpc = 0; resource_t *rsc = NULL; resource_t *child_rsc = NULL; crm_debug_3("Looking for %s in %d objects", id, g_list_length(rsc_list)); for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) { rsc = g_list_nth_data(rsc_list, lpc); if(rsc != NULL && safe_str_eq(rsc->id, id)){ crm_debug_3("Found a match for %s", id); return rsc; } } for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) { rsc = g_list_nth_data(rsc_list, lpc); child_rsc = rsc->fns->find_child(rsc, id); if(child_rsc != NULL) { crm_debug_3("Found a match for %s in %s", id, rsc->id); return child_rsc; } } /* error */ return NULL; } node_t * pe_find_node(GListPtr nodes, const char *uname) { unsigned lpc = 0; node_t *node = NULL; for(lpc = 0; lpc < g_list_length(nodes); lpc++) { node = g_list_nth_data(nodes, lpc); if(node != NULL && safe_str_eq(node->details->uname, uname)) { return node; } } /* error */ return NULL; } node_t * pe_find_node_id(GListPtr nodes, const char *id) { unsigned lpc = 0; node_t *node = NULL; for(lpc = 0; lpc < g_list_length(nodes); lpc++) { node = g_list_nth_data(nodes, lpc); if(safe_str_eq(node->details->id, id)) { return node; } } /* error */ return NULL; } gint gslist_color_compare(gconstpointer a, gconstpointer b); color_t * find_color(GListPtr candidate_colors, color_t *other_color) { GListPtr tmp = g_list_find_custom(candidate_colors, other_color, gslist_color_compare); if(tmp != NULL) { return (color_t *)tmp->data; } return NULL; } gint gslist_color_compare(gconstpointer a, gconstpointer b) { const color_t *color_a = (const color_t*)a; const color_t *color_b = (const color_t*)b; /* crm_debug_4("%d vs. %d", a?color_a->id:-2, b?color_b->id:-2); */ if(a == b) { return 0; } else if(a == NULL || b == NULL) { return 1; } else if(color_a->id == color_b->id) { return 0; } return 1; } gint sort_rsc_priority(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t*)a; const resource_t *resource2 = (const resource_t*)b; if(a == NULL && b == NULL) { return 0; } if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(resource1->priority > resource2->priority) { return -1; } if(resource1->priority < resource2->priority) { return 1; } return 0; } gint sort_cons_strength(gconstpointer a, gconstpointer b) { const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a; const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(rsc_constraint1->strength > rsc_constraint2->strength) { return 1; } if(rsc_constraint1->strength < rsc_constraint2->strength) { return -1; } return 0; } gint sort_color_weight(gconstpointer a, gconstpointer b) { const color_t *color1 = (const color_t*)a; const color_t *color2 = (const color_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(color1->local_weight > color2->local_weight) { return -1; } if(color1->local_weight < color2->local_weight) { return 1; } return 0; } /* return -1 if 'a' is more preferred * return 1 if 'b' is more preferred */ gint sort_node_weight(gconstpointer a, gconstpointer b) { const node_t *node1 = (const node_t*)a; const node_t *node2 = (const node_t*)b; float node1_weight = 0; float node2_weight = 0; if(a == NULL) { return 1; } if(b == NULL) { return -1; } node1_weight = node1->weight; node2_weight = node2->weight; if(node1->details->unclean || node1->details->shutdown) { node1_weight = -INFINITY; } if(node2->details->unclean || node2->details->shutdown) { node2_weight = -INFINITY; } if(node1_weight > node2_weight) { crm_debug_3("%s (%f) > %s (%f) : weight", node1->details->id, node1_weight, node2->details->id, node2_weight); return -1; } if(node1_weight < node2_weight) { crm_debug_3("%s (%f) < %s (%f) : weight", node1->details->id, node1_weight, node2->details->id, node2_weight); return 1; } /* now try to balance resources across the cluster */ if(node1->details->num_resources < node2->details->num_resources) { crm_debug_3("%s (%d) < %s (%d) : resources", node1->details->id, node1->details->num_resources, node2->details->id, node2->details->num_resources); return -1; } else if(node1->details->num_resources > node2->details->num_resources) { crm_debug_3("%s (%d) > %s (%d) : resources", node1->details->id, node1->details->num_resources, node2->details->id, node2->details->num_resources); return 1; } crm_debug_3("%s = %s", node1->details->id, node2->details->id); return 0; } action_t * custom_action( resource_t *rsc, char *key, const char *task, node_t *on_node) { action_t *action = NULL; GListPtr possible_matches = NULL; CRM_DEV_ASSERT(key != NULL); if(crm_assert_failed) { return NULL; } CRM_DEV_ASSERT(task != NULL); if(crm_assert_failed) { return NULL; } if(rsc != NULL) { possible_matches = find_actions(rsc->actions, key, on_node); } if(possible_matches != NULL) { if(g_list_length(possible_matches) > 1) { pe_warn("Action %s for %s on %s exists %d times", task, rsc?rsc->id:"", on_node?on_node->details->id:"", g_list_length(possible_matches)); } action = g_list_nth_data(possible_matches, 0); crm_debug_3("Found existing action (%d) %s for %s on %s", action->id, task, rsc?rsc->id:"", on_node?on_node->details->id:""); } if(action == NULL) { crm_debug_3("Creating action %s for %s on %s", task, rsc?rsc->id:"", on_node?on_node->details->id:""); crm_malloc0(action, sizeof(action_t)); if(action != NULL) { action->id = action_id++; action->rsc = rsc; action->task = task; action->node = on_node; action->actions_before = NULL; action->actions_after = NULL; action->failure_is_fatal = TRUE; action->pseudo = FALSE; action->dumped = FALSE; action->runnable = TRUE; action->processed = FALSE; action->optional = FALSE; action->seen_count = 0; action->extra = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); global_action_list = g_list_append( global_action_list, action); action->uuid = key; if(rsc != NULL) { action->op_entry = find_rsc_op_entry(rsc, key); unpack_operation(action->op_entry,action->extra); rsc->actions = g_list_append( rsc->actions, action); } crm_debug_3("Action %d created", action->id); } } if(rsc != NULL) { if(action->node == NULL) { action->runnable = FALSE; } else if(action->node->details->online == FALSE) { pe_warn("Action %d %s for %s on %s is unrunnable", action->id, task, rsc?rsc->id:"", action->node?action->node->details->id:""); action->runnable = FALSE; } else { action->runnable = TRUE; } switch(text2task(action->task)) { case stop_rsc: rsc->stopping = TRUE; break; case start_rsc: rsc->starting = FALSE; if(action->runnable) { rsc->starting = TRUE; } break; default: break; } } return action; } void unpack_operation(crm_data_t *xml_obj, GHashTable *hash) { int lpc = 0; const char *value = NULL; const char *fields[] = { "interval", "timeout", "start_delay" }; if(xml_obj == NULL) { return; } for(;lpc < DIMOF(fields); lpc++) { value = crm_element_value(xml_obj, fields[lpc]); add_hash_param(hash, fields[lpc], value); } unpack_instance_attributes(xml_obj, hash); } crm_data_t * find_rsc_op_entry(resource_t *rsc, const char *key) { const char *name = NULL; const char *interval = NULL; char *match_key = NULL; xml_child_iter( rsc->ops_xml, operation, "op", name = crm_element_value(operation, "name"); interval = crm_element_value(operation, "interval"); match_key = generate_op_key(rsc->id,name,crm_get_msec(interval)); crm_debug("Matching %s with %s", key, match_key); if(safe_str_eq(key, match_key)) { crm_free(match_key); return operation; } crm_free(match_key); ); crm_debug("No matching for %s", key); return NULL; } const char * strength2text(enum con_strength strength) { const char *result = ""; switch(strength) { case pecs_ignore: result = "ignore"; break; case pecs_must: result = XML_STRENGTH_VAL_MUST; break; case pecs_must_not: result = XML_STRENGTH_VAL_MUSTNOT; break; case pecs_startstop: result = "start/stop"; break; } return result; } enum action_tasks text2task(const char *task) { if(safe_str_eq(task, CRMD_ACTION_STOP)) { return stop_rsc; } else if(safe_str_eq(task, CRMD_ACTION_STOPPED)) { return stopped_rsc; } else if(safe_str_eq(task, CRMD_ACTION_START)) { return start_rsc; } else if(safe_str_eq(task, CRMD_ACTION_STARTED)) { return started_rsc; } else if(safe_str_eq(task, CRM_OP_SHUTDOWN)) { return shutdown_crm; } else if(safe_str_eq(task, CRM_OP_FENCE)) { return stonith_node; } else if(safe_str_eq(task, CRMD_ACTION_MON)) { return monitor_rsc; } pe_err("Unsupported action: %s", task); return no_action; } const char * task2text(enum action_tasks task) { const char *result = ""; switch(task) { case no_action: result = "no_action"; break; case stop_rsc: result = CRMD_ACTION_STOP; break; case stopped_rsc: result = CRMD_ACTION_STOPPED; break; case start_rsc: result = CRMD_ACTION_START; break; case started_rsc: result = CRMD_ACTION_STARTED; break; case shutdown_crm: result = CRM_OP_SHUTDOWN; break; case stonith_node: result = CRM_OP_FENCE; break; case monitor_rsc: result = CRMD_ACTION_MON; break; } return result; } void print_node(const char *pre_text, node_t *node, gboolean details) { if(node == NULL) { crm_debug_3("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_3("%s%s%sNode %s: (weight=%f, fixed=%s)", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ", node->details->uname, node->weight, node->fixed?"True":"False"); if(details && node->details != NULL) { char *pe_mutable = crm_strdup("\t\t"); crm_debug_3("\t\t===Node Attributes"); g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable); crm_free(pe_mutable); } - if(details) { + if(details && node != NULL) { crm_debug_3("\t\t===Node Attributes"); slist_iter( rsc, resource_t, node->details->running_rsc, lpc, print_resource("\t\t", rsc, FALSE); ); } } /* * Used by the HashTable for-loop */ void print_str_str(gpointer key, gpointer value, gpointer user_data) { crm_debug_3("%s%s %s ==> %s", user_data==NULL?"":(char*)user_data, user_data==NULL?"":": ", (char*)key, (char*)value); } void print_color_details(const char *pre_text, struct color_shared_s *color, gboolean details) { if(color == NULL) { crm_debug_3("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_3("%s%sColor %d: node=%s (from %d candidates)", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", color->id, color->chosen_node==NULL?"":color->chosen_node->details->uname, g_list_length(color->candidate_nodes)); if(details) { slist_iter(node, node_t, color->candidate_nodes, lpc, print_node("\t", node, FALSE)); } } void print_color(const char *pre_text, color_t *color, gboolean details) { if(color == NULL) { crm_debug_3("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_3("%s%sColor %d: (weight=%f, node=%s, possible=%d)", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", color->id, color->local_weight, safe_val5("",color,details,chosen_node,details,uname), g_list_length(color->details->candidate_nodes)); if(details) { print_color_details("\t", color->details, details); } } void print_rsc_to_node(const char *pre_text, rsc_to_node_t *cons, gboolean details) { if(cons == NULL) { crm_debug_3("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_3("%s%s%s Constraint %s (%p) - %d nodes:", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", "rsc_to_node", cons->id, cons, g_list_length(cons->node_list_rh)); if(details == FALSE) { crm_debug_3("\t%s (score=%f : node placement rule)", safe_val3(NULL, cons, rsc_lh, id), cons->weight); slist_iter( node, node_t, cons->node_list_rh, lpc, print_node("\t\t-->", node, FALSE) ); } } void print_rsc_colocation(const char *pre_text, rsc_colocation_t *cons, gboolean details) { if(cons == NULL) { crm_debug_3("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_3("%s%s%s Constraint %s (%p):", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", XML_CONS_TAG_RSC_DEPEND, cons->id, cons); if(details == FALSE) { crm_debug_3("\t%s --> %s, %s", safe_val3(NULL, cons, rsc_lh, id), safe_val3(NULL, cons, rsc_rh, id), strength2text(cons->strength)); } } void print_resource(const char *pre_text, resource_t *rsc, gboolean details) { if(rsc == NULL) { crm_debug_3("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } rsc->fns->dump(rsc, pre_text, details); } void print_action(const char *pre_text, action_t *action, gboolean details) { log_action(LOG_DEBUG_3, pre_text, action, details); } #define util_log(fmt...) do_crm_log(log_level, __FILE__, __FUNCTION__, fmt) void log_action(int log_level, const char *pre_text, action_t *action, gboolean details) { if(action == NULL) { util_log("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } switch(text2task(action->task)) { case stonith_node: case shutdown_crm: util_log("%s%s%sAction %d: %s @ %s", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", action->pseudo?"Pseduo ":action->optional?"Optional ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ", action->id, action->task, safe_val4(NULL, action, node, details, uname)); break; default: util_log("%s%s%sAction %d: %s %s @ %s", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", action->optional?"Optional ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ", action->id, action->task, safe_val3(NULL, action, rsc, id), safe_val4(NULL, action, node, details, uname)); break; } if(details) { #if 1 util_log("\t\t====== Preceeding Actions"); slist_iter( other, action_wrapper_t, action->actions_before, lpc, log_action(log_level-1, "\t\t", other->action, FALSE); ); util_log("\t\t====== Subsequent Actions"); slist_iter( other, action_wrapper_t, action->actions_after, lpc, log_action(log_level-1, "\t\t", other->action, FALSE); ); #else util_log("\t\t====== Subsequent Actions"); slist_iter( other, action_wrapper_t, action->actions_after, lpc, log_action(log_level-1, "\t\t", other->action, FALSE); ); #endif util_log("\t\t====== End"); } else { util_log("\t\t(seen=%d, before=%d, after=%d)", action->seen_count, g_list_length(action->actions_before), g_list_length(action->actions_after)); } } void pe_free_nodes(GListPtr nodes) { while(nodes != NULL) { GListPtr list_item = nodes; node_t *node = (node_t*)list_item->data; struct node_shared_s *details = node->details; nodes = nodes->next; crm_debug_4("deleting node"); crm_debug_4("%s is being deleted", details->uname); print_node("delete", node, FALSE); if(details != NULL) { if(details->attrs != NULL) { g_hash_table_foreach_remove(details->attrs, ghash_free_str_str, NULL); g_hash_table_destroy(details->attrs); } } } if(nodes != NULL) { g_list_free(nodes); } } gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data) { crm_free(key); crm_free(value); return TRUE; } void pe_free_colors(GListPtr colors) { while(colors != NULL) { GListPtr list_item = colors; color_t *color = (color_t *)list_item->data; struct color_shared_s *details = color->details; colors = colors->next; if(details != NULL) { pe_free_shallow(details->candidate_nodes); pe_free_shallow_adv(details->allocated_resources, FALSE); crm_free(details->chosen_node); crm_free(details); } crm_free(color); } if(colors != NULL) { g_list_free(colors); } } void pe_free_shallow(GListPtr alist) { pe_free_shallow_adv(alist, TRUE); } void pe_free_shallow_adv(GListPtr alist, gboolean with_data) { GListPtr item; GListPtr item_next = alist; while(item_next != NULL) { item = item_next; item_next = item_next->next; if(with_data) { /* crm_debug_4("freeing %p", item->data); */ crm_free(item->data); } item->data = NULL; item->next = NULL; g_list_free(item); } } void pe_free_resources(GListPtr resources) { volatile GListPtr list_item = NULL; resource_t *rsc = NULL; while(resources != NULL) { list_item = resources; rsc = (resource_t *)list_item->data; resources = resources->next; pe_free_shallow_adv(rsc->candidate_colors, TRUE); g_hash_table_destroy(rsc->parameters); rsc->fns->free(rsc); } if(resources != NULL) { g_list_free(resources); } } void pe_free_actions(GListPtr actions) { while(actions != NULL) { GListPtr list_item = actions; action_t *action = (action_t *)list_item->data; actions = actions->next; pe_free_shallow(action->actions_before);/* action_warpper_t* */ pe_free_shallow(action->actions_after); /* action_warpper_t* */ action->actions_before = NULL; action->actions_after = NULL; g_hash_table_destroy(action->extra); crm_free(action->uuid); crm_free(action); } if(actions != NULL) { g_list_free(actions); } } void pe_free_ordering(GListPtr constraints) { while(constraints != NULL) { GListPtr list_item = constraints; order_constraint_t *order = list_item->data; constraints = constraints->next; crm_free(order->lh_action_task); crm_free(order->rh_action_task); crm_free(order); } if(constraints != NULL) { g_list_free(constraints); } } void pe_free_rsc_colocation(rsc_colocation_t *cons) { if(cons != NULL) { crm_debug_3("Freeing constraint %s (%p)", cons->id, cons); crm_free(cons); } } void pe_free_rsc_to_node(rsc_to_node_t *cons) { if(cons != NULL) { /* right now we dont make copies so this isnt required */ /* pe_free_shallow(cons->node_list_rh); */ /* node_t* */ crm_free(cons); } } GListPtr find_actions(GListPtr input, const char *key, node_t *on_node) { GListPtr result = NULL; CRM_DEV_ASSERT(key != NULL); slist_iter( action, action_t, input, lpc, crm_debug_4("Matching %s against %s", key, action->uuid); if(safe_str_neq(key, action->uuid)) { continue; } else if(on_node == NULL) { result = g_list_append(result, action); } else if(action->node == NULL) { /* skip */ crm_debug("While looking for %s action on %s, " "found an unallocated one. Assigning" " it to the requested node...", key, on_node->details->uname); action->node = on_node; result = g_list_append(result, action); } else if(safe_str_eq(on_node->details->id, action->node->details->id)) { result = g_list_append(result, action); } ); return result; } void set_id(crm_data_t * xml_obj, const char *prefix, int child) { int id_len = 0; gboolean use_prefix = TRUE; gboolean use_child = TRUE; char *new_id = NULL; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); id_len = 1 + strlen(id); if(child > 999) { pe_err("Are you insane?!?" " The CRM does not support > 1000 children per resource"); return; } else if(child < 0) { use_child = FALSE; } else { id_len += 4; /* child */ } if(prefix == NULL || safe_str_eq(id, prefix)) { use_prefix = FALSE; } else { id_len += (1 + strlen(prefix)); } crm_malloc0(new_id, id_len); if(use_child) { snprintf(new_id, id_len, "%s%s%s:%d", use_prefix?prefix:"", use_prefix?":":"", id, child); } else { snprintf(new_id, id_len, "%s%s%s", use_prefix?prefix:"", use_prefix?":":"", id); } set_xml_property_copy(xml_obj, XML_ATTR_ID, new_id); crm_free(new_id); } float merge_weights(float w1, float w2) { float result = w1 + w2; if(w1 <= -INFINITY || w2 <= -INFINITY) { if(w1 == INFINITY || w2 == INFINITY) { pe_warn("-INFINITY + INFINITY == -INFINITY"); } return -INFINITY; } else if(w1 >= INFINITY || w2 >= INFINITY) { return INFINITY; } /* detect wrap-around */ if(result > 0) { if(w1 <= 0 && w2 < 0) { result = -INFINITY; } } else if(w1 > 0 && w2 > 0) { result = INFINITY; } /* detect +/- INFINITY */ if(result >= INFINITY) { result = INFINITY; } else if(result <= -INFINITY) { result = -INFINITY; } return result; }