diff --git a/crmd/te_actions.c b/crmd/te_actions.c index 9cdf4e96e9..f728b740cc 100644 --- a/crmd/te_actions.c +++ b/crmd/te_actions.c @@ -1,517 +1,517 @@ /* * 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include char *te_uuid = NULL; void send_rsc_command(crm_action_t *action); static void te_start_action_timer(crm_graph_t *graph, crm_action_t *action) { crm_malloc0(action->timer, sizeof(crm_action_timer_t)); action->timer->timeout = action->timeout; action->timer->reason = timeout_action; action->timer->action = action; action->timer->source_id = g_timeout_add( action->timer->timeout + graph->network_delay, action_timer_callback, (void*)action->timer); CRM_ASSERT(action->timer->source_id != 0); } static gboolean te_pseudo_action(crm_graph_t *graph, crm_action_t *pseudo) { crm_info("Pseudo action %d fired and confirmed", pseudo->id); pseudo->confirmed = TRUE; update_graph(graph, pseudo); trigger_graph(); return TRUE; } void send_stonith_update(crm_action_t *action, const char *target, const char *uuid) { enum cib_errors rc = cib_ok; /* zero out the node-status & remove all LRM status info */ xmlNode *node_state = create_xml_node(NULL, XML_CIB_TAG_STATE); CRM_CHECK(target != NULL, return); CRM_CHECK(uuid != NULL, return); crm_xml_add(node_state, XML_ATTR_UUID, uuid); crm_xml_add(node_state, XML_ATTR_UNAME, target); crm_xml_add(node_state, XML_CIB_ATTR_HASTATE, DEADSTATUS); crm_xml_add(node_state, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); crm_xml_add(node_state, XML_CIB_ATTR_CRMDSTATE, OFFLINESTATUS); crm_xml_add(node_state, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_DOWN); crm_xml_add(node_state, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_DOWN); crm_xml_add(node_state, XML_ATTR_ORIGIN, __FUNCTION__); rc = fsa_cib_conn->cmds->update( fsa_cib_conn, XML_CIB_TAG_STATUS, node_state, cib_quorum_override|cib_scope_local|cib_can_create); /* Delay processing the trigger until the update completes */ crm_info("Sending fencing update %d for %s", rc, target); add_cib_op_callback(fsa_cib_conn, rc, FALSE, crm_strdup(target), cib_fencing_updated); /* Make sure it sticks */ /* fsa_cib_conn->cmds->bump_epoch(fsa_cib_conn, cib_quorum_override|cib_scope_local); */ erase_status_tag(target, XML_CIB_TAG_LRM, cib_scope_local); erase_status_tag(target, XML_TAG_TRANSIENT_NODEATTRS, cib_scope_local); free_xml(node_state); /* Make sure the membership cache is accurate */ crm_update_peer(0, 0, 0, -1, crm_proc_none, uuid, target, NULL, CRM_NODE_LOST); return; } static gboolean te_fence_node(crm_graph_t *graph, crm_action_t *action) { int rc = 0; const char *id = NULL; const char *uuid = NULL; const char *target = NULL; const char *type = NULL; gboolean invalid_action = FALSE; id = ID(action->xml); target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID); type = crm_meta_value(action->params, "stonith_action"); CRM_CHECK(id != NULL, invalid_action = TRUE); CRM_CHECK(uuid != NULL, invalid_action = TRUE); CRM_CHECK(type != NULL, invalid_action = TRUE); CRM_CHECK(target != NULL, invalid_action = TRUE); if(invalid_action) { crm_log_xml_warn(action->xml, "BadAction"); return FALSE; } te_log_action(LOG_INFO, "Executing %s fencing operation (%s) on %s (timeout=%d)", type, id, target, transition_graph->stonith_timeout); /* Passing NULL means block until we can connect... */ te_connect_stonith(NULL); rc = stonith_api->cmds->fence( - stonith_api, 0, target, action->params, type, transition_graph->stonith_timeout/1000); + stonith_api, 0, target, type, transition_graph->stonith_timeout/1000); stonith_api->cmds->register_callback( stonith_api, rc, transition_graph->stonith_timeout/1000, FALSE, generate_transition_key(transition_graph->id, action->id, 0, te_uuid), "tengine_stonith_callback", tengine_stonith_callback); return TRUE; } static int get_target_rc(crm_action_t *action) { const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC); if(target_rc_s != NULL) { return crm_parse_int(target_rc_s, "0"); } return 0; } static gboolean te_crm_command(crm_graph_t *graph, crm_action_t *action) { char *counter = NULL; xmlNode *cmd = NULL; gboolean is_local = FALSE; const char *id = NULL; const char *task = NULL; const char *value = NULL; const char *on_node = NULL; gboolean rc = TRUE; gboolean no_wait = FALSE; id = ID(action->xml); task = crm_element_value(action->xml, XML_LRM_ATTR_TASK); on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); CRM_CHECK(on_node != NULL && strlen(on_node) != 0, te_log_action(LOG_ERR, "Corrupted command (id=%s) %s: no node", crm_str(id), crm_str(task)); return FALSE); te_log_action(LOG_INFO, "Executing crm-event (%s): %s on %s%s%s", crm_str(id), crm_str(task), on_node, is_local?" (local)":"", no_wait?" - no waiting":""); if(safe_str_eq(on_node, fsa_our_uname)) { is_local = TRUE; } value = crm_meta_value(action->params, XML_ATTR_TE_NOWAIT); if(crm_is_true(value)) { no_wait = TRUE; } if(is_local && safe_str_eq(task, CRM_OP_SHUTDOWN)) { /* defer until everything else completes */ te_log_action(LOG_INFO, "crm-event (%s) is a local shutdown", crm_str(id)); graph->completion_action = tg_shutdown; graph->abort_reason = "local shutdown"; action->confirmed = TRUE; update_graph(graph, action); trigger_graph(); return TRUE; } cmd = create_request(task, action->xml, on_node, CRM_SYSTEM_CRMD, CRM_SYSTEM_TENGINE, NULL); counter = generate_transition_key( transition_graph->id, action->id, get_target_rc(action), te_uuid); crm_xml_add(cmd, XML_ATTR_TRANSITION_KEY, counter); rc = send_cluster_message(on_node, crm_msg_crmd, cmd, TRUE); crm_free(counter); free_xml(cmd); if(rc == FALSE) { crm_err("Action %d failed: send", action->id); return FALSE; } else if(no_wait) { action->confirmed = TRUE; update_graph(graph, action); trigger_graph(); } else { if(action->timeout <= 0) { crm_err("Action %d: %s on %s had an invalid timeout (%dms). Using %dms instead", action->id, task, on_node, action->timeout, graph->network_delay); action->timeout = graph->network_delay; } te_start_action_timer(graph, action); } return TRUE; } gboolean cib_action_update(crm_action_t *action, int status, int op_rc) { lrm_op_t *op = NULL; xmlNode *state = NULL; xmlNode *rsc = NULL; xmlNode *xml_op = NULL; xmlNode *action_rsc = NULL; enum cib_errors rc = cib_ok; const char *name = NULL; const char *value = NULL; const char *rsc_id = NULL; const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK); const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); const char *task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY); const char *target_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID); int call_options = cib_quorum_override|cib_scope_local; int target_rc = get_target_rc(action); if(status == LRM_OP_PENDING) { crm_debug("%s %d: Recording pending operation %s on %s", crm_element_name(action->xml), action->id, task_uuid, target); } else { crm_warn("%s %d: %s on %s timed out", crm_element_name(action->xml), action->id, task_uuid, target); } action_rsc = find_xml_node(action->xml, XML_CIB_TAG_RESOURCE, TRUE); if(action_rsc == NULL) { return FALSE; } rsc_id = ID(action_rsc); CRM_CHECK(rsc_id != NULL, crm_log_xml_err(action->xml, "Bad:action"); return FALSE); /* update the CIB */ state = create_xml_node(NULL, XML_CIB_TAG_STATE); crm_xml_add(state, XML_ATTR_UUID, target_uuid); crm_xml_add(state, XML_ATTR_UNAME, target); rsc = create_xml_node(state, XML_CIB_TAG_LRM); crm_xml_add(rsc, XML_ATTR_ID, target_uuid); rsc = create_xml_node(rsc, XML_LRM_TAG_RESOURCES); rsc = create_xml_node(rsc, XML_LRM_TAG_RESOURCE); crm_xml_add(rsc, XML_ATTR_ID, rsc_id); name = XML_ATTR_TYPE; value = crm_element_value(action_rsc, name); crm_xml_add(rsc, name, value); name = XML_AGENT_ATTR_CLASS; value = crm_element_value(action_rsc, name); crm_xml_add(rsc, name, value); name = XML_AGENT_ATTR_PROVIDER; value = crm_element_value(action_rsc, name); crm_xml_add(rsc, name, value); op = convert_graph_action(NULL, action, status, op_rc); op->call_id = -1; op->user_data = generate_transition_key( transition_graph->id, action->id, target_rc, te_uuid); xml_op = create_operation_update( rsc, op, CRM_FEATURE_SET, target_rc, __FUNCTION__, LOG_INFO); free_lrm_op(op); crm_debug_3("Updating CIB with \"%s\" (%s): %s %s on %s", status<0?"new action":XML_ATTR_TIMEOUT, crm_element_name(action->xml), crm_str(task), rsc_id, target); rc = fsa_cib_conn->cmds->update( fsa_cib_conn, XML_CIB_TAG_STATUS, state, call_options); crm_debug_2("Updating CIB with %s action %d: %s on %s (call_id=%d)", op_status2text(status), action->id, task_uuid, target, rc); add_cib_op_callback(fsa_cib_conn, rc, FALSE, NULL, cib_action_updated); free_xml(state); action->sent_update = TRUE; if(rc < cib_ok) { return FALSE; } return TRUE; } static gboolean te_rsc_command(crm_graph_t *graph, crm_action_t *action) { /* never overwrite stop actions in the CIB with * anything other than completed results * * Writing pending stops makes it look like the * resource is running again */ xmlNode *cmd = NULL; xmlNode *rsc_op = NULL; gboolean rc = TRUE; gboolean no_wait = FALSE; gboolean is_local = FALSE; char *counter = NULL; const char *task = NULL; const char *value = NULL; const char *on_node = NULL; const char *task_uuid = NULL; CRM_ASSERT(action != NULL); CRM_ASSERT(action->xml != NULL); action->executed = FALSE; on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); CRM_CHECK(on_node != NULL && strlen(on_node) != 0, te_log_action(LOG_ERR, "Corrupted command(id=%s) %s: no node", ID(action->xml), crm_str(task)); return FALSE); rsc_op = action->xml; task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY); on_node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET); counter = generate_transition_key( transition_graph->id, action->id, get_target_rc(action), te_uuid); crm_xml_add(rsc_op, XML_ATTR_TRANSITION_KEY, counter); if(safe_str_eq(on_node, fsa_our_uname)) { is_local = TRUE; } value = crm_meta_value(action->params, XML_ATTR_TE_NOWAIT); if(crm_is_true(value)) { no_wait = TRUE; } crm_info("Initiating action %d: %s %s on %s%s%s", action->id, task, task_uuid, on_node, is_local?" (local)":"", no_wait?" - no waiting":""); cmd = create_request(CRM_OP_INVOKE_LRM, rsc_op, on_node, CRM_SYSTEM_LRMD, CRM_SYSTEM_TENGINE, NULL); if(is_local) { /* shortcut local resource commands */ ha_msg_input_t data = { .msg = cmd, .xml = rsc_op, }; fsa_data_t msg = { .id = 0, .data = &data, .data_type = fsa_dt_ha_msg, .fsa_input = I_NULL, .fsa_cause = C_FSA_INTERNAL, .actions = A_LRM_INVOKE, .origin = __FUNCTION__, }; do_lrm_invoke(A_LRM_INVOKE, C_FSA_INTERNAL, fsa_state, I_NULL, &msg); } else { rc = send_cluster_message(on_node, crm_msg_lrmd, cmd, TRUE); } crm_free(counter); free_xml(cmd); action->executed = TRUE; if(rc == FALSE) { crm_err("Action %d failed: send", action->id); return FALSE; } else if(no_wait) { action->confirmed = TRUE; update_graph(transition_graph, action); trigger_graph(); } else { if(action->timeout <= 0) { crm_err("Action %d: %s %s on %s had an invalid timeout (%dms). Using %dms instead", action->id, task, task_uuid, on_node, action->timeout, graph->network_delay); action->timeout = graph->network_delay; } te_start_action_timer(graph, action); } value = crm_meta_value(action->params, XML_OP_ATTR_PENDING); if(crm_is_true(value)) { /* write a "pending" entry to the CIB, inhibit notification */ crm_info("Recording pending op %s in the CIB", task_uuid); cib_action_update(action, LRM_OP_PENDING, EXECRA_STATUS_UNKNOWN); } return TRUE; } crm_graph_functions_t te_graph_fns = { te_pseudo_action, te_rsc_command, te_crm_command, te_fence_node }; void notify_crmd(crm_graph_t *graph) { int log_level = LOG_DEBUG; const char *type = "unknown"; enum crmd_fsa_input event = I_NULL; crm_debug("Processing transition completion in state %s", fsa_state2string(fsa_state)); CRM_CHECK(graph->complete, graph->complete = TRUE); switch(graph->completion_action) { case tg_stop: type = "stop"; /* fall through */ case tg_done: type = "done"; log_level = LOG_INFO; if(fsa_state == S_TRANSITION_ENGINE) { event = I_TE_SUCCESS; } break; case tg_restart: type = "restart"; if(fsa_state == S_TRANSITION_ENGINE) { event = I_PE_CALC; } else if(fsa_state == S_POLICY_ENGINE) { register_fsa_action(A_PE_INVOKE); } break; case tg_shutdown: type = "shutdown"; if(is_set(fsa_input_register, R_SHUTDOWN)) { event = I_STOP; } else { crm_err("We didn't ask to be shut down, yet our" " PE is telling us too."); event = I_TERMINATE; } } te_log_action(log_level, "Transition %d status: %s - %s", graph->id, type, crm_str(graph->abort_reason)); graph->abort_reason = NULL; graph->completion_action = tg_done; clear_bit_inplace(fsa_input_register, R_IN_TRANSITION); if(event != I_NULL) { register_fsa_input(C_FSA_INTERNAL, event, NULL); } else if(fsa_source) { mainloop_set_trigger(fsa_source); } } diff --git a/fencing/admin.c b/fencing/admin.c index ed68e973df..878d575bd8 100644 --- a/fencing/admin.c +++ b/fencing/admin.c @@ -1,304 +1,256 @@ /* * Copyright (C) 2009 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct crm_option long_options[] = { {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"list", 1, 0, 'l', "List devices that can terminate the specified host"}, {"list-all", 0, 0, 'L', "List all registered devices"}, {"metadata", 0, 0, 'M', "Check the device's metadata"}, {"query", 1, 0, 'Q', "Check the device's status"}, {"fence", 1, 0, 'F', "Fence the named host"}, {"unfence", 1, 0, 'U', "Unfence the named host"}, {"confirm", 1, 0, 'C', "Confirm the named host is now safely down"}, {"register", 1, 0, 'R', "Register a stonith device"}, {"deregister", 1, 0, 'D', "De-register a stonith device"}, {"env-option", 1, 0, 'e'}, {"option", 1, 0, 'o'}, {"agent", 1, 0, 'a'}, {0, 0, 0, 0} }; int st_opts = st_opt_sync_call; static void st_callback(stonith_t *st, const char *event, xmlNode *msg) { crm_log_xml_notice(msg, event); } -static void -hash_copy(gpointer key, gpointer value, gpointer user_data) -{ - const char *name = key; - const char *s_value = value; - GHashTable *hash = user_data; - - if(g_hash_table_lookup(hash, name) == NULL) { - crm_trace("Copying in %s=%s", name, s_value); - g_hash_table_insert(hash, strdup(name), strdup(value)); - } -} extern void cleanup_calculations(pe_working_set_t *data_set); extern gboolean unpack_nodes(xmlNode * xml_nodes, pe_working_set_t *data_set); -static void st_get_node_attributes(const char *target, GHashTable *attrs) -{ - int rc = 0; - node_t *node = NULL; - cib_t *global_cib = NULL; - pe_working_set_t data_set; - - set_working_set_defaults(&data_set); - - global_cib = cib_new(); - global_cib->cmds->signon(global_cib, crm_system_name, cib_command); - - rc = global_cib->cmds->query(global_cib, NULL, &(data_set.input), cib_sync_call|cib_scope_local); - if(rc == cib_ok) { - crm_trace("Looking up current node attributes for %s", target); - unpack_nodes(get_object_root(XML_CIB_TAG_NODES, data_set.input), &data_set); - } - - /* Assume uname and fall back to uuid if there is no match */ - node = pe_find_node(data_set.nodes, target); - if(node) { - node = pe_find_node_id(data_set.nodes, target); - } - - if(node) { - /* Copy in any parameters not explicitly set from the command line */ - g_hash_table_foreach(node->details->attrs, hash_copy, attrs); - } - - cleanup_calculations(&data_set); - global_cib->cmds->signoff(global_cib); - cib_delete(global_cib); -} int main(int argc, char ** argv) { int flag; int rc = 0; int argerr = 0; int option_index = 0; char name[512]; char value[512]; const char *agent = NULL; const char *device = NULL; const char *target = NULL; char action = 0; stonith_t *st = NULL; GHashTable *hash = g_hash_table_new(g_str_hash, g_str_equal); crm_log_init(NULL, LOG_INFO, TRUE, TRUE, argc, argv); crm_set_options("V?$LQ:R:D:o:a:l:e:F:U:M", "mode [options]", long_options, "Provides access to the stonith-ng API.\n"); while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': alter_debug(DEBUG_INC); cl_log_enable_stderr(1); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'L': action = flag; break; case 'Q': case 'R': case 'D': action = flag; device = optarg; break; case 'a': agent = optarg; break; case 'l': target = optarg; action = 'L'; break; case 'M': action = flag; break; case 'F': case 'U': case 'C': target = optarg; action = flag; break; case 'o': crm_info("Scanning: -o %s", optarg); rc = sscanf(optarg, "%[^=]=%[^=]", name, value); if(rc != 2) { crm_err("Invalid option: -o %s", optarg); ++argerr; } else { crm_info("Got: '%s'='%s'", name, value); g_hash_table_insert(hash, crm_strdup(name), crm_strdup(value)); } break; case 'e': { char *key = crm_concat("OCF_RESKEY", optarg, '_'); const char *env = getenv(key); if(env == NULL) { crm_err("Invalid option: -e %s", optarg); ++argerr; } else { crm_info("Got: '%s'='%s'", optarg, env); g_hash_table_insert(hash, crm_strdup(optarg), crm_strdup(env)); } } break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } #if 0 g_hash_table_insert(hash, crm_strdup("ipaddr"), crm_strdup("localhost")); g_hash_table_insert(hash, crm_strdup("pcmk-portmap"), crm_strdup("some-host=pcmk-1 pcmk-3=3,4")); g_hash_table_insert(hash, crm_strdup("login"), crm_strdup("root")); g_hash_table_insert(hash, crm_strdup("identity_file"), crm_strdup("/root/.ssh/id_dsa")); #endif crm_debug("Create"); st = stonith_api_new(); if(action != 'M') { rc = st->cmds->connect(st, crm_system_name, NULL, NULL); crm_debug("Connect: %d", rc); rc = st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, st_callback); } switch(action) { case 'L': { GListPtr devices = NULL; rc = st->cmds->query(st, st_opts, target, &devices, 10); if(rc == 0) { fprintf(stderr, "No devices found\n"); } else if(rc > 0) { GListPtr lpc = NULL; fprintf(stderr, "%d devices found\n", rc); for(lpc = devices; lpc != NULL; lpc = lpc->next) { char *device = (char*)lpc->data; fprintf(stdout, " %s\n", device); } rc = 0; } } break; case 'Q': rc = st->cmds->call(st, st_opts, device, "monitor", NULL, 10); if(rc < 0) { rc = st->cmds->call(st, st_opts, device, "list", NULL, 10); } break; case 'R': rc = st->cmds->register_device(st, st_opts, device, "stonith-ng", agent, hash); break; case 'D': rc = st->cmds->remove_device(st, st_opts, device); break; case 'M': { char *buffer = NULL; st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer, 0); printf("%s\n", buffer); crm_free(buffer); } break; case 'C': - st_get_node_attributes(target, hash); rc = st->cmds->confirm(st, st_opts, target); break; case 'F': - st_get_node_attributes(target, hash); - rc = st->cmds->fence(st, st_opts, target, hash, "off", 120); + rc = st->cmds->fence(st, st_opts, target, "off", 120); break; case 'U': - st_get_node_attributes(target, hash); - rc = st->cmds->fence(st, st_opts, target, hash, "on", 120); + rc = st->cmds->fence(st, st_opts, target, "on", 120); break; } st->cmds->disconnect(st); crm_debug("Disconnect: %d", rc); crm_debug("Destroy"); stonith_api_delete(st); return rc; } diff --git a/fencing/test.c b/fencing/test.c index 9b12010532..09b78a6e61 100644 --- a/fencing/test.c +++ b/fencing/test.c @@ -1,193 +1,193 @@ /* * Copyright (C) 2009 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct crm_option long_options[] = { {"verbose", 0, 0, 'V'}, {"version", 0, 0, '$'}, {"help", 0, 0, '?'}, {"passive", 0, 0, 'p'}, {0, 0, 0, 0} }; int st_opts = st_opt_sync_call; GMainLoop *mainloop = NULL; static void st_callback(stonith_t *st, const char *event, xmlNode *msg) { crm_log_xml_notice(msg, event); } static gboolean timeout_handler(gpointer data) { g_main_quit(mainloop); return FALSE; } int main(int argc, char ** argv) { int flag; int rc = 0; int argerr = 0; int option_index = 0; stonith_t *st = NULL; GHashTable *hash = NULL; gboolean passive_mode = FALSE; crm_log_init(NULL, LOG_INFO, TRUE, TRUE, argc, argv); crm_set_options("V?$p", "mode [options]", long_options, "Provides a summary of cluster's current state." "\n\nOutputs varying levels of detail in a number of different formats.\n"); while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': alter_debug(DEBUG_INC); cl_log_enable_stderr(1); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'p': passive_mode = TRUE; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } hash = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(hash, crm_strdup("ipaddr"), crm_strdup("localhost")); g_hash_table_insert(hash, crm_strdup("pcmk-portmap"), crm_strdup("some-host=pcmk-1 pcmk-3=3,4")); g_hash_table_insert(hash, crm_strdup("login"), crm_strdup("root")); g_hash_table_insert(hash, crm_strdup("identity_file"), crm_strdup("/root/.ssh/id_dsa")); crm_debug("Create"); st = stonith_api_new(); rc = st->cmds->connect(st, crm_system_name, NULL, NULL); crm_debug("Connect: %d", rc); rc = st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, st_callback); if(passive_mode) { rc = st->cmds->register_notification(st, STONITH_OP_FENCE, st_callback); rc = st->cmds->register_notification(st, STONITH_OP_DEVICE_ADD, st_callback); rc = st->cmds->register_notification(st, STONITH_OP_DEVICE_DEL, st_callback); mainloop = g_main_new(FALSE); crm_info("Looking for notification"); g_timeout_add(500*1000, timeout_handler, NULL); g_main_run(mainloop); } else { rc = st->cmds->register_device(st, st_opts, "test-id", "stonith-ng", "fence_virsh", hash); crm_debug("Register: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "list", NULL, 10); crm_debug("List: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "monitor", NULL, 10); crm_debug("Monitor: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "status", "pcmk-2", 10); crm_debug("Status pcmk-2: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "status", "pcmk-1", 10); crm_debug("Status pcmk-1: %d", rc); - rc = st->cmds->fence(st, st_opts, "unknown-host", NULL, "off", 60); + rc = st->cmds->fence(st, st_opts, "unknown-host", "off", 60); crm_debug("Fence unknown-host: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "status", "pcmk-1", 10); crm_debug("Status pcmk-1: %d", rc); - rc = st->cmds->fence(st, st_opts, "pcmk-1", NULL, "off", 60); + rc = st->cmds->fence(st, st_opts, "pcmk-1", "off", 60); crm_debug("Fence pcmk-1: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "status", "pcmk-1", 10); crm_debug("Status pcmk-1: %d", rc); - rc = st->cmds->fence(st, st_opts, "pcmk-1", NULL, "on", 10); + rc = st->cmds->fence(st, st_opts, "pcmk-1", "on", 10); crm_debug("Unfence pcmk-1: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "status", "pcmk-1", 10); crm_debug("Status pcmk-1: %d", rc); - rc = st->cmds->fence(st, st_opts, "some-host", NULL, "off", 10); + rc = st->cmds->fence(st, st_opts, "some-host", "off", 10); crm_debug("Fence alias: %d", rc); rc = st->cmds->call(st, st_opts, "test-id", "status", "some-host", 10); crm_debug("Status alias: %d", rc); - rc = st->cmds->fence(st, st_opts, "pcmk-1", NULL, "on", 10); + rc = st->cmds->fence(st, st_opts, "pcmk-1", "on", 10); crm_debug("Unfence pcmk-1: %d", rc); rc = st->cmds->remove_device(st, st_opts, "test-id"); crm_debug("Remove test-id: %d", rc); } rc = st->cmds->disconnect(st); crm_debug("Disconnect: %d", rc); crm_debug("Destroy"); stonith_api_delete(st); return rc; } diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h index fef83ff370..745ba1555a 100644 --- a/include/crm/stonith-ng.h +++ b/include/crm/stonith-ng.h @@ -1,200 +1,200 @@ /* * 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef STONITH_NG__H #define STONITH_NG__H #include #include #include enum stonith_state { stonith_connected_command, stonith_connected_query, stonith_disconnected }; enum stonith_call_options { st_opt_none = 0x00000000, st_opt_verbose = 0x00000001, st_opt_allow_suicide = 0x00000002, st_opt_local_first = 0x00000004, st_opt_discard_reply = 0x00000010, st_opt_all_replies = 0x00000020, st_opt_scope_local = 0x00000100, st_opt_sync_call = 0x00001000, }; #define stonith_default_options = stonith_none enum stonith_errors { stonith_ok = 0, st_err_generic = -1, st_err_internal = -2, st_err_not_supported = -3, st_err_connection = -4, st_err_missing = -5, st_err_exists = -6, st_err_timeout = -7, st_err_ipc = -8, st_err_peer = -9, st_err_unknown_operation = -10, st_err_unknown_device = -11, st_err_unknown_port = -12, st_err_none_available = -13, st_err_authentication = -14, st_err_signal = -15, }; #define F_STONITH_CLIENTID "st_clientid" #define F_STONITH_CALLOPTS "st_callopt" #define F_STONITH_CALLID "st_callid" #define F_STONITH_CALLDATA "st_calldata" #define F_STONITH_OPERATION "st_op" #define F_STONITH_TARGET "st_target" #define F_STONITH_REMOTE "st_remote_op" #define F_STONITH_RC "st_rc" #define F_STONITH_TIMEOUT "st_timeout" #define F_STONITH_CALLBACK_TOKEN "st_async_id" #define F_STONITH_CLIENTNAME "st_clientname" #define F_STONITH_NOTIFY_TYPE "st_notify_type" #define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate" #define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate" #define F_STONITH_DELEGATE "st_delegate" #define F_STONITH_ORIGIN "st_origin" #define T_STONITH_NG "stonith-ng" #define T_STONITH_REPLY "st-reply" #define F_STONITH_DEVICE "st_device_id" #define F_STONITH_ACTION "st_device_action" #define T_STONITH_NOTIFY "st_notify" #define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect" #define STONITH_ATTR_ARGMAP "pcmk_arg_map" #define STONITH_ATTR_HOSTMAP "pcmk_host_map" #define STONITH_ATTR_HOSTLIST "pcmk_host_list" #define STONITH_ATTR_HOSTCHECK "pcmk_host_check" #define STONITH_ATTR_ACTION_OP "option" /* To be replaced by 'action' at some point */ #define STONITH_OP_EXEC "st_execute" #define STONITH_OP_QUERY "st_query" #define STONITH_OP_FENCE "st_fence" #define STONITH_OP_CONFIRM "st_confirm" #define STONITH_OP_DEVICE_ADD "st_device_register" #define STONITH_OP_DEVICE_DEL "st_device_remove" #define STONITH_OP_DEVICE_METADATA "st_device_metadata" #define stonith_channel "st_command" #define stonith_channel_callback "st_callback" typedef struct stonith_s stonith_t; typedef struct stonith_api_operations_s { int (*free) (stonith_t *st); int (*connect) (stonith_t *st, const char *name, int *async_fd, int *sync_fd); int (*disconnect)(stonith_t *st); int (*remove_device)( stonith_t *st, int options, const char *name); int (*register_device)( stonith_t *st, int options, const char *id, const char *namespace, const char *agent, GHashTable *parameters); int (*metadata)(stonith_t *st, int options, const char *device, const char *namespace, char **output, int timeout); int (*call)(stonith_t *st, int options, const char *id, const char *action, const char *port, int timeout); int (*query)(stonith_t *st, int options, const char *node, GListPtr *devices, int timeout); - int (*fence)(stonith_t *st, int options, const char *node, GHashTable *parameters, const char *action, int timeout); + int (*fence)(stonith_t *st, int options, const char *node, const char *action, int timeout); int (*confirm)(stonith_t *st, int options, const char *node); int (*register_notification)( stonith_t *st, const char *event, void (*notify)(stonith_t *st, const char *event, xmlNode *msg)); int (*remove_notification)(stonith_t *st, const char *event); int (*register_callback)( stonith_t *st, int call_id, int timeout, gboolean only_success, void *userdata, const char *callback_name, void (*callback)(stonith_t *st, const xmlNode *msg, int call, int rc, xmlNode *output, void *userdata)); int (*remove_callback)(stonith_t *st, int call_id, gboolean all_callbacks); } stonith_api_operations_t; struct stonith_s { enum stonith_state state; int call_id; int call_timeout; void *private; GList *notify_list; stonith_api_operations_t *cmds; }; /* Core functions */ extern stonith_t *stonith_api_new(void); extern void stonith_api_delete(stonith_t *st); extern const char *stonith_error2string(enum stonith_errors return_code); extern void stonith_dump_pending_callbacks(stonith_t *st); /* internal details - move elsewhere */ typedef struct async_command_s { int id; int stdout; int options; int timeout; char *op; char *origin; char *client; char *remote; char *victim; char *action; char *device; GListPtr device_list; GListPtr device_next; ProcTrack_ops *pt_ops; GHashTable *node_attrs; ProcTrackKillInfo killseq[3]; } async_command_t; extern int run_stonith_agent( const char *agent, GHashTable *dev_hash, GHashTable *node_hash, const char *action, const char *victim, int *agent_result, char **output, async_command_t *track); extern gboolean is_redhat_agent(const char *agent); extern const char *get_stonith_provider(const char *agent, const char *provider); #endif diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index d225bed625..b8e871d65c 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.c @@ -1,1630 +1,1626 @@ /* * Copyright (c) 2004 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FE_AGENT_FORK -2 #define FE_AGENT_ERROR -3 CRM_TRACE_INIT_DATA(stonith); typedef struct stonith_private_s { char *token; IPC_Channel *command_channel; IPC_Channel *callback_channel; GCHSource *callback_source; GHashTable *stonith_op_callback_table; void (*op_callback)( stonith_t *st, const xmlNode *msg, int call, int rc, xmlNode *output, void *userdata); } stonith_private_t; typedef struct stonith_notify_client_s { const char *event; const char *obj_id; /* implement one day */ const char *obj_type; /* implement one day */ void (*notify)(stonith_t *st, const char *event, xmlNode *msg); } stonith_notify_client_t; typedef struct stonith_callback_client_s { void (*callback)( stonith_t *st, const xmlNode *msg, int call, int rc, xmlNode *output, void *userdata); const char *id; void *user_data; gboolean only_success; struct timer_rec_s *timer; } stonith_callback_client_t; struct notify_blob_s { stonith_t *stonith; xmlNode *xml; }; struct timer_rec_s { int call_id; int timeout; guint ref; stonith_t *stonith; }; typedef enum stonith_errors (*stonith_op_t)( const char *, int, const char *, xmlNode *, xmlNode*, xmlNode*, xmlNode**, xmlNode**); static const char META_TEMPLATE[] = "\n" "\n" "\n" " 1.0\n" " \n" "%s\n" " \n" " %s\n" "%s\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " 2.0\n" " \n" "\n"; gboolean stonith_dispatch(IPC_Channel *channel, gpointer user_data); void stonith_perform_callback(stonith_t *stonith, xmlNode *msg, int call_id, int rc); xmlNode *stonith_create_op( int call_id, const char *token, const char *op, xmlNode *data, int call_options); int stonith_send_command( stonith_t *stonith, const char *op, xmlNode *data, xmlNode **output_data, int call_options, int timeout); static void stonith_connection_destroy(gpointer user_data); static void stonith_send_notification(gpointer data, gpointer user_data); static void stonith_connection_destroy(gpointer user_data) { stonith_t *stonith = user_data; stonith_private_t *native = NULL; struct notify_blob_s blob; blob.stonith = stonith; blob.xml = create_xml_node(NULL, "notify");; native = stonith->private; native->callback_source = NULL; stonith->state = stonith_disconnected; crm_xml_add(blob.xml, F_TYPE, T_STONITH_NOTIFY); crm_xml_add(blob.xml, F_SUBTYPE, T_STONITH_NOTIFY_DISCONNECT); g_list_foreach(stonith->notify_list, stonith_send_notification, &blob); free_xml(blob.xml); } static int stonith_api_register_device( stonith_t *stonith, int call_options, const char *id, const char *namespace, const char *agent, GHashTable *params) { int rc = 0; xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE); xmlNode *args = create_xml_node(data, XML_TAG_ATTRS); crm_xml_add(data, XML_ATTR_ID, id); crm_xml_add(data, "origin", __FUNCTION__); crm_xml_add(data, "agent", agent); crm_xml_add(data, "namespace", namespace); g_hash_table_foreach(params, hash2field, args); rc = stonith_send_command(stonith, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0); free_xml(data); return rc; } static int stonith_api_remove_device( stonith_t *stonith, int call_options, const char *name) { int rc = 0; xmlNode *data = NULL; data = create_xml_node(NULL, F_STONITH_DEVICE); crm_xml_add(data, "origin", __FUNCTION__); crm_xml_add(data, XML_ATTR_ID, name); rc = stonith_send_command(stonith, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0); free_xml(data); return rc; } static void append_arg( gpointer key, gpointer value, gpointer user_data) { int len = 3; /* =, \n, \0 */ int last = 0; char **args = user_data; CRM_CHECK(key != NULL, return); CRM_CHECK(value != NULL, return); if(strstr(key, "pcmk_")) { return; } else if(strstr(key, CRM_META)) { return; } else if(safe_str_eq(key, "crm_feature_set")) { return; } len += strlen(key); len += strlen(value); if(*args != NULL) { last = strlen(*args); } crm_realloc(*args, last+len); crm_debug_2("Appending: %s=%s", (char *)key, (char *)value); sprintf((*args)+last, "%s=%s\n", (char *)key, (char *)value); } static void append_const_arg(const char *key, const char *value, char **arg_list) { char *glib_sucks_key = crm_strdup(key); char *glib_sucks_value = crm_strdup(value); append_arg(glib_sucks_key, glib_sucks_value, arg_list); crm_free(glib_sucks_value); crm_free(glib_sucks_key); } static void append_host_specific_args(const char *victim, const char *map, GHashTable *params, char **arg_list) { char *name = NULL; int last = 0, lpc = 0, max = 0; if(map == NULL) { /* The best default there is for now... */ crm_debug("Using default arg map: port=uname"); append_const_arg("port", victim, arg_list); return; } max = strlen(map); crm_debug("Processing arg map: %s", map); for(; lpc < max + 1; lpc++) { if(isalpha(map[lpc])) { /* keep going */ } else if(map[lpc] == '=' || map[lpc] == ':') { crm_free(name); crm_malloc0(name, 1 + lpc - last); strncpy(name, map + last, lpc - last); crm_debug("Got name: %s", name); last = lpc + 1; } else if(map[lpc] == 0 || map[lpc] == ',' || isspace(map[lpc])) { char *param = NULL; const char *value = NULL; crm_malloc0(param, 1 + lpc - last); strncpy(param, map + last, lpc - last); last = lpc + 1; crm_debug("Got key: %s", param); if(name == NULL) { crm_err("Misparsed '%s', found '%s' without a name", map, param); crm_free(param); continue; } if(safe_str_eq(param, "uname")) { value = victim; } else { char *key = crm_meta_name(param); value = g_hash_table_lookup(params, key); crm_free(key); } if(value) { crm_debug("Setting '%s'='%s' (%s) for %s", name, value, param, victim); append_const_arg(name, value, arg_list); } else { crm_err("No node attribute '%s' for '%s'", name, victim); } crm_free(name); name=NULL; crm_free(param); if(map[lpc] == 0) { break; } } else if(isspace(map[lpc])) { last = lpc; } } crm_free(name); } static char *make_args(GHashTable *dev_hash, GHashTable *node_hash, const char *action, const char *victim) { char buffer[512]; char *arg_list = NULL; const char *map = NULL; const char *value = NULL; CRM_CHECK(action != NULL, return NULL); if(dev_hash) { map = g_hash_table_lookup(dev_hash, STONITH_ATTR_ARGMAP); g_hash_table_foreach(dev_hash, append_arg, &arg_list); } buffer[511] = 0; snprintf(buffer, 511, "pcmk_%s_action", action); value = g_hash_table_lookup(dev_hash, buffer); if(value == NULL) { /* Legacy support for early 1.1 releases - Remove for 1.2 */ snprintf(buffer, 511, "pcmk_%s_cmd", action); value = g_hash_table_lookup(dev_hash, buffer); } if(value) { crm_info("Substituting action '%s' for requested operation '%s'", value, action); action = value; } append_const_arg(STONITH_ATTR_ACTION_OP, action, &arg_list); if(victim && safe_str_neq("none", map)) { append_const_arg("nodename", victim, &arg_list); append_host_specific_args(victim, map, node_hash, &arg_list); } crm_debug_3("Calculated: %s", arg_list); return arg_list; } /* Borrowed from libfence and extended */ int run_stonith_agent( const char *agent, GHashTable *dev_hash, GHashTable *node_hash, const char *action, const char *victim, int *agent_result, char **output, async_command_t *track) { char *args = make_args(dev_hash, node_hash, action, victim); int pid, status, len, rc = -1; int p_read_fd, p_write_fd; /* parent read/write file descriptors */ int c_read_fd, c_write_fd; /* child read/write file descriptors */ int fd1[2]; int fd2[2]; c_read_fd = c_write_fd = p_read_fd = p_write_fd = -1; if (args == NULL || agent == NULL) goto fail; len = strlen(args); if (pipe(fd1)) goto fail; p_read_fd = fd1[0]; c_write_fd = fd1[1]; if (pipe(fd2)) goto fail; c_read_fd = fd2[0]; p_write_fd = fd2[1]; crm_debug("forking"); pid = fork(); if (pid < 0) { *agent_result = FE_AGENT_FORK; goto fail; } if (pid) { /* parent */ int ret; fcntl(p_read_fd, F_SETFL, fcntl(p_read_fd, F_GETFL, 0) | O_NONBLOCK); do { crm_debug("sending args"); ret = write(p_write_fd, args, len); } while (ret < 0 && errno == EINTR); if (ret != len) { if(ret >= 0) { rc = st_err_generic; } goto fail; } close(p_write_fd); if(track) { track->stdout = p_read_fd; NewTrackedProc(pid, 0, PT_LOGNORMAL, track, track->pt_ops); crm_trace("Op: %s on %s, timeout: %d", action, agent, track->timeout); if(track->timeout) { track->killseq[0].mstimeout = track->timeout; /* after timeout send TERM */ track->killseq[0].signalno = SIGTERM; track->killseq[1].mstimeout = 5000; /* after another 5s remove it */ track->killseq[1].signalno = SIGKILL; track->killseq[2].mstimeout = 5000; /* if it's still there after another 5s, complain */ track->killseq[2].signalno = 0; SetTrackedProcTimeouts(pid, track->killseq); } else { crm_err("No timeout set for stonith operation %s with device %s", action, agent); } close(c_write_fd); close(c_read_fd); crm_free(args); return pid; } else { waitpid(pid, &status, 0); if(output != NULL) { len = 0; do { char buf[500]; ret = read(p_read_fd, buf, 500); if(ret > 0) { buf[ret] = 0; crm_realloc(*output, len + ret + 1); sprintf((*output)+len, "%s", buf); crm_debug("%d: %s", ret, (*output)+len); len += ret; } } while (ret == 500 || (ret < 0 && errno == EINTR)); } *agent_result = FE_AGENT_ERROR; if (WIFEXITED(status)) { crm_debug("result = %d", WEXITSTATUS(status)); *agent_result = -WEXITSTATUS(status); rc = 0; } if(node_hash) { g_hash_table_destroy(node_hash); } } } else { /* child */ close(1); if (dup(c_write_fd) < 0) goto fail; close(2); if (dup(c_write_fd) < 0) goto fail; close(0); if (dup(c_read_fd) < 0) goto fail; /* keep c_write_fd open so parent can report all errors. */ close(c_read_fd); close(p_read_fd); close(p_write_fd); execlp(agent, agent, NULL); exit(EXIT_FAILURE); } fail: crm_free(args); if(p_read_fd >= 0) { close(p_read_fd); } if(p_write_fd >= 0) { close(p_write_fd); } if(c_read_fd >= 0) { close(c_read_fd); } if(c_write_fd >= 0) { close(c_write_fd); } return rc; } static int stonith_api_device_metadata( stonith_t *stonith, int call_options, const char *agent, const char *namespace, char **output, int timeout) { int rc = 0; int bufferlen = 0; char *buffer = NULL; char *xml_meta_longdesc = NULL; char *xml_meta_shortdesc = NULL; char *meta_param = NULL; char *meta_longdesc = NULL; char *meta_shortdesc = NULL; const char *provider = get_stonith_provider(agent, namespace); Stonith *stonith_obj = NULL; static const char *no_parameter_info = ""; crm_info("looking up %s/%s metadata", agent, provider); /* By having this in a library, we can access it from stonith_admin * when neither lrmd or stonith-ng are running * Important for the crm shell's validations... */ if(safe_str_eq(provider, "redhat")) { int exec_rc = run_stonith_agent( agent, NULL, NULL, "metadata", NULL, &rc, &buffer, NULL); if(exec_rc < 0 || rc != 0 || buffer == NULL) { /* failed */ crm_debug("Query failed: %d %d: %s", exec_rc, rc, crm_str(buffer)); /* provide a fake metadata entry */ meta_longdesc = crm_strdup(no_parameter_info); meta_shortdesc = crm_strdup(no_parameter_info); meta_param = crm_strdup( " \n" " \n" " \n" " \n" " Fencing action (null, off, on, [reboot], status, hostlist, devstatus)\n" " \n" " "); goto build; } } else { stonith_obj = stonith_new(agent); meta_longdesc = crm_strdup(stonith_get_info(stonith_obj, ST_DEVICEDESCR)); if (meta_longdesc == NULL) { crm_warn("no long description in %s's metadata.", agent); meta_longdesc = crm_strdup(no_parameter_info); } meta_shortdesc = crm_strdup(stonith_get_info(stonith_obj, ST_DEVICEID)); if (meta_shortdesc == NULL) { crm_warn("no short description in %s's metadata.", agent); meta_shortdesc = crm_strdup(no_parameter_info); } meta_param = crm_strdup(stonith_get_info(stonith_obj, ST_CONF_XML)); if (meta_param == NULL) { crm_warn("no list of parameters in %s's metadata.", agent); meta_param = crm_strdup(no_parameter_info); } build: xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc); xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc); bufferlen = strlen(META_TEMPLATE) + strlen(agent) + strlen(xml_meta_longdesc) + strlen(xml_meta_shortdesc) + strlen(meta_param) + 1; crm_malloc0(buffer, bufferlen); snprintf(buffer, bufferlen-1, META_TEMPLATE, agent, xml_meta_longdesc, xml_meta_shortdesc, meta_param); xmlFree(xml_meta_longdesc); xmlFree(xml_meta_shortdesc); if(stonith_obj) { stonith_delete(stonith_obj); } crm_free(meta_shortdesc); crm_free(meta_longdesc); crm_free(meta_param); } if(output) { *output = buffer; } else { crm_free(buffer); } return rc; } static int stonith_api_confirm( stonith_t *stonith, int call_options, const char *target) { int rc = 0; xmlNode *data = NULL; data = create_xml_node(NULL, __FUNCTION__); crm_xml_add(data, F_STONITH_TARGET, target); rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, 0); free_xml(data); return rc; } static int stonith_api_query( stonith_t *stonith, int call_options, const char *target, GListPtr *devices, int timeout) { int rc = 0, lpc = 0, max = 0; xmlNode *data = NULL; xmlNode *output = NULL; xmlXPathObjectPtr xpathObj = NULL; CRM_CHECK(devices != NULL, return st_err_missing); data = create_xml_node(NULL, F_STONITH_DEVICE); crm_xml_add(data, "origin", __FUNCTION__); crm_xml_add(data, F_STONITH_TARGET, target); rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout); if(rc < 0) { return rc; } xpathObj = xpath_search(output, "//@agent"); if(xpathObj) { max = xpathObj->nodesetval->nodeNr; for(lpc = 0; lpc < max; lpc++) { xmlNode *match = getXpathResult(xpathObj, lpc); CRM_CHECK(match != NULL, continue); crm_info("%s[%d] = %s", "//@agent", lpc, xmlGetNodePath(match)); *devices = g_list_append(*devices, crm_element_value_copy(match, XML_ATTR_ID)); } } free_xml(output); free_xml(data); return max; } static int stonith_api_call( stonith_t *stonith, int call_options, const char *id, const char *action, const char *victim, int timeout) { int rc = 0; xmlNode *data = NULL; data = create_xml_node(NULL, F_STONITH_DEVICE); crm_xml_add(data, "origin", __FUNCTION__); crm_xml_add(data, F_STONITH_DEVICE, id); crm_xml_add(data, F_STONITH_ACTION, action); crm_xml_add(data, F_STONITH_TARGET, victim); rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, NULL, call_options, timeout); free_xml(data); return rc; } static int stonith_api_fence( - stonith_t *stonith, int call_options, const char *node, GHashTable *parameters, - const char *action, int timeout) + stonith_t *stonith, int call_options, const char *node, const char *action, int timeout) { int rc = 0; xmlNode *data = NULL; - xmlNode *params = NULL; data = create_xml_node(NULL, __FUNCTION__); crm_xml_add(data, F_STONITH_TARGET, node); crm_xml_add(data, F_STONITH_ACTION, action); crm_xml_add_int(data, F_STONITH_TIMEOUT, timeout); - params = create_xml_node(data, XML_TAG_ATTRS); - g_hash_table_foreach(parameters, hash2nvpair, params); rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout); free_xml(data); return rc; } const char * stonith_error2string(enum stonith_errors return_code) { const char *error_msg = NULL; switch(return_code) { case stonith_ok: error_msg = "OK"; break; case st_err_not_supported: error_msg = "Not supported"; break; case st_err_authentication: error_msg = "Not authenticated"; break; case st_err_generic: error_msg = "Generic error"; break; case st_err_internal: error_msg = "Internal error"; break; case st_err_unknown_device: error_msg = "Unknown device"; break; case st_err_unknown_operation: error_msg = "Unknown operation"; break; case st_err_unknown_port: error_msg = "Unknown victim"; break; case st_err_none_available: error_msg = "No available fencing devices"; break; case st_err_connection: error_msg = "Not connected"; break; case st_err_missing: error_msg = "Missing input"; break; case st_err_exists: error_msg = "Device exists"; break; case st_err_timeout: error_msg = "Operation timed out"; break; case st_err_signal: error_msg = "Killed by signal"; break; case st_err_ipc: error_msg = "IPC connection failed"; break; case st_err_peer: error_msg = "Error from peer"; break; } if(error_msg == NULL) { crm_err("Unknown Stonith error code: %d", return_code); error_msg = ""; } return error_msg; } gboolean is_redhat_agent(const char *agent) { int rc = 0; struct stat prop; char buffer[FILENAME_MAX+1]; snprintf(buffer,FILENAME_MAX,"%s/%s", RH_STONITH_DIR, agent); rc = stat(buffer, &prop); if (rc >= 0 && S_ISREG(prop.st_mode)) { return TRUE; } return FALSE; } const char *get_stonith_provider(const char *agent, const char *provider) { /* This function sucks */ if(is_redhat_agent(agent)) { return "redhat"; } else { Stonith *stonith_obj = stonith_new(agent); if(stonith_obj) { stonith_delete(stonith_obj); return "heartbeat"; } } crm_err("No such device: %s", agent); return NULL; } static gint stonithlib_GCompareFunc(gconstpointer a, gconstpointer b) { int rc = 0; const stonith_notify_client_t *a_client = a; const stonith_notify_client_t *b_client = b; CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0); rc = strcmp(a_client->event, b_client->event); if(rc == 0) { if(a_client->notify == NULL || b_client->notify == NULL) { return 0; } else if(a_client->notify == b_client->notify) { return 0; } else if(((long)a_client->notify) < ((long)b_client->notify)) { crm_err("callbacks for %s are not equal: %p vs. %p", a_client->event, a_client->notify, b_client->notify); return -1; } crm_err("callbacks for %s are not equal: %p vs. %p", a_client->event, a_client->notify, b_client->notify); return 1; } return rc; } static int get_stonith_token(IPC_Channel *ch, char **token) { int rc = stonith_ok; xmlNode *reg_msg = NULL; const char *msg_type = NULL; const char *tmp_ticket = NULL; CRM_CHECK(ch != NULL, return st_err_missing); CRM_CHECK(token != NULL, return st_err_missing); crm_debug_4("Waiting for msg on command channel"); reg_msg = xmlfromIPC(ch, MAX_IPC_DELAY); if(ch->ops->get_chan_status(ch) != IPC_CONNECT) { crm_err("No reply message - disconnected"); free_xml(reg_msg); return st_err_connection; } else if(reg_msg == NULL) { crm_err("No reply message - empty"); return st_err_ipc; } msg_type = crm_element_value(reg_msg, F_STONITH_OPERATION); tmp_ticket = crm_element_value(reg_msg, F_STONITH_CLIENTID); if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) { crm_err("Invalid registration message: %s", msg_type); rc = st_err_internal; } else if(tmp_ticket == NULL) { crm_err("No registration token provided"); crm_log_xml_warn(reg_msg, "Bad reply"); rc = st_err_internal; } else { crm_debug("Obtained registration token: %s", tmp_ticket); *token = crm_strdup(tmp_ticket); } free_xml(reg_msg); return rc; } xmlNode *stonith_create_op( int call_id, const char *token, const char *op, xmlNode *data, int call_options) { int rc = HA_OK; xmlNode *op_msg = create_xml_node(NULL, "stonith_command"); CRM_CHECK(op_msg != NULL, return NULL); CRM_CHECK(token != NULL, return NULL); crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command"); crm_xml_add(op_msg, F_TYPE, T_STONITH_NG); crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token); crm_xml_add(op_msg, F_STONITH_OPERATION, op); crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id); crm_debug_4("Sending call options: %.8lx, %d", (long)call_options, call_options); crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options); if(data != NULL) { add_message_xml(op_msg, F_STONITH_CALLDATA, data); } if (rc != HA_OK) { crm_err("Failed to create STONITH operation message"); crm_log_xml(LOG_ERR, "BadOp", op_msg); free_xml(op_msg); return NULL; } return op_msg; } static void stonith_destroy_op_callback(gpointer data) { stonith_callback_client_t *blob = data; if(blob->timer && blob->timer->ref > 0) { g_source_remove(blob->timer->ref); } crm_free(blob->timer); crm_free(blob); } static int stonith_api_signoff(stonith_t* stonith) { stonith_private_t *native = stonith->private; crm_debug("Signing out of the STONITH Service"); /* close channels */ if (native->command_channel != NULL) { native->command_channel->ops->destroy( native->command_channel); native->command_channel = NULL; } if (native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } if (native->callback_channel != NULL) { #ifdef BUG native->callback_channel->ops->destroy( native->callback_channel); #endif native->callback_channel = NULL; } stonith->state = stonith_disconnected; return stonith_ok; } static int stonith_api_signon( stonith_t* stonith, const char *name, int *async_fd, int *sync_fd) { int rc = stonith_ok; xmlNode *hello = NULL; char *uuid_ticket = NULL; stonith_private_t *native = stonith->private; crm_debug_4("Connecting command channel"); stonith->state = stonith_connected_command; native->command_channel = init_client_ipc_comms_nodispatch(stonith_channel); if(native->command_channel == NULL) { crm_debug("Connection to command channel failed"); rc = st_err_connection; } else if(native->command_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to command channel failed"); rc = st_err_authentication; } if(rc == stonith_ok) { rc = get_stonith_token(native->command_channel, &uuid_ticket); if(rc == stonith_ok) { native->token = uuid_ticket; uuid_ticket = NULL; } else { stonith->state = stonith_disconnected; native->command_channel->ops->disconnect(native->command_channel); return rc; } } native->callback_channel = init_client_ipc_comms_nodispatch( stonith_channel_callback); if(native->callback_channel == NULL) { crm_debug("Connection to callback channel failed"); rc = st_err_connection; } else if(native->callback_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to command channel failed"); rc = st_err_authentication; } if(rc == stonith_ok) { native->callback_channel->send_queue->max_qlen = 500; rc = get_stonith_token(native->callback_channel, &uuid_ticket); if(rc == stonith_ok) { crm_free(native->token); native->token = uuid_ticket; } } if(rc == stonith_ok) { CRM_CHECK(native->token != NULL, ;); hello = stonith_create_op(0, native->token, CRM_OP_REGISTER, NULL, 0); crm_xml_add(hello, F_STONITH_CLIENTNAME, name); if(send_ipc_message(native->command_channel, hello) == FALSE) { rc = st_err_internal; } free_xml(hello); } if(rc == stonith_ok) { gboolean do_mainloop = TRUE; if(async_fd != NULL) { do_mainloop = FALSE; *async_fd = native->callback_channel->ops->get_recv_select_fd(native->callback_channel); } if(sync_fd != NULL) { do_mainloop = FALSE; *sync_fd = native->callback_channel->ops->get_send_select_fd(native->callback_channel); } if(do_mainloop) { crm_debug_4("Connecting callback channel"); native->callback_source = G_main_add_IPC_Channel( G_PRIORITY_HIGH, native->callback_channel, FALSE, stonith_dispatch, stonith, default_ipc_connection_destroy); if(native->callback_source == NULL) { crm_err("Callback source not recorded"); rc = st_err_connection; } else { set_IPC_Channel_dnotify( native->callback_source, stonith_connection_destroy); } } } if(rc == stonith_ok) { #if HAVE_MSGFROMIPC_TIMEOUT stonith->call_timeout = MAX_IPC_DELAY; #endif crm_debug("Connection to STONITH successful"); return stonith_ok; } crm_debug("Connection to STONITH failed: %s", stonith_error2string(rc)); stonith->cmds->disconnect(stonith); return rc; } static int stonith_set_notification(stonith_t* stonith, const char *callback, int enabled) { xmlNode *notify_msg = create_xml_node(NULL, __FUNCTION__); stonith_private_t *native = stonith->private; if(stonith->state != stonith_disconnected) { crm_xml_add(notify_msg, F_STONITH_OPERATION, T_STONITH_NOTIFY); if(enabled) { crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback); } else { crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback); } send_ipc_message(native->callback_channel, notify_msg); } free_xml(notify_msg); return stonith_ok; } static int stonith_api_add_notification( stonith_t *stonith, const char *event, void (*callback)(stonith_t *stonith, const char *event, xmlNode *msg)) { GList *list_item = NULL; stonith_notify_client_t *new_client = NULL; crm_debug_2("Adding callback for %s events (%d)", event, g_list_length(stonith->notify_list)); crm_malloc0(new_client, sizeof(stonith_notify_client_t)); new_client->event = event; new_client->notify = callback; list_item = g_list_find_custom( stonith->notify_list, new_client, stonithlib_GCompareFunc); if(list_item != NULL) { crm_warn("Callback already present"); crm_free(new_client); return st_err_exists; } else { stonith->notify_list = g_list_append( stonith->notify_list, new_client); stonith_set_notification(stonith, event, 1); crm_debug_3("Callback added (%d)", g_list_length(stonith->notify_list)); } return stonith_ok; } static int stonith_api_del_notification(stonith_t *stonith, const char *event) { GList *list_item = NULL; stonith_notify_client_t *new_client = NULL; crm_debug("Removing callback for %s events", event); crm_malloc0(new_client, sizeof(stonith_notify_client_t)); new_client->event = event; new_client->notify = NULL; list_item = g_list_find_custom( stonith->notify_list, new_client, stonithlib_GCompareFunc); stonith_set_notification(stonith, event, 0); if(list_item != NULL) { stonith_notify_client_t *list_client = list_item->data; stonith->notify_list = g_list_remove(stonith->notify_list, list_client); crm_free(list_client); crm_debug_3("Removed callback"); } else { crm_debug_3("Callback not present"); } crm_free(new_client); return stonith_ok; } static gboolean stonith_async_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; crm_debug("Async call %d timed out after %dms", timer->call_id, timer->timeout); stonith_perform_callback(timer->stonith, NULL, timer->call_id, st_err_timeout); /* Always return TRUE, never remove the handler * We do that in stonith_del_callback() */ return TRUE; } static int stonith_api_add_callback( stonith_t *stonith, int call_id, int timeout, gboolean only_success, void *user_data, const char *callback_name, void (*callback)( stonith_t *st, const xmlNode *msg, int call, int rc, xmlNode *output, void *userdata)) { stonith_callback_client_t *blob = NULL; stonith_private_t *private = NULL; CRM_CHECK(stonith != NULL, return st_err_missing); CRM_CHECK(stonith->private != NULL, return st_err_missing); private = stonith->private; if(call_id == 0) { private->op_callback = callback; } else if(call_id < 0) { if(only_success == FALSE) { callback(stonith, NULL, call_id, call_id, NULL, user_data); } else { crm_warn("STONITH call failed: %s", stonith_error2string(call_id)); } return FALSE; } crm_malloc0(blob, sizeof(stonith_callback_client_t)); blob->id = callback_name; blob->only_success = only_success; blob->user_data = user_data; blob->callback = callback; if(timeout > 0) { struct timer_rec_s *async_timer = NULL; crm_malloc0(async_timer, sizeof(struct timer_rec_s)); blob->timer = async_timer; async_timer->stonith = stonith; async_timer->call_id = call_id; async_timer->timeout = timeout*1100; async_timer->ref = g_timeout_add( async_timer->timeout, stonith_async_timeout_handler, async_timer); } g_hash_table_insert(private->stonith_op_callback_table, GINT_TO_POINTER(call_id), blob); return TRUE; } static int stonith_api_del_callback(stonith_t *stonith, int call_id, gboolean all_callbacks) { stonith_private_t *private = stonith->private; if(all_callbacks) { private->op_callback = NULL; g_hash_table_destroy(private->stonith_op_callback_table); private->stonith_op_callback_table = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, stonith_destroy_op_callback); } else if(call_id == 0) { private->op_callback = NULL; } else { g_hash_table_remove(private->stonith_op_callback_table, GINT_TO_POINTER(call_id)); } return stonith_ok; } static void stonith_dump_pending_op( gpointer key, gpointer value, gpointer user_data) { int call = GPOINTER_TO_INT(key); stonith_callback_client_t *blob = value; crm_debug("Call %d (%s): pending", call, crm_str(blob->id)); } void stonith_dump_pending_callbacks(stonith_t *stonith) { stonith_private_t *private = stonith->private; if(private->stonith_op_callback_table == NULL) { return; } return g_hash_table_foreach( private->stonith_op_callback_table, stonith_dump_pending_op, NULL); } void stonith_perform_callback(stonith_t *stonith, xmlNode *msg, int call_id, int rc) { xmlNode *output = NULL; stonith_private_t *private = NULL; stonith_callback_client_t *blob = NULL; stonith_callback_client_t local_blob; CRM_CHECK(stonith != NULL, return); CRM_CHECK(stonith->private != NULL, return); private = stonith->private; local_blob.id = NULL; local_blob.callback = NULL; local_blob.user_data = NULL; local_blob.only_success = FALSE; if(msg != NULL) { crm_element_value_int(msg, F_STONITH_RC, &rc); crm_element_value_int(msg, F_STONITH_CALLID, &call_id); output = get_message_xml(msg, F_STONITH_CALLDATA); } CRM_CHECK(call_id > 0, crm_warn("Strange or missing call-id")); blob = g_hash_table_lookup( private->stonith_op_callback_table, GINT_TO_POINTER(call_id)); if(blob != NULL) { local_blob = *blob; blob = NULL; stonith_api_del_callback(stonith, call_id, FALSE); } else { crm_debug_2("No callback found for call %d", call_id); local_blob.callback = NULL; } if(stonith == NULL) { crm_debug("No stonith object supplied"); } if(local_blob.callback != NULL && (rc == stonith_ok || local_blob.only_success == FALSE)) { crm_debug_2("Invoking callback %s for call %d", crm_str(local_blob.id), call_id); local_blob.callback(stonith, msg, call_id, rc, output, local_blob.user_data); } else if(private->op_callback == NULL && rc != stonith_ok) { crm_warn("STONITH command failed: %s", stonith_error2string(rc)); crm_log_xml(LOG_DEBUG, "Failed STONITH Update", msg); } if(private->op_callback != NULL) { crm_debug_2("Invoking global callback for call %d", call_id); private->op_callback(stonith, msg, call_id, rc, output, NULL); } crm_debug_4("OP callback activated."); } static void stonith_send_notification(gpointer data, gpointer user_data) { struct notify_blob_s *blob = user_data; stonith_notify_client_t *entry = data; const char *event = NULL; if(blob->xml == NULL) { crm_warn("Skipping callback - NULL message"); return; } event = crm_element_value(blob->xml, F_SUBTYPE); if(entry == NULL) { crm_warn("Skipping callback - NULL callback client"); return; } else if(entry->notify == NULL) { crm_warn("Skipping callback - NULL callback"); return; } else if(safe_str_neq(entry->event, event)) { crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event); return; } crm_debug_4("Invoking callback for %p/%s event...", entry, event); entry->notify(blob->stonith, event, blob->xml); crm_debug_4("Callback invoked..."); } int stonith_send_command( stonith_t *stonith, const char *op, xmlNode *data, xmlNode **output_data, int call_options, int timeout) { int rc = HA_OK; xmlNode *op_msg = NULL; xmlNode *op_reply = NULL; stonith_private_t *native = stonith->private; if(stonith->state == stonith_disconnected) { return st_err_connection; } if(output_data != NULL) { *output_data = NULL; } if(op == NULL) { crm_err("No operation specified"); return st_err_missing; } stonith->call_id++; /* prevent call_id from being negative (or zero) and conflicting * with the stonith_errors enum * use 2 because we use it as (stonith->call_id - 1) below */ if(stonith->call_id < 1) { stonith->call_id = 1; } CRM_CHECK(native->token != NULL, ;); op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options); if(op_msg == NULL) { return st_err_missing; } crm_xml_add_int(op_msg, F_STONITH_TIMEOUT, timeout); crm_debug_3("Sending %s message to STONITH service, Timeout: %d", op, timeout); if(send_ipc_message(native->command_channel, op_msg) == FALSE) { crm_err("Sending message to STONITH service FAILED"); free_xml(op_msg); return st_err_ipc; } else { crm_debug_3("Message sent"); } free_xml(op_msg); if((call_options & st_opt_discard_reply)) { crm_debug_3("Discarding reply"); return stonith_ok; } else if(!(call_options & st_opt_sync_call)) { crm_debug_3("Async call, returning"); CRM_CHECK(stonith->call_id != 0, return st_err_ipc); return stonith->call_id; } rc = IPC_OK; crm_debug_3("Waiting for a syncronous reply"); rc = stonith_ok; while(IPC_ISRCONN(native->command_channel)) { int reply_id = -1; int msg_id = stonith->call_id; op_reply = xmlfromIPC(native->command_channel, timeout); if(op_reply == NULL) { rc = st_err_peer; break; } crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id); if(reply_id <= 0) { rc = st_err_peer; break; } else if(reply_id == msg_id) { crm_debug_3("Syncronous reply received"); crm_log_xml(LOG_MSG, "Reply", op_reply); if(crm_element_value_int(op_reply, F_STONITH_RC, &rc) != 0) { rc = st_err_peer; } if(output_data != NULL && is_not_set(call_options, st_opt_discard_reply)) { *output_data = op_reply; op_reply = NULL; } break; } else if(reply_id < msg_id) { crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml(LOG_MSG, "Old reply", op_reply); } else if((reply_id - 10000) > msg_id) { /* wrap-around case */ crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml(LOG_MSG, "Old reply", op_reply); } else { crm_err("Received a __future__ reply:" " %d (wanted %d)", reply_id, msg_id); } free_xml(op_reply); op_reply = NULL; } if(IPC_ISRCONN(native->command_channel) == FALSE) { crm_err("STONITH disconnected: %d", native->command_channel->ch_status); stonith->state = stonith_disconnected; } if(op_reply == NULL && stonith->state == stonith_disconnected) { rc = st_err_connection; } else if(rc == stonith_ok && op_reply == NULL) { rc = st_err_peer; } free_xml(op_reply); return rc; } static gboolean stonith_msgready(stonith_t* stonith) { stonith_private_t *private = NULL; if (stonith == NULL) { crm_err("No STONITH!"); return FALSE; } private = stonith->private; if(private->command_channel != NULL) { /* drain the channel */ IPC_Channel *cmd_ch = private->command_channel; xmlNode *cmd_msg = NULL; while(cmd_ch->ch_status != IPC_DISCONNECT && cmd_ch->ops->is_message_pending(cmd_ch)) { /* this will happen when the STONITH exited from beneath us */ cmd_msg = xmlfromIPC(cmd_ch, MAX_IPC_DELAY); free_xml(cmd_msg); } } else { crm_err("No command channel"); } if(private->callback_channel == NULL) { crm_err("No callback channel"); return FALSE; } else if(private->callback_channel->ch_status == IPC_DISCONNECT) { crm_info("Lost connection to the STONITH service [%d].", private->callback_channel->farside_pid); return FALSE; } else if(private->callback_channel->ops->is_message_pending( private->callback_channel)) { crm_debug_4("Message pending on command channel [%d]", private->callback_channel->farside_pid); return TRUE; } crm_debug_3("No message pending"); return FALSE; } static int stonith_rcvmsg(stonith_t* stonith) { const char *type = NULL; stonith_private_t *private = NULL; struct notify_blob_s blob; if (stonith == NULL) { crm_err("No STONITH!"); return FALSE; } blob.stonith = stonith; private = stonith->private; /* if it is not blocking mode and no message in the channel, return */ if (stonith_msgready(stonith) == FALSE) { crm_debug_3("No message ready and non-blocking..."); return 0; } /* IPC_INTR is not a factor here */ blob.xml = xmlfromIPC(private->callback_channel, MAX_IPC_DELAY); if (blob.xml == NULL) { crm_warn("Received a NULL msg from STONITH service."); return 0; } /* do callbacks */ type = crm_element_value(blob.xml, F_TYPE); crm_debug_4("Activating %s callbacks...", type); if(safe_str_eq(type, T_STONITH_NG)) { stonith_perform_callback(stonith, blob.xml, 0, 0); } else if(safe_str_eq(type, T_STONITH_NOTIFY)) { g_list_foreach(stonith->notify_list, stonith_send_notification, &blob); } else { crm_err("Unknown message type: %s", type); crm_log_xml_warn(blob.xml, "BadReply"); } free_xml(blob.xml); return 1; } gboolean stonith_dispatch(IPC_Channel *channel, gpointer user_data) { stonith_t *stonith = user_data; stonith_private_t *private = NULL; gboolean stay_connected = TRUE; CRM_CHECK(stonith != NULL, return FALSE); private = stonith->private; CRM_CHECK(private->callback_channel == channel, return FALSE); while(stonith_msgready(stonith)) { /* invoke the callbacks but dont block */ int rc = stonith_rcvmsg(stonith); if( rc < 0) { crm_err("Message acquisition failed: %d", rc); break; } else if(rc == 0) { break; } } if(private->callback_channel && private->callback_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the STONITH service [%d/callback].", channel->farside_pid); private->callback_source = NULL; stay_connected = FALSE; } if(private->command_channel && private->command_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the STONITH service [%d/command].", channel->farside_pid); private->callback_source = NULL; stay_connected = FALSE; } return stay_connected; } static int stonith_api_free (stonith_t* stonith) { int rc = stonith_ok; if(stonith->state != stonith_disconnected) { rc = stonith->cmds->disconnect(stonith); } if(stonith->state == stonith_disconnected) { stonith_private_t *private = stonith->private; g_hash_table_destroy(private->stonith_op_callback_table); crm_free(private->token); crm_free(stonith->private); crm_free(stonith->cmds); crm_free(stonith); } return rc; } void stonith_api_delete(stonith_t *stonith) { GList *list = stonith->notify_list; while(list != NULL) { stonith_notify_client_t *client = g_list_nth_data(list, 0); list = g_list_remove(list, client); crm_free(client); } stonith->cmds->free(stonith); stonith = NULL; } stonith_t *stonith_api_new(void) { stonith_t* new_stonith = NULL; stonith_private_t* private = NULL; crm_malloc0(new_stonith, sizeof(stonith_t)); crm_malloc0(private, sizeof(stonith_private_t)); new_stonith->private = private; private->stonith_op_callback_table = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, stonith_destroy_op_callback); new_stonith->call_id = 1; new_stonith->notify_list = NULL; new_stonith->state = stonith_disconnected; crm_malloc0(new_stonith->cmds, sizeof(stonith_api_operations_t)); new_stonith->cmds->free = stonith_api_free; new_stonith->cmds->connect = stonith_api_signon; new_stonith->cmds->disconnect = stonith_api_signoff; new_stonith->cmds->call = stonith_api_call; new_stonith->cmds->fence = stonith_api_fence; new_stonith->cmds->confirm = stonith_api_confirm; new_stonith->cmds->metadata = stonith_api_device_metadata; new_stonith->cmds->query = stonith_api_query; new_stonith->cmds->remove_device = stonith_api_remove_device; new_stonith->cmds->register_device = stonith_api_register_device; new_stonith->cmds->remove_callback = stonith_api_del_callback; new_stonith->cmds->register_callback = stonith_api_add_callback; new_stonith->cmds->remove_notification = stonith_api_del_notification; new_stonith->cmds->register_notification = stonith_api_add_notification; return new_stonith; }