diff --git a/crm/admin/cibadmin.c b/crm/admin/cibadmin.c index 414cc67b9e..043b799d7a 100644 --- a/crm/admin/cibadmin.c +++ b/crm/admin/cibadmin.c @@ -1,593 +1,593 @@ -/* $Id: cibadmin.c,v 1.10 2004/10/06 19:34:42 andrew Exp $ */ +/* $Id: cibadmin.c,v 1.11 2004/10/24 13:00:11 lge Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* someone complaining about _ha_msg_mod not being found */ #include int message_timer_id = -1; int message_timeout_ms = 30*1000; GMainLoop *mainloop = NULL; const char *crm_system_name = "cibadmin"; IPC_Channel *crmd_channel = NULL; char *admin_uuid = NULL; void usage(const char *cmd, int exit_status); ll_cluster_t *do_init(void); int do_work(ll_cluster_t * hb_cluster, const char *xml_text); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); xmlNodePtr handleCibMod(const char *xml); gboolean admin_message_timeout(gpointer data); gboolean BE_VERBOSE = FALSE; int expected_responses = 1; gboolean DO_HEALTH = FALSE; const char *cib_action = NULL; xmlNodePtr msg_options = NULL; typedef struct str_list_s { int num_items; char *value; struct str_list_s *next; } str_list_t; const char *verbose = XML_BOOLEAN_FALSE; char *id = NULL; char *this_msg_reference = NULL; char *obj_type = NULL; char *clear = NULL; char *status = NULL; char *migrate_from = NULL; char *migrate_res = NULL; char *subtype = NULL; char *reset = NULL; int operation_status = 0; -const char *sys_to = NULL;; +const char *sys_to = NULL; #define OPTARGS "V?i:o:QDUCEX:t:" int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; ll_cluster_t *hb_cluster = NULL; int level = 0; char *xml_text = NULL; static struct option long_options[] = { /* Top-level Options */ {CRM_OP_ERASE, 0, 0, 'E'}, {CRM_OP_QUERY, 0, 0, 'Q'}, {CRM_OP_CREATE, 0, 0, 'C'}, {CRM_OP_REPLACE, 0, 0, 'R'}, {CRM_OP_UPDATE, 0, 0, 'U'}, {CRM_OP_DELETE, 0, 0, 'D'}, {"xml", 1, 0, 'X'}, {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"reference", 1, 0, 0}, {"timeout", 1, 0, 't'}, /* common options */ {XML_ATTR_ID, 1, 0, 'i'}, {"obj_type", 1, 0, 'o'}, {0, 0, 0, 0} }; if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } /* Redirect messages from glib functions to our handler */ g_log_set_handler(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, cl_glib_msg_handler, NULL); /* and for good measure... */ g_log_set_always_fatal((GLogLevelFlags)0); cl_log_set_entity(crm_system_name); cl_log_set_facility(LOG_USER); while (1) { flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); if (flag == -1) break; switch(flag) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); if ((safe_str_eq(CRM_OP_ERASE, long_options[option_index].name)) || (safe_str_eq(CRM_OP_CREATE, long_options[option_index].name)) || (safe_str_eq(CRM_OP_UPDATE, long_options[option_index].name)) || (safe_str_eq(CRM_OP_DELETE, long_options[option_index].name)) || (safe_str_eq(CRM_OP_REPLACE, long_options[option_index].name)) || (safe_str_eq(CRM_OP_QUERY, long_options[option_index].name))){ cib_action = crm_strdup(long_options[option_index].name); } else if (safe_str_eq("reference", long_options[option_index].name)) { this_msg_reference = crm_strdup(optarg); } else { printf("Long option (--%s) is not (yet?) properly supported\n", long_options[option_index].name); ++argerr; } break; /* a sample test for multiple instance if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); */ case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30*1000; } break; case 'E': cib_action = crm_strdup(CRM_OP_ERASE); break; case 'Q': cib_action = crm_strdup(CRM_OP_QUERY); break; case 'U': cib_action = crm_strdup(CRM_OP_UPDATE); break; case 'R': cib_action = crm_strdup(CRM_OP_REPLACE); break; case 'C': cib_action = crm_strdup(CRM_OP_CREATE); break; case 'D': cib_action = crm_strdup(CRM_OP_DELETE); break; case 'V': level = get_crm_log_level(); BE_VERBOSE = TRUE; verbose = XML_BOOLEAN_TRUE; cl_log_enable_stderr(TRUE); set_crm_log_level(level+1); break; case '?': usage(crm_system_name, LSB_EXIT_OK); break; case 'i': crm_verbose("Option %c => %s", flag, optarg); id = crm_strdup(optarg); break; case 'o': crm_verbose("Option %c => %s", flag, optarg); obj_type = crm_strdup(optarg); break; case 'X': xml_text = crm_strdup(optarg); break; default: printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (optind > argc) { ++argerr; } if (argerr) { usage(crm_system_name, LSB_EXIT_GENERIC); } hb_cluster = do_init(); if (hb_cluster != NULL) { if (do_work(hb_cluster, xml_text) > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); crm_info("%s waiting for reply from the local CRM", crm_system_name); message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); g_main_run(mainloop); return_to_orig_privs(); } else { crm_err("No message to send"); operation_status = -1; } } else { crm_err("Init failed, could not perform requested operations"); operation_status = -2; } crm_debug("%s exiting normally", crm_system_name); return operation_status; } xmlNodePtr handleCibMod(const char *xml) { const char *attr_name = NULL; const char *attr_value = NULL; xmlNodePtr fragment = NULL; xmlNodePtr cib_object = NULL; if(xml == NULL) { cib_object = file2xml(stdin); } else { cib_object = string2xml(xml); } if(cib_object == NULL) { return NULL; } attr_name = XML_ATTR_ID; attr_value = xmlGetProp(cib_object, attr_name); if(attr_name == NULL || strlen(attr_name) == 0) { crm_err("No value for %s specified.", attr_name); return NULL; } crm_trace("Object creation complete"); /* create the cib request */ fragment = create_cib_fragment(cib_object, NULL); set_xml_property_copy(msg_options, XML_ATTR_OP, cib_action); return fragment; } int do_work(ll_cluster_t * hb_cluster, const char *xml_text) { /* construct the request */ xmlNodePtr msg_data = NULL; const char *dest_node = NULL; gboolean all_is_good = TRUE; char *obj_type_parent = NULL; msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); set_xml_property_copy(msg_options, XML_ATTR_VERBOSE, verbose); set_xml_property_copy(msg_options, XML_ATTR_TIMEOUT, "0"); if(strcmp(CRM_OP_QUERY, cib_action) == 0) { crm_debug("Querying the CIB"); obj_type_parent = pluralSection(obj_type); crm_verbose("Querying the CIB for section: %s", obj_type_parent); set_xml_property_copy(msg_options, XML_ATTR_OP, CRM_OP_QUERY); if(obj_type_parent != NULL) { set_xml_property_copy( msg_options, XML_ATTR_FILTER_TYPE, obj_type_parent); } if(id != NULL) { set_xml_property_copy( msg_options, XML_ATTR_FILTER_ID, id); } dest_node = status; crm_verbose("CIB query creation %s", msg_data == NULL ? "failed." : "passed."); sys_to = CRM_SYSTEM_DCIB; } else if (strcmp(CRM_OP_ERASE, cib_action) == 0) { set_xml_property_copy(msg_options, XML_ATTR_OP, CRM_OP_ERASE); dest_node = status; crm_trace("CIB Erase op in progress"); sys_to = CRM_SYSTEM_DCIB; } else if(cib_action != NULL) { msg_data = handleCibMod(xml_text); sys_to = CRM_SYSTEM_DCIB; if(msg_data == NULL) all_is_good = FALSE; } else { crm_err("Unknown options"); all_is_good = FALSE; } if(all_is_good == FALSE) { crm_err("Creation of request failed. No message to send"); return -1; } /* send it */ if (crmd_channel == NULL) { crm_err("The IPC connection is not valid, cannot send anything"); return -1; } if(sys_to == NULL) { if (dest_node != NULL) sys_to = CRM_SYSTEM_CRMD; else sys_to = CRM_SYSTEM_DC; } send_ipc_request( crmd_channel, msg_options, msg_data, dest_node, sys_to, crm_system_name, admin_uuid, this_msg_reference); return 1; } ll_cluster_t * do_init(void) { int facility; ll_cluster_t *hb_cluster = NULL; /* docs say only do this once, but in their code they do it every time! */ xmlInitParser (); /* change the logging facility to the one used by heartbeat daemon */ hb_cluster = ll_cluster_new("heartbeat"); crm_info("Switching to Heartbeat logger"); if (( facility = hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) { cl_log_set_facility(facility); } crm_malloc(admin_uuid, sizeof(char) * 11); if(admin_uuid != NULL) { snprintf(admin_uuid, 10, "%d", getpid()); admin_uuid[10] = '\0'; } crmd_channel = init_client_ipc_comms(CRM_SYSTEM_CRMD,admin_msg_callback,NULL); if(crmd_channel != NULL) { send_hello_message(crmd_channel, admin_uuid, crm_system_name, "0", "1"); return hb_cluster; } return NULL; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { int lpc = 0; IPC_Message *msg = NULL; gboolean hack_return_good = TRUE; static int received_responses = 0; char *filename; int filename_len = 0; const char *result = NULL; xmlNodePtr options = NULL; xmlNodePtr xml_root_node = NULL; char *buffer = NULL; g_source_remove(message_timer_id); while (server->ch_status != IPC_DISCONNECT && server->ops->is_message_pending(server) == TRUE) { if (server->ops->recv(server, &msg) != IPC_OK) { perror("Receive failure:"); return !hack_return_good; } if (msg == NULL) { crm_trace("No message this time"); continue; } lpc++; buffer =(char *) msg->msg_body; crm_verbose("Got xml [text=%s]", buffer); xml_root_node = find_xml_in_ipcmessage(msg, TRUE); if (xml_root_node == NULL) { crm_info( "XML in IPC message was not valid... " "discarding."); continue; } else if (validate_crm_message(xml_root_node, crm_system_name, admin_uuid, "response") == FALSE) { crm_info( "Message was not a CRM response. Discarding."); continue; } options = find_xml_node(xml_root_node, XML_TAG_OPTIONS); result = xmlGetProp(options, XML_ATTR_RESULT); if(result == NULL || strcmp(result, "ok") == 0) { result = "pass"; } else { result = "fail"; } received_responses++; crm_xml_devel(xml_root_node, cib_action); buffer = dump_xml_formatted(xml_root_node); fprintf(stderr, "%s", crm_str(buffer)); crm_free(buffer); if (this_msg_reference != NULL) { /* in testing mode... */ /* 31 = "test-_.xml" + an_int_as_string + '\0' */ filename_len = 31 + strlen(this_msg_reference); crm_malloc(filename, sizeof(char) * filename_len); if(filename != NULL) { sprintf(filename, "%s-%s_%d.xml", result, this_msg_reference, received_responses); filename[filename_len - 1] = '\0'; if (xmlSaveFormatFile( filename, xml_root_node->doc, 1) < 0) { crm_crit("Could not save response to" " %s_%s_%d.xml", this_msg_reference, result, received_responses); } } } } if (server->ch_status == IPC_DISCONNECT) { crm_info("admin_msg_callback: received HUP"); return !hack_return_good; } if (received_responses >= expected_responses) { crm_info( "Recieved expected number (%d) of messages from Heartbeat." " Exiting normally.", expected_responses); g_main_quit(mainloop); return !hack_return_good; } message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); return hack_return_good; } void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-?Vio] command\n" "\twhere necessary, XML data will be expected using -X" " or on STDIN if -X isnt specified\n", cmd); fprintf(stream, "Options\n"); fprintf(stream, "\t--%s (-%c) \tid of the object being operated on\n", XML_ATTR_ID, 'i'); fprintf(stream, "\t--%s (-%c) \tobject type being operated on\n", "obj_type", 'o'); fprintf(stream, "\t--%s (-%c)\tturn on debug info." " additional instance increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help", '?'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_ERASE, 'E'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_QUERY, 'Q'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CREATE, 'C'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_REPLACE, 'R'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_UPDATE, 'U'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_DELETE, 'D'); fprintf(stream, "\nXML data\n"); fprintf(stream, "\t--%s (-%c) \t\n", "xml", 'X'); fflush(stream); exit(exit_status); } gboolean admin_message_timeout(gpointer data) { fprintf(stderr, "No messages received in %d seconds.. aborting\n", (int)message_timeout_ms/1000); crm_err("No messages received in %d seconds", (int)message_timeout_ms/1000); g_main_quit(mainloop); return FALSE; } diff --git a/crm/admin/crmadmin.c b/crm/admin/crmadmin.c index 8223572658..df5834f0b2 100644 --- a/crm/admin/crmadmin.c +++ b/crm/admin/crmadmin.c @@ -1,884 +1,884 @@ -/* $Id: crmadmin.c,v 1.12 2004/10/21 18:25:42 andrew Exp $ */ +/* $Id: crmadmin.c,v 1.13 2004/10/24 13:00:11 lge Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int message_timer_id = -1; int message_timeout_ms = 30*1000; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; char *admin_uuid = NULL; void usage(const char *cmd, int exit_status); ll_cluster_t *do_init(void); int do_work(ll_cluster_t * hb_cluster); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); char *pluralSection(const char *a_section); xmlNodePtr handleCibMod(void); int do_find_resource(const char *rsc, xmlNodePtr xml_node); int do_find_resource_list(xmlNodePtr xml_node); int do_find_node_list(xmlNodePtr xml_node); gboolean admin_message_timeout(gpointer data); gboolean is_node_online(xmlNodePtr node_state); enum debug { debug_none, debug_dec, debug_inc }; gboolean BE_VERBOSE = FALSE; int expected_responses = 1; gboolean DO_HEALTH = FALSE; gboolean DO_RESET = FALSE; gboolean DO_RESOURCE = FALSE; gboolean DO_ELECT_DC = FALSE; gboolean DO_WHOIS_DC = FALSE; gboolean DO_NODE_LIST = FALSE; gboolean BE_SILENT = FALSE; gboolean DO_RESOURCE_LIST = FALSE; gboolean DO_OPTION = FALSE; enum debug DO_DEBUG = debug_none; xmlNodePtr msg_options = NULL; const char *admin_verbose = XML_BOOLEAN_FALSE; char *id = NULL; char *this_msg_reference = NULL; char *disconnect = NULL; char *dest_node = NULL; char *rsc_name = NULL; char *crm_option = NULL; int operation_status = 0; -const char *sys_to = NULL;; +const char *sys_to = NULL; const char *crm_system_name = "crmadmin"; #define OPTARGS "V?K:S:HE:DW:d:i:RNst:o:" int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; ll_cluster_t *hb_cluster = NULL; int level = 0; static struct option long_options[] = { /* Top-level Options */ {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"silent", 0, 0, 's'}, {"reference", 1, 0, 0}, {"timeout", 1, 0, 't'}, /* daemon options */ {"kill", 1, 0, 'K'}, /* stop a node */ {"crm_debug_inc", 1, 0, 'i'}, {"crm_debug_dec", 1, 0, 'd'}, {"status", 1, 0, 'S'}, {"health", 0, 0, 'H'}, {"election", 0, 0, 'E'}, {"dc_lookup", 0, 0, 'D'}, {"resources", 0, 0, 'R'}, {"nodes", 0, 0, 'N'}, {"whereis", 1, 0, 'W'}, {"option", 1, 0, 'o'}, {0, 0, 0, 0} }; /* Redirect messages from glib functions to our handler */ g_log_set_handler(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, cl_glib_msg_handler, NULL); /* and for good measure... */ g_log_set_always_fatal((GLogLevelFlags)0); crm_system_name = basename(argv[0]); cl_log_set_entity(crm_system_name); cl_log_set_facility(LOG_USER); if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } while (1) { flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); if (flag == -1) break; switch(flag) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); if (strcmp("reference", long_options[option_index].name) == 0) { this_msg_reference = crm_strdup(optarg); } else { printf( "?? Long option (--%s) is not yet properly supported ??\n", long_options[option_index].name); ++argerr; } break; /* a sample test for multiple instance if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); */ case 'V': level = get_crm_log_level(); BE_VERBOSE = TRUE; admin_verbose = XML_BOOLEAN_TRUE; cl_log_enable_stderr(TRUE); set_crm_log_level(level+1); break; case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30*1000; } break; case '?': usage(crm_system_name, LSB_EXIT_OK); break; case 'D': DO_WHOIS_DC = TRUE; break; case 'W': DO_RESOURCE = TRUE; crm_verbose("Option %c => %s", flag, optarg); rsc_name = crm_strdup(optarg); break; case 'K': DO_RESET = TRUE; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'o': DO_OPTION = TRUE; crm_verbose("Option %c => %s", flag, optarg); crm_option = crm_strdup(optarg); break; case 's': BE_SILENT = TRUE; break; case 'i': DO_DEBUG = debug_inc; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'd': DO_DEBUG = debug_dec; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'S': DO_HEALTH = TRUE; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'E': DO_ELECT_DC = TRUE; break; case 'N': DO_NODE_LIST = TRUE; break; case 'R': DO_RESOURCE_LIST = TRUE; break; case 'H': DO_HEALTH = TRUE; break; default: printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (optind > argc) { ++argerr; } if (argerr) { usage(crm_system_name, LSB_EXIT_GENERIC); } hb_cluster = do_init(); if (hb_cluster != NULL) { int res = do_work(hb_cluster); if (res > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); crm_verbose("%s waiting for reply from the local CRM", crm_system_name); message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); g_main_run(mainloop); return_to_orig_privs(); } else if(res == 0) { crm_verbose("%s: no reply expected", crm_system_name); } else { crm_err("No message to send"); operation_status = -1; } } else { crm_err("Init failed, could not perform requested operations"); operation_status = -2; } crm_verbose("%s exiting normally", crm_system_name); return operation_status; } int do_work(ll_cluster_t * hb_cluster) { int ret = 1; /* construct the request */ xmlNodePtr msg_data = NULL; gboolean all_is_good = TRUE; msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); set_xml_property_copy(msg_options, XML_ATTR_VERBOSE, admin_verbose); set_xml_property_copy(msg_options, XML_ATTR_TIMEOUT, "0"); if (DO_HEALTH == TRUE) { crm_verbose("Querying the system"); sys_to = CRM_SYSTEM_DC; if (dest_node != NULL) { sys_to = CRM_SYSTEM_CRMD; if (BE_VERBOSE) { expected_responses = -1;/* wait until timeout instead */ } set_xml_property_copy( msg_options, XML_ATTR_OP, CRM_OP_PING); set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); } else { crm_info("Cluster-wide health not available yet"); all_is_good = FALSE; } } else if(DO_OPTION) { char *name = NULL; char *value = NULL; xmlNodePtr xml_option = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); sys_to = CRM_SYSTEM_DCIB; set_xml_property_copy( msg_options, XML_ATTR_OP, CRM_OP_UPDATE); set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); if(decodeNVpair(crm_option, '=', &name, &value) == FALSE) { crm_err("%s needs to be of the form =", crm_option); all_is_good = FALSE; } else { set_xml_property_copy( xml_option, XML_NVPAIR_ATTR_NAME, name); set_xml_property_copy( xml_option, XML_NVPAIR_ATTR_VALUE, value); msg_data = create_cib_fragment(xml_option, NULL); free_xml(xml_option); crm_free(name); crm_free(value); } } else if(DO_ELECT_DC) { /* tell the local node to initiate an election */ sys_to = CRM_SYSTEM_CRMD; set_xml_property_copy( msg_options, XML_ATTR_OP, CRM_OP_VOTE); set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; ret = 0; /* no return message */ } else if(DO_WHOIS_DC) { sys_to = CRM_SYSTEM_DC; set_xml_property_copy( msg_options, XML_ATTR_OP, CRM_OP_PING); set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; } else if(DO_RESOURCE) { set_xml_property_copy(msg_options, XML_ATTR_OP, CRM_OP_QUERY); set_xml_property_copy( msg_options, XML_ATTR_FILTER_TYPE, XML_CIB_TAG_STATUS); sys_to = CRM_SYSTEM_CIB; } else if(DO_RESOURCE_LIST) { set_xml_property_copy(msg_options, XML_ATTR_OP, CRM_OP_QUERY); set_xml_property_copy( msg_options, XML_ATTR_FILTER_TYPE, XML_CIB_TAG_RESOURCES); sys_to = CRM_SYSTEM_CIB; } else if(DO_NODE_LIST) { set_xml_property_copy(msg_options, XML_ATTR_OP, CRM_OP_QUERY); set_xml_property_copy( msg_options, XML_ATTR_FILTER_TYPE, XML_CIB_TAG_NODES); sys_to = CRM_SYSTEM_CIB; } else if(DO_RESET) { /* tell dest_node to initiate the shutdown proceedure * * if dest_node is NULL, the request will be sent to the * local node */ sys_to = CRM_SYSTEM_CRMD; set_xml_property_copy( msg_options, XML_ATTR_OP, "init_shutdown"); set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); ret = 0; /* no return message */ } else if(DO_DEBUG == debug_inc) { /* tell dest_node to increase its debug level * * if dest_node is NULL, the request will be sent to the * local node */ sys_to = CRM_SYSTEM_CRMD; set_xml_property_copy(msg_options, XML_ATTR_OP, "debug_inc"); set_xml_property_copy(msg_options, XML_ATTR_TIMEOUT, "0"); ret = 0; /* no return message */ } else if(DO_DEBUG == debug_dec) { /* tell dest_node to increase its debug level * * if dest_node is NULL, the request will be sent to the * local node */ sys_to = CRM_SYSTEM_CRMD; set_xml_property_copy(msg_options, XML_ATTR_OP, "debug_dec"); set_xml_property_copy(msg_options, XML_ATTR_TIMEOUT, "0"); ret = 0; /* no return message */ } else { crm_err("Unknown options"); all_is_good = FALSE; } if(all_is_good == FALSE) { crm_err("Creation of request failed. No message to send"); return -1; } /* send it */ if (crmd_channel == NULL) { crm_err("The IPC connection is not valid, cannot send anything"); return -1; } if(sys_to == NULL) { if (dest_node != NULL) sys_to = CRM_SYSTEM_CRMD; else sys_to = CRM_SYSTEM_DC; } send_ipc_request( crmd_channel, msg_options, msg_data, dest_node, sys_to, crm_system_name, admin_uuid, this_msg_reference); return ret; } ll_cluster_t * do_init(void) { int facility; ll_cluster_t *hb_cluster = NULL; /* docs say only do this once, but in their code they do it every time! */ xmlInitParser (); /* change the logging facility to the one used by heartbeat daemon */ hb_cluster = ll_cluster_new("heartbeat"); crm_verbose("Switching to Heartbeat logger"); if (( facility = hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) { cl_log_set_facility(facility); } crm_malloc(admin_uuid, sizeof(char) * 11); if(admin_uuid != NULL) { snprintf(admin_uuid, 10, "%d", getpid()); admin_uuid[10] = '\0'; } crmd_channel = init_client_ipc_comms( CRM_SYSTEM_CRMD, admin_msg_callback, NULL); if(crmd_channel != NULL) { send_hello_message( crmd_channel, admin_uuid, crm_system_name,"0", "1"); return hb_cluster; } return NULL; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { int lpc = 0; IPC_Message *msg = NULL; gboolean hack_return_good = TRUE; static int received_responses = 0; char *filename; int filename_len = 0; const char *result = NULL; xmlNodePtr options = NULL; xmlNodePtr xml_root_node = NULL; char *buffer = NULL; g_source_remove(message_timer_id); while (server->ch_status != IPC_DISCONNECT && server->ops->is_message_pending(server) == TRUE) { if (server->ops->recv(server, &msg) != IPC_OK) { perror("Receive failure:"); return !hack_return_good; } if (msg == NULL) { crm_trace("No message this time"); continue; } lpc++; buffer =(char *) msg->msg_body; crm_verbose("Got xml [text=%s]", buffer); xml_root_node = find_xml_in_ipcmessage(msg, TRUE); if (xml_root_node == NULL) { crm_info( "XML in IPC message was not valid... " "discarding."); continue; } else if (validate_crm_message(xml_root_node, crm_system_name, admin_uuid, "response") == FALSE) { crm_info( "Message was not a CRM response. Discarding."); continue; } options = find_xml_node(xml_root_node, XML_TAG_OPTIONS); result = xmlGetProp(options, XML_ATTR_RESULT); if(result == NULL || strcmp(result, "ok") == 0) { result = "pass"; } else { result = "fail"; } received_responses++; if(DO_HEALTH) { xmlNodePtr ping = find_xml_node( xml_root_node, XML_CRM_TAG_PING); const char *state = xmlGetProp(ping, "crmd_state"); printf("Status of %s@%s: %s (%s)\n", xmlGetProp(ping, XML_PING_ATTR_SYSFROM), xmlGetProp(xml_root_node, XML_ATTR_HOSTFROM), state, xmlGetProp(ping, XML_PING_ATTR_STATUS)); if(BE_SILENT && state != NULL) { fprintf(stderr, "%s\n", state); } } else if(DO_RESOURCE) { do_find_resource(rsc_name, xml_root_node); } else if(DO_RESOURCE_LIST) { do_find_resource_list(xml_root_node); } else if(DO_NODE_LIST) { do_find_node_list(xml_root_node); } else if(DO_WHOIS_DC) { const char *dc = xmlGetProp( xml_root_node, XML_ATTR_HOSTFROM); printf("Designated Controller is: %s\n", dc); if(BE_SILENT && dc != NULL) { fprintf(stderr, "%s\n", dc); } } if (this_msg_reference != NULL) { /* in testing mode... */ /* 31 = "test-_.xml" + an_int_as_string + '\0' */ filename_len = 31 + strlen(this_msg_reference); crm_malloc(filename, sizeof(char) * filename_len); if(filename != NULL) { sprintf(filename, "%s-%s_%d.xml", result, this_msg_reference, received_responses); filename[filename_len - 1] = '\0'; if (xmlSaveFormatFile( filename, xml_root_node->doc, 1) < 0) { crm_crit("Could not save response to" " %s_%s_%d.xml", this_msg_reference, result, received_responses); } } } } if (server->ch_status == IPC_DISCONNECT) { crm_verbose("admin_msg_callback: received HUP"); return !hack_return_good; } if (received_responses >= expected_responses) { crm_verbose( "Recieved expected number (%d) of messages from Heartbeat." " Exiting normally.", expected_responses); g_main_quit(mainloop); return !hack_return_good; } message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); return hack_return_good; } gboolean admin_message_timeout(gpointer data) { fprintf(stderr, "No messages received in %d seconds.. aborting\n", (int)message_timeout_ms/1000); crm_err("No messages received in %d seconds", (int)message_timeout_ms/1000); g_main_quit(mainloop); return FALSE; } int do_find_resource(const char *rsc, xmlNodePtr xml_node) { int found = 0; const char *path[] = { XML_TAG_FRAGMENT, XML_TAG_CIB, XML_CIB_TAG_STATUS, XML_CIB_TAG_STATE }; const char *path2[] = { XML_CIB_TAG_LRM, XML_LRM_TAG_RESOURCES, XML_LRM_TAG_RESOURCE }; xmlNodePtr nodestates = find_xml_node_nested( xml_node, path, DIMOF(path)); while(nodestates != NULL) { xmlNodePtr rscstates = NULL; xmlNodePtr a_node = nodestates; nodestates = nodestates->next; if(is_node_online(a_node) == FALSE) { crm_debug("Skipping offline node: %s", xmlGetProp(a_node, XML_ATTR_ID)); continue; } rscstates = find_xml_node_nested(a_node, path2, DIMOF(path2)); while(rscstates != NULL) { const char *id = xmlGetProp(rscstates,XML_ATTR_ID); const char *target = xmlGetProp(rscstates,XML_LRM_ATTR_TARGET); const char *last_op = xmlGetProp(rscstates,XML_LRM_ATTR_LASTOP); const char *op_code = xmlGetProp(rscstates,XML_LRM_ATTR_OPSTATUS); rscstates = rscstates->next; crm_debug("checking %s:%s for %s", target, id, rsc); if(safe_str_neq(rsc, id)){ crm_trace("no match"); continue; } if(safe_str_eq("stop", last_op)) { crm_debug("resource %s is stopped on: %s\n", rsc, target); } else if(safe_str_neq(op_code, "0")) { crm_debug("resource %s is failed on: %s\n", rsc, target); } else { crm_debug("resource %s is running on: %s\n", rsc, target); printf("resource %s is running on: %s\n", rsc, target); if(BE_SILENT) { fprintf(stderr, "%s ", target); } found++; } } if(BE_SILENT) { fprintf(stderr, "\n"); } } if(found == 0) { printf("resource %s is NOT running\n", rsc); } return found; } gboolean is_node_online(xmlNodePtr node_state) { const char *uname = xmlGetProp(node_state,XML_ATTR_UNAME); const char *join_state = xmlGetProp(node_state,XML_CIB_ATTR_JOINSTATE); const char *crm_state = xmlGetProp(node_state,XML_CIB_ATTR_CRMDSTATE); const char *ha_state = xmlGetProp(node_state,XML_CIB_ATTR_HASTATE); const char *ccm_state = xmlGetProp(node_state,XML_CIB_ATTR_INCCM); if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER) && safe_str_eq(ha_state, ACTIVESTATUS) && safe_str_eq(ccm_state, XML_BOOLEAN_YES) && safe_str_eq(crm_state, ONLINESTATUS)) { crm_debug("Node %s is online", uname); return TRUE; } crm_debug("Node %s: %s %s %s", uname, join_state, ccm_state, crm_state); crm_debug("Node %s is offline", uname); return FALSE; } int do_find_resource_list(xmlNodePtr xml_node) { int found = 0; const char *path[] = { XML_TAG_FRAGMENT, XML_TAG_CIB, XML_CIB_TAG_RESOURCES, XML_CIB_TAG_RESOURCE }; xmlNodePtr rscs = find_xml_node_nested( xml_node, path, DIMOF(path)); while(rscs != NULL) { printf("%s resource: %s (%s)\n", xmlGetProp(rscs, "class"), xmlGetProp(rscs, XML_ATTR_ID), xmlGetProp(rscs, XML_ATTR_TYPE)); rscs = rscs->next; found++; } if(found == 0) { printf("NO resources configured\n"); } return found; } int do_find_node_list(xmlNodePtr xml_node) { int found = 0; const char *path[] = { XML_TAG_FRAGMENT, XML_TAG_CIB, XML_CIB_TAG_NODES, XML_CIB_TAG_NODE }; xmlNodePtr nodes = find_xml_node_nested( xml_node, path, DIMOF(path)); while(nodes != NULL) { printf("%s node: %s (%s)\n", xmlGetProp(nodes, XML_ATTR_TYPE), xmlGetProp(nodes, XML_ATTR_UNAME), xmlGetProp(nodes, XML_ATTR_ID)); nodes = nodes->next; found++; } if(found == 0) { printf("NO nodes configured\n"); } return found; } void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-?vs] [command] [command args]\n", cmd); fprintf(stream, "Options\n"); fprintf(stream, "\t--%s (-%c)\t: " "turn on debug info. additional instances increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\t: be very very quiet\n", "silent", 's'); fprintf(stream, "\t--%s (-%c)\t: this help message\n", "help", '?'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c) \t: " "increment the CRMd debug level on \n", "debug_inc",'i'); fprintf(stream, "\t--%s (-%c) \t: " "decrement the CRMd debug level on \n", "debug_dec",'d'); fprintf(stream, "\t--%s (-%c) \t: " "shutdown the CRMd on \n", "kill", 'K'); fprintf(stream, "\t--%s (-%c) \t: " "request the status of \n", "status", 'S'); fprintf(stream, "\t--%s (-%c)\t\t: " "request the status of all nodes\n", "health", 'H'); fprintf(stream, "\t--%s (-%c) \t: " "initiate an election from \n", "election", 'E'); fprintf(stream, "\t--%s (-%c)\t: " "request the uname of the DC\n", "dc_lookup", 'D'); fprintf(stream, "\t--%s (-%c)\t\t: " "request the uname of all member nodes\n", "nodes", 'N'); fprintf(stream, "\t--%s (-%c)\t: " "request the names of all resources\n", "resources", 'R'); fprintf(stream, "\t--%s (-%c) \t: " "request the location of \n", "whereis", 'W'); /* fprintf(stream, "\t--%s (-%c)\t\n", "disconnect", 'D'); */ fflush(stream); exit(exit_status); } diff --git a/crm/crmd/fsa_defines.h b/crm/crmd/fsa_defines.h index 5269292a2e..8ed4e71879 100644 --- a/crm/crmd/fsa_defines.h +++ b/crm/crmd/fsa_defines.h @@ -1,468 +1,468 @@ -/* $Id: fsa_defines.h,v 1.26 2004/10/23 11:58:42 andrew Exp $ */ +/* $Id: fsa_defines.h,v 1.27 2004/10/24 13:00:11 lge Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef FSA_DEFINES__H #define FSA_DEFINES__H /*====================================== * States the DC/CRMd can be in *======================================*/ enum crmd_fsa_state { S_IDLE = 0, /* Nothing happening */ S_ELECTION, /* Take part in the election algorithm as * described below */ S_INTEGRATION, /* integrate that status of new nodes (which is * all of them if we have just been elected DC) * to form a complete and up-to-date picture of * the CIB */ S_FINALIZE_JOIN,/* integrate that status of new nodes (which is * all of them if we have just been elected DC) * to form a complete and up-to-date picture of * the CIB */ S_NOT_DC, /* we are in crmd/slave mode */ S_POLICY_ENGINE,/* Determin the next stable state of the cluster */ S_RECOVERY, /* Something bad happened, check everything is ok * before continuing and attempt to recover if * required */ S_RELEASE_DC, /* we were the DC, but now we arent anymore, * possibly by our own request, and we should * release all unnecessary sub-systems, finish * any pending actions, do general cleanup and * unset anything that makes us think we are * special :) */ S_STARTING, /* we are just starting out */ S_PENDING, /* we are not a full/active member yet */ S_STOPPING, /* We are in the final stages of shutting down */ S_TERMINATE, /* We are going to shutdown, this is the equiv of * "Sending TERM signal to all processes" in Linux * and in worst case scenarios could be considered * a self STONITH */ S_TRANSITION_ENGINE,/* Attempt to make the calculated next stable * state of the cluster a reality */ /* ----------- Last input found in table is above ---------- */ - S_ILLEGAL, /* This is an illegal FSA state */ + S_ILLEGAL /* This is an illegal FSA state */ /* (must be last) */ }; #define MAXSTATE S_ILLEGAL /* A state diagram can be constructed from the dc_fsa.dot with the following command: dot -Tpng crmd_fsa.dot > crmd_fsa.png Description: Once we start and do some basic sanity checks, we go into the S_NOT_DC state and await instructions from the DC or input from the CCM which indicates the election algorithm needs to run. If the election algorithm is triggered we enter the S_ELECTION state from where we can either go back to the S_NOT_DC state or progress to the S_INTEGRATION state (or S_RELEASE_DC if we used to be the DC but arent anymore). The election algorithm has been adapted from http://www.cs.indiana.edu/cgi-bin/techreports/TRNNN.cgi?trnum=TR521 Loosly known as the Bully Algorithm, its major points are: - Election is initiated by any node (N) notices that the coordinator is no longer responding - Concurrent multiple elections are possible - Algorithm + N sends ELECTION messages to all nodes that occur earlier in the CCM's membership list. + If no one responds, N wins and becomes coordinator + N sends out COORDINATOR messages to all other nodes in the partition + If one of higher-ups answers, it takes over. N is done. Once the election is complete, if we are the DC, we enter the S_INTEGRATION state which is a DC-in-waiting style state. We are the DC, but we shouldnt do anything yet because we may not have an up-to-date picture of the cluster. There may of course be times when this fails, so we should go back to the S_RECOVERY stage and check everything is ok. We may also end up here if a new node came online, since each node is authorative on itself and we would want to incorporate its information into the CIB. Once we have the latest CIB, we then enter the S_POLICY_ENGINE state where invoke the Policy Engine. It is possible that between invoking the Policy Engine and recieving an answer, that we recieve more input. In this case we would discard the orginal result and invoke it again. Once we are satisfied with the output from the Policy Engine we enter S_TRANSITION_ENGINE and feed the Policy Engine's output to the Transition Engine who attempts to make the Policy Engine's calculation a reality. If the transition completes successfully, we enter S_IDLE, otherwise we go back to S_POLICY_ENGINE with the current unstable state and try again. Of course we may be asked to shutdown at any time, however we must progress to S_NOT_DC before doing so. Once we have handed over DC duties to another node, we can then shut down like everyone else, that is by asking the DC for permission and waiting it to take all our resources away. The case where we are the DC and the only node in the cluster is a special case and handled as an escalation which takes us to S_SHUTDOWN. Similarly if any other point in the shutdown fails or stalls, this is escalated and we end up in S_TERMINATE. At any point, the CRMd/DC can relay messages for its sub-systems, but outbound messages (from sub-systems) should probably be blocked until S_INTEGRATION (for the DC case) or the join protocol has completed (for the CRMd case) */ /*====================================== * * Inputs/Events/Stimuli to be given to the finite state machine * * Some of these a true events, and others a synthesised based on * the "register" (see below) and the contents or source of messages. * * At this point, my plan is to have a loop of some sort that keeps * going until recieving I_NULL * *======================================*/ enum crmd_fsa_input { /* 0 */ I_NULL, /* Nothing happened */ /* 1 */ I_CCM_EVENT, I_CIB_OP, /* An update to the CIB occurred */ I_CIB_UPDATE, /* An update to the CIB occurred */ I_DC_TIMEOUT, /* We have lost communication with the DC */ I_ELECTION, /* Someone started an election */ I_PE_CALC, /* The Policy Engine needs to be invoked */ I_RELEASE_DC, /* The election completed and we were not * elected, but we were the DC beforehand */ I_ELECTION_DC, /* The election completed and we were (re-)elected * DC */ I_ERROR, /* Something bad happened (more serious than * I_FAIL) and may not have been due to the action * being performed. For example, we may have lost * our connection to the CIB. */ /* 10 */ I_FAIL, /* The action failed to complete successfully */ I_INTEGRATED, I_FINALIZED, I_NODE_JOIN, /* A node has entered the cluster */ I_NOT_DC, /* We are not and were not the DC before or after * the current operation or state */ I_RECOVERED, /* The recovery process completed successfully */ I_RELEASE_FAIL, /* We could not give up DC status for some reason */ I_RELEASE_SUCCESS, /* We are no longer the DC */ I_RESTART, /* The current set of actions needs to be * restarted */ I_TE_SUCCESS, /* Some non-resource, non-ccm action is required * of us, eg. ping */ /* 20 */ I_ROUTER, /* Do our job as router and forward this to the * right place */ I_SHUTDOWN, /* We are asking to shutdown */ I_TERMINATE, /* We have been told to shutdown */ I_STARTUP, I_PE_SUCCESS, /* The action completed successfully */ I_JOIN_OFFER, /* The DC is offering membership */ I_JOIN_REQUEST, /* The client is requesting membership */ I_JOIN_RESULT, /* If not the DC: The result of a join request * Else: A client is responding with its local state info */ I_WAIT_FOR_EVENT, /* we may be waiting for an async task to "happen" * and until it does, we cant do anything else */ I_DC_HEARTBEAT, /* The DC is telling us that it is alive and well */ I_LRM_EVENT, /* 30 */ I_PENDING, /* ------------ Last input found in table is above ----------- */ - I_ILLEGAL, /* This is an illegal value for an FSA input */ + I_ILLEGAL /* This is an illegal value for an FSA input */ /* (must be last) */ }; #define MAXINPUT I_ILLEGAL #define I_MESSAGE I_ROUTER /*====================================== * * actions * * Some of the actions below will always occur together for now, but I can * forsee that this may not always be the case. So I've spilt them up so * that if they ever do need to be called independantly in the future, it * wont be a problem. * * For example, separating A_LRM_CONNECT from A_STARTUP might be useful * if we ever try to recover from a faulty or disconnected LRM. * *======================================*/ /* Dont do anything */ #define A_NOTHING 0x0000000000000000ULL /* -- Startup actions -- */ /* Hook to perform any actions (other than starting the CIB, * connecting to HA or the CCM) that might be needed as part * of the startup. */ #define A_STARTUP 0x0000000000000001ULL /* Hook to perform any actions that might be needed as part * after startup is successful. */ #define A_STARTED 0x0000000000000002ULL /* Connect to Heartbeat */ #define A_HA_CONNECT 0x0000000000000004ULL #define A_HA_DISCONNECT 0x0000000000000008ULL /* -- Election actions -- */ #define A_DC_TIMER_START 0x0000000000000010ULL #define A_DC_TIMER_STOP 0x0000000000000020ULL #define A_ELECTION_COUNT 0x0000000000000040ULL #define A_ELECTION_VOTE 0x0000000000000080ULL #define A_INTEGRATE_TIMER_START 0x0000000000000100ULL #define A_INTEGRATE_TIMER_STOP 0x0000000000000200ULL #define A_FINALIZE_TIMER_START 0x0000000000000400ULL #define A_FINALIZE_TIMER_STOP 0x0000000000000800ULL /* -- Message processing -- */ /* Process the queue of requests */ #define A_MSG_PROCESS 0x0000000000001000ULL /* Send the message to the correct recipient */ #define A_MSG_ROUTE 0x0000000000002000ULL /* Put the request into a queue for processing. We do this every * time so that the processing is consistent. The intent is to * allow the DC to keep doing important work while still not * loosing requests. * Messages are not considered recieved until processed. */ #define A_MSG_STORE 0x0000000000004000ULL /* -- Client Join protocol actions -- */ #define A_CL_JOIN_ANNOUNCE 0x0000000000010000ULL /* Request membership to the DC list */ #define A_CL_JOIN_REQUEST 0x0000000000020000ULL /* Did the DC accept or reject the request */ #define A_CL_JOIN_RESULT 0x0000000000040000ULL /* -- Server Join protocol actions -- */ /* Send a welcome message to new node(s) */ #define A_DC_JOIN_OFFER_ONE 0x0000000000080000ULL /* Send a welcome message to all nodes */ #define A_DC_JOIN_OFFER_ALL 0x0000000000100000ULL /* Process the remote node's ack of our join message */ #define A_DC_JOIN_PROCESS_REQ 0x0000000000200000ULL /* Send out the reults of the Join phase */ #define A_DC_JOIN_FINALIZE 0x0000000000400000ULL /* Send out the reults of the Join phase */ #define A_DC_JOIN_PROCESS_ACK 0x0000000000800000ULL /* -- Recovery, DC start/stop -- */ /* Something bad happened, try to recover */ #define A_RECOVER 0x0000000001000000ULL /* Hook to perform any actions (apart from starting, the TE, PE * and gathering the latest CIB) that might be necessary before * giving up the responsibilities of being the DC. */ #define A_DC_RELEASE 0x0000000002000000ULL /* */ #define A_DC_RELEASED 0x0000000004000000ULL /* Hook to perform any actions (apart from starting, the TE, PE * and gathering the latest CIB) that might be necessary before * taking over the responsibilities of being the DC. */ #define A_DC_TAKEOVER 0x0000000008000000ULL /* -- Shutdown actions -- */ #define A_SHUTDOWN 0x0000000010000000ULL #define A_STOP 0x0000000020000000ULL #define A_EXIT_0 0x0000000040000000ULL #define A_EXIT_1 0x0000000080000000ULL #define A_SHUTDOWN_REQ 0x0000000100000000ULL /* -- CCM actions -- */ #define A_CCM_CONNECT 0x0000001000000000ULL #define A_CCM_DISCONNECT 0x0000002000000000ULL /* Process whatever it is the CCM is trying to tell us. * This will generate inputs such as I_NODE_JOIN, * I_NODE_LEAVE, I_SHUTDOWN, I_DC_RELEASE, I_DC_TAKEOVER */ #define A_CCM_EVENT 0x0000004000000000ULL #define A_CCM_UPDATE_CACHE 0x0000008000000000ULL /* -- CBI actions -- */ #define A_CIB_INVOKE 0x0000010000000000ULL #define A_CIB_START 0x0000020000000000ULL #define A_CIB_STOP 0x0000040000000000ULL #define A_CIB_INVOKE_LOCAL 0x0000080000000000ULL /* -- Transition Engine actions -- */ /* Attempt to reach the newly calculated cluster state. This is * only called once per transition (except if it is asked to * stop the transition or start a new one). * Once given a cluster state to reach, the TE will determin * tasks that can be performed in parallel, execute them, wait * for replies and then determin the next set until the new * state is reached or no further tasks can be taken. */ #define A_TE_INVOKE 0x0000100000000000ULL #define A_TE_START 0x0000200000000000ULL #define A_TE_STOP 0x0000400000000000ULL #define A_TE_CANCEL 0x0000800000000000ULL #define A_TE_COPYTO 0x0001000000000000ULL /* -- Policy Engine actions -- */ /* Calculate the next state for the cluster. This is only * invoked once per needed calculation. */ #define A_PE_INVOKE 0x0002000000000000ULL #define A_PE_START 0x0004000000000000ULL #define A_PE_STOP 0x0008000000000000ULL /* -- Misc actions -- */ /* Add a system generate "block" so that resources arent moved * to or are activly moved away from the affected node. This * way we can return quickly even if busy with other things. */ #define A_NODE_BLOCK 0x0010000000000000ULL /* Update our information in the local CIB */ #define A_UPDATE_NODESTATUS 0x0020000000000000ULL #define A_CIB_BUMPGEN 0x0040000000000000ULL #define A_READCONFIG 0x0080000000000000ULL /* -- LRM Actions -- */ /* Connect to the Local Resource Manager */ #define A_LRM_CONNECT 0x0100000000000000ULL /* Disconnect from the Local Resource Manager */ #define A_LRM_DISCONNECT 0x0200000000000000ULL #define A_LRM_INVOKE 0x0400000000000000ULL #define A_LRM_EVENT 0x0800000000000000ULL /* -- Logging actions -- */ #define A_LOG 0x1000000000000000ULL #define A_ERROR 0x2000000000000000ULL #define A_WARN 0x4000000000000000ULL #define O_SHUTDOWN (A_CCM_DISCONNECT|A_LRM_DISCONNECT|A_HA_DISCONNECT|A_SHUTDOWN|A_STOP|A_EXIT_0|A_CIB_STOP) #define O_RELEASE (A_DC_TIMER_STOP|A_DC_RELEASE|A_PE_STOP|A_TE_STOP|A_DC_RELEASED) #define O_DC_TIMER_RESTART (A_DC_TIMER_STOP|A_DC_TIMER_START) #define O_PE_RESTART (A_PE_START|A_PE_STOP) #define O_TE_RESTART (A_TE_START|A_TE_STOP) #define O_CIB_RESTART (A_CIB_START|A_CIB_STOP) #define O_DC_TICKLE O_DC_TIMER_RESTART /*====================================== * * "register" contents * * Things we may want to remember regardless of which state we are in. * * These also count as inputs for synthesizing I_* * *======================================*/ #define R_THE_DC 0x00000001 /* Are we the DC? */ #define R_STARTING 0x00000002 /* Are we starting up? */ #define R_SHUTDOWN 0x00000004 /* Are we trying to shut down? */ #define R_JOIN_OK 0x00000010 /* Have we completed the join process */ #define R_HAVE_RES 0x00000040 /* Do we have any resources running locally */ #define R_INVOKE_PE 0x00000080 /* Does the PE needed to be invoked at the next appropriate point? */ #define R_CIB_CONNECTED 0x00000100 /* Is the CIB connected? */ #define R_PE_CONNECTED 0x00000200 /* Is the Policy Engine connected? */ #define R_TE_CONNECTED 0x00000400 /* Is the Transition Engine connected? */ #define R_LRM_CONNECTED 0x00000800 /* Is the Local Resource Manager connected? */ #define R_REQ_PEND 0x00001000 /* Are there Requests waiting for processing? */ #define R_PE_PEND 0x00002000 /* Has the PE been invoked and we're awaiting a reply? */ #define R_TE_PEND 0x00004000 /* Has the TE been invoked and we're awaiting completion? */ #define R_RESP_PEND 0x00008000 /* Do we have clients waiting on a response? if so perhaps we shouldnt stop yet */ #define R_CIB_DONE 0x00010000 /* Have we calculated the CIB? */ #define R_HAVE_CIB 0x00020000 /* Do we have an up-to-date CIB */ #define R_CIB_ASKED 0x00040000 /* Have we asked for an up-to-date CIB */ #define R_CCM_DATA 0x00100000 /* Have we got CCM data yet */ #define R_PEER_DATA 0x00200000 /* Have we got T_CL_STATUS data yet */ enum crmd_fsa_cause { C_UNKNOWN = 0, C_STARTUP, C_IPC_MESSAGE, C_HA_MESSAGE, C_CCM_CALLBACK, C_CRMD_STATUS_CALLBACK, C_LRM_OP_CALLBACK, C_LRM_MONITOR_CALLBACK, C_TIMER_POPPED, C_SHUTDOWN, C_HEARTBEAT_FAILED, C_SUBSYSTEM_CONNECT, C_HA_DISCONNECT, C_FSA_INTERNAL, C_ILLEGAL }; extern const char *fsa_input2string(enum crmd_fsa_input input); extern const char *fsa_state2string(enum crmd_fsa_state state); extern const char *fsa_cause2string(enum crmd_fsa_cause cause); extern const char *fsa_action2string(long long action); #endif diff --git a/crm/tengine/tengine.h b/crm/tengine/tengine.h index 3be379251a..203631d5ab 100644 --- a/crm/tengine/tengine.h +++ b/crm/tengine/tengine.h @@ -1,110 +1,109 @@ -/* $Id: tengine.h,v 1.9 2004/10/01 13:23:45 andrew Exp $ */ +/* $Id: tengine.h,v 1.10 2004/10/24 13:00:11 lge Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef TENGINE__H #define TENGINE__H #include extern FILE *msg_te_strm; extern IPC_Channel *crm_ch; extern GListPtr graph; extern GMainLoop* mainloop; extern gboolean in_transition; typedef enum { action_type_pseudo, action_type_rsc, - action_type_crm, - + action_type_crm } action_type_e; typedef struct synapse_s { int id; gboolean complete; gboolean confirmed; GListPtr actions; /* action_t* */ GListPtr inputs; /* action_t* */ } synapse_t; typedef struct te_timer_s te_timer_t; typedef struct action_s { int id; int timeout; te_timer_t *timer; action_type_e type; gboolean invoked; gboolean complete; gboolean can_fail; xmlNodePtr xml; } action_t; enum timer_reason { timeout_action, timeout_timeout, timeout_fuzz }; struct te_timer_s { int source_id; int timeout; enum timer_reason reason; action_t *action; }; /* tengine */ extern gboolean initialize_graph(void); extern gboolean process_graph_event(xmlNodePtr event); /* const char *event_node, const char *event_rsc, const char *rsc_state, * const char *event_action, const char *event_rc, const char *op_status); */ extern int match_graph_event(action_t *action, xmlNodePtr event); extern gboolean initiate_transition(void); /* utils */ extern void print_state(gboolean to_file); extern void send_success(const char *text); extern void send_abort(const char *text, xmlNodePtr msg); extern gboolean stop_te_timer(te_timer_t *timer); extern gboolean start_te_timer(te_timer_t *timer); extern gboolean do_update_cib(xmlNodePtr xml_action, int status); /* unpack */ extern gboolean unpack_graph(xmlNodePtr xml_graph); extern gboolean extract_event(xmlNodePtr msg); extern gboolean process_te_message(xmlNodePtr msg, IPC_Channel *sender); extern uint transition_timeout; extern uint transition_fuzz_timeout; extern uint default_transition_timeout; extern te_timer_t *transition_timer; extern te_timer_t *transition_fuzz_timer; #endif diff --git a/include/crm/cib.h b/include/crm/cib.h index 43e544e1ad..fac4554c51 100644 --- a/include/crm/cib.h +++ b/include/crm/cib.h @@ -1,76 +1,76 @@ -/* $Id: cib.h,v 1.6 2004/10/21 18:25:43 andrew Exp $ */ +/* $Id: cib.h,v 1.7 2004/10/24 13:00:12 lge Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB__H #define CIB__H #include enum cib_op { CIB_OP_NONE = 0, CIB_OP_ADD, CIB_OP_MODIFY, CIB_OP_DELETE, CIB_OP_MAX }; enum cib_result { CIBRES_OK = 0, CIBRES_MISSING_ID, CIBRES_MISSING_TYPE, CIBRES_MISSING_FIELD, CIBRES_OBJTYPE_MISMATCH, CIBRES_CORRUPT, CIBRES_OTHER, CIBRES_FAILED, CIBRES_FAILED_STALE, CIBRES_FAILED_EXISTS, CIBRES_FAILED_NOTEXISTS, CIBRES_FAILED_ACTIVATION, CIBRES_FAILED_NOSECTION, CIBRES_FAILED_NOOBJECT, CIBRES_FAILED_NOPARENT, CIBRES_FAILED_NODECOPY, - CIBRES_FAILED_NOTSUPPORTED, + CIBRES_FAILED_NOTSUPPORTED }; /* Core functions */ extern gboolean startCib(const char *filename); extern xmlNodePtr get_cib_copy(void); extern xmlNodePtr cib_get_generation(void); extern int compare_cib_generation(xmlNodePtr left, xmlNodePtr right); extern xmlNodePtr process_cib_message(xmlNodePtr message, gboolean auto_reply); extern xmlNodePtr process_cib_request(const char *op, const xmlNodePtr options, const xmlNodePtr fragment); /* Utility functions */ extern xmlNodePtr get_object_root(const char *object_type,xmlNodePtr the_root); extern xmlNodePtr create_cib_fragment_adv( xmlNodePtr update, const char *section, const char *source); extern char *pluralSection(const char *a_section); /* Error Interpretation*/ extern const char *cib_error2string(enum cib_result); extern const char *cib_op2string(enum cib_op); #define create_cib_fragment(update,section) create_cib_fragment_adv(update, section, __FUNCTION__) #endif diff --git a/lib/plugins/lrm/raexechb.c b/lib/plugins/lrm/raexechb.c index 017ce93c12..63ea78faa5 100644 --- a/lib/plugins/lrm/raexechb.c +++ b/lib/plugins/lrm/raexechb.c @@ -1,250 +1,250 @@ /* * 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: raexechb.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 #include #include #include #include #define PIL_PLUGINTYPE RA_EXEC_TYPE #define PIL_PLUGIN heartbeat #define PIL_PLUGINTYPE_S "RAExec" #define PIL_PLUGIN_S "heartbeat" #define PIL_PLUGINLICENSE LICENSE_PUBDOM #define PIL_PLUGINLICENSEURL URL_PUBDOM static const char * RA_PATH = HB_RA_DIR; static const int status_op_exitcode_map[] = { 0, 11, 12, 13, 14 }; /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type); 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 */ /* The begin of internal used function & data list */ #define MAX_PARAMETER_NUM 40 typedef char * RA_ARGV[MAX_PARAMETER_NUM]; const int MAX_LENGTH_OF_RSCNAME = 40, MAX_LENGTH_OF_OPNAME = 40; static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params, RA_ARGV params_argv); /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; -PIL_PLUGIN_BOILERPLATE2("1.0", Debug); +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, NULL, &OurInterface, &OurImports, interfprivate); } /* * Real work starts here ;-) */ static int execra( const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, GHashTable * params) { RA_ARGV params_argv; char ra_pathname[RA_MAX_NAME_LENGTH]; uniform_ret_execra_t exit_value; GString * debug_info; int index_tmp = 0; /* Prepare the call parameter */ if (0 > prepare_cmd_parameters(rsc_type, op_type, params, params_argv)) { cl_log(LOG_ERR, "HB RA: Error of preparing parameters"); return -1; } get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname); debug_info = g_string_new(""); do { g_string_append(debug_info, params_argv[index_tmp]); g_string_append(debug_info, " "); } while (params_argv[++index_tmp] != NULL); debug_info->str[debug_info->len-1] = '\0'; cl_log(LOG_DEBUG, "Will execute a heartbeat RA: %s", debug_info->str); g_string_free(debug_info, TRUE); execv(ra_pathname, params_argv); cl_log(LOG_ERR, "execv error when to execute a heartbeat RA %s.", rsc_type); switch (errno) { case ENOENT: /* No such file or directory */ case EISDIR: /* Is a directory */ exit_value = EXECRA_NO_RA; cl_log(LOG_ERR, "Cause: No such file or directory."); break; default: exit_value = EXECRA_EXEC_UNKNOWN_ERROR; cl_log(LOG_ERR, "Cause: execv unknow error."); } exit(exit_value); } static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params_ht, RA_ARGV params_argv) { int tmp_len, index; int ht_size = 0; char buf_tmp[20]; void * value_tmp; if (params_ht) { ht_size = g_hash_table_size(params_ht); } if ( ht_size+3 > MAX_PARAMETER_NUM ) { cl_log(LOG_ERR, "Too many parameters"); return -1; } tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME); params_argv[0] = g_strndup(rsc_type, tmp_len); /* Add operation code as the last argument */ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME); params_argv[ht_size+1] = g_strndup(op_type, tmp_len); /* Add the teminating NULL pointer */ params_argv[ht_size+2] = NULL; /* No actual arguments except op_type */ if (ht_size == 0) { return 0; } /* Now suppose the parameter formate stored in Hashtabe is like * key="1", value="-Wl,soname=test" * Moreover, the key is supposed as a string transfered from an integer. * It may be changed in the future. */ for (index = 1; index <= ht_size; index++ ) { snprintf(buf_tmp, sizeof(buf_tmp), "%d", index); value_tmp = g_hash_table_lookup(params_ht, buf_tmp); /* suppose the key is consecutive */ if ( value_tmp == NULL ) { cl_log(LOG_ERR, "Parameter ordering error in"\ "prepare_cmd_parameters, raexeclsb.c"); cl_log(LOG_ERR, "search key=%s.", buf_tmp); return -1; } params_argv[index] = g_strdup((char *)value_tmp); } return 0; } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type) { /* Now there is no formal related specification for Heartbeat RA * scripts. Temporarily deal as LSB init script. */ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible with LSB standard. */ if ( strncmp(op_type, "status", strlen("status")) == 0 ) { if (ret_execra < 0 || ret_execra > 4 ) { ret_execra = 4; } return status_op_exitcode_map[ret_execra]; } else { return ret_execra; } } static int get_resource_list(GList ** rsc_info) { return get_runnable_list(RA_PATH, rsc_info); } static char* get_resource_meta(const char* rsc_type, const char* provider) { return g_strndup(rsc_type, strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME)); } static int get_provider_list(const char* op_type, GList ** providers) { *providers = NULL; return 0; } diff --git a/lib/plugins/lrm/raexeclsb.c b/lib/plugins/lrm/raexeclsb.c index 9dbb3799ea..0a7f23124f 100644 --- a/lib/plugins/lrm/raexeclsb.c +++ b/lib/plugins/lrm/raexeclsb.c @@ -1,275 +1,275 @@ /* * 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: raexeclsb.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 /* Add it for compiling on OSX */ #include #include #include #include #include #define PIL_PLUGINTYPE RA_EXEC_TYPE #define PIL_PLUGIN lsb #define PIL_PLUGINTYPE_S "RAExec" #define PIL_PLUGIN_S "lsb" #define PIL_PLUGINLICENSE LICENSE_PUBDOM #define PIL_PLUGINLICENSEURL URL_PUBDOM /* * Are there multiple paths? Now according to LSB init scripts, the answer * is 'no', but should be 'yes' for lsb none-init scripts? */ static const char * RA_PATH = LSB_RA_DIR; static const int status_op_exitcode_map[] = { 0, 11, 12, 13, 14 }; /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type); static char* get_resource_meta(const char* rsc_type, const char* provider); static int get_resource_list(GList ** rsc_info); static int get_provider_list(const char* op_type, GList ** providers); /* The end of exported function list */ /* The begin of internal used function & data list */ #define MAX_PARAMETER_NUM 40 const int MAX_LENGTH_OF_RSCNAME = 40, MAX_LENGTH_OF_OPNAME = 40; typedef char * RA_ARGV[MAX_PARAMETER_NUM]; static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params, RA_ARGV params_argv); /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; -PIL_PLUGIN_BOILERPLATE2("1.0", Debug); +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, NULL, &OurInterface, &OurImports, interfprivate); } /* * Real work starts here ;-) */ static int execra( const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, GHashTable * params) { uniform_ret_execra_t exit_value; RA_ARGV params_argv; char ra_pathname[RA_MAX_NAME_LENGTH]; GString * debug_info; int index_tmp = 0; /* Prepare the call parameter */ if ( prepare_cmd_parameters(rsc_type, op_type, params, params_argv) != 0) { cl_log(LOG_ERR, "lsb RA: Error of preparing parameters"); return -1; } get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname); debug_info = g_string_new(""); do { g_string_append(debug_info, params_argv[index_tmp]); g_string_append(debug_info, " "); } while (params_argv[++index_tmp] != NULL); debug_info->str[debug_info->len-1] = '\0'; cl_log(LOG_DEBUG, "Will execute a lsb RA: %s", debug_info->str); g_string_free(debug_info, TRUE); execv(ra_pathname, params_argv); cl_log(LOG_ERR, "execv error when to execute a LSB RA %s.", rsc_type); switch (errno) { case ENOENT: /* No such file or directory */ /* Fall down */ case EISDIR: /* Is a directory */ exit_value = EXECRA_NO_RA; cl_log(LOG_ERR, "Cause: No such file or directory."); break; default: exit_value = EXECRA_EXEC_UNKNOWN_ERROR; cl_log(LOG_ERR, "Cause: execv unknow error."); } exit(exit_value); } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type) { /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible with LSB standard. */ if ( strncmp(op_type, "status", strlen("status")) == 0 ) { if (ret_execra < 0 || ret_execra > 4 ) { ret_execra = 4; } return status_op_exitcode_map[ret_execra]; } else { return ret_execra; } } static int get_resource_list(GList ** rsc_info) { return get_runnable_list(RA_PATH, rsc_info); } static int prepare_cmd_parameters(const char * rsc_type, const char * op_type, GHashTable * params_ht, RA_ARGV params_argv) { int tmp_len; int ht_size = 0; #if 0 /* Reserve it for possible furture use */ int index; void * value_tmp = NULL; char buf_tmp[20]; #endif if (params_ht) { ht_size = g_hash_table_size(params_ht); } /* Need 3 additonal spaces for accomodating: * argv[0] = RA_file_name(RA_TYPE) * argv[1] = operation * a terminal NULL */ if ( ht_size+3 > MAX_PARAMETER_NUM ) { cl_log(LOG_ERR, "Too many parameters"); return -1; } tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME); params_argv[0] = g_strndup(rsc_type, tmp_len); /* Add operation code as the first argument */ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME); params_argv[1] = g_strndup(op_type, tmp_len); /* Add the teminating NULL pointer */ params_argv[ht_size+2] = NULL; /* No actual arguments except op_type */ if (ht_size != 0) { /* Too strict? maybe */ cl_log(LOG_ERR, "For LSB init script, no parameter needed."); return -1; } /* Actually comment the following code, but I still think it may be used * in the future for LSB none-initial scripts, so reserver it. */ #if 0 /* Now suppose the parameter formate stored in Hashtabe is like * key="1", value="-Wl,soname=test" * Moreover, the key is supposed as a string transfered from an integer. * It may be changed in the future. */ for (index = 1; index <= ht_size; index++ ) { snprintf(buf_tmp, sizeof(buf_tmp), "%d", index); value_tmp = g_hash_table_lookup(params_ht, buf_tmp); /* suppose the key is consecutive */ if ( value_tmp == NULL ) { cl_log(LOG_ERR, "Parameter ordering error in"\ "prepare_cmd_parameters, raexeclsb.c"); return -1; } params_argv[index+1] = g_strdup((char *)value_tmp); } #endif return 0; } static char* get_resource_meta(const char* rsc_type, const char* provider) { return g_strndup(rsc_type, strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME)); } static int get_provider_list(const char* op_type, GList ** providers) { *providers = NULL; return 0; } diff --git a/lib/plugins/lrm/raexecocf.c b/lib/plugins/lrm/raexecocf.c index ec8ae3a1b2..5419d5fff0 100644 --- a/lib/plugins/lrm/raexecocf.c +++ b/lib/plugins/lrm/raexecocf.c @@ -1,446 +1,446 @@ /* * 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 #include #include #include #include /* Add it for compiling on OSX */ #include #include # define PIL_PLUGINTYPE RA_EXEC_TYPE # define PIL_PLUGINTYPE_S "RAExec" # define PIL_PLUGINLICENSE LICENSE_PUBDOM # define PIL_PLUGINLICENSEURL URL_PUBDOM #ifndef COMPILE_AS_STONITH # define PIL_PLUGIN ocf # define PIL_PLUGIN_S "ocf" /* * Are there multiple paths? Now according to OCF spec, the answer is 'no'. * But actually or for future? */ static const char * RA_PATH = OCF_RA_DIR; #else # define PIL_PLUGIN stonith # define PIL_PLUGIN_S "stonith" /* * Are there multiple paths? Now according to OCF spec, the answer is 'no'. * But actually or for future? */ static const char * RA_PATH = STONITH_RA_DIR; #endif /* The begin of exported function list */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, GHashTable * params); static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type); 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 */ /* The begin of internal used function & data list */ static void add_OCF_prefix(GHashTable * params, GHashTable * new_params); static void add_OCF_env_vars(GHashTable * env, const char * rsc_id, const char * rsc_type, const char * provider); static void add_prefix_foreach(gpointer key, gpointer value, gpointer user_data); static int raexec_setenv(GHashTable * env_params); static void set_env(gpointer key, gpointer value, gpointer user_data); static gboolean let_remove_eachitem(gpointer key, gpointer value, gpointer user_data); static int get_providers(const char* class_path, const char* op_type, GList ** providers); static void merge_string_list(GList** old, GList* new); static gint compare_str(gconstpointer a, gconstpointer b); /* The end of internal function & data list */ /* Rource agent execution plugin operations */ static struct RAExecOps raops = { execra, map_ra_retvalue, get_resource_list, get_provider_list, get_resource_meta }; -PIL_PLUGIN_BOILERPLATE2("1.0", Debug); +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, NULL, &OurInterface, &OurImports, interfprivate); } /* * The function to execute a RA. */ static int execra(const char * rsc_id, const char * rsc_type, const char * provider, const char * op_type, GHashTable * params) { uniform_ret_execra_t exit_value; char ra_pathname[RA_MAX_NAME_LENGTH]; GHashTable * tmp_for_setenv; get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname); /* Setup environment correctly */ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal); add_OCF_prefix(params, tmp_for_setenv); add_OCF_env_vars(tmp_for_setenv, rsc_id, rsc_type, provider); raexec_setenv(tmp_for_setenv); g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL); g_hash_table_destroy(tmp_for_setenv); /* execute the RA */ cl_log(LOG_DEBUG, "Will execute OCF RA : %s %s", ra_pathname, op_type); execl(ra_pathname, ra_pathname, op_type, NULL); switch (errno) { case ENOENT: /* No such file or directory */ case EISDIR: /* Is a directory */ exit_value = EXECRA_NO_RA; break; default: exit_value = EXECRA_EXEC_UNKNOWN_ERROR; } cl_log(LOG_ERR, "execl error when to execute RA %s.", rsc_type); exit(exit_value); } static uniform_ret_execra_t map_ra_retvalue(int ret_execra, const char * op_type) { /* Because the UNIFORM_RET_EXECRA is compatible with OCF standard */ return ret_execra; } static gint compare_str(gconstpointer a, gconstpointer b) { return strncmp(a,b,RA_MAX_NAME_LENGTH); } static int get_resource_list(GList ** rsc_info) { struct dirent **namelist; GList* item; int file_num; char subdir[FILENAME_MAX+1]; if ( rsc_info == NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list"); return -2; } if ( *rsc_info != NULL ) { cl_log(LOG_ERR, "Parameter error: get_resource_list."\ "will cause memory leak."); *rsc_info = NULL; } file_num = scandir(RA_PATH, &namelist, 0, alphasort); if (file_num < 0) { return -2; } while (file_num--) { GList* ra_subdir = NULL; if ((DT_DIR != namelist[file_num]->d_type) || ('.' == namelist[file_num]->d_name[0])) { free(namelist[file_num]); continue; } snprintf(subdir,FILENAME_MAX,"%s/%s", RA_PATH, namelist[file_num]->d_name); get_runnable_list(subdir,&ra_subdir); merge_string_list(rsc_info,ra_subdir); while (NULL != (item = g_list_first(ra_subdir))) { ra_subdir = g_list_remove_link(ra_subdir, item); g_free(item->data); g_list_free_1(item); } free(namelist[file_num]); } free(namelist); return 0; } static void merge_string_list(GList** old, GList* new) { GList* item = NULL; char* newitem; for( item=g_list_first(new); NULL!=item; item=g_list_next(item)){ if (!g_list_find_custom(*old, item->data,compare_str)){ newitem = strndup(item->data,RA_MAX_NAME_LENGTH); *old = g_list_append(*old, newitem); } } } static int get_provider_list(const char* op_type, GList ** providers) { int ret; ret = get_providers(RA_PATH, op_type, providers); if (0>ret) { cl_log(LOG_ERR, "scandir failed in OCF RA plugin"); } return ret; } static char* get_resource_meta(const char* rsc_type, const char* provider) { const int BUFF_LEN=4096; int read_len = 0; char buff[BUFF_LEN]; char* data = NULL; GString* g_str_tmp = NULL; char ra_pathname[RA_MAX_NAME_LENGTH]; FILE* file = NULL; GHashTable * tmp_for_setenv; get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname); strncat(ra_pathname, " meta-data",RA_MAX_NAME_LENGTH); tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal); add_OCF_env_vars(tmp_for_setenv, "DUMMY_INSTANCE", rsc_type, provider); raexec_setenv(tmp_for_setenv); g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL); g_hash_table_destroy(tmp_for_setenv); file = popen(ra_pathname, "r"); if (NULL==file) { return NULL; } g_str_tmp = g_string_new(""); while(!feof(file)) { memset(buff, 0, BUFF_LEN); read_len = fread(buff, 1, BUFF_LEN, file); if (0len) { pclose(file); return NULL; } data = (char*)g_new(char, g_str_tmp->len+1); data[0] = data[g_str_tmp->len] = 0; strncpy(data, g_str_tmp->str, g_str_tmp->len); g_string_free(g_str_tmp, TRUE); pclose(file); return data; } static void add_OCF_prefix(GHashTable * env_params, GHashTable * new_env_params) { if (env_params) { g_hash_table_foreach(env_params, add_prefix_foreach, new_env_params); } } static void add_prefix_foreach(gpointer key, gpointer value, gpointer user_data) { const int MAX_LENGTH_OF_ENV = 50; int prefix = strlen("OCF_RESKEY_"); GHashTable * new_hashtable = (GHashTable *) user_data; char * newkey; int keylen = strnlen((char*)key, MAX_LENGTH_OF_ENV-prefix)+prefix; newkey = g_new(gchar, keylen); strncpy(newkey, "OCF_RESKEY_", keylen); strncat(newkey, key, keylen); g_hash_table_insert(new_hashtable, (gpointer)newkey, g_strdup(value)); } static gboolean let_remove_eachitem(gpointer key, gpointer value, gpointer user_data) { g_free(key); g_free(value); return TRUE; } static int raexec_setenv(GHashTable * env_params) { if (env_params) { g_hash_table_foreach(env_params, set_env, NULL); } return 0; } static void set_env(gpointer key, gpointer value, gpointer user_data) { if (setenv(key, value, 1) != 0) { cl_log(LOG_ERR, "setenv failed in raexecocf."); } } static int get_providers(const char* class_path, const char* op_type, GList ** providers) { struct dirent **namelist; int file_num; if ( providers == NULL ) { cl_log(LOG_ERR, "Parameter error: get_providers"); return -2; } if ( *providers != NULL ) { cl_log(LOG_ERR, "Parameter error: get_providers."\ "will cause memory leak."); *providers = NULL; } file_num = scandir(class_path, &namelist, 0, alphasort); if (file_num < 0) { return -2; }else{ char tmp_buffer[FILENAME_MAX+1]; while (file_num--) { if ((DT_DIR != namelist[file_num]->d_type) || ('.' == namelist[file_num]->d_name[0])) { free(namelist[file_num]); continue; } snprintf(tmp_buffer,FILENAME_MAX,"%s/%s/%s", class_path, namelist[file_num]->d_name, op_type); if ( filtered(tmp_buffer) == TRUE ) { *providers = g_list_append(*providers, g_strdup(namelist[file_num]->d_name)); } free(namelist[file_num]); } free(namelist); } return g_list_length(*providers); } static void add_OCF_env_vars(GHashTable * env, const char * rsc_id, const char * rsc_type, const char * provider) { if ( env == NULL ) { cl_log(LOG_WARNING, "env should not be a NULL pointer."); return; } g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MAJOR"), g_strdup("1")); g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MINOR"), g_strdup("0")); g_hash_table_insert(env, g_strdup("OCF_ROOT"), g_strdup(OCF_ROOT_DIR)); if ( rsc_id != NULL ) { g_hash_table_insert(env, g_strdup("OCF_RESOURCE_INSTANCE"), g_strdup(rsc_id)); } /* Currently the rsc_type=="the filename of the RA script/executable", * It seems always correct even in the furture. ;-) */ if ( rsc_type != NULL ) { g_hash_table_insert(env, g_strdup("OCF_RESOURCE_TYPE"), g_strdup(rsc_type)); } /* Notes: this is not added to specification yet. Sept 10,2004 */ if ( provider != NULL ) { g_hash_table_insert(env, g_strdup("OCF_RESOURCE_PROVIDER"), g_strdup(provider)); } }