diff --git a/crmd/te_actions.c b/crmd/te_actions.c index c2a2f73a56..ae52448243 100644 --- a/crmd/te_actions.c +++ b/crmd/te_actions.c @@ -1,570 +1,570 @@ /* * 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 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) { enum cib_errors rc = cib_ok; const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); const char *uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID); /* 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); if(rc < cib_ok) { crm_err("CIB update failed: %s", cib_error2string(rc)); abort_transition( INFINITY, tg_shutdown, "CIB update failed", node_state); } else { /* delay processing the trigger until the update completes */ add_cib_op_callback(fsa_cib_conn, rc, FALSE, crm_strdup(target), cib_fencing_updated); } erase_status_tag(target, XML_CIB_TAG_LRM); erase_status_tag(target, XML_TAG_TRANSIENT_NODEATTRS); free_xml(node_state); #if 0 /* Make sure the membership cache is accurate */ crm_update_peer(0, 0, 0, -1, 0, uuid, target, NULL, CRM_NODE_LOST); #endif 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); if(type == NULL) { type = "reboot"; } - + rc = stonith_api->cmds->fence( - stonith_api, 0, target, type, transition_graph->stonith_timeout/1000); + stonith_api, 0, target, action->params, 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, NULL, 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); value = crm_meta_value(action->params, XML_ATTR_TE_NOWAIT); 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) { char *op_id = NULL; char *code = NULL; char *digest = NULL; xmlNode *tmp = NULL; xmlNode *params = 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; 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); xml_op = create_xml_node(rsc, XML_LRM_TAG_RSC_OP); crm_xml_add(xml_op, XML_ATTR_ID, task); op_id = generate_op_key(rsc_id, task, action->interval); crm_xml_add(xml_op, XML_ATTR_ID, op_id); crm_free(op_id); crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, -1); crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task); crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, status); crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, action->interval); crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op_rc); crm_xml_add(xml_op, XML_ATTR_ORIGIN, __FUNCTION__); if(crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) { char *key = crm_meta_name("migrate_source_uuid"); xmlNode *attrs = first_named_child(action->xml, XML_TAG_ATTRS); const char *host = crm_element_value(attrs, key); CRM_CHECK(host != NULL, crm_log_xml_err(action->xml, "Bad Op")); crm_xml_add(xml_op, CRMD_ACTION_MIGRATED, host); crm_free(key); } code = generate_transition_key( transition_graph->id, action->id, get_target_rc(action), te_uuid); crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, code); crm_free(code); code = generate_transition_magic( crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY), status, op_rc); crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, code); crm_free(code); tmp = find_xml_node(action->xml, "attributes", TRUE); params = create_xml_node(NULL, XML_TAG_PARAMS); copy_in_properties(params, tmp); filter_action_parameters(params, CRM_FEATURE_SET); digest = calculate_xml_digest(params, TRUE, FALSE); /* info for now as this area has been problematic to debug */ crm_debug("Calculated digest %s for %s (%s)\n", digest, ID(xml_op), crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); crm_log_xml(LOG_DEBUG, "digest:source", params); crm_xml_add(xml_op, XML_LRM_ATTR_OP_DIGEST, digest); crm_free(digest); free_xml(params); 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 { 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); } } diff --git a/fencing/admin.c b/fencing/admin.c index 9c22c5303e..d297b48ade 100644 --- a/fencing/admin.c +++ b/fencing/admin.c @@ -1,241 +1,241 @@ /* * 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.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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"}, {"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); } 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("stonith-admin", 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': 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) { fprintf(stderr, "%d devices found\n", rc); slist_iter(device, char, devices, lpc, 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 'F': - rc = st->cmds->fence(st, st_opts, target, "off", 120); + rc = st->cmds->fence(st, st_opts, target, hash, "off", 120); break; case 'U': - rc = st->cmds->fence(st, st_opts, target, "on", 120); + rc = st->cmds->fence(st, st_opts, target, hash, "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/commands.c b/fencing/commands.c index 671775097f..f465039985 100644 --- a/fencing/commands.c +++ b/fencing/commands.c @@ -1,915 +1,838 @@ /* * 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.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#define FE_AGENT_FORK -2 -#define FE_AGENT_ERROR -3 - GHashTable *device_list = NULL; static int active_children = 0; static void exec_child_done(ProcTrack* proc, int status, int signo, int rc, int waslogged); static void exec_child_new(ProcTrack* p) { active_children++; } static const char *exec_child_name(ProcTrack* p) { async_command_t *cmd = proctrack_data(p); return cmd->client?cmd->client:cmd->remote; } static ProcTrack_ops StonithdProcessTrackOps = { exec_child_done, exec_child_new, exec_child_name, }; static async_command_t *create_async_command(xmlNode *msg, const char *action) { async_command_t *cmd = NULL; CRM_CHECK(action != NULL, crm_log_xml_warn(msg, "NoAction"); return NULL); crm_malloc0(cmd, sizeof(async_command_t)); crm_element_value_int(msg, F_STONITH_CALLID, &cmd->id); crm_element_value_int(msg, F_STONITH_CALLOPTS, &cmd->options); cmd->origin = crm_element_value_copy(msg, F_ORIG); cmd->remote = crm_element_value_copy(msg, F_STONITH_REMOTE); cmd->client = crm_element_value_copy(msg, F_STONITH_CLIENTID); cmd->op = crm_element_value_copy(msg, F_STONITH_OPERATION); cmd->action = crm_strdup(action); - cmd->port = crm_element_value_copy(msg, F_STONITH_TARGET); + cmd->victim = crm_element_value_copy(msg, F_STONITH_TARGET); + cmd->pt_ops = &StonithdProcessTrackOps; CRM_CHECK(cmd->op != NULL, crm_log_xml_warn(msg, "NoOp"); return NULL); CRM_CHECK(cmd->client != NULL || cmd->remote != NULL, crm_log_xml_warn(msg, "NoClient")); return cmd; } static void free_async_command(async_command_t *cmd) { + if(cmd->node_attrs) { + g_hash_table_destroy(cmd->node_attrs); + } crm_free(cmd->action); - crm_free(cmd->port); + crm_free(cmd->victim); crm_free(cmd->remote); crm_free(cmd->client); crm_free(cmd->origin); crm_free(cmd->op); crm_free(cmd); } -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); - - len += strlen(key); - len += strlen(value); - if(*args != NULL) { - last = strlen(*args); - } - - crm_realloc(*args, last+len); - - 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 char *make_args(GHashTable *args, const char *action, const char *port) -{ - char *arg_list = NULL; - CRM_CHECK(action != NULL, return NULL); - - g_hash_table_foreach(args, append_arg, &arg_list); - append_const_arg("option", action, &arg_list); - if(port) { - append_const_arg("port", port, &arg_list); - } - crm_debug_3("Calculated: %s", arg_list); - return arg_list; -} - -/* Borrowed from libfence */ -static int run_agent( - char *agent, GHashTable *arg_hash, const char *action, const char *port, - int *agent_result, char **output, async_command_t *track) -{ - char *args = make_args(arg_hash, action, port); - 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]; - - 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 { - ret = write(p_write_fd, args, len); - - } while (ret < 0 && errno == EINTR); - - if (ret != len) { - if(rc >= 0) { - rc = st_err_generic; - } - goto fail; - } - - close(p_write_fd); - - if(track) { - NewTrackedProc(pid, 0, PT_LOGNORMAL, track, &StonithdProcessTrackOps); - -#if 0 - ProcTrackKillInfo *info = NULL; - crm_malloc0(info, sizeof(ProcTrackKillInfo) * 3); - - killseq[0].mstimeout = timeout; /* after timeout send TERM */ - killseq[0].signalno = SIGTERM; - killseq[1].mstimeout = 5000; /* after 5 secs remove it */ - killseq[1].signalno = SIGKILL; - killseq[2].mstimeout = 5000; /* if it's still there after 5, complain */ - killseq[2].signalno = 0; - SetTrackedProcTimeouts(pid,killseq); -#endif - track->stdout = p_read_fd; - - crm_free(args); - close(c_write_fd); - close(c_read_fd); - 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); - len += ret; - } - - } while (ret == 500 || (ret < 0 && errno == EINTR)); - } - - *agent_result = FE_AGENT_ERROR; - if (WIFEXITED(status)) { - *agent_result = -WEXITSTATUS(status); - rc = 0; - } - } - - } 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); - - close(p_read_fd); - close(p_write_fd); - - close(c_read_fd); - close(c_write_fd); - return rc; -} - static void free_device(gpointer data) { stonith_device_t *device = data; g_hash_table_destroy(device->params); slist_destroy(char, item, device->targets, crm_free(item)); crm_free(device->namespace); crm_free(device->agent); crm_free(device->id); crm_free(device); } -static void build_port_aliases(stonith_device_t *device) +static GHashTable *build_port_aliases(const char *hostmap, GListPtr *targets) { char *name = NULL; char *value = NULL; int last = 0, lpc = 0, max = 0; + GHashTable *aliases = g_hash_table_new_full(g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); - const char *portmap = g_hash_table_lookup(device->params, "portmap"); - if(portmap == NULL) { - return; + if(hostmap == NULL) { + return aliases; } - max = strlen(portmap); + max = strlen(hostmap); for(; lpc < max; lpc++) { - if(portmap[lpc] == 0) { + if(hostmap[lpc] == 0) { break; - } else if(isalpha(portmap[lpc])) { + } else if(isalpha(hostmap[lpc])) { /* keep going */ - } else if(portmap[lpc] == '=') { + } else if(hostmap[lpc] == '=') { crm_malloc0(name, 1 + lpc - last); - strncpy(name, portmap + last, lpc - last); + strncpy(name, hostmap + last, lpc - last); last = lpc + 1; - } else if(name && isspace(portmap[lpc])) { + } else if(name && isspace(hostmap[lpc])) { crm_malloc0(value, 1 + lpc - last); - strncpy(value, portmap + last, lpc - last); + strncpy(value, hostmap + last, lpc - last); last = lpc + 1; - crm_info("Adding alias '%s'='%s' for %s", name, value, device->id); - g_hash_table_replace(device->aliases, name, value); + crm_debug("Adding alias '%s'='%s'", name, value); + g_hash_table_replace(aliases, name, value); + if(targets) { + *targets = g_list_append(*targets, crm_strdup(value)); + } value=NULL; name=NULL; - } else if(isspace(portmap[lpc])) { + } else if(isspace(hostmap[lpc])) { last = lpc; } } + return aliases; +} + +static GListPtr parse_host_list(const char *hosts) +{ + int lpc = 0; + int max = 0; + int last = 0; + GListPtr output = NULL; + + if(hosts) { + max = strlen(hosts); + } + + for(lpc = 0; lpc < max; lpc++) { + if(isspace(hosts[lpc]) || hosts[lpc] == ',') { + int rc = 0; + char *entry = NULL; + + /* TODO: Skip past lines containing the text "illegal" or "unknown" */ + + crm_malloc0(entry, 1 + lpc - last); + rc = sscanf(hosts+last, "%[a-zA-Z0-9_-]", entry); + if(rc == 1) { + crm_debug("Adding '%s'", entry); + output = g_list_append(output, entry); + entry = NULL; + } + + crm_free(entry); + last = lpc + 1; + } + } + + return output; } static stonith_device_t *build_device_from_xml(xmlNode *msg) { xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR); stonith_device_t *device = NULL; crm_malloc0(device, sizeof(stonith_device_t)); device->id = crm_element_value_copy(dev, XML_ATTR_ID); device->agent = crm_element_value_copy(dev, "agent"); device->namespace = crm_element_value_copy(dev, "namespace"); device->params = xml2list(dev); - device->aliases = g_hash_table_new_full(g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); + return device; } static int stonith_device_register(xmlNode *msg) { + const char *value = NULL; stonith_device_t *device = build_device_from_xml(msg); - build_port_aliases(device); + value = g_hash_table_lookup(device->params, STONITH_ATTR_HOSTLIST); + if(value) { + device->targets = parse_host_list(value); + } + + value = g_hash_table_lookup(device->params, STONITH_ATTR_HOSTMAP); + device->aliases = build_port_aliases(value, &(device->targets)); + g_hash_table_replace(device_list, device->id, device); crm_info("Added '%s' to the device list (%d active devices)", device->id, g_hash_table_size(device_list)); return stonith_ok; } static int stonith_device_remove(xmlNode *msg) { xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR); const char *id = crm_element_value(dev, XML_ATTR_ID); if(g_hash_table_remove(device_list, id)) { crm_info("Removed '%s' from the device list (%d active devices)", id, g_hash_table_size(device_list)); } else { crm_info("Device '%s' not found (%d active devices)", id, g_hash_table_size(device_list)); } return stonith_ok; } -static GListPtr parse_host_list(const char *hosts) -{ - int lpc = 0; - int max = 0; - int last = 0; - GListPtr output = NULL; - - if(hosts) { - max = strlen(hosts); - } - - for(lpc = 0; lpc < max; lpc++) { - if(isspace(hosts[lpc]) || hosts[lpc] == ',') { - int rc = 0; - char *entry = NULL; - crm_malloc0(entry, 1 + lpc - last); - rc = sscanf(hosts+last, "%[a-zA-Z0-9_-]", entry); - if(rc == 1) { - crm_debug("Adding '%s'", entry); - output = g_list_append(output, entry); - entry = NULL; - } - - crm_free(entry); - last = lpc + 1; - } - } - - return output; -} - static gboolean string_in_list(GListPtr list, const char *item) { int lpc = 0; int max = g_list_length(list); for(lpc = 0; lpc < max; lpc ++) { const char *value = g_list_nth_data(list, lpc); if(safe_str_eq(item, value)) { return TRUE; } } return FALSE; } -static const char *get_device_port(stonith_device_t *dev, const char *host) +static const char *get_victim_name(stonith_device_t *dev, const char *host) { - time_t now; - char *alias = NULL; - - if(host == NULL) { + if(dev == NULL) { return NULL; - } - - now = time(NULL); - alias = g_hash_table_lookup(dev->aliases, host); - - if(dev->targets == NULL || dev->targets_age + 300 < now) { - char *output = NULL; - int rc = stonith_ok; - int exec_rc = stonith_ok; - - slist_destroy(char, item, dev->targets, crm_free(item)); - dev->targets = NULL; - exec_rc = run_agent(dev->agent, dev->params, "hostlist", NULL, &rc, &output, NULL); - if(exec_rc < 0 || rc != 0) { - crm_info("Disabling port list queries for %s", dev->id); - dev->targets_age = -1; - - } else { - crm_info("Refreshing port list for %s", dev->id); - dev->targets = parse_host_list(output); - dev->targets_age = now; + } else if(host && dev->aliases) { + char *alias = g_hash_table_lookup(dev->aliases, host); + if(alias) { + return alias; } - - crm_free(output); - } - - /* See if portmap is defined and look up the translated name */ - if(alias && dev->targets == NULL) { - return alias; - - } else if(alias && string_in_list(dev->targets, alias)) { - return alias; - - } else if(dev->targets && string_in_list(dev->targets, host)) { - return host; } - - return NULL; + + return host; } static int stonith_device_action(xmlNode *msg, char **output) { int rc = stonith_ok; xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR); const char *id = crm_element_value(dev, F_STONITH_DEVICE); const char *action = crm_element_value(dev, F_STONITH_ACTION); async_command_t *cmd = NULL; stonith_device_t *device = NULL; + GHashTable *node_attrs = xml2list(dev); if(id) { crm_debug_2("Looking for '%s'", id); device = g_hash_table_lookup(device_list, id); } else { CRM_CHECK(safe_str_eq(action, "metadata"), crm_log_xml_warn(msg, "StrangeOp")); device = build_device_from_xml(msg); if(device != NULL && device->id == NULL) { device->id = crm_strdup(device->agent); } } if(device) { int exec_rc = 0; - const char *device_port = NULL; + const char *victim = NULL; cmd = create_async_command(msg, action); if(cmd == NULL) { return st_err_internal; } - - device_port = get_device_port(device, cmd->port); - if(cmd->port && device_port == NULL) { - crm_err("Unknown or unhandled port '%s' for device '%s'", cmd->port, device->id); + + cmd->node_attrs = node_attrs; + victim = get_victim_name(device, cmd->victim); + if(cmd->victim && victim == NULL) { + crm_err("Unknown or unhandled port '%s' for device '%s'", cmd->victim, device->id); free_async_command(cmd); return st_err_unknown_port; } cmd->device = crm_strdup(device->id); crm_debug("Calling '%s' with action '%s'%s%s", - device->id, action, device_port?" on port ":"", device_port?device_port:""); + device->id, action, victim?" on port ":"", victim?victim:""); - exec_rc = run_agent( - device->agent, device->params, action, device_port, &rc, output, cmd); + exec_rc = run_stonith_agent( + device->agent, device->params, cmd->node_attrs, action, victim, &rc, output, cmd); if(exec_rc < 0 || rc != 0) { crm_warn("Operation %s on %s failed (%d/%d): %.100s", action, device->id, exec_rc, rc, *output); } else if(exec_rc > 0) { crm_info("Operation %s on %s active with pid: %d", action, device->id, exec_rc); rc = exec_rc; } else { crm_info("Operation %s on %s passed: %.100s", action, device->id, *output); } } else { crm_notice("Device %s not found", id); rc = st_err_unknown_device; } if(id == NULL) { free_device(device); } return rc; } +static gboolean can_fence_host_with_device(stonith_device_t *dev, const char *host) +{ + const char *victim = get_victim_name(dev, host); + const char *check_type = g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTCHECK); + + if(dev == NULL) { + return FALSE; + + } else if(host == NULL) { + return TRUE; + } + + if(safe_str_eq(check_type, "none")) { + return TRUE; + + } else if(safe_str_eq(check_type, "static-list")) { + + /* Presence in the hostmap is sufficient + * Only use if all hosts on which the device can be active can always fence all listed hosts + */ + + if(string_in_list(dev->targets, victim)) { + return TRUE; + } + + } else if(safe_str_eq(check_type, "dynamic-list")) { + time_t now = time(NULL); + + /* Host/alias must be in the list output to be eligable to be fenced + * + * Will cause problems if down'd nodes aren't listed or (for virtual nodes) + * if the guest is still listed despite being moved to another machine + */ + + if(dev->targets == NULL || dev->targets_age + 60 < now) { + char *output = NULL; + int rc = stonith_ok; + int exec_rc = stonith_ok; + + /* Some use hostlist instead of the "standard" list */ + const char *list_cmd = g_hash_table_lookup(dev->params, STONITH_ATTR_LIST_OP); + if(list_cmd == NULL) { + list_cmd = "list"; + } + + /* Check for the target's presence in the output of the 'list' command */ + slist_destroy(char, item, dev->targets, crm_free(item)); + dev->targets = NULL; + + exec_rc = run_stonith_agent(dev->agent, dev->params, NULL, list_cmd, NULL, &rc, &output, NULL); + if(exec_rc < 0 || rc != 0) { + crm_notice("Disabling port list queries for %s", dev->id); + dev->targets_age = -1; + + } else { + crm_info("Refreshing port list for %s", dev->id); + dev->targets = parse_host_list(output); + dev->targets_age = now; + } + + crm_free(output); + } + + if(string_in_list(dev->targets, victim)) { + return TRUE; + } + + } else if(safe_str_eq(check_type, "status")) { + int rc = 0; + int exec_rc = 0; + + /* Some use stat instead of the "standard" status */ + const char *status = g_hash_table_lookup(dev->params, STONITH_ATTR_STATUS_OP); + if(status == NULL) { + status = "status"; + } + + /* Run the status operation for the device/target combination + * Will cause problems if the device doesn't return 2 for down'd nodes or + * (for virtual nodes) if the device doesn't return 1 for guests that + * have been moved to another host + */ + + /* TODO: Get node_attrs in here */ + + exec_rc = run_stonith_agent( + dev->agent, dev->params, NULL, status, victim, &rc, NULL, NULL); + + if(exec_rc != 0) { + crm_err("Could not invoke %s: rc=%d", dev->id, exec_rc); + + } else if(rc == 1 /* unkown */) { + crm_debug_2("Host %s is not known by %s", victim, dev->id); + + } else if(rc == 0 /* active */ || rc == 2 /* inactive */) { + return TRUE; + + } else { + crm_err("Unkown result calling %s for %s with %s: rc=%d", status, victim, dev->id, rc); + } + + } else { + crm_err("Unknown check type: %s", check_type); + } + + return FALSE; +} + + struct device_search_s { const char *host; GListPtr capable; }; static void search_devices( gpointer key, gpointer value, gpointer user_data) { stonith_device_t *dev = value; struct device_search_s *search = user_data; - if(search->host == NULL || get_device_port(dev, search->host)) { + if(can_fence_host_with_device(dev, search->host)) { search->capable = g_list_append(search->capable, value); } } static int stonith_query(xmlNode *msg, xmlNode **list) { struct device_search_s search; xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_ERR); search.host = NULL; search.capable = NULL; if(dev) { search.host = crm_element_value(dev, F_STONITH_TARGET); } crm_log_xml_info(msg, "Query"); g_hash_table_foreach(device_list, search_devices, &search); crm_info("Found %d matching devices for '%s'", g_list_length(search.capable), search.host); /* Pack the results into data */ if(list) { *list = create_xml_node(NULL, __FUNCTION__); crm_xml_add_int(*list, "st-available-devices", g_list_length(search.capable)); slist_iter(device, stonith_device_t, search.capable, lpc, dev = create_xml_node(*list, F_STONITH_DEVICE); crm_xml_add(dev, XML_ATTR_ID, device->id); crm_xml_add(dev, "namespace", device->namespace); crm_xml_add(dev, "agent", device->agent); ); } return g_list_length(search.capable); } static void log_operation(async_command_t *cmd, int rc, int pid, const char *next, const char *output) { if(rc == 0) { next = NULL; } - if(cmd->port != NULL) { + if(cmd->victim != NULL) { do_crm_log(rc==0?LOG_INFO:LOG_ERR, "Operation '%s' [%d] for host '%s' with device '%s' returned: %d%s%s", - cmd->action, pid, cmd->port, cmd->device, rc, next?". Trying: ":"", next?next:""); + cmd->action, pid, cmd->victim, cmd->device, rc, next?". Trying: ":"", next?next:""); } else { do_crm_log(rc==0?LOG_INFO:LOG_NOTICE, "Operation '%s' [%d] for device '%s' returned: %d%s%s", cmd->action, pid, cmd->device, rc, next?". Trying: ":"", next?next:""); } if(output) { /* Logging the whole string confuses syslog when the string is xml */ char *local_copy = crm_strdup(output); int lpc = 0, last = 0, more = strlen(local_copy); for(lpc = 0; lpc < more; lpc++) { if(local_copy[lpc] == '\n' || local_copy[lpc] == 0) { local_copy[lpc] = 0; crm_debug("%s output: %s", cmd->device, local_copy+last); last = lpc+1; } } crm_debug("%s output: %s (total %d bytes)", cmd->device, local_copy+last, more); crm_free(local_copy); } } #define READ_MAX 500 static void exec_child_done(ProcTrack* proc, int status, int signum, int rc, int waslogged) { int len = 0; int more = 0; char *output = NULL; xmlNode *data = NULL; xmlNode *reply = NULL; int pid = proctrack_pid(proc); async_command_t *cmd = proctrack_data(proc); CRM_CHECK(cmd != NULL, return); active_children--; if( signum ) { rc = st_err_signal; if( proctrack_timedout(proc) ) { crm_warn("Child '%d' performing action '%s' with '%s' timed out", pid, cmd->action, cmd->device); rc = st_err_timeout; } } do { char buffer[READ_MAX]; errno = 0; memset(&buffer, 0, READ_MAX); more = read(cmd->stdout, buffer, READ_MAX-1); crm_debug_3("Got %d more bytes", more); if(more > 0) { crm_realloc(output, len + more + 1); sprintf(output+len, "%s", buffer); len += more; } } while (more == (READ_MAX-1) || (more < 0 && errno == EINTR)); if(cmd->stdout) { close(cmd->stdout); cmd->stdout = 0; } while(rc != 0 && cmd->device_next) { int exec_rc = 0; stonith_device_t *dev = cmd->device_next->data; - const char *port = get_device_port(dev, cmd->port); + const char *victim = get_victim_name(dev, cmd->victim); log_operation(cmd, rc, pid, dev->id, output); cmd->device = dev->id; cmd->device_next = cmd->device_next->next; - exec_rc = run_agent(dev->agent, dev->params, cmd->action, port, &rc, NULL, cmd); + exec_rc = run_stonith_agent(dev->agent, dev->params, cmd->node_attrs, cmd->action, victim, &rc, NULL, cmd); if(exec_rc > 0) { goto done; } pid = exec_rc; } reply = stonith_construct_async_reply(cmd, output, data, rc); if(safe_str_eq(cmd->action, "metadata")) { /* Too verbose to log */ crm_free(output); output = NULL; } log_operation(cmd, rc, pid, NULL, output); crm_log_xml_debug_3(reply, "Reply"); if(cmd->origin) { send_cluster_message(cmd->origin, crm_msg_stonith_ng, reply, FALSE); } else { do_local_reply(reply, cmd->client, cmd->options & st_opt_sync_call, FALSE); } free_async_command(cmd); done: reset_proctrack_data(proc); crm_free(output); free_xml(reply); free_xml(data); } -static int stonith_fence(xmlNode *msg, const char *action) +static int stonith_fence(xmlNode *msg) { int rc = 0; struct device_search_s search; stonith_device_t *device = NULL; async_command_t *cmd = create_async_command(msg, crm_element_value(msg, F_STONITH_ACTION)); xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_ERR); - + GHashTable *node_attrs = xml2list(dev); + if(cmd == NULL) { return st_err_internal; } search.capable = NULL; search.host = crm_element_value(dev, F_STONITH_TARGET); crm_log_xml_info(msg, "Exec"); g_hash_table_foreach(device_list, search_devices, &search); crm_info("Found %d matching devices for '%s'", g_list_length(search.capable), search.host); if(g_list_length(search.capable) == 0) { return st_err_none_available; } device = search.capable->data; cmd->device = device->id; if(g_list_length(search.capable) > 1) { /* TODO: Order based on priority */ cmd->device_list = search.capable; + cmd->node_attrs = node_attrs; } - return run_agent(device->agent, device->params, cmd->action, cmd->port, &rc, NULL, cmd); + return run_stonith_agent(device->agent, device->params, node_attrs, cmd->action, cmd->victim, &rc, NULL, cmd); } xmlNode *stonith_construct_reply(xmlNode *request, char *output, xmlNode *data, int rc) { int lpc = 0; xmlNode *reply = NULL; const char *name = NULL; const char *value = NULL; const char *names[] = { F_STONITH_OPERATION, F_STONITH_CALLID, F_STONITH_CLIENTID, F_STONITH_REMOTE, F_STONITH_CALLOPTS }; crm_debug_4("Creating a basic reply"); reply = create_xml_node(NULL, T_STONITH_REPLY); crm_xml_add(reply, "st_origin", __FUNCTION__); crm_xml_add(reply, F_TYPE, T_STONITH_NG); for(lpc = 0; lpc < DIMOF(names); lpc++) { name = names[lpc]; value = crm_element_value(request, name); crm_xml_add(reply, name, value); } crm_xml_add_int(reply, F_STONITH_RC, rc); crm_xml_add(reply, "st_output", output); if(data != NULL) { crm_debug_4("Attaching reply output"); add_message_xml(reply, F_STONITH_CALLDATA, data); } return reply; } xmlNode *stonith_construct_async_reply(async_command_t *cmd, char *output, xmlNode *data, int rc) { xmlNode *reply = NULL; crm_debug_4("Creating a basic reply"); reply = create_xml_node(NULL, T_STONITH_REPLY); crm_xml_add(reply, "st_origin", __FUNCTION__); crm_xml_add(reply, F_TYPE, T_STONITH_NG); crm_xml_add(reply, F_STONITH_OPERATION, cmd->op); crm_xml_add(reply, F_STONITH_REMOTE, cmd->remote); crm_xml_add(reply, F_STONITH_CLIENTID, cmd->client); crm_xml_add_int(reply, F_STONITH_CALLID, cmd->id); crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options); crm_xml_add_int(reply, F_STONITH_RC, rc); crm_xml_add(reply, "st_output", output); if(data != NULL) { crm_info("Attaching reply output"); add_message_xml(reply, F_STONITH_CALLDATA, data); } return reply; } void stonith_command(stonith_client_t *client, xmlNode *request, const char *remote) { int rc = st_err_generic; int call_options = 0; gboolean is_reply = FALSE; xmlNode *reply = NULL; xmlNode *data = NULL; char *output = NULL; const char *op = crm_element_value(request, F_STONITH_OPERATION); const char *client_id = crm_element_value(request, F_STONITH_CLIENTID); crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options); if(get_xpath_object("//"T_STONITH_REPLY, request, LOG_DEBUG_3)) { is_reply = TRUE; } if(device_list == NULL) { device_list = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, free_device); } crm_debug("Processing %s%s from %s", op, is_reply?" reply":"", client?client->name:remote); if(crm_str_eq(op, CRM_OP_REGISTER, TRUE)) { return; + } else if(is_reply && crm_str_eq(op, STONITH_OP_FENCE, TRUE)) { + process_remote_stonith_exec(request); + return; + + } else if(is_reply && crm_str_eq(op, STONITH_OP_QUERY, TRUE)) { + process_remote_stonith_query(request); + + } else if(is_reply && crm_str_eq(op, T_STONITH_NOTIFY, TRUE)) { + process_remote_stonith_exec(request); + return; + } else if(crm_str_eq(op, T_STONITH_NOTIFY, TRUE)) { const char *flag_name = NULL; flag_name = crm_element_value(request, F_STONITH_NOTIFY_ACTIVATE); if(flag_name) { crm_debug("Setting %s callbacks for %s (%s): ON", flag_name, client->name, client->id); client->flags |= get_stonith_flag(flag_name); } flag_name = crm_element_value(request, F_STONITH_NOTIFY_DEACTIVATE); if(flag_name) { crm_debug("Setting %s callbacks for %s (%s): off", flag_name, client->name, client->id); client->flags |= get_stonith_flag(flag_name); } return; } else if(crm_str_eq(op, STONITH_OP_DEVICE_ADD, TRUE)) { rc = stonith_device_register(request); do_stonith_notify(call_options, op, rc, request, NULL); } else if(crm_str_eq(op, STONITH_OP_DEVICE_DEL, TRUE)) { rc = stonith_device_remove(request); do_stonith_notify(call_options, op, rc, request, NULL); } else if(crm_str_eq(op, STONITH_OP_EXEC, TRUE)) { rc = stonith_device_action(request, &output); } else if(crm_str_eq(op, STONITH_OP_FENCE, TRUE)) { - xmlNode *cmd = NULL; - const char *action = NULL; - - if(is_reply) { - process_remote_stonith_exec(request); - return; - } - - cmd = get_xpath_object("//@"F_STONITH_TARGET, request, LOG_ERR); - action = crm_element_value(cmd, F_STONITH_ACTION); if(remote) { - rc = stonith_fence(request, action); + rc = stonith_fence(request); + reply = stonith_construct_reply(request, output, data, rc); + crm_debug("Processing %s%s from %s: rc=%d", op, is_reply?" reply":"", + client?client->name:remote, rc); + + if(TRUE || rc == stonith_ok) { + /* Send reply as T_STONITH_NOTIFY so everyone does notifications */ + crm_xml_add(reply, F_STONITH_OPERATION, T_STONITH_NOTIFY); + } + + send_cluster_message(remote, crm_msg_stonith_ng, reply, FALSE); + return; } else if(call_options & st_opt_local_first) { - rc = stonith_fence(request, action); + rc = stonith_fence(request); if(rc < 0) { crm_log_xml_info(request, "EscalateLocal"); - initiate_remote_stonith_op(client, request, action); + initiate_remote_stonith_op(client, request); return; } } else { crm_log_xml_info(request, "Escalate"); - initiate_remote_stonith_op(client, request, action); + initiate_remote_stonith_op(client, request); return; } } else if(crm_str_eq(op, STONITH_OP_QUERY, TRUE)) { - if(is_reply) { - process_remote_stonith_query(request); - - } else { - rc = stonith_query(request, &data); - } + create_remote_stonith_op(client_id, request); /* Record it for the future notification */ + rc = stonith_query(request, &data); } crm_debug("Processing %s%s from %s: rc=%d", op, is_reply?" reply":"", client?client->name:remote, rc); if(is_reply) { } else if(remote) { reply = stonith_construct_reply(request, output, data, rc); send_cluster_message(remote, crm_msg_stonith_ng, reply, FALSE); - } else if(rc <= 0) { + } else if(rc <= 0 || crm_str_eq(op, STONITH_OP_QUERY, TRUE)) { reply = stonith_construct_reply(request, output, data, rc); do_local_reply(reply, client_id, call_options & st_opt_sync_call, remote!=NULL); free_xml(reply); } crm_free(output); free_xml(data); } diff --git a/fencing/internal.h b/fencing/internal.h index f332e44523..70fe1a9764 100644 --- a/fencing/internal.h +++ b/fencing/internal.h @@ -1,74 +1,55 @@ typedef struct stonith_device_s { char *id; char *agent; char *namespace; GListPtr targets; time_t targets_age; + gboolean has_attr_map; GHashTable *params; GHashTable *aliases; } stonith_device_t; typedef struct stonith_client_s { char *id; char *name; char *callback_id; const char *channel_name; IPC_Channel *channel; GCHSource *source; long long flags; } stonith_client_t; -typedef struct async_command_s -{ - - int id; - int stdout; - int options; - - char *op; - char *origin; - char *client; - char *remote; - - char *port; - char *action; - char *device; - - GListPtr device_list; - GListPtr device_next; - -} async_command_t; - extern long long get_stonith_flag(const char *name); extern void stonith_command( stonith_client_t *client, xmlNode *op_request, const char *remote); extern void do_local_reply( xmlNode *notify_src, const char *client_id, gboolean sync_reply, gboolean from_peer); extern xmlNode *stonith_construct_reply( xmlNode *request, char *output, xmlNode *data, int rc); extern xmlNode *stonith_construct_async_reply( async_command_t *cmd, char *output, xmlNode *data, int rc);; extern void do_stonith_notify( int options, const char *type, enum stonith_errors result, xmlNode *data, const char *remote); -extern void initiate_remote_stonith_op( - stonith_client_t *client, xmlNode *request, const char *action); +extern void initiate_remote_stonith_op(stonith_client_t *client, xmlNode *request); extern int process_remote_stonith_exec(xmlNode *msg); extern int process_remote_stonith_query(xmlNode *msg); + +extern void *create_remote_stonith_op(const char *client, xmlNode *request); diff --git a/fencing/remote.c b/fencing/remote.c index a5597105b0..2b4b51742d 100644 --- a/fencing/remote.c +++ b/fencing/remote.c @@ -1,379 +1,394 @@ /* * 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.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern xmlNode *stonith_create_op( int call_id, const char *token, const char *op, xmlNode *data, int call_options); enum op_state { st_query, st_exec, st_done, st_failed, }; GHashTable *remote_op_list = NULL; typedef struct st_query_result_s { char *host; int devices; } st_query_result_t; typedef struct remote_fencing_op_s { char *id; char *target; char *action; guint replies; + guint op_timer; guint query_timer; - long long call_options; + guint base_timeout; char *delegate; time_t completed; + long long call_options; enum op_state state; char *originator; GListPtr query_results; xmlNode *request; } remote_fencing_op_t; static void free_remote_query(gpointer data) { st_query_result_t *query = data; crm_free(query->host); crm_free(query); } static void free_remote_op(gpointer data) { remote_fencing_op_t *op = data; crm_free(op->id); crm_free(op->action); crm_free(op->target); crm_free(op->originator); if(op->query_timer) { g_source_remove(op->query_timer); } if(op->op_timer) { g_source_remove(op->op_timer); } if(op->query_results) { slist_destroy(st_query_result_t, result, op->query_results, free_remote_query(result); ); } if(op->request) { free_xml(op->request); op->request = NULL; } crm_free(op); } static void remote_op_reply_and_notify(remote_fencing_op_t *op, xmlNode *data, int rc) { xmlNode *reply = NULL; xmlNode *local_data = NULL; - /* TODO: Have the delegate perform the notification */ op->completed = time(NULL); + if(data == NULL) { data = create_xml_node(NULL, "remote-op"); local_data = data; } else { op->delegate = crm_element_value_copy(data, F_ORIG); } crm_xml_add_int(data, "state", op->state); crm_xml_add(data, F_STONITH_TARGET, op->target); crm_xml_add(data, F_STONITH_OPERATION, op->action); reply = stonith_construct_reply(op->request, NULL, data, rc); crm_xml_add(reply, F_STONITH_DELEGATE, op->delegate); + crm_info("Notifing clients of %s (%s of %s by %s): %d", + op->id, op->action, op->target, op->delegate, op->state); + do_stonith_notify(0, STONITH_OP_FENCE, rc, reply, NULL); do_local_reply(reply, op->originator, op->call_options & st_opt_sync_call, FALSE); free_xml(local_data); free_xml(reply); /* Free non-essential parts of the record * Keep the record around so we can query the history */ if(op->query_results) { slist_destroy(st_query_result_t, result, op->query_results, free_remote_query(result); ); op->query_results = NULL; } if(op->request) { free_xml(op->request); op->request = NULL; } } static gboolean remote_op_timeout(gpointer userdata) { remote_fencing_op_t *op = userdata; crm_err("Action %s (%s) for %s timed out", op->action, op->id, op->target); op->query_timer = 0; remote_op_reply_and_notify(op, NULL, st_err_timeout); op->state = st_failed; return FALSE; } static gboolean remote_op_query_timeout(gpointer data) { remote_fencing_op_t *op = data; crm_err("Query %s for %s timed out", op->id, op->target); op->query_timer = 0; if(op->op_timer) { g_source_remove(op->op_timer); op->op_timer = 0; } remote_op_timeout(op); return FALSE; } -void initiate_remote_stonith_op( - stonith_client_t *client, xmlNode *request, const char *action) +void *create_remote_stonith_op(const char *client, xmlNode *request) { cl_uuid_t new_uuid; char uuid_str[UU_UNPARSE_SIZEOF]; - int timeout = 0; - xmlNode *query = NULL; remote_fencing_op_t *op = NULL; xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, request, LOG_ERR); - if(remote_op_list == NULL) { - remote_op_list = g_hash_table_new_full( - g_str_hash, g_str_equal, NULL, free_remote_op); - } - crm_malloc0(op, sizeof(remote_fencing_op_t)); - crm_element_value_int(dev, "timeout", &timeout); + crm_element_value_int(dev, "timeout", (int*)&(op->base_timeout)); cl_uuid_generate(&new_uuid); cl_uuid_unparse(&new_uuid, uuid_str); op->id = crm_strdup(uuid_str); g_hash_table_replace(remote_op_list, op->id, op); op->state = st_query; - op->action = crm_strdup(action); - op->originator = crm_strdup(client->id); + op->action = crm_element_value_copy(dev, F_STONITH_ACTION); + + op->originator = crm_strdup(client); op->target = crm_element_value_copy(dev, F_STONITH_TARGET); - op->op_timer = g_timeout_add(1000*timeout, remote_op_timeout, op); - op->query_timer = g_timeout_add(100*timeout, remote_op_query_timeout, op); op->request = copy_xml(request); /* TODO: Figure out how to avoid this */ - crm_element_value_int(request, F_STONITH_CALLOPTS, (int*)&(op->call_options)); + crm_element_value_int(request, F_STONITH_CALLOPTS, (int*)&(op->call_options)); + + return op; +} + + +void initiate_remote_stonith_op(stonith_client_t *client, xmlNode *request) +{ + xmlNode *query = NULL; + remote_fencing_op_t *op = NULL; + + if(remote_op_list == NULL) { + remote_op_list = g_hash_table_new_full( + g_str_hash, g_str_equal, NULL, free_remote_op); + } + + op = create_remote_stonith_op(client->id, request); + op->op_timer = g_timeout_add(1000*op->base_timeout, remote_op_timeout, op); + op->query_timer = g_timeout_add(100*op->base_timeout, remote_op_query_timeout, op); query = stonith_create_op(0, op->id, STONITH_OP_QUERY, NULL, 0); crm_xml_add(query, F_STONITH_REMOTE, op->id); crm_xml_add(query, F_STONITH_TARGET, op->target); + crm_xml_add(query, F_STONITH_ACTION, op->action); crm_info("Initiating remote operation %s for %s: %s", op->action, op->target, op->id); CRM_CHECK(op->action, return); send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE); free_xml(query); } static void call_remote_stonith(remote_fencing_op_t *op, st_query_result_t *result) { xmlNode *query = stonith_create_op(0, op->id, STONITH_OP_FENCE, NULL, 0);; crm_xml_add(query, F_STONITH_REMOTE, op->id); crm_xml_add(query, F_STONITH_TARGET, op->target); crm_xml_add(query, F_STONITH_ACTION, op->action); op->state = st_exec; crm_info("Requesting that %s perform op %s %s", result->host, op->action, op->target); send_cluster_message(result->host, crm_msg_stonith_ng, query, FALSE); free_xml(query); } int process_remote_stonith_query(xmlNode *msg) { int devices = 0; const char *id = NULL; remote_fencing_op_t *op = NULL; st_query_result_t *result = NULL; xmlNode *dev = get_xpath_object("//@"F_STONITH_REMOTE, msg, LOG_ERR); crm_log_xml_info(msg, "QueryResult"); CRM_CHECK(dev != NULL, return st_err_internal); id = crm_element_value(dev, F_STONITH_REMOTE); CRM_CHECK(id != NULL, return st_err_internal); dev = get_xpath_object("//@st-available-devices", msg, LOG_ERR); CRM_CHECK(dev != NULL, return st_err_internal); crm_element_value_int(dev, "st-available-devices", &devices); op = g_hash_table_lookup(remote_op_list, id); if(op == NULL) { crm_debug("Unknown or expired remote op: %s", id); return st_err_unknown_operation; } op->replies++; crm_malloc0(result, sizeof(st_query_result_t)); result->host = crm_element_value_copy(msg, F_ORIG); result->devices = devices; /* TODO: Implement options * A) If we have anyone that can do the job * B) If we have someone that can do the job and some percent of the known peers * C) If all known peers have responded * * Implement A first */ /* Track A */ if(result->devices > 0) { if(op->call_options & st_opt_allow_suicide) { crm_info("Allowing %s to potentialy fence itself", op->target); } else if(safe_str_eq(result->host, op->target)) { crm_info("Ignoring reply from %s, hosts are not permitted to commit suicide", op->target); free_remote_query(result); return 0; } if(op->query_timer) { g_source_remove(op->query_timer); op->query_timer = 0; } if(op->state == st_query) { call_remote_stonith(op, result); free_remote_query(result); } else if(op->state == st_exec) { /* TODO: insert in sorted order (key = num devices) */ crm_info("Queuing query result from %s while operation is pending", result->host); op->query_results = g_list_append(op->query_results, result); } else { crm_info("Discarding query result from %s. Operation is in state %d", result->host, op->state); free_remote_query(result); } } else { crm_info("Discarding query result from %s. No valid devices", result->host); free_remote_query(result); } return 0; } int process_remote_stonith_exec(xmlNode *msg) { int rc = 0; const char *id = NULL; remote_fencing_op_t *op = NULL; xmlNode *dev = get_xpath_object("//@"F_STONITH_REMOTE, msg, LOG_ERR); crm_log_xml_info(msg, "ExecResult"); CRM_CHECK(dev != NULL, return st_err_internal); id = crm_element_value(dev, F_STONITH_REMOTE); CRM_CHECK(id != NULL, return st_err_internal); dev = get_xpath_object("//@"F_STONITH_RC, msg, LOG_ERR); CRM_CHECK(dev != NULL, return st_err_internal); op = g_hash_table_lookup(remote_op_list, id); if(op == NULL) { - crm_debug("Unknown or expired remote op: %s", id); + crm_err("Unknown or expired remote op: %s", id); return st_err_unknown_operation; } crm_element_value_int(dev, F_STONITH_RC, &rc); - if(rc == stonith_ok) { + if(rc == stonith_ok || op->state != st_exec) { if(op->op_timer) { g_source_remove(op->op_timer); op->op_timer = 0; } remote_op_reply_and_notify(op, msg, rc); } else if(rc < stonith_ok) { if(op->state == st_exec) { st_query_result_t *result = g_list_nth_data(op->query_results, 0); op->query_results = g_list_remove(op->query_results, result); if(result && result->devices > 0) { call_remote_stonith(op, result); } else { remote_op_timeout(op); } if(result) { free_remote_query(result); } } } return rc; } diff --git a/fencing/test.c b/fencing/test.c index 0d8e213e61..843b4c64cd 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.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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("stonith-test", 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", "off", 60); + rc = st->cmds->fence(st, st_opts, "unknown-host", NULL, "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", "off", 60); + rc = st->cmds->fence(st, st_opts, "pcmk-1", NULL, "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", "on", 10); + rc = st->cmds->fence(st, st_opts, "pcmk-1", NULL, "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", "off", 10); + rc = st->cmds->fence(st, st_opts, "some-host", NULL, "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", "on", 10); + rc = st->cmds->fence(st, st_opts, "pcmk-1", NULL, "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 34f82bb56c..46f3b4a4e6 100644 --- a/include/crm/stonith-ng.h +++ b/include/crm/stonith-ng.h @@ -1,153 +1,197 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef STONITH_NG__H #define STONITH_NG__H #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_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 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_ATTR_LIST_OP "pcmk_list_cmd" +#define STONITH_ATTR_STATUS_OP "pcmk_status_cmd" +#define STONITH_ATTR_MONITOR_OP "pcmk_monitor_cmd" + #define STONITH_OP_EXEC "st_execute" #define STONITH_OP_QUERY "st_query" #define STONITH_OP_FENCE "st_fence" #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, const char *action, int timeout); + int (*fence)(stonith_t *st, int options, const char *node, GHashTable *parameters, const char *action, int timeout); 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(void); +extern void stonith_dump_pending_callbacks(stonith_t *st); + +/* internal details - move elsewhere */ + +typedef struct async_command_s +{ + + int id; + int stdout; + int options; + + 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; + +} 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/Makefile.am b/lib/fencing/Makefile.am index a1eca709db..be9b618989 100644 --- a/lib/fencing/Makefile.am +++ b/lib/fencing/Makefile.am @@ -1,32 +1,32 @@ # File: Makefile.am # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # 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 program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir) -I$(top_srcdir) lib_LTLIBRARIES = libstonithd.la libstonithd_la_SOURCES = st_client.c libstonithd_la_LDFLAGS = -version-info 1:0:0 \ - -lplumb $(CLUSTERLIBS) \ + -lplumb -lstonith $(CLUSTERLIBS) \ $(top_builddir)/lib/common/libcrmcommon.la AM_CFLAGS = $(INCLUDES) diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c index b0797335a6..a05582cda6 100644 --- a/lib/fencing/st_client.c +++ b/lib/fencing/st_client.c @@ -1,1185 +1,1553 @@ /* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include +#include + +#include +#include #include #include #include #include #include -GHashTable *stonith_op_callback_table = NULL; +#define FE_AGENT_FORK -2 +#define FE_AGENT_ERROR -3 + 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; struct notify_blob_s blob; blob.stonith = stonith; blob.xml = create_xml_node(NULL, "notify");; 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; + } + + len += strlen(key); + len += strlen(value); + if(*args != NULL) { + last = strlen(*args); + } + + crm_realloc(*args, last+len); + + 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, GHashTable *params, char **arg_list) +{ + char *name = NULL; + int last = 0, lpc = 0, max = 0; + const char *map = NULL; + + if(params) { + map = g_hash_table_lookup(params, STONITH_ATTR_ARGMAP); + } + + if(map == NULL) { + /* The best default there is for now... */ + append_const_arg("port", victim, arg_list); + return; + } + + max = strlen(map); + for(; lpc < max; lpc++) { + if(map[lpc] == 0) { + break; + + } else if(isalpha(map[lpc])) { + /* keep going */ + + } else if(map[lpc] == '=') { + crm_malloc0(name, 1 + lpc - last); + strncpy(name, map + last, lpc - last); + last = lpc + 1; + + } else if(name && isspace(map[lpc])) { + char *key = NULL; + char *param = NULL; + const char *value = NULL; + + crm_malloc0(param, 1 + lpc - last); + strncpy(param, map + last, lpc - last); + last = lpc + 1; + + key = crm_meta_name(param); + value = g_hash_table_lookup(params, key); + + crm_info("Setting '%s'='%s' (%s) for %s", name, value, param, victim); + append_const_arg(name, value, arg_list); + + crm_free(name); name=NULL; + crm_free(param); + crm_free(key); + + } else if(isspace(map[lpc])) { + last = lpc; + } + } +} + +static char *make_args(GHashTable *dev_hash, GHashTable *node_hash, const char *action, const char *victim) +{ + char *arg_list = NULL; + CRM_CHECK(action != NULL, return NULL); + + if(dev_hash) { + g_hash_table_foreach(dev_hash, append_arg, &arg_list); + } + + append_const_arg(STONITH_ATTR_ACTION_OP, action, &arg_list); + if(victim) { + append_const_arg("nodename", victim, &arg_list); + append_host_specific_args(victim, 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(rc >= 0) { + rc = st_err_generic; + } + goto fail; + } + + close(p_write_fd); + + if(track) { + NewTrackedProc(pid, 0, PT_LOGNORMAL, track, track->pt_ops); + +#if 0 + ProcTrackKillInfo *info = NULL; + crm_malloc0(info, sizeof(ProcTrackKillInfo) * 3); + + killseq[0].mstimeout = timeout; /* after timeout send TERM */ + killseq[0].signalno = SIGTERM; + killseq[1].mstimeout = 5000; /* after 5 secs remove it */ + killseq[1].signalno = SIGKILL; + killseq[2].mstimeout = 5000; /* if it's still there after 5, complain */ + killseq[2].signalno = 0; + SetTrackedProcTimeouts(pid,killseq); +#endif + track->stdout = p_read_fd; + + crm_free(args); + close(c_write_fd); + close(c_read_fd); + return pid; + + } else { + crm_debug("waiting for %s", agent); + waitpid(pid, &status, 0); + + if(output != NULL) { + len = 0; + crm_debug("reading"); + 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); + 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); + + close(p_read_fd); + close(p_write_fd); + + close(c_read_fd); + 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; - xmlNode *xml = NULL; - xmlNode *data = NULL; + int bufferlen = 0; - data = create_xml_node(NULL, F_STONITH_DEVICE); - crm_xml_add(data, "origin", __FUNCTION__); - crm_xml_add(data, "agent", agent); - crm_xml_add(data, "namespace", namespace); - crm_xml_add(data, F_STONITH_ACTION, "metadata"); - crm_xml_add_int(data, "timeout", timeout); + char *buffer = NULL; + char *xml_meta_longdesc = NULL; + char *xml_meta_shortdesc = NULL; - rc = stonith_send_command( - stonith, STONITH_OP_EXEC, data, &xml, call_options, timeout); + const char *meta_param = NULL; + const char *meta_longdesc = NULL; + const char *meta_shortdesc = NULL; + const char *provider = get_stonith_provider(agent, namespace); - if(xml && output) { - xmlNode *meta = get_xpath_object("//resource-agent", xml, LOG_ERR); - if(meta) { - *output = dump_xml_formatted(meta); - } else { - crm_log_xml_warn(xml, "NoMetadata"); - rc = st_err_internal; + 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 = no_parameter_info; + meta_shortdesc = no_parameter_info; + meta_param = +" \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 = stonith_get_info(stonith_obj, ST_DEVICEDESCR); + if (meta_longdesc == NULL) { + crm_warn("no long description in %s's metadata.", agent); + meta_longdesc = no_parameter_info; + } + + meta_shortdesc = stonith_get_info(stonith_obj, ST_DEVICENAME); + if (meta_shortdesc == NULL) { + crm_warn("no short description in %s's metadata.", agent); + meta_shortdesc = no_parameter_info; + } + + meta_param = 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 = 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); } } + + if(output) { + *output = buffer; + + } else { + crm_free(buffer); + } - free_xml(data); - free_xml(xml); 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"); 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 *port, int timeout) + 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, port); + crm_xml_add(data, F_STONITH_TARGET, victim); crm_xml_add_int(data, "timeout", timeout); 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, const char *action, int timeout) + stonith_t *stonith, int call_options, const char *node, GHashTable *parameters, + 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, "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 = ""; break; case st_err_not_supported: error_msg = ""; break; case st_err_authentication: error_msg = ""; break; case st_err_generic: error_msg = ""; break; case st_err_internal: error_msg = ""; break; case st_err_unknown_device: error_msg = ""; break; case st_err_unknown_operation: error_msg = ""; break; case st_err_unknown_port: error_msg = ""; break; case st_err_none_available: error_msg = ""; break; case st_err_connection: error_msg = ""; break; case st_err_missing: error_msg = ""; break; case st_err_exists: error_msg = ""; break; case st_err_timeout: error_msg = ""; break; case st_err_signal: error_msg = ""; break; case st_err_ipc: error_msg = ""; break; case st_err_peer: error_msg = ""; 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"; + } + + return "heartbeat"; +} + 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, "op", 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 %ds", 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) { - stonith_private_t *private = stonith->private; 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*1000; async_timer->ref = g_timeout_add( async_timer->timeout, stonith_async_timeout_handler, async_timer); } - g_hash_table_insert(stonith_op_callback_table, GINT_TO_POINTER(call_id), blob); + 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; - if(stonith_op_callback_table != NULL) { - g_hash_table_destroy(stonith_op_callback_table); - } - - stonith_op_callback_table = g_hash_table_new_full( + 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(stonith_op_callback_table, GINT_TO_POINTER(call_id)); + 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(void) +void stonith_dump_pending_callbacks(stonith_t *stonith) { - if(stonith_op_callback_table == NULL) { + stonith_private_t *private = stonith->private; + if(private->stonith_op_callback_table == NULL) { return; } return g_hash_table_foreach( - stonith_op_callback_table, stonith_dump_pending_op, NULL); + 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); } blob = g_hash_table_lookup( - stonith_op_callback_table, GINT_TO_POINTER(call_id)); + 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_debug_3("Sending %s message to STONITH service", op); 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, stonith->call_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); } - g_hash_table_destroy(stonith_op_callback_table); 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; - if(stonith_op_callback_table != NULL) { - g_hash_table_destroy(stonith_op_callback_table); - stonith_op_callback_table = NULL; - } - if(stonith_op_callback_table == NULL) { - stonith_op_callback_table = g_hash_table_new_full( - g_direct_hash, g_direct_equal, - NULL, stonith_destroy_op_callback); - } + 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->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; } diff --git a/lib/plugins/lrm/Makefile.am b/lib/plugins/lrm/Makefile.am index 8e9a9cb183..8d53d52780 100644 --- a/lib/plugins/lrm/Makefile.am +++ b/lib/plugins/lrm/Makefile.am @@ -1,38 +1,38 @@ # # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # 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 program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl havarlibdir = $(localstatedir)/lib/@HB_PKG@ lrmdir = $(havarlibdir)/lrm halibdir = $(libdir)/@HB_PKG@ plugindir = $(halibdir)/plugins/RAExec plugin_LTLIBRARIES = stonith.la stonith_la_SOURCES = raexecstonith.c stonith_la_LDFLAGS = -lpils -export-dynamic -module -avoid-version \ - $(top_builddir)/lib/fencing/libstonithd.la -lstonith -llrm + $(top_builddir)/lib/fencing/libstonithd.la -llrm install-exec-local: $(mkinstalldirs) $(DESTDIR)$(lrmdir) -chgrp $(CRM_DAEMON_GROUP) $(DESTDIR)/$(lrmdir) chmod 770 $(DESTDIR)/$(lrmdir) diff --git a/lib/plugins/lrm/raexecstonith.c b/lib/plugins/lrm/raexecstonith.c index f4862dd42e..1839711f19 100644 --- a/lib/plugins/lrm/raexecstonith.c +++ b/lib/plugins/lrm/raexecstonith.c @@ -1,372 +1,286 @@ /* * 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 * * File: raexecocf.c * Author: Sun Jiang Dong * Copyright (c) 2004 International Business Machines * * This code implements the Resource Agent Plugin Module for LSB style. * It's a part of Local Resource Manager. Currently it's used by lrmd only. */ #include #include #include #include #include #include #include #include #include #include #include #if HAVE_HB_CONFIG_H #include #endif #if HAVE_GLUE_CONFIG_H #include #endif #include #include #include #include #include /* Add it for compiling on OSX */ #include #include #include #include # define PIL_PLUGINTYPE RA_EXEC_TYPE # define PIL_PLUGINTYPE_S "RAExec" # define PIL_PLUGINLICENSE LICENSE_PUBDOM # define PIL_PLUGINLICENSEURL URL_PUBDOM # define PIL_PLUGIN stonith # define PIL_PLUGIN_S "stonith" static PIL_rc close_stonithRA(PILInterface*, void* ud_interface); /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, const int timeout, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra , const char * op_type, const char * std_output); static int get_resource_list(GList ** rsc_info); static char* get_resource_meta(const char* rsc_type, const char* provider); static int get_provider_list(const char* op_type, GList ** providers); /* The end of exported function list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; -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"; - PIL_PLUGIN_BOILERPLATE2("1.0", Debug); static const PILPluginImports* PluginImports; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static void* OurImports; static void* interfprivate; /* * Our plugin initialization and registration function * It gets called when the plugin gets loaded. */ PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports); PIL_rc PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports) { /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interfaces */ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S, &raops, close_stonithRA, &OurInterface, &OurImports, interfprivate); } static PIL_rc close_stonithRA(PILInterface* pif, void* ud_interface) { return PIL_OK; } -static 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; -} - -static const char *get_provider(const char *agent, const char *provider) -{ - /* This function sucks */ - if(is_redhat_agent(agent)) { - return "redhat"; - } - - return "heartbeat"; -} - static int execra(const char *rsc_id, const char *rsc_type, const char *provider, const char *op_type, const int timeout, GHashTable *params) { int rc = 0; stonith_t *stonith_api = NULL; - provider = get_provider(rsc_type, provider); + provider = get_stonith_provider(rsc_type, provider); + crm_log_init("lrm-stonith", LOG_INFO, FALSE, FALSE, 0, NULL); if ( 0 == STRNCMP_CONST(op_type, "meta-data")) { char *meta = get_resource_meta(rsc_type, provider); printf("%s", meta); free(meta); exit(0); } stonith_api = stonith_api_new(); rc = stonith_api->cmds->connect(stonith_api, "lrmd", NULL, NULL); if ( 0 == STRNCMP_CONST(op_type, "monitor") ) { + /* monitor isn't universally supported yet - allow another option to be specified */ + const char *action = g_hash_table_lookup(params, STONITH_ATTR_MONITOR_OP); + if(action == NULL) { + action = "monitor"; + } else { + crm_debug("Using action %s for %s", action, op_type); + } + rc = stonith_api->cmds->call( - stonith_api, st_opt_sync_call, rsc_id, "monitor", NULL, timeout); + stonith_api, st_opt_sync_call, rsc_id, action, NULL, timeout); } else if ( 0 == STRNCMP_CONST(op_type, "start") ) { const char *agent = rsc_type; if(provider == NULL || 0 != STRNCMP_CONST(provider, "redhat")) { agent = "fence_legacy"; g_hash_table_replace(params, strdup("plugin"), strdup(rsc_type)); } - + rc = stonith_api->cmds->register_device( stonith_api, st_opt_sync_call, rsc_id, provider, agent, params); } else if ( 0 == STRNCMP_CONST(op_type, "stop") ) { rc = stonith_api->cmds->remove_device( stonith_api, st_opt_sync_call, rsc_id); } + crm_debug("%s_%s returned %d", rsc_id, op_type, rc); stonith_api->cmds->disconnect(stonith_api); stonith_api_delete(stonith_api); /* cl_log(LOG_DEBUG, "stonithRA orignal exit code=%d", exit_value); */ exit(map_ra_retvalue(rc, op_type, NULL)); } static uniform_ret_execra_t map_ra_retvalue(int rc, const char * op_type, const char * std_output) { if(rc == st_err_unknown_device) { if ( 0 == STRNCMP_CONST(op_type, "stop") ) { rc = 0; } else if ( 0 != STRNCMP_CONST(op_type, "start") ) { rc = 7; } } else if (rc < 0 || rc > EXECRA_STATUS_UNKNOWN) { crm_warn("Mapped the invalid return code %d.", rc); rc = EXECRA_UNKNOWN_ERROR; } return rc; } static int get_resource_list(GList ** rsc_info) { int file_num; char **entry = NULL; char **type_list = NULL; struct dirent **namelist; if ( rsc_info == NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list"); return -2; } /* Include Heartbeat agents */ type_list = stonith_types(); for(entry = type_list; *entry; ++entry) { cl_log(LOG_INFO, "Added: %s", *entry); *rsc_info = g_list_append(*rsc_info, *entry); } /* Include Red Hat agents, basically: ls -1 @sbin_dir@/fence_* */ file_num = scandir(RH_STONITH_DIR, &namelist, 0, alphasort); if (file_num > 0) { struct stat prop; char buffer[FILENAME_MAX+1]; while (file_num--) { if ('.' == namelist[file_num]->d_name[0]) { free(namelist[file_num]); continue; } else if(0 != strncmp(RH_STONITH_PREFIX, namelist[file_num]->d_name, strlen(RH_STONITH_PREFIX))) { free(namelist[file_num]); continue; } snprintf(buffer,FILENAME_MAX,"%s/%s", RH_STONITH_DIR, namelist[file_num]->d_name); stat(buffer, &prop); if (S_ISREG(prop.st_mode)) { *rsc_info = g_list_append(*rsc_info, g_strdup(namelist[file_num]->d_name)); } free(namelist[file_num]); } free(namelist); } return 0; } static int get_provider_list(const char* op_type, GList ** providers) { if(providers == NULL) { return -1; } else if(op_type == NULL) { return -2; } if (is_redhat_agent(op_type)) { *providers = g_list_append(*providers, g_strdup("redhat")); } else { *providers = g_list_append(*providers, g_strdup("heartbeat")); } return 1; } static char * get_resource_meta(const char* rsc_type, const char* provider) { - int bufferlen = 0; - char *buffer = NULL; - const char * meta_param = NULL; - const char * meta_longdesc = NULL; - const char * meta_shortdesc = NULL; - char *xml_meta_longdesc = NULL; - char *xml_meta_shortdesc = NULL; - Stonith * stonith_obj = NULL; - static const char * no_parameter_info = ""; - - cl_log(LOG_INFO, "stonithRA plugin: looking up %s/%s metadata.", rsc_type, provider); - provider = get_provider(rsc_type, provider); - - if(0 == STRNCMP_CONST(provider, "redhat")) { - stonith_t *stonith_api = stonith_api_new(); - stonith_api->cmds->connect(stonith_api, "lrmd", NULL, NULL); - stonith_api->cmds->metadata( - stonith_api, st_opt_sync_call, rsc_type, provider, &buffer, 0); - stonith_api->cmds->disconnect(stonith_api); - stonith_api_delete(stonith_api); - cl_log(LOG_INFO, "stonithRA plugin: got metadata: %s", buffer); - return buffer; - } - - /* TODO: Move this to stonithd */ - stonith_obj = stonith_new(rsc_type); - - meta_longdesc = stonith_get_info(stonith_obj, ST_DEVICEDESCR); - if (meta_longdesc == NULL) { - cl_log(LOG_WARNING, "stonithRA plugin: no long description in %s's metadata.", rsc_type); - meta_longdesc = no_parameter_info; - } - xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc); - - meta_shortdesc = stonith_get_info(stonith_obj, ST_DEVICENAME); - if (meta_shortdesc == NULL) { - cl_log(LOG_WARNING, "stonithRA plugin: no short description in %s's metadata.", rsc_type); - meta_shortdesc = no_parameter_info; - } - xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc); - - meta_param = stonith_get_info(stonith_obj, ST_CONF_XML); - if (meta_param == NULL) { - cl_log(LOG_WARNING, "stonithRA plugin: no list of parameters in %s's metadata.", rsc_type); - meta_param = no_parameter_info; - } - - bufferlen = STRLEN_CONST(META_TEMPLATE) + strlen(rsc_type) - + strlen(xml_meta_longdesc) + strlen(xml_meta_shortdesc) - + strlen(meta_param) + 1; - - buffer = malloc(sizeof(char) * bufferlen); - memset(buffer, 0, bufferlen); - snprintf(buffer, bufferlen-1, META_TEMPLATE, - rsc_type, xml_meta_longdesc, xml_meta_shortdesc, meta_param); + char *buffer = NULL; + stonith_t *stonith_api = stonith_api_new(); + stonith_api->cmds->metadata( + stonith_api, st_opt_sync_call, rsc_type, provider, &buffer, 0); + stonith_api_delete(stonith_api); + cl_log(LOG_INFO, "stonithRA plugin: got metadata: %s", buffer); - stonith_delete(stonith_obj); - xmlFree(xml_meta_longdesc); - xmlFree(xml_meta_shortdesc); - return buffer; + /* TODO: Convert to XML and ensure our standard actions exist */ + return buffer; } +