diff --git a/crm/admin/crmadmin.c b/crm/admin/crmadmin.c index 44aa98e2e2..2226f91ee8 100644 --- a/crm/admin/crmadmin.c +++ b/crm/admin/crmadmin.c @@ -1,721 +1,715 @@ /* * 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 #ifdef HAVE_GETOPT_H # include #endif 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); gboolean do_init(void); int do_work(void); void crmadmin_ipc_connection_destroy(gpointer user_data); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); char *pluralSection(const char *a_section); xmlNode *handleCibMod(void); int do_find_node_list(xmlNode *xml_node); gboolean admin_message_timeout(gpointer data); gboolean is_node_online(xmlNode *node_state); enum debug { debug_none, debug_dec, debug_inc }; gboolean BE_VERBOSE = FALSE; int expected_responses = 1; gboolean BASH_EXPORT = FALSE; 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; enum debug DO_DEBUG = debug_none; const char *crmd_operation = NULL; xmlNode *msg_options = NULL; const char *standby_on_off = "on"; 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; #define OPTARGS "V?K:S:HE:Dd:i:RNqt:Bv" int main(int argc, char **argv) { int argerr = 0; int flag; #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { /* Top-level Options */ {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"quiet", 0, 0, 'q'}, {"reference", 1, 0, 0}, {XML_ATTR_TIMEOUT, 1, 0, 't'}, {"bash-export", 0, 0, 'B'}, /* daemon options */ {"kill", 1, 0, 'K'}, /* stop a node */ {"die", 0, 0, 0}, /* kill a node, no respawn */ {"debug_inc", 1, 0, 'i'}, {"debug_dec", 1, 0, 'd'}, {"status", 1, 0, 'S'}, {"standby", 1, 0, 's'}, {"active", 1, 0, 'a'}, {"health", 0, 0, 'H'}, {"election", 0, 0, 'E'}, {"dc_lookup", 0, 0, 'D'}, {"nodes", 0, 0, 'N'}, {"option", 1, 0, 'o'}, {"version", 0, 0, 'v'}, {0, 0, 0, 0} }; #endif crm_log_init(basename(argv[0]), LOG_ERR, FALSE, TRUE, argc, argv); if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } while (1) { #ifdef HAVE_GETOPT_H flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); #else flag = getopt(argc, argv, OPTARGS); #endif if (flag == -1) break; switch(flag) { #ifdef HAVE_GETOPT_H case 0: if (strcasecmp("reference", long_options[option_index].name) == 0) { this_msg_reference = crm_strdup(optarg); } else if (strcasecmp("die", long_options[option_index].name) == 0) { DO_RESET = TRUE; crmd_operation = CRM_OP_DIE; } else { printf( "?? Long option (--%s) is not yet properly supported ??\n", long_options[option_index].name); ++argerr; } break; #endif /* 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': fprintf(stdout, "HA Version %s, CRM Version %s (CIB feature set %s) %s\n", VERSION, CRM_FEATURE_SET, CIB_FEATURE_SET, HA_HG_VERSION); exit(0); break; case 'V': BE_VERBOSE = TRUE; admin_verbose = XML_BOOLEAN_TRUE; cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); 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 'B': BASH_EXPORT = TRUE; break; case 'K': DO_RESET = TRUE; crm_debug_2("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); crmd_operation = CRM_OP_LOCAL_SHUTDOWN; break; case 'q': BE_SILENT = TRUE; break; case 'i': DO_DEBUG = debug_inc; crm_debug_2("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'd': DO_DEBUG = debug_dec; crm_debug_2("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'S': DO_HEALTH = TRUE; crm_debug_2("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 '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); } if (do_init()) { int res = 0; res = do_work(); if (res > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); expected_responses++; crm_debug_2("Waiting for %d replies from the local CRM", expected_responses); 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_err("No message to send"); operation_status = -1; } } else { crm_warn("Init failed, could not perform requested operations"); operation_status = -2; } crm_debug_2("%s exiting normally", crm_system_name); return operation_status; } int do_work(void) { int ret = 1; /* construct the request */ xmlNode *msg_data = NULL; gboolean all_is_good = TRUE; msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); crm_xml_add(msg_options, XML_ATTR_VERBOSE, admin_verbose); crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); if (DO_HEALTH == TRUE) { crm_debug_2("Querying the system"); sys_to = CRM_SYSTEM_DC; if (dest_node != NULL) { sys_to = CRM_SYSTEM_CRMD; crmd_operation = CRM_OP_PING; if (BE_VERBOSE) { expected_responses = 1; } crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); } else { crm_info("Cluster-wide health not available yet"); all_is_good = FALSE; } } else if(DO_ELECT_DC) { /* tell the local node to initiate an election */ sys_to = CRM_SYSTEM_CRMD; crmd_operation = CRM_OP_VOTE; crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; ret = 0; /* no return message */ } else if(DO_WHOIS_DC) { sys_to = CRM_SYSTEM_DC; crmd_operation = CRM_OP_PING; crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; } else if(DO_NODE_LIST) { cib_t * the_cib = cib_new(); xmlNode *output = NULL; enum cib_errors rc = the_cib->cmds->signon( the_cib, crm_system_name, cib_command_synchronous); if(rc != cib_ok) { return -1; } output = get_cib_copy(the_cib); do_find_node_list(output); free_xml(output); the_cib->cmds->signoff(the_cib); exit(rc); } 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; crm_xml_add(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; crmd_operation = CRM_OP_DEBUG_UP; 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; crmd_operation = CRM_OP_DEBUG_DOWN; 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; } } { xmlNode *cmd = create_request( crmd_operation, msg_data, dest_node, sys_to, crm_system_name, admin_uuid); if(this_msg_reference != NULL) { crm_xml_add(cmd, XML_ATTR_REFERENCE, this_msg_reference); } send_ipc_message(crmd_channel, cmd); free_xml(cmd); } return ret; } void crmadmin_ipc_connection_destroy(gpointer user_data) { crm_err("Connection to CRMd was terminated"); if(mainloop) { g_main_quit(mainloop); } else { exit(1); } } gboolean do_init(void) { GCHSource *src = NULL; crm_malloc0(admin_uuid, 11); if(admin_uuid != NULL) { snprintf(admin_uuid, 10, "%d", getpid()); admin_uuid[10] = '\0'; } src = init_client_ipc_comms( CRM_SYSTEM_CRMD, admin_msg_callback, NULL, &crmd_channel); if(DO_RESOURCE || DO_RESOURCE_LIST || DO_NODE_LIST) { return TRUE; } else if(crmd_channel != NULL) { send_hello_message( crmd_channel, admin_uuid, crm_system_name,"0", "1"); set_IPC_Channel_dnotify(src, crmadmin_ipc_connection_destroy); return TRUE; } return FALSE; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { int rc = 0; int lpc = 0; xmlNode *xml = NULL; IPC_Message *msg = NULL; - ha_msg_input_t *new_input = NULL; gboolean hack_return_good = TRUE; static int received_responses = 0; char *filename = NULL; int filename_len = 0; const char *result = NULL; Gmain_timeout_remove(message_timer_id); while (server->ch_status != IPC_DISCONNECT && server->ops->is_message_pending(server) == TRUE) { - if(new_input != NULL) { - delete_ha_msg_input(new_input); - new_input = NULL; + if(xml) { + free_xml(xml); } - + xml = NULL; + rc = server->ops->recv(server, &msg); if (rc != IPC_OK) { cl_perror("Receive failure (%d)", rc); return !hack_return_good; } if (msg == NULL) { crm_debug_4("No message this time"); continue; } lpc++; received_responses++; xml = convert_ipc_message(msg, __FUNCTION__); - new_input = new_ipc_msg_input(xml); - free_xml(xml); - - crm_log_xml(LOG_MSG, "ipc", new_input->msg); msg->msg_done(msg); + crm_log_xml(LOG_MSG, "ipc", xml); - if (new_input->xml == NULL) { + if (xml == NULL) { crm_info("XML in IPC message was not valid... " "discarding."); goto cleanup; } else if (validate_crm_message( - new_input->msg, crm_system_name, admin_uuid, + xml, crm_system_name, admin_uuid, XML_ATTR_RESPONSE) == FALSE) { crm_debug_2("Message was not a CRM response. Discarding."); goto cleanup; } - result = crm_element_value(new_input->msg, XML_ATTR_RESULT); + result = crm_element_value(xml, XML_ATTR_RESULT); if(result == NULL || strcasecmp(result, "ok") == 0) { result = "pass"; } else { result = "fail"; } if(DO_HEALTH) { - const char *state = crm_element_value( - new_input->xml, "crmd_state"); + xmlNode *data = get_message_xml(xml, F_CRM_DATA); + const char *state = crm_element_value(data, "crmd_state"); printf("Status of %s@%s: %s (%s)\n", - crm_element_value(new_input->xml,XML_PING_ATTR_SYSFROM), - crm_element_value(new_input->msg, F_CRM_HOST_FROM), + crm_element_value(data,XML_PING_ATTR_SYSFROM), + crm_element_value(xml, F_CRM_HOST_FROM), state, - crm_element_value(new_input->xml,XML_PING_ATTR_STATUS)); + crm_element_value(data,XML_PING_ATTR_STATUS)); if(BE_SILENT && state != NULL) { fprintf(stderr, "%s\n", state); } } else if(DO_WHOIS_DC) { - const char *dc = crm_element_value( - new_input->msg, F_CRM_HOST_FROM); + const char *dc = crm_element_value(xml, F_CRM_HOST_FROM); 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' */ + xmlNode *data = get_message_xml(xml, F_CRM_DATA); filename_len = 31 + strlen(this_msg_reference); crm_malloc0(filename, filename_len); if(filename != NULL) { snprintf(filename, filename_len, "%s-%s_%d.xml", result, this_msg_reference, received_responses); filename[filename_len - 1] = '\0'; - if (0 > write_xml_file( - new_input->xml, filename, FALSE)) { + if (0 > write_xml_file(data, filename, FALSE)) { crm_crit("Could not save response to" " %s", filename); } } } cleanup: - delete_ha_msg_input(new_input); - new_input = NULL; + free_xml(xml); } if (server->ch_status == IPC_DISCONNECT) { crm_debug_2("admin_msg_callback: received HUP"); return !hack_return_good; } if (received_responses >= expected_responses) { crm_debug_2( "Recieved expected number (%d) of messages from Heartbeat." " Exiting normally.", expected_responses); exit(0); } 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); operation_status = -3; g_main_quit(mainloop); return FALSE; } gboolean is_node_online(xmlNode *node_state) { const char *uname = crm_element_value(node_state,XML_ATTR_UNAME); const char *join_state = crm_element_value(node_state,XML_CIB_ATTR_JOINSTATE); const char *exp_state = crm_element_value(node_state,XML_CIB_ATTR_EXPSTATE); const char *crm_state = crm_element_value(node_state,XML_CIB_ATTR_CRMDSTATE); const char *ha_state = crm_element_value(node_state,XML_CIB_ATTR_HASTATE); const char *ccm_state = crm_element_value(node_state,XML_CIB_ATTR_INCCM); if(safe_str_neq(join_state, CRMD_JOINSTATE_DOWN) && (ha_state == NULL || safe_str_eq(ha_state, "active")) && crm_is_true(ccm_state) && safe_str_eq(crm_state, "online")) { crm_debug_3("Node %s is online", uname); return TRUE; } crm_debug_3("Node %s: ha=%s ccm=%s join=%s exp=%s crm=%s", uname, crm_str(ha_state), crm_str(ccm_state), crm_str(join_state), crm_str(exp_state), crm_str(crm_state)); crm_debug_3("Node %s is offline", uname); return FALSE; } int do_find_node_list(xmlNode *xml_node) { int found = 0; xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); xml_child_iter_filter( nodes, node, XML_CIB_TAG_NODE, if(BASH_EXPORT) { printf("export %s=%s\n", crm_element_value(node, XML_ATTR_UNAME), crm_element_value(node, XML_ATTR_ID)); } else { printf("%s node: %s (%s)\n", crm_element_value(node, XML_ATTR_TYPE), crm_element_value(node, XML_ATTR_UNAME), crm_element_value(node, XML_ATTR_ID)); } 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: this help message\n", "help", '?'); fprintf(stream, "\t--%s (-%c)\t: version details\n", "version", 'v'); 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", "quiet", 'q'); fprintf(stream, "\t--%s (-%c)\t: Only applies to -N.\n" "\t\tCreate Bash export entries of the form \"export uname=uuid\"\n", "bash-export", 'B'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c) \t: " "increment the CRMd debug level on \n", CRM_OP_DEBUG_UP,'i'); fprintf(stream, "\t--%s (-%c) \t: " "decrement the CRMd debug level on \n", CRM_OP_DEBUG_DOWN,'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'); #if 0 fprintf(stream, "\t--%s (-%c)\t\t: " "request the status of all nodes\n", "health", 'H'); #endif 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\n", "disconnect", 'D'); */ fflush(stream); exit(exit_status); } diff --git a/crmd/atest.c b/crmd/atest.c index 24799061ce..e47dcbd050 100644 --- a/crmd/atest.c +++ b/crmd/atest.c @@ -1,143 +1,118 @@ /* * 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 #define OPTARGS "V?X:I:" #ifdef HAVE_GETOPT_H # include #endif + int main(int argc, char ** argv) { int flag; - char *buffer = NULL; xmlNode *xml = NULL; - HA_Message *ha = NULL; const char *xml_file = NULL; const char *input_file = NULL; crm_log_init("atest", LOG_DEBUG, FALSE, TRUE, argc, argv); while (1) { #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?'}, {"verbose", 0, 0, 'V'}, {"xml-file", 1, 0, 'X'}, {"save-input", 1, 0, 'I'}, {0, 0, 0, 0} }; #endif #ifdef HAVE_GETOPT_H flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); #else flag = getopt(argc, argv, OPTARGS); #endif if (flag == -1) break; switch(flag) { #ifdef HAVE_GETOPT_H case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); break; #endif case 'X': xml_file = optarg; break; case 'I': input_file = optarg; break; case '?': /* usage("ptest", 0); */ break; default: printf("?? getopt returned character code 0%o ??\n", flag); break; } } if(xml_file != NULL) { FILE *xml_strm = fopen(xml_file, "r"); if(xml_strm == NULL) { cl_perror("Could not open %s for reading", xml_file); } else { if(strstr(xml_file, ".bz2") != NULL) { xml = file2xml(xml_strm, TRUE); } else { xml = file2xml(xml_strm, FALSE); } fclose(xml_strm); } } - crm_log_xml_info(xml, "read"); - - ha = convert_xml_message(xml); - crm_log_message_adv(LOG_INFO, "ha", ha); - - xml = convert_ha_message(NULL, ha, "foo"); + xml = update_validation(xml, TRUE, FALSE); + crm_log_xml_info(xml, "fixed"); - crm_log_xml_info(xml, "write"); - - if(input_file != NULL) { - FILE *input_strm = fopen(input_file, "w"); - if(input_strm == NULL) { - cl_perror("Could not open %s for writing", input_file); - } else { - buffer = dump_xml_formatted(xml); - if(fprintf(input_strm, "%s\n", buffer) < 0) { - cl_perror("Write to %s failed", input_file); - } - fflush(input_strm); - fclose(input_strm); - crm_free(buffer); - } - } - - if(crm_get_msec("0") < 0) { - fprintf(stderr, "error\n"); - } return 0; } diff --git a/crmd/callbacks.c b/crmd/callbacks.c index 89e9734e6c..9e7dc5d305 100644 --- a/crmd/callbacks.c +++ b/crmd/callbacks.c @@ -1,650 +1,651 @@ /* * 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 xmlNode *find_xml_in_hamessage(const xmlNode * msg); void crmd_ha_connection_destroy(gpointer user_data); void crmd_ha_msg_filter(xmlNode *msg); /* From join_dc... */ extern gboolean check_join_state( enum crmd_fsa_state cur_state, const char *source); /* #define MAX_EMPTY_CALLBACKS 20 */ /* int empty_callbacks = 0; */ #define trigger_fsa(source) crm_debug_3("Triggering FSA: %s", __FUNCTION__); \ G_main_set_trigger(source); #if SUPPORT_HEARTBEAT gboolean crmd_ha_msg_dispatch(ll_cluster_t *cluster_conn, gpointer user_data) { IPC_Channel *channel = NULL; gboolean stay_connected = TRUE; crm_debug_3("Invoked"); if(cluster_conn != NULL) { channel = cluster_conn->llc_ops->ipcchan(cluster_conn); } CRM_CHECK(cluster_conn != NULL, ;); CRM_CHECK(channel != NULL, ;); if(channel != NULL && IPC_ISRCONN(channel)) { if(cluster_conn->llc_ops->msgready(cluster_conn) == 0) { crm_debug_2("no message ready yet"); } /* invoke the callbacks but dont block */ cluster_conn->llc_ops->rcvmsg(cluster_conn, 0); } if (channel == NULL || channel->ch_status != IPC_CONNECT) { if(is_set(fsa_input_register, R_HA_DISCONNECTED) == FALSE) { crm_crit("Lost connection to heartbeat service."); } else { crm_info("Lost connection to heartbeat service."); } trigger_fsa(fsa_source); stay_connected = FALSE; } return stay_connected; } #endif void crmd_ha_connection_destroy(gpointer user_data) { crm_debug_3("Invoked"); if(is_set(fsa_input_register, R_HA_DISCONNECTED)) { /* we signed out, so this is expected */ crm_info("Heartbeat disconnection complete"); return; } crm_crit("Lost connection to heartbeat service!"); register_fsa_input(C_HA_DISCONNECT, I_ERROR, NULL); trigger_fsa(fsa_source); } void crmd_ha_msg_filter(xmlNode *msg) { ha_msg_input_t *new_input = NULL; const char *from = crm_element_value(msg, F_ORIG); const char *seq = crm_element_value(msg, F_SEQ); const char *op = crm_element_value(msg, F_CRM_TASK); const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO); const char *sys_from = crm_element_value(msg, F_CRM_SYS_FROM); if(safe_str_eq(sys_to, CRM_SYSTEM_DC) && AM_I_DC == FALSE) { crm_debug_2("Ignoring message for the DC [F_SEQ=%s]", seq); return; } else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) { if(AM_I_DC && safe_str_neq(from, fsa_our_uname)) { crm_err("Another DC detected: %s (op=%s)", from, op); /* make sure the election happens NOW */ if(fsa_state != S_ELECTION) { new_input = new_ha_msg_input(msg); register_fsa_error_adv(C_FSA_INTERNAL, I_ELECTION, NULL, new_input, __FUNCTION__); } } else { crm_debug_2("Processing DC message from %s [F_SEQ=%s]", from, seq); } } if(new_input == NULL) { crm_log_xml(LOG_MSG, "HA[inbound]", msg); new_input = new_ha_msg_input(msg); route_message(C_HA_MESSAGE, new_input); } - delete_ha_msg_input(new_input); + crm_free(new_input); trigger_fsa(fsa_source); } #if SUPPORT_HEARTBEAT void crmd_ha_msg_callback(HA_Message *hamsg, void* private_data) { int level = LOG_DEBUG; oc_node_t *from_node = NULL; xmlNode *msg = convert_ha_message(NULL, hamsg, __FUNCTION__); const char *from = crm_element_value(msg, F_ORIG); const char *op = crm_element_value(msg, F_CRM_TASK); const char *sys_from = crm_element_value(msg, F_CRM_SYS_FROM); CRM_CHECK(from != NULL, crm_log_xml_err(msg, "anon"); return); crm_debug_2("HA[inbound]: %s from %s", op, from); if(crm_peer_cache == NULL || crm_active_members() == 0) { crm_debug("Ignoring HA messages until we are" " connected to the CCM (%s op from %s)", op, from); crm_log_xml(LOG_MSG, "HA[inbound]: Ignore (No CCM)", msg); goto bail; } from_node = g_hash_table_lookup(crm_peer_cache, from); if(from_node == NULL) { if(safe_str_eq(op, CRM_OP_VOTE)) { level = LOG_WARNING; } else if(AM_I_DC && safe_str_eq(op, CRM_OP_JOIN_ANNOUNCE)) { level = LOG_WARNING; } else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) { level = LOG_WARNING; } do_crm_log(level, "Ignoring HA message (op=%s) from %s: not in our" " membership list (size=%d)", op, from, crm_active_members()); crm_log_xml(LOG_MSG, "HA[inbound]: CCM Discard", msg); } else { crmd_ha_msg_filter(msg); } bail: free_xml(msg); return; } #endif /* * Apparently returning TRUE means "stay connected, keep doing stuff". * Returning FALSE means "we're all done, close the connection" */ gboolean crmd_ipc_msg_callback(IPC_Channel *client, gpointer user_data) { int lpc = 0; xmlNode *msg = NULL; ha_msg_input_t *new_input = NULL; crmd_client_t *curr_client = (crmd_client_t*)user_data; gboolean stay_connected = TRUE; crm_debug_2("Invoked: %s", curr_client->table_key); while(IPC_ISRCONN(client)) { if(client->ops->is_message_pending(client) == 0) { break; } msg = xmlfromIPC(client, 0); if (msg == NULL) { break; } lpc++; new_input = new_ha_msg_input(msg); - free_xml(msg); crm_debug_2("Processing msg from %s", curr_client->table_key); crm_log_xml(LOG_DEBUG_2, "CRMd[inbound]", new_input->msg); if(crmd_authorize_message(new_input, curr_client)) { route_message(C_IPC_MESSAGE, new_input); } - delete_ha_msg_input(new_input); + crm_free(new_input); + free_xml(msg); + new_input = NULL; msg = NULL; if(client->ch_status != IPC_CONNECT) { break; } } crm_debug_2("Processed %d messages", lpc); if (client->ch_status != IPC_CONNECT) { stay_connected = FALSE; process_client_disconnect(curr_client); } trigger_fsa(fsa_source); return stay_connected; } extern GCHSource *lrm_source; gboolean lrm_dispatch(IPC_Channel *src_not_used, gpointer user_data) { /* ?? src == lrm_channel ?? */ ll_lrm_t *lrm = (ll_lrm_t*)user_data; IPC_Channel *lrm_channel = lrm->lrm_ops->ipcchan(lrm); crm_debug_3("Invoked"); lrm->lrm_ops->rcvmsg(lrm, FALSE); if(lrm_channel->ch_status != IPC_CONNECT) { if(is_set(fsa_input_register, R_LRM_CONNECTED)) { crm_crit("LRM Connection failed"); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); } else { crm_info("LRM Connection disconnected"); } lrm_source = NULL; return FALSE; } return TRUE; } extern gboolean process_lrm_event(lrm_op_t *op); void lrm_op_callback(lrm_op_t* op) { CRM_CHECK(op != NULL, return); process_lrm_event(op); } void crmd_ha_status_callback(const char *node, const char *status, void *private) { xmlNode *update = NULL; crm_node_t *member = NULL; crm_notice("Status update: Node %s now has status [%s] (DC=%s)", node, status, AM_I_DC?"true":"false"); member = g_hash_table_lookup(crm_peer_cache, node); if(member == NULL) { /* Make sure it is created so crm_update_peer_proc() succeeds */ const char *uuid = get_uuid(node); member = crm_update_peer(0, 0, -1, 0, uuid, node, NULL, NULL); } if(safe_str_eq(status, PINGSTATUS)) { return; } if(safe_str_eq(status, DEADSTATUS)) { /* this node is toast */ crm_update_peer_proc(node, crm_proc_ais, OFFLINESTATUS); if(AM_I_DC) { update = create_node_state( node, DEADSTATUS, XML_BOOLEAN_NO, OFFLINESTATUS, CRMD_JOINSTATE_DOWN, NULL, TRUE, __FUNCTION__); } } else { crm_update_peer_proc(node, crm_proc_ais, ONLINESTATUS); if(AM_I_DC) { update = create_node_state( node, ACTIVESTATUS, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__); } } trigger_fsa(fsa_source); if(update != NULL) { fsa_cib_anon_update( XML_CIB_TAG_STATUS, update, cib_scope_local|cib_quorum_override); free_xml(update); } } void crmd_client_status_callback(const char * node, const char * client, const char * status, void * private) { const char *join = NULL; crm_node_t *member = NULL; xmlNode *update = NULL; gboolean clear_shutdown = FALSE; crm_debug_3("Invoked"); if(safe_str_neq(client, CRM_SYSTEM_CRMD)) { return; } if(safe_str_eq(status, JOINSTATUS)){ status = ONLINESTATUS; clear_shutdown = TRUE; } else if(safe_str_eq(status, LEAVESTATUS)){ status = OFFLINESTATUS; join = CRMD_JOINSTATE_DOWN; /* clear_shutdown = TRUE; */ } set_bit_inplace(fsa_input_register, R_PEER_DATA); crm_notice("Status update: Client %s/%s now has status [%s] (DC=%s)", node, client, status, AM_I_DC?"true":"false"); if(safe_str_eq(status, ONLINESTATUS)) { /* remove the cached value in case it changed */ crm_debug_2("Uncaching UUID for %s", node); unget_uuid(node); } member = g_hash_table_lookup(crm_peer_cache, node); if(member == NULL) { /* Make sure it is created so crm_update_peer_proc() succeeds */ const char *uuid = get_uuid(node); member = crm_update_peer(0, 0, -1, 0, uuid, node, NULL, NULL); } crm_update_peer_proc(node, crm_proc_crmd, status); if(is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) { return; } else if(fsa_state == S_STOPPING) { return; } if(safe_str_eq(node, fsa_our_dc) && safe_str_eq(status, OFFLINESTATUS)){ /* did our DC leave us */ crm_info("Got client status callback - our DC is dead"); register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ELECTION, NULL); } else if(AM_I_DC == FALSE) { crm_info("Not the DC"); } else { crm_debug_3("Got client status callback"); update = create_node_state(node, NULL, NULL, status, join, NULL, clear_shutdown, __FUNCTION__); if(safe_str_eq(status, ONLINESTATUS)){ crm_xml_add(update, XML_CIB_ATTR_REPLACE, XML_CIB_TAG_LRM","XML_TAG_TRANSIENT_NODEATTRS","); } fsa_cib_anon_update( XML_CIB_TAG_STATUS, update, cib_scope_local|cib_quorum_override); free_xml(update); if(safe_str_eq(status, OFFLINESTATUS)) { erase_node_from_join(node); check_join_state(fsa_state, __FUNCTION__); } } trigger_fsa(fsa_source); } void crmd_ipc_connection_destroy(gpointer user_data) { GCHSource *source = NULL; crmd_client_t *client = user_data; /* Calling this function on an _active_ connection results in: * crmd_ipc_connection_destroy (callbacks.c:431) * -> G_main_del_IPC_Channel (GSource.c:478) * -> g_source_unref * -> G_CH_destroy_int (GSource.c:647) * -> crmd_ipc_connection_destroy (callbacks.c:437)\ * * A better alternative is to call G_main_del_IPC_Channel() directly */ if(client == NULL) { crm_debug_4("No client to delete"); return; } crm_debug_2("Disconnecting client %s (%p)", client->table_key, client); source = client->client_source; client->client_source = NULL; if(source != NULL) { crm_debug_3("Deleting %s (%p) from mainloop", client->table_key, source); G_main_del_IPC_Channel(source); } crm_free(client->table_key); crm_free(client->sub_sys); crm_free(client->uuid); crm_free(client); return; } gboolean crmd_client_connect(IPC_Channel *client_channel, gpointer user_data) { crm_debug_3("Invoked"); if (client_channel == NULL) { crm_err("Channel was NULL"); } else if (client_channel->ch_status == IPC_DISCONNECT) { crm_err("Channel was disconnected"); } else { crmd_client_t *blank_client = NULL; crm_debug_3("Channel connected"); crm_malloc0(blank_client, sizeof(crmd_client_t)); CRM_ASSERT(blank_client != NULL); crm_debug_2("Created client: %p", blank_client); client_channel->ops->set_recv_qlen(client_channel, 1024); client_channel->ops->set_send_qlen(client_channel, 1024); blank_client->client_channel = client_channel; blank_client->sub_sys = NULL; blank_client->uuid = NULL; blank_client->table_key = NULL; blank_client->client_source = G_main_add_IPC_Channel( G_PRIORITY_LOW, client_channel, FALSE, crmd_ipc_msg_callback, blank_client, crmd_ipc_connection_destroy); } return TRUE; } #if SUPPORT_HEARTBEAT static gboolean fsa_have_quorum = FALSE; gboolean ccm_dispatch(int fd, gpointer user_data) { int rc = 0; oc_ev_t *ccm_token = (oc_ev_t*)user_data; gboolean was_error = FALSE; crm_debug_3("Invoked"); rc = oc_ev_handle_event(ccm_token); if(rc != 0) { if(is_set(fsa_input_register, R_CCM_DISCONNECTED) == FALSE) { /* we signed out, so this is expected */ register_fsa_input(C_CCM_CALLBACK, I_ERROR, NULL); crm_err("CCM connection appears to have failed: rc=%d.", rc); } was_error = TRUE; } trigger_fsa(fsa_source); return !was_error; } void crmd_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data) { gboolean update_cache = FALSE; const oc_ev_membership_t *membership = data; gboolean update_quorum = FALSE; gboolean trigger_transition = FALSE; crm_debug_3("Invoked"); CRM_ASSERT(data != NULL); crm_info("Quorum %s after event=%s (id=%d)", ccm_have_quorum(event)?"(re)attained":"lost", ccm_event_name(event), membership->m_instance); if(crm_peer_seq > membership->m_instance) { crm_err("Membership instance ID went backwards! %llu->%d", crm_peer_seq, membership->m_instance); CRM_ASSERT(crm_peer_seq <= membership->m_instance); return; } /* * OC_EV_MS_NEW_MEMBERSHIP: membership with quorum * OC_EV_MS_MS_INVALID: membership without quorum * OC_EV_MS_NOT_PRIMARY: previous membership no longer valid * OC_EV_MS_PRIMARY_RESTORED: previous membership restored * OC_EV_MS_EVICTED: the client is evicted from ccm. */ switch(event) { case OC_EV_MS_NEW_MEMBERSHIP: case OC_EV_MS_INVALID: update_cache = TRUE; update_quorum = TRUE; break; case OC_EV_MS_NOT_PRIMARY: break; case OC_EV_MS_PRIMARY_RESTORED: update_cache = TRUE; crm_peer_seq = membership->m_instance; if(AM_I_DC && need_transition(fsa_state)) { trigger_transition = TRUE; } break; case OC_EV_MS_EVICTED: update_quorum = TRUE; register_fsa_input(C_FSA_INTERNAL, I_STOP, NULL); crm_err("Shutting down after CCM event: %s", ccm_event_name(event)); break; default: crm_err("Unknown CCM event: %d", event); } if(update_quorum) { crm_have_quorum = ccm_have_quorum(event); crm_update_quorum(crm_have_quorum); if(crm_have_quorum == FALSE) { /* did we just loose quorum? */ if(fsa_have_quorum && need_transition(fsa_state)) { crm_info("Quorum lost: triggering transition (%s)", ccm_event_name(event)); trigger_transition = TRUE; } } } if(update_cache) { crm_debug_2("Updating cache after event %s", ccm_event_name(event)); do_ccm_update_cache(C_CCM_CALLBACK, fsa_state, event, data, NULL); } else if(event != OC_EV_MS_NOT_PRIMARY) { crm_peer_seq = membership->m_instance; register_fsa_action(A_TE_CANCEL); } oc_ev_callback_done(cookie); return; } #endif void crmd_cib_connection_destroy(gpointer user_data) { crm_debug_3("Invoked"); trigger_fsa(fsa_source); if(is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) { crm_info("Connection to the CIB terminated..."); return; } /* eventually this will trigger a reconnect, not a shutdown */ crm_err("Connection to the CIB terminated..."); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); clear_bit_inplace(fsa_input_register, R_CIB_CONNECTED); return; } longclock_t fsa_start = 0; longclock_t fsa_stop = 0; longclock_t fsa_diff = 0; gboolean crm_fsa_trigger(gpointer user_data) { unsigned int fsa_diff_ms = 0; if(fsa_diff_max_ms > 0) { fsa_start = time_longclock(); } crm_debug_2("Invoked (queue len: %d)", g_list_length(fsa_message_queue)); s_crmd_fsa(C_FSA_INTERNAL); crm_debug_2("Exited (queue len: %d)", g_list_length(fsa_message_queue)); if(fsa_diff_max_ms > 0) { fsa_stop = time_longclock(); fsa_diff = sub_longclock(fsa_stop, fsa_start); fsa_diff_ms = longclockto_ms(fsa_diff); if(fsa_diff_ms > fsa_diff_max_ms) { crm_err("FSA took %dms to complete", fsa_diff_ms); } else if(fsa_diff_ms > fsa_diff_warn_ms) { crm_warn("FSA took %dms to complete", fsa_diff_ms); } } return TRUE; } diff --git a/crmd/cib.c b/crmd/cib.c index 7e9ea13e35..3338bb4e59 100644 --- a/crmd/cib.c +++ b/crmd/cib.c @@ -1,310 +1,310 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /* for access */ #include #include #include /* for calls to open */ #include /* for calls to open */ #include /* for calls to open */ #include /* for getpwuid */ #include /* for initgroups */ #include /* for getrlimit */ #include /* for getrlimit */ #include #include #include #include #include #include #include struct crm_subsystem_s *cib_subsystem = NULL; int cib_retries = 0; static void revision_check_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { int cmp = -1; const char *revision = NULL; xmlNode *generation = NULL; #if CRM_DEPRECATED_SINCE_2_0_4 if(safe_str_eq(crm_element_name(output), XML_TAG_CIB)) { generation = output; } else { generation = find_xml_node(output, XML_TAG_CIB, TRUE); } #else generation = output; CRM_DEV_ASSERT(safe_str_eq(crm_element_name(generation), XML_TAG_CIB)); #endif if(rc != cib_ok) { fsa_data_t *msg_data = NULL; register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return; } crm_debug_3("Checking our feature revision is allowed: %s", CIB_FEATURE_SET); revision = crm_element_value(generation, XML_ATTR_CIB_REVISION); cmp = compare_version(revision, CIB_FEATURE_SET); if(cmp > 0) { crm_err("This build (%s) does not support the current" " resource configuration", VERSION); crm_err("We can support up to CIB feature set %s (current=%s)", CIB_FEATURE_SET, revision); crm_err("Shutting down the CRM"); /* go into a stall state */ register_fsa_error_adv( C_FSA_INTERNAL, I_SHUTDOWN, NULL, NULL, __FUNCTION__); return; } revision = crm_element_value(generation, XML_ATTR_CRM_VERSION); cmp = compare_version(revision, CRM_FEATURE_SET); if(cmp > 0) { crm_err("This build (%s) does not support the current" " resource configuration", VERSION); crm_err("We can support up to CRM feature set %s (current=%s)", revision, CRM_FEATURE_SET); crm_err("Shutting down the CRM"); /* go into a stall state */ register_fsa_error_adv( C_FSA_INTERNAL, I_SHUTDOWN, NULL, NULL, __FUNCTION__); return; } } static void do_cib_replaced(const char *event, xmlNode *msg) { crm_debug("Updating the CIB after a replace: DC=%s", AM_I_DC?"true":"false"); if(AM_I_DC) { /* start the join process again so we get everyone's LRM status */ populate_cib_nodes(FALSE); do_update_cib_nodes(TRUE, __FUNCTION__); register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL); } } /* A_CIB_STOP, A_CIB_START, A_CIB_RESTART, */ void do_cib_control(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { struct crm_subsystem_s *this_subsys = cib_subsystem; long long stop_actions = A_CIB_STOP; long long start_actions = A_CIB_START; if(action & stop_actions) { crm_info("Disconnecting CIB"); clear_bit_inplace(fsa_input_register, R_CIB_CONNECTED); CRM_ASSERT(fsa_cib_conn != NULL); if(fsa_cib_conn->state != cib_disconnected) { fsa_cib_conn->cmds->set_slave( fsa_cib_conn, cib_scope_local); fsa_cib_conn->cmds->signoff(fsa_cib_conn); } } if(action & start_actions) { int rc = cib_ok; CRM_ASSERT(fsa_cib_conn != NULL); if(cur_state == S_STOPPING) { crm_err("Ignoring request to start %s after shutdown", this_subsys->name); return; } rc = fsa_cib_conn->cmds->signon( fsa_cib_conn, CRM_SYSTEM_CRMD, cib_command); if(rc != cib_ok) { /* a short wait that usually avoids stalling the FSA */ sleep(1); rc = fsa_cib_conn->cmds->signon( fsa_cib_conn, CRM_SYSTEM_CRMD, cib_command); } if(rc != cib_ok){ crm_debug("Could not connect to the CIB service"); } else if(cib_ok != fsa_cib_conn->cmds->set_connection_dnotify( fsa_cib_conn, crmd_cib_connection_destroy)) { crm_err("Could not set dnotify callback"); } else if(cib_ok != fsa_cib_conn->cmds->add_notify_callback( fsa_cib_conn, T_CIB_REPLACE_NOTIFY, do_cib_replaced)) { crm_err("Could not set CIB notification callback"); } else { set_bit_inplace( fsa_input_register, R_CIB_CONNECTED); } if(is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) { cib_retries++; crm_warn("Couldn't complete CIB registration %d" " times... pause and retry", cib_retries); if(cib_retries < 30) { crm_timer_start(wait_timer); crmd_fsa_stall(NULL); } else { crm_err("Could not complete CIB" " registration %d times..." " hard error", cib_retries); register_fsa_error( C_FSA_INTERNAL, I_ERROR, NULL); } } else { int call_id = 0; crm_info("CIB connection established"); call_id = fsa_cib_conn->cmds->query( fsa_cib_conn, NULL, NULL, cib_scope_local); add_cib_op_callback(call_id, FALSE, NULL, revision_check_callback); cib_retries = 0; } } } /* A_CIB_INVOKE, A_CIB_BUMPGEN, A_UPDATE_NODESTATUS */ void do_cib_invoke(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { xmlNode *answer = NULL; ha_msg_input_t *cib_msg = fsa_typed_data(fsa_dt_ha_msg); const char *sys_from = crm_element_value(cib_msg->msg, F_CRM_SYS_FROM); if(fsa_cib_conn->state == cib_disconnected) { if(cur_state != S_STOPPING) { crm_err("CIB is disconnected"); crm_log_xml(LOG_WARNING, "CIB Input", cib_msg->msg); return; } crm_info("CIB is disconnected"); crm_log_xml(LOG_DEBUG, "CIB Input", cib_msg->msg); return; } if(action & A_CIB_INVOKE) { if(safe_str_eq(sys_from, CRM_SYSTEM_CRMD)) { action = A_CIB_INVOKE_LOCAL; } else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) { action = A_CIB_INVOKE_LOCAL; } } if(action & A_CIB_INVOKE || action & A_CIB_INVOKE_LOCAL) { int call_options = 0; enum cib_errors rc = cib_ok; xmlNode *cib_frag = NULL; const char *section = NULL; const char *op = crm_element_value(cib_msg->msg, F_CRM_TASK); section = crm_element_value(cib_msg->msg, F_CIB_SECTION); crm_element_value_int(cib_msg->msg, F_CIB_CALLOPTS, &call_options); crm_log_xml(LOG_MSG, "udpate", cib_msg->msg); if(op == NULL) { crm_err("Invalid CIB Message"); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return; } cib_frag = NULL; rc = fsa_cib_conn->cmds->variant_op( fsa_cib_conn, op, NULL, section, cib_msg->xml, &cib_frag, call_options); if(rc < cib_ok || (action & A_CIB_INVOKE)) { answer = create_reply(cib_msg->msg, cib_frag); crm_xml_add(answer,XML_ATTR_RESULT,cib_error2string(rc)); } if(action & A_CIB_INVOKE) { if(relay_message(answer, TRUE) == FALSE) { crm_err("Confused what to do with cib result"); crm_log_xml(LOG_ERR, "reply", answer); free_xml(answer); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); return; } } else if(rc < cib_ok) { ha_msg_input_t *input = NULL; crm_err("Internal CRM/CIB command from %s() failed: %s", msg_data->origin, cib_error2string(rc)); crm_log_xml(LOG_WARNING, "CIB Input", cib_msg->msg); crm_log_xml(LOG_WARNING, "CIB Reply", answer); input = new_ha_msg_input(answer); register_fsa_input(C_FSA_INTERNAL, I_ERROR, input); free_xml(answer); - delete_ha_msg_input(input); + crm_free(input); } } else { crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__); } } diff --git a/crmd/messages.c b/crmd/messages.c index 4781b33c87..258f8b335f 100644 --- a/crmd/messages.c +++ b/crmd/messages.c @@ -1,1207 +1,1203 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include GListPtr fsa_message_queue = NULL; extern void crm_shutdown(int nsig); enum crmd_fsa_input handle_request(ha_msg_input_t *stored_msg); enum crmd_fsa_input handle_response(ha_msg_input_t *stored_msg); enum crmd_fsa_input handle_shutdown_request(xmlNode *stored_msg); ha_msg_input_t *copy_ha_msg_input(ha_msg_input_t *orig); gboolean ipc_queue_helper(gpointer key, gpointer value, gpointer user_data); #ifdef MSG_LOG # define ROUTER_RESULT(x) crm_debug_3("Router result: %s", x); \ crm_log_xml(LOG_MSG, "router.log", relay_message); #else # define ROUTER_RESULT(x) crm_debug_3("Router result: %s", x) #endif /* debug only, can wrap all it likes */ int last_data_id = 0; void register_fsa_error_adv( enum crmd_fsa_cause cause, enum crmd_fsa_input input, fsa_data_t *cur_data, void *new_data, const char *raised_from) { /* save the current actions if any */ if(fsa_actions != A_NOTHING) { register_fsa_input_adv( cur_data?cur_data->fsa_cause:C_FSA_INTERNAL, I_NULL, cur_data?cur_data->data:NULL, fsa_actions, TRUE, __FUNCTION__); } /* reset the action list */ fsa_actions = A_NOTHING; /* register the error */ register_fsa_input_adv( cause, input, new_data, A_NOTHING, TRUE, raised_from); } static gboolean last_was_vote = FALSE; int register_fsa_input_adv( enum crmd_fsa_cause cause, enum crmd_fsa_input input, void *data, long long with_actions, gboolean prepend, const char *raised_from) { unsigned old_len = g_list_length(fsa_message_queue); fsa_data_t *fsa_data = NULL; last_data_id++; CRM_CHECK(raised_from != NULL, raised_from = ""); crm_debug("%s %s FSA input %d (%s) (cause=%s) %s data", raised_from, prepend?"prepended":"appended",last_data_id, fsa_input2string(input), fsa_cause2string(cause), data?"with":"without"); if(input == I_WAIT_FOR_EVENT) { do_fsa_stall = TRUE; crm_debug("Stalling the FSA pending further input: cause=%s", fsa_cause2string(cause)); if(old_len > 0) { crm_warn("%s stalled the FSA with pending inputs", raised_from); fsa_dump_queue(LOG_DEBUG); } if(data == NULL) { set_bit_inplace(fsa_actions, with_actions); with_actions = A_NOTHING; return 0; } crm_err("%s stalled the FSA with data - this may be broken", raised_from); } if(old_len == 0) { last_was_vote = FALSE; } if(input == I_NULL && with_actions == A_NOTHING /* && data == NULL */){ /* no point doing anything */ crm_err("Cannot add entry to queue: no input and no action"); return 0; } else if(data == NULL) { last_was_vote = FALSE; #if 0 } else if(last_was_vote && cause == C_HA_MESSAGE && input == I_ROUTER) { const char *op = crm_element_value( ((ha_msg_input_t*)data)->msg, F_CRM_TASK); if(safe_str_eq(op, CRM_OP_VOTE)) { /* It is always safe to treat N successive votes as * a single one * * If all the discarded votes are more "loosing" than * the first then the result is accurate * (win or loose). * * If any of the discarded votes are less "loosing" * than the first then we will cast our vote and the * eventual winner will vote us down again (which * even in the case that N=2, is no worse than if we * had not disarded the vote). */ crm_debug_2("Vote compression: %d", old_len); return 0; } #endif } else if (cause == C_HA_MESSAGE && input == I_ROUTER) { const char *op = crm_element_value( ((ha_msg_input_t*)data)->msg, F_CRM_TASK); if(safe_str_eq(op, CRM_OP_VOTE)) { last_was_vote = TRUE; crm_debug_3("Added vote: %d", old_len); } } else { last_was_vote = FALSE; } crm_malloc0(fsa_data, sizeof(fsa_data_t)); fsa_data->id = last_data_id; fsa_data->fsa_input = input; fsa_data->fsa_cause = cause; fsa_data->origin = raised_from; fsa_data->data = NULL; fsa_data->data_type = fsa_dt_none; fsa_data->actions = with_actions; if(with_actions != A_NOTHING) { crm_debug_3("Adding actions %.16llx to input", with_actions); } if(data != NULL) { switch(cause) { case C_FSA_INTERNAL: case C_CRMD_STATUS_CALLBACK: case C_IPC_MESSAGE: case C_HA_MESSAGE: crm_debug_3("Copying %s data from %s as a HA msg", fsa_cause2string(cause), raised_from); fsa_data->data = copy_ha_msg_input(data); fsa_data->data_type = fsa_dt_ha_msg; break; case C_LRM_OP_CALLBACK: crm_debug_3("Copying %s data from %s as lrm_op_t", fsa_cause2string(cause), raised_from); fsa_data->data = copy_lrm_op((lrm_op_t*)data); fsa_data->data_type = fsa_dt_lrm; break; case C_CCM_CALLBACK: case C_SUBSYSTEM_CONNECT: case C_LRM_MONITOR_CALLBACK: case C_TIMER_POPPED: case C_SHUTDOWN: case C_HEARTBEAT_FAILED: case C_HA_DISCONNECT: case C_ILLEGAL: case C_UNKNOWN: case C_STARTUP: crm_err("Copying %s data (from %s)" " not yet implemented", fsa_cause2string(cause), raised_from); exit(1); break; } crm_debug_4("%s data copied", fsa_cause2string(fsa_data->fsa_cause)); } /* make sure to free it properly later */ if(prepend) { crm_debug_2("Prepending input"); fsa_message_queue = g_list_prepend(fsa_message_queue, fsa_data); } else { fsa_message_queue = g_list_append(fsa_message_queue, fsa_data); } crm_debug_2("Queue len: %d", g_list_length(fsa_message_queue)); fsa_dump_queue(LOG_DEBUG_2); if(old_len == g_list_length(fsa_message_queue)){ crm_err("Couldnt add message to the queue"); } if(fsa_source) { crm_debug_3("Triggering FSA: %s", __FUNCTION__); G_main_set_trigger(fsa_source); } return last_data_id; } void fsa_dump_queue(int log_level) { if(log_level < (int)crm_log_level) { return; } slist_iter( data, fsa_data_t, fsa_message_queue, lpc, do_crm_log(log_level, "queue[%d(%d)]: input %s raised by %s()\t(cause=%s)", lpc, data->id, fsa_input2string(data->fsa_input), data->origin, fsa_cause2string(data->fsa_cause)); ); } ha_msg_input_t * copy_ha_msg_input(ha_msg_input_t *orig) { - ha_msg_input_t *input_copy = NULL; - crm_malloc0(input_copy, sizeof(ha_msg_input_t)); - - if(orig != NULL) { - crm_debug_4("Copy msg"); - input_copy->msg = copy_xml(orig->msg); - if(orig->xml != NULL) { - crm_debug_4("Copy xml"); - input_copy->xml = copy_xml(orig->xml); - } - } else { - crm_debug_3("No message to copy"); - } - return input_copy; + xmlNodePtr data = NULL; + + if(orig != NULL) { + crm_debug_4("Copy msg"); + data = copy_xml(orig->msg); + + } else { + crm_debug_3("No message to copy"); + } + return new_ha_msg_input(data); } void delete_fsa_input(fsa_data_t *fsa_data) { lrm_op_t *op = NULL; xmlNode *foo = NULL; if(fsa_data == NULL) { return; } crm_debug_4("About to free %s data", fsa_cause2string(fsa_data->fsa_cause)); if(fsa_data->data != NULL) { switch(fsa_data->data_type) { case fsa_dt_ha_msg: delete_ha_msg_input(fsa_data->data); break; case fsa_dt_xml: foo = fsa_data->data; free_xml(foo); break; case fsa_dt_lrm: op = (lrm_op_t*)fsa_data->data; free_lrm_op(op); break; case fsa_dt_none: if(fsa_data->data != NULL) { crm_err("Dont know how to free %s data from %s", fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin); exit(1); } break; } crm_debug_4("%s data freed", fsa_cause2string(fsa_data->fsa_cause)); } crm_free(fsa_data); } /* returns the next message */ fsa_data_t * get_message(void) { fsa_data_t* message = g_list_nth_data(fsa_message_queue, 0); fsa_message_queue = g_list_remove(fsa_message_queue, message); crm_debug_2("Processing input %d", message->id); return message; } /* returns the current head of the FIFO queue */ gboolean is_message(void) { return (g_list_length(fsa_message_queue) > 0); } void * fsa_typed_data_adv( fsa_data_t *fsa_data, enum fsa_data_type a_type, const char *caller) { void *ret_val = NULL; if(fsa_data == NULL) { do_crm_log(LOG_ERR, "%s: No FSA data available", caller); } else if(fsa_data->data == NULL) { do_crm_log(LOG_ERR, "%s: No message data available", caller); } else if(fsa_data->data_type != a_type) { do_crm_log(LOG_CRIT, "%s: Message data was the wrong type! %d vs. requested=%d." " Origin: %s", caller, fsa_data->data_type, a_type, fsa_data->origin); CRM_ASSERT(fsa_data->data_type == a_type); } else { ret_val = fsa_data->data; } return ret_val; } /* A_MSG_ROUTE */ void do_msg_route(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); route_message(msg_data->fsa_cause, input); } void route_message(enum crmd_fsa_cause cause, ha_msg_input_t *input) { enum crmd_fsa_input result = I_NULL; CRM_CHECK(cause == C_IPC_MESSAGE || cause == C_HA_MESSAGE, return); /* try passing the buck first */ crm_debug_4("Attempting to route message"); if(relay_message(input->msg, cause==C_IPC_MESSAGE)) { crm_debug_4("Message routed..."); input->msg = NULL; return; } crm_debug_4("Message wasn't routed... try handling locally"); /* calculate defer */ result = handle_message(input); switch(result) { case I_NULL: crm_debug_4("Message processed"); break; case I_CIB_OP: break; case I_ROUTER: break; case I_NODE_JOIN: case I_JOIN_REQUEST: case I_JOIN_RESULT: break; default: crm_debug_4("Defering local processing of message"); register_fsa_input_later(cause, result, input); result = I_NULL; break; } if(result != I_NULL) { /* add to the front of the queue */ register_fsa_input(cause, result, input); } } /* * This method frees msg */ gboolean send_request(xmlNode *msg, char **msg_reference) { gboolean was_sent = FALSE; /* crm_log_xml_debug_3(request, "Final request..."); */ if(msg_reference != NULL) { *msg_reference = crm_strdup( crm_element_value(msg, XML_ATTR_REFERENCE)); } was_sent = relay_message(msg, TRUE); if(was_sent == FALSE) { ha_msg_input_t *fsa_input = new_ha_msg_input(msg); register_fsa_input(C_IPC_MESSAGE, I_ROUTER, fsa_input); - delete_ha_msg_input(fsa_input); + crm_free(fsa_input); + free_xml(msg); } return was_sent; } /* unless more processing is required, relay_message is freed */ gboolean relay_message(xmlNode *relay_message, gboolean originated_locally) { int is_for_dc = 0; int is_for_dcib = 0; int is_for_te = 0; int is_for_crm = 0; int is_for_cib = 0; int is_local = 0; gboolean processing_complete = FALSE; const char *host_to = crm_element_value(relay_message, F_CRM_HOST_TO); const char *sys_to = crm_element_value(relay_message, F_CRM_SYS_TO); const char *sys_from= crm_element_value(relay_message, F_CRM_SYS_FROM); const char *type = crm_element_value(relay_message, F_TYPE); const char *msg_error = NULL; crm_debug_3("Routing message %s", crm_element_value(relay_message, XML_ATTR_REFERENCE)); if(relay_message == NULL) { msg_error = "Cannot route empty message"; } else if(safe_str_eq(CRM_OP_HELLO, crm_element_value(relay_message, F_CRM_TASK))){ /* quietly ignore */ processing_complete = TRUE; } else if(safe_str_neq(type, T_CRM)) { msg_error = "Bad message type"; } else if(sys_to == NULL) { msg_error = "Bad message destination: no subsystem"; } if(msg_error != NULL) { processing_complete = TRUE; crm_err("%s", msg_error); crm_log_xml(LOG_WARNING, "bad msg", relay_message); } if(processing_complete) { free_xml(relay_message); return TRUE; } processing_complete = TRUE; is_for_dc = (strcasecmp(CRM_SYSTEM_DC, sys_to) == 0); is_for_dcib = (strcasecmp(CRM_SYSTEM_DCIB, sys_to) == 0); is_for_te = (strcasecmp(CRM_SYSTEM_TENGINE, sys_to) == 0); is_for_cib = (strcasecmp(CRM_SYSTEM_CIB, sys_to) == 0); is_for_crm = (strcasecmp(CRM_SYSTEM_CRMD, sys_to) == 0); is_local = 0; if(host_to == NULL || strlen(host_to) == 0) { if(is_for_dc || is_for_te) { is_local = 0; } else if(is_for_crm && originated_locally) { is_local = 0; } else { is_local = 1; } } else if(safe_str_eq(fsa_our_uname, host_to)) { is_local=1; } if(is_for_dc || is_for_dcib || is_for_te) { if(AM_I_DC && is_for_te) { ROUTER_RESULT("Message result: Local relay"); send_msg_via_ipc(relay_message, sys_to); } else if(AM_I_DC) { ROUTER_RESULT("Message result: DC/CRMd process"); processing_complete = FALSE; /* more to be done by caller */ } else if(originated_locally && safe_str_neq(sys_from, CRM_SYSTEM_PENGINE) && safe_str_neq(sys_from, CRM_SYSTEM_TENGINE)) { /* Neither the TE or PE should be sending messages * to DC's on other nodes * * By definition, if we are no longer the DC, then * the PE or TE's data should be discarded */ ROUTER_RESULT("Message result: External relay to DC"); send_msg_via_ha(relay_message); } else { /* discard */ ROUTER_RESULT("Message result: Discard, not DC"); free_xml(relay_message); } } else if(is_local && (is_for_crm || is_for_cib)) { ROUTER_RESULT("Message result: CRMd process"); processing_complete = FALSE; /* more to be done by caller */ } else if(is_local) { ROUTER_RESULT("Message result: Local relay"); send_msg_via_ipc(relay_message, sys_to); } else { ROUTER_RESULT("Message result: External relay"); send_msg_via_ha(relay_message); } return processing_complete; } gboolean crmd_authorize_message(ha_msg_input_t *client_msg, crmd_client_t *curr_client) { /* check the best case first */ const char *sys_from = crm_element_value(client_msg->msg, F_CRM_SYS_FROM); char *uuid = NULL; char *client_name = NULL; char *major_version = NULL; char *minor_version = NULL; const char *filtered_from; gpointer table_key = NULL; gboolean auth_result = FALSE; struct crm_subsystem_s *the_subsystem = NULL; gboolean can_reply = FALSE; /* no-one has registered with this id */ const char *op = crm_element_value(client_msg->msg, F_CRM_TASK); if (safe_str_neq(CRM_OP_HELLO, op)) { if(sys_from == NULL) { crm_warn("Message [%s] was had no value for %s... discarding", crm_element_value(client_msg->msg, XML_ATTR_REFERENCE), F_CRM_SYS_FROM); return FALSE; } filtered_from = sys_from; /* The CIB can have two names on the DC */ if(strcasecmp(sys_from, CRM_SYSTEM_DCIB) == 0) filtered_from = CRM_SYSTEM_CIB; if (g_hash_table_lookup (ipc_clients, filtered_from) != NULL) { can_reply = TRUE; /* reply can be routed */ } crm_debug_2("Message reply can%s be routed from %s.", can_reply?"":" not", sys_from); if(can_reply == FALSE) { crm_warn("Message [%s] not authorized", crm_element_value(client_msg->msg, XML_ATTR_REFERENCE)); } return can_reply; } crm_debug_3("received client join msg"); crm_log_xml(LOG_MSG, "join", client_msg->msg); auth_result = process_hello_message( client_msg->xml, &uuid, &client_name, &major_version, &minor_version); if (auth_result == TRUE) { if(client_name == NULL || uuid == NULL) { crm_err("Bad client details (client_name=%s, uuid=%s)", crm_str(client_name), crm_str(uuid)); auth_result = FALSE; } } if (auth_result == TRUE) { /* check version */ int mav = atoi(major_version); int miv = atoi(minor_version); crm_debug_3("Checking client version number"); if (mav < 0 || miv < 0) { crm_err("Client version (%d:%d) is not acceptable", mav, miv); auth_result = FALSE; } crm_free(major_version); crm_free(minor_version); } if (safe_str_eq(CRM_SYSTEM_PENGINE, client_name)) { the_subsystem = pe_subsystem; } else if (safe_str_eq(CRM_SYSTEM_TENGINE, client_name)) { the_subsystem = te_subsystem; } if (auth_result == TRUE && the_subsystem != NULL) { /* if we already have one of those clients * only applies to te, pe etc. not admin clients */ crm_debug_3("Checking if %s is required/already connected", client_name); table_key = (gpointer)crm_strdup(client_name); if(is_set(fsa_input_register, the_subsystem->flag_connected)) { auth_result = FALSE; crm_free(table_key); table_key = NULL; crm_warn("Bit\t%.16llx set in %.16llx", the_subsystem->flag_connected, fsa_input_register); crm_err("Client %s is already connected", client_name); } else if(FALSE == is_set(fsa_input_register, the_subsystem->flag_required)) { crm_warn("Bit\t%.16llx not set in %.16llx", the_subsystem->flag_connected, fsa_input_register); crm_warn("Client %s joined but we dont need it", client_name); stop_subsystem(the_subsystem, TRUE); } else { the_subsystem->ipc = curr_client->client_channel; set_bit_inplace(fsa_input_register, the_subsystem->flag_connected); } } else { table_key = (gpointer)generate_hash_key(client_name, uuid); } if (auth_result == TRUE) { crm_debug_2("Accepted client %s", crm_str(table_key)); curr_client->table_key = table_key; curr_client->sub_sys = crm_strdup(client_name); curr_client->uuid = crm_strdup(uuid); g_hash_table_insert (ipc_clients, table_key, curr_client->client_channel); send_hello_message(curr_client->client_channel, "n/a", CRM_SYSTEM_CRMD, "0", "1"); crm_debug_3("Updated client list with %s", crm_str(table_key)); crm_debug_3("Triggering FSA: %s", __FUNCTION__); G_main_set_trigger(fsa_source); if(the_subsystem != NULL) { CRM_CHECK(the_subsystem->client == NULL, process_client_disconnect(the_subsystem->client)); the_subsystem->client = curr_client; } } else { crm_free(table_key); crm_warn("Rejected client logon request"); curr_client->client_channel->ch_status = IPC_DISC_PENDING; } if(uuid != NULL) crm_free(uuid); if(minor_version != NULL) crm_free(minor_version); if(major_version != NULL) crm_free(major_version); if(client_name != NULL) crm_free(client_name); /* hello messages should never be processed further */ return FALSE; } enum crmd_fsa_input handle_message(ha_msg_input_t *stored_msg) { enum crmd_fsa_input next_input = I_NULL; const char *type = NULL; if(stored_msg == NULL || stored_msg->msg == NULL) { crm_err("No message to handle"); return I_NULL; } type = crm_element_value(stored_msg->msg, F_CRM_MSG_TYPE); if(safe_str_eq(type, XML_ATTR_REQUEST)) { next_input = handle_request(stored_msg); } else if(safe_str_eq(type, XML_ATTR_RESPONSE)) { next_input = handle_response(stored_msg); } else { crm_err("Unknown message type: %s", type); } /* crm_debug_2("%s: Next input is %s", __FUNCTION__, */ /* fsa_input2string(next_input)); */ return next_input; } #define schedule_pe() do { \ next_input = I_PE_CALC; \ if(fsa_pe_ref) { \ crm_debug("Cancelling %s...", fsa_pe_ref); \ crm_free(fsa_pe_ref); \ fsa_pe_ref = NULL; \ } \ } while(0) enum crmd_fsa_input handle_request(ha_msg_input_t *stored_msg) { xmlNode *msg = NULL; enum crmd_fsa_input next_input = I_NULL; const char *op = crm_element_value(stored_msg->msg, F_CRM_TASK); const char *sys_to = crm_element_value(stored_msg->msg, F_CRM_SYS_TO); const char *host_from = crm_element_value(stored_msg->msg, F_CRM_HOST_FROM); crm_debug_2("Received %s "XML_ATTR_REQUEST" from %s in state %s", op, host_from, fsa_state2string(fsa_state)); if(op == NULL) { crm_log_xml(LOG_ERR, "Bad message", stored_msg->msg); /*========== common actions ==========*/ } else if(strcasecmp(op, CRM_OP_NOOP) == 0) { crm_debug_2("no-op from %s", crm_str(host_from)); } else if(strcasecmp(op, CRM_OP_NOVOTE) == 0) { register_fsa_input_adv(C_HA_MESSAGE, I_NULL, stored_msg, A_ELECTION_COUNT|A_ELECTION_CHECK, FALSE, __FUNCTION__); } else if(strcasecmp(op, CRM_OP_VOTE) == 0) { /* count the vote and decide what to do after that */ register_fsa_input_adv(C_HA_MESSAGE, I_NULL, stored_msg, A_ELECTION_COUNT|A_ELECTION_CHECK, FALSE, __FUNCTION__); /* Sometimes we _must_ go into S_ELECTION */ if(fsa_state == S_HALT) { crm_debug("Forcing an election from S_HALT"); next_input = I_ELECTION; #if 0 } else if(AM_I_DC) { /* This is the old way of doing things but what is gained? */ next_input = I_ELECTION; #endif } } else if(strcasecmp(op, CRM_OP_LOCAL_SHUTDOWN) == 0) { crm_shutdown(SIGTERM); /*next_input = I_SHUTDOWN; */ next_input = I_NULL; } else if(strcasecmp(op, CRM_OP_PING) == 0) { /* eventually do some stuff to figure out * if we /are/ ok */ xmlNode *ping = createPingAnswerFragment(sys_to, "ok"); crm_xml_add(ping, "crmd_state", fsa_state2string(fsa_state)); crm_info("Current ping state: %s", fsa_state2string(fsa_state)); msg = create_reply(stored_msg->msg, ping); free_xml(ping); if(relay_message(msg, TRUE) == FALSE) { free_xml(msg); } /* probably better to do this via signals on the * local node */ } else if(strcasecmp(op, CRM_OP_DEBUG_UP) == 0) { alter_debug(DEBUG_INC); crm_info("Debug set to %d", get_crm_log_level()); } else if(strcasecmp(op, CRM_OP_DEBUG_DOWN) == 0) { alter_debug(DEBUG_DEC); crm_info("Debug set to %d", get_crm_log_level()); } else if(strcasecmp(op, CRM_OP_JOIN_OFFER) == 0) { next_input = I_JOIN_OFFER; crm_debug("Raising I_JOIN_OFFER: join-%s", crm_element_value(stored_msg->msg, F_CRM_JOIN_ID)); } else if(strcasecmp(op, CRM_OP_JOIN_ACKNAK) == 0) { next_input = I_JOIN_RESULT; crm_debug("Raising I_JOIN_RESULT: join-%s", crm_element_value(stored_msg->msg, F_CRM_JOIN_ID)); } else if(strcasecmp(op, CRM_OP_LRM_DELETE) == 0 || strcasecmp(op, CRM_OP_LRM_FAIL) == 0 || strcasecmp(op, CRM_OP_LRM_REFRESH) == 0 || strcasecmp(op, CRM_OP_REPROBE) == 0) { crm_xml_add(stored_msg->msg, F_CRM_SYS_TO, CRM_SYSTEM_LRMD); next_input = I_ROUTER; /* this functionality should only be enabled * if this is a development build */ } else if(CRM_DEV_BUILD && strcasecmp(op, CRM_OP_DIE) == 0/*constant condition*/) { crm_warn("Test-only code: Killing the CRM without mercy"); crm_warn("Inhibiting respawns"); exit(100); /*========== (NOT_DC)-Only Actions ==========*/ } else if(AM_I_DC == FALSE){ gboolean dc_match = safe_str_eq(host_from, fsa_our_dc); if(dc_match || fsa_our_dc == NULL) { if(strcasecmp(op, CRM_OP_HBEAT) == 0) { crm_debug_3("Received DC heartbeat from %s", host_from); next_input = I_DC_HEARTBEAT; } else if(fsa_our_dc == NULL) { crm_warn("CRMd discarding request: %s" " (DC: %s, from: %s)", op, crm_str(fsa_our_dc), host_from); crm_log_xml(LOG_WARNING, "Ignored Request", stored_msg->msg); } else if(strcasecmp(op, CRM_OP_SHUTDOWN) == 0) { next_input = I_STOP; } else { crm_err("CRMd didnt expect request: %s", op); crm_log_xml(LOG_ERR, "bad request", stored_msg->msg); } } else { crm_warn("Discarding %s op from %s", op, host_from); } /*========== DC-Only Actions ==========*/ } else if(AM_I_DC) { const char *message = crm_element_value( stored_msg->msg, "message"); /* setting "fsa_pe_ref = NULL" makes sure we ignore any * PE reply that might be pending or in the queue while * we ask the CIB for a more up-to-date copy */ if(safe_str_eq(op, CRM_OP_TEABORT)) { crm_debug("Transition cancelled: %s/%s", op, message); clear_bit_inplace(fsa_input_register, R_IN_TRANSITION); if(need_transition(fsa_state)) { schedule_pe(); } else { crm_debug("Filtering %s op in state %s", op, fsa_state2string(fsa_state)); } } else if(strcasecmp(op, CRM_OP_TECOMPLETE) == 0) { crm_debug("Transition complete: %s/%s", op, message); clear_bit_inplace(fsa_input_register, R_IN_TRANSITION); if(fsa_state == S_TRANSITION_ENGINE) { next_input = I_TE_SUCCESS; } else { crm_debug("Filtering %s op in state %s", op, fsa_state2string(fsa_state)); } } else if(strcasecmp(op, CRM_OP_JOIN_ANNOUNCE) == 0) { next_input = I_NODE_JOIN; } else if(strcasecmp(op, CRM_OP_JOIN_REQUEST) == 0) { next_input = I_JOIN_REQUEST; } else if(strcasecmp(op, CRM_OP_JOIN_CONFIRM) == 0) { next_input = I_JOIN_RESULT; } else if(strcasecmp(op, CRM_OP_SHUTDOWN) == 0) { gboolean dc_match = safe_str_eq(host_from, fsa_our_dc); if(dc_match) { crm_err("We didnt ask to be shut down yet our" " TE is telling us too." " Better get out now!"); next_input = I_TERMINATE; } else if(is_set(fsa_input_register, R_SHUTDOWN)) { crm_info("Shutting ourselves down (DC)"); next_input = I_STOP; } else if(fsa_state != S_STOPPING) { crm_err("Another node is asking us to shutdown" " but we think we're ok."); next_input = I_ELECTION; } } else if(strcasecmp(op, CRM_OP_SHUTDOWN_REQ) == 0) { /* a slave wants to shut down */ /* create cib fragment and add to message */ next_input = handle_shutdown_request(stored_msg->msg); } else { crm_err("Unexpected request (%s) sent to the DC", op); crm_log_xml(LOG_ERR, "Unexpected", stored_msg->msg); } } return next_input; } enum crmd_fsa_input handle_response(ha_msg_input_t *stored_msg) { enum crmd_fsa_input next_input = I_NULL; const char *op = crm_element_value(stored_msg->msg, F_CRM_TASK); const char *sys_from = crm_element_value(stored_msg->msg, F_CRM_SYS_FROM); const char *host_from = crm_element_value(stored_msg->msg, F_CRM_HOST_FROM); const char *msg_ref = crm_element_value(stored_msg->msg, XML_ATTR_REFERENCE); crm_debug_2("Received %s "XML_ATTR_RESPONSE" from %s in state %s", op, host_from, fsa_state2string(fsa_state)); if(op == NULL) { crm_log_xml(LOG_ERR, "Bad message", stored_msg->msg); } else if(AM_I_DC && strcasecmp(op, CRM_OP_PECALC) == 0) { crm_debug_2("Processing %s reply %s (fsa=%s)", sys_from, msg_ref, crm_str(fsa_pe_ref)); if(msg_ref != NULL && safe_str_eq(msg_ref, fsa_pe_ref)) { next_input = I_PE_SUCCESS; crm_debug_2("Completed: %s...", fsa_pe_ref); crm_free(fsa_pe_ref); fsa_pe_ref = NULL; } else { crm_debug_2("Skipping superceeded reply from %s", sys_from); } } else if(strcasecmp(op, CRM_OP_VOTE) == 0 || strcasecmp(op, CRM_OP_HBEAT) == 0 || strcasecmp(op, CRM_OP_SHUTDOWN_REQ) == 0 || strcasecmp(op, CRM_OP_SHUTDOWN) == 0) { crm_debug_2("Ignoring %s from %s in %s", op, host_from, fsa_state2string(fsa_state)); next_input = I_NULL; } else { crm_err("Unexpected response (op=%s) sent to the %s", op, AM_I_DC?"DC":"CRMd"); next_input = I_NULL; } return next_input; } enum crmd_fsa_input handle_shutdown_request(xmlNode *stored_msg) { /* handle here to avoid potential version issues * where the shutdown message/proceedure may have * been changed in later versions. * * This way the DC is always in control of the shutdown */ time_t now = time(NULL); xmlNode *node_state = NULL; const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM); if(host_from == NULL) { /* we're shutting down and the DC */ host_from = fsa_our_uname; } crm_info("Creating shutdown request for %s",host_from); crm_log_xml(LOG_MSG, "message", stored_msg); node_state = create_node_state( host_from, NULL, NULL, NULL, NULL, CRMD_STATE_INACTIVE, FALSE, __FUNCTION__); crm_xml_add_int(node_state, XML_CIB_ATTR_SHUTDOWN, (int)now); fsa_cib_anon_update(XML_CIB_TAG_STATUS,node_state, cib_quorum_override); crm_log_xml_debug_2(node_state, "Shutdown update"); free_xml(node_state); /* will be picked up by the TE as long as its running */ if(need_transition(fsa_state) && is_set(fsa_input_register, R_TE_CONNECTED) == FALSE) { register_fsa_action(A_TE_CANCEL); } return I_NULL; } /* frees msg upon completion */ gboolean send_msg_via_ha(xmlNode *msg) { int log_level = LOG_DEBUG_3; gboolean broadcast = FALSE; gboolean all_is_good = TRUE; const char *op = crm_element_value(msg, F_CRM_TASK); const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO); const char *host_to = crm_element_value(msg, F_CRM_HOST_TO); enum crm_ais_msg_types dest = 0; if(is_openais_cluster()) { dest = 1; #if SUPPORT_AIS dest = text2msg_type(sys_to); #endif } if (msg == NULL) { crm_err("Attempt to send NULL Message via HA failed."); all_is_good = FALSE; } else { crm_debug_4("Relaying message to (%s) via HA", host_to); } if (all_is_good) { if (sys_to == NULL || strlen(sys_to) == 0) { crm_err("You did not specify a destination sub-system" " for this message."); all_is_good = FALSE; } } /* There are a number of messages may not need to be ordered. * At a later point perhaps we should detect them and send them * as unordered messages. */ if (all_is_good) { if (host_to == NULL || strlen(host_to) == 0 || safe_str_eq(sys_to, CRM_SYSTEM_DC)) { broadcast = TRUE; all_is_good = send_cluster_message(NULL, dest, msg, FALSE); } else { all_is_good = send_cluster_message(host_to, dest, msg, FALSE); } } if(all_is_good == FALSE) { log_level = LOG_WARNING; } if(log_level == LOG_WARNING || (safe_str_neq(op, CRM_OP_HBEAT))) { do_crm_log(log_level, "Sending %sHA message (ref=%s) to %s@%s %s.", broadcast?"broadcast ":"directed ", crm_element_value(msg, XML_ATTR_REFERENCE), crm_str(sys_to), host_to==NULL?"":host_to, all_is_good?"succeeded":"failed"); } free_xml(msg); return all_is_good; } /* msg is deleted by the time this returns */ gboolean send_msg_via_ipc(xmlNode *msg, const char *sys) { gboolean send_ok = TRUE; IPC_Channel *client_channel; crm_debug_4("relaying msg to sub_sys=%s via IPC", sys); client_channel = (IPC_Channel*)g_hash_table_lookup(ipc_clients, sys); if(crm_element_value(msg, F_CRM_HOST_FROM) == NULL) { crm_xml_add(msg, F_CRM_HOST_FROM, fsa_our_uname); } if (client_channel != NULL) { crm_debug_3("Sending message via channel %s.", sys); send_ok = send_ipc_message(client_channel, msg); } else if(sys != NULL && strcasecmp(sys, CRM_SYSTEM_CIB) == 0) { crm_err("Sub-system (%s) has been incorporated into the CRMd.", sys); crm_err("Change the way we handle this CIB message"); crm_log_xml(LOG_ERR, "cib op", msg); send_ok = FALSE; } else if(sys != NULL && strcasecmp(sys, CRM_SYSTEM_LRMD) == 0) { fsa_data_t *fsa_data = NULL; ha_msg_input_t *msg_copy = new_ha_msg_input(msg); crm_malloc0(fsa_data, sizeof(fsa_data_t)); fsa_data->fsa_input = I_MESSAGE; fsa_data->fsa_cause = C_IPC_MESSAGE; fsa_data->data = msg_copy; fsa_data->origin = __FUNCTION__; fsa_data->data_type = fsa_dt_ha_msg; #ifdef FSA_TRACE crm_debug_2("Invoking action %s (%.16llx)", fsa_action2string(A_LRM_INVOKE), A_LRM_INVOKE); #endif - do_lrm_invoke(A_LRM_INVOKE, C_IPC_MESSAGE, - fsa_state, I_MESSAGE, fsa_data); + do_lrm_invoke(A_LRM_INVOKE, C_IPC_MESSAGE, fsa_state, I_MESSAGE, fsa_data); - delete_ha_msg_input(msg_copy); + crm_free(msg_copy); crm_free(fsa_data); } else { crm_err("Unknown Sub-system (%s)... discarding message.", crm_str(sys)); send_ok = FALSE; } free_xml(msg); return send_ok; } void msg_queue_helper(void) { #if SUPPORT_HEARTBEAT IPC_Channel *ipc = NULL; if(fsa_cluster_conn != NULL) { ipc = fsa_cluster_conn->llc_ops->ipcchan( fsa_cluster_conn); } if(ipc != NULL) { ipc->ops->resume_io(ipc); } /* g_hash_table_foreach_remove(ipc_clients, ipc_queue_helper, NULL); */ #endif } gboolean ipc_queue_helper(gpointer key, gpointer value, gpointer user_data) { crmd_client_t *ipc_client = value; if(ipc_client->client_channel != NULL) { ipc_client->client_channel->ops->is_message_pending(ipc_client->client_channel); } return FALSE; } diff --git a/include/crm/common/ipc.h b/include/crm/common/ipc.h index 8f3c8f6a0f..724af715b3 100644 --- a/include/crm/common/ipc.h +++ b/include/crm/common/ipc.h @@ -1,98 +1,97 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM_COMMON_IPC__H #define CRM_COMMON_IPC__H #include #include #include #include typedef struct crmd_client_s { char *sub_sys; char *uuid; char *table_key; IPC_Channel *client_channel; GCHSource *client_source; } crmd_client_t; extern gboolean send_ipc_message(IPC_Channel *ipc_client, xmlNode *msg); extern void default_ipc_connection_destroy(gpointer user_data); extern int init_server_ipc_comms( char *channel_name, gboolean (*channel_client_connect)( IPC_Channel *newclient, gpointer user_data), void (*channel_connection_destroy)(gpointer user_data)); extern GCHSource *init_client_ipc_comms( const char *channel_name, gboolean (*dispatch)( IPC_Channel* source_data, gpointer user_data), void *client_data, IPC_Channel **ch); extern IPC_Channel *init_client_ipc_comms_nodispatch(const char *channel_name); extern gboolean subsystem_msg_dispatch(IPC_Channel *sender, void *user_data); extern IPC_WaitConnection *wait_channel_init(char daemonsocket[]); extern gboolean is_ipc_empty(IPC_Channel *ch); extern xmlNode *createPingRequest(const char *crm_msg_reference, const char *to); extern xmlNode *validate_crm_message(xmlNode *msg, const char *sys, const char *uuid, const char *msg_type); extern void send_hello_message(IPC_Channel *ipc_client, const char *uuid, const char *client_name, const char *major_version, const char *minor_version); #define create_reply(request, xml_response_data) create_reply_adv(request, xml_response_data, __FUNCTION__); extern xmlNode *create_reply_adv(xmlNode *request, xmlNode *xml_response_data, const char *origin); #define create_request(task, xml_data, host_to, sys_to, sys_from, uuid_from) create_request_adv(task, xml_data, host_to, sys_to, sys_from, uuid_from, __FUNCTION__) extern xmlNode *create_request_adv( const char *task, xmlNode *xml_data, const char *host_to, const char *sys_to, const char *sys_from, const char *uuid_from, const char *origin); typedef struct ha_msg_input_s { xmlNode *msg; xmlNode *xml; } ha_msg_input_t; -extern ha_msg_input_t *new_ipc_msg_input(xmlNode *orig); extern ha_msg_input_t *new_ha_msg_input(xmlNode *orig); extern void delete_ha_msg_input(ha_msg_input_t *orig); extern xmlNode *xmlfromIPC(IPC_Channel *ch, int timeout); #endif diff --git a/lib/crm/common/ipc.c b/lib/crm/common/ipc.c index 8428a56fa9..3cc2c82cb1 100644 --- a/lib/crm/common/ipc.c +++ b/lib/crm/common/ipc.c @@ -1,718 +1,705 @@ /* * 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 #include #include xmlNode *xmlfromIPC(IPC_Channel *ch, int timeout) { xmlNode *xml = NULL; HA_Message *msg = NULL; #if HAVE_MSGFROMIPC_TIMEOUT int ipc_rc = IPC_OK; msg = msgfromIPC_timeout(ch, MSG_ALLOWINTR, timeout, &ipc_rc); if(ipc_rc == IPC_TIMEOUT) { crm_warn("No message received in the required interval (%ds)", timeout); return NULL; } else if(ipc_rc == IPC_BROKEN) { crm_debug("Peer disconnected"); return NULL; } else if(ipc_rc != IPC_OK) { crm_err("msgfromIPC_timeout failed: rc=%d", ipc_rc); return NULL; } else if(msg == NULL) { crm_err("Empty reply from msgfromIPC_timeout"); return NULL; } #else static gboolean do_show_error = TRUE; if(timeout && do_show_error) { crm_err("Timeouts are not supported by the current heartbeat libraries"); do_show_error = FALSE; } msg = msgfromIPC_noauth(ch); if(msg == NULL) { crm_debug("Empty reply from msgfromIPC_noauth"); return NULL; } #endif xml = convert_ha_message(NULL, msg, __FUNCTION__); crm_msg_del(msg); return xml; } static int xml2ipcchan(xmlNode *m, IPC_Channel *ch) { HA_Message *msg = NULL; IPC_Message *imsg = NULL; if (m == NULL || ch == NULL) { cl_log(LOG_ERR, "Invalid msg2ipcchan argument"); errno = EINVAL; return HA_FAIL; } msg = convert_xml_message(m); if ((imsg = hamsg2ipcmsg(msg, ch)) == NULL) { cl_log(LOG_ERR, "hamsg2ipcmsg() failure"); crm_msg_del(msg); return HA_FAIL; } crm_msg_del(msg); if (ch->ops->send(ch, imsg) != IPC_OK) { if (ch->ch_status == IPC_CONNECT) { snprintf(ch->failreason,MAXFAILREASON, "send failed,farside_pid=%d, sendq length=%ld(max is %ld)", ch->farside_pid, (long)ch->send_queue->current_qlen, (long)ch->send_queue->max_qlen); } imsg->msg_done(imsg); return HA_FAIL; } return HA_OK; } /* frees msg */ gboolean send_ipc_message(IPC_Channel *ipc_client, xmlNode *msg) { gboolean all_is_good = TRUE; int fail_level = LOG_WARNING; if(ipc_client != NULL && ipc_client->conntype == IPC_CLIENT) { fail_level = LOG_ERR; } if (msg == NULL) { crm_err("cant send NULL message"); all_is_good = FALSE; } else if (ipc_client == NULL) { crm_err("cant send message without an IPC Channel"); all_is_good = FALSE; } else if(ipc_client->ops->get_chan_status(ipc_client) != IPC_CONNECT) { do_crm_log(fail_level, "IPC Channel to %d is not connected", (int)ipc_client->farside_pid); all_is_good = FALSE; } if(all_is_good && xml2ipcchan(msg, ipc_client) != HA_OK) { do_crm_log(fail_level, "Could not send IPC message to %d", (int)ipc_client->farside_pid); all_is_good = FALSE; if(ipc_client->ops->get_chan_status(ipc_client) != IPC_CONNECT) { do_crm_log(fail_level, "IPC Channel to %d is no longer connected", (int)ipc_client->farside_pid); } else if(ipc_client->conntype == IPC_CLIENT) { if(ipc_client->send_queue->current_qlen >= ipc_client->send_queue->max_qlen) { crm_err("Send queue to %d (size=%d) full.", ipc_client->farside_pid, (int)ipc_client->send_queue->max_qlen); } } } /* ipc_client->ops->resume_io(ipc_client); */ crm_log_xml(all_is_good?LOG_MSG:LOG_WARNING,"IPC[outbound]",msg); return all_is_good; } void default_ipc_connection_destroy(gpointer user_data) { return; } int init_server_ipc_comms( char *channel_name, gboolean (*channel_client_connect)(IPC_Channel *newclient,gpointer user_data), void (*channel_connection_destroy)(gpointer user_data)) { /* the clients wait channel is the other source of events. * This source delivers the clients connection events. * listen to this source at a relatively lower priority. */ char commpath[SOCKET_LEN]; IPC_WaitConnection *wait_ch; sprintf(commpath, CRM_SOCK_DIR "/%s", channel_name); wait_ch = wait_channel_init(commpath); if (wait_ch == NULL) { return 1; } G_main_add_IPC_WaitConnection( G_PRIORITY_LOW, wait_ch, NULL, FALSE, channel_client_connect, channel_name, channel_connection_destroy); crm_debug_3("Listening on: %s", commpath); return 0; } GCHSource* init_client_ipc_comms(const char *channel_name, gboolean (*dispatch)( IPC_Channel* source_data, gpointer user_data), void *client_data, IPC_Channel **ch) { IPC_Channel *a_ch = NULL; GCHSource *the_source = NULL; void *callback_data = client_data; a_ch = init_client_ipc_comms_nodispatch(channel_name); if(ch != NULL) { *ch = a_ch; if(callback_data == NULL) { callback_data = a_ch; } } if(a_ch == NULL) { crm_warn("Setup of client connection failed," " not adding channel to mainloop"); return NULL; } if(dispatch == NULL) { crm_warn("No dispatch method specified..." "maybe you meant init_client_ipc_comms_nodispatch()?"); } else { crm_debug_3("Adding dispatch method to channel"); the_source = G_main_add_IPC_Channel( G_PRIORITY_HIGH, a_ch, FALSE, dispatch, callback_data, default_ipc_connection_destroy); } return the_source; } IPC_Channel * init_client_ipc_comms_nodispatch(const char *channel_name) { IPC_Channel *ch; GHashTable *attrs; static char path[] = IPC_PATH_ATTR; char *commpath = NULL; int local_socket_len = 2; /* 2 = '/' + '\0' */ local_socket_len += strlen(channel_name); local_socket_len += strlen(CRM_SOCK_DIR); crm_malloc0(commpath, local_socket_len); sprintf(commpath, CRM_SOCK_DIR "/%s", channel_name); commpath[local_socket_len - 1] = '\0'; crm_debug("Attempting to talk on: %s", commpath); attrs = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(attrs, path, commpath); ch = ipc_channel_constructor(IPC_ANYTYPE, attrs); g_hash_table_destroy(attrs); if (ch == NULL) { crm_err("Could not access channel on: %s", commpath); crm_free(commpath); return NULL; } else if (ch->ops->initiate_connection(ch) != IPC_OK) { crm_debug("Could not init comms on: %s", commpath); ch->ops->destroy(ch); crm_free(commpath); return NULL; } ch->ops->set_recv_qlen(ch, 512); ch->ops->set_send_qlen(ch, 512); ch->should_send_block = TRUE; crm_debug_3("Processing of %s complete", commpath); crm_free(commpath); return ch; } IPC_WaitConnection * wait_channel_init(char daemonsocket[]) { IPC_WaitConnection *wait_ch; mode_t mask; char path[] = IPC_PATH_ATTR; GHashTable * attrs; attrs = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(attrs, path, daemonsocket); mask = umask(0); wait_ch = ipc_wait_conn_constructor(IPC_ANYTYPE, attrs); if (wait_ch == NULL) { cl_perror("Can't create wait channel of type %s", IPC_ANYTYPE); exit(1); } mask = umask(mask); g_hash_table_destroy(attrs); return wait_ch; } longclock_t ipc_call_start = 0; longclock_t ipc_call_stop = 0; longclock_t ipc_call_diff = 0; gboolean subsystem_msg_dispatch(IPC_Channel *sender, void *user_data) { int lpc = 0; xmlNode *msg = NULL; xmlNode *data = NULL; gboolean all_is_well = TRUE; const char *sys_to; const char *task; gboolean (*process_function) (xmlNode *msg, xmlNode *data, IPC_Channel *sender) = NULL; while(IPC_ISRCONN(sender)) { gboolean process = FALSE; if(sender->ops->is_message_pending(sender) == 0) { break; } msg = xmlfromIPC(sender, 0); if (msg == NULL) { break; } lpc++; crm_log_xml(LOG_MSG, __FUNCTION__, msg); sys_to = crm_element_value(msg, F_CRM_SYS_TO); task = crm_element_value(msg, F_CRM_TASK); if(safe_str_eq(task, CRM_OP_HELLO)) { process = TRUE; } else if(sys_to == NULL) { crm_err("Value of %s was NULL!!", F_CRM_SYS_TO); } else if(task == NULL) { crm_err("Value of %s was NULL!!", F_CRM_TASK); } else { process = TRUE; } if(process == FALSE) { free_xml(msg); msg = NULL; continue; } data = get_message_xml(msg, F_CRM_DATA); process_function = user_data; if(ipc_call_diff_max_ms > 0) { ipc_call_start = time_longclock(); } if(FALSE == process_function(msg, data, sender)) { crm_warn("Received a message destined for %s" " by mistake", sys_to); } if(ipc_call_diff_max_ms > 0) { unsigned int ipc_call_diff_ms = 0; ipc_call_stop = time_longclock(); ipc_call_diff = sub_longclock( ipc_call_stop, ipc_call_start); ipc_call_diff_ms = longclockto_ms(ipc_call_diff); if(ipc_call_diff_ms > ipc_call_diff_max_ms) { crm_err("%s took %dms to complete", sys_to, ipc_call_diff_ms); } } free_xml(msg); msg = NULL; if(sender->ch_status == IPC_CONNECT) { break; } } crm_debug_2("Processed %d messages", lpc); if (sender->ch_status != IPC_CONNECT) { crm_err("The server %d has left us: Shutting down...NOW", sender->farside_pid); exit(1); /* shutdown properly later */ return !all_is_well; } return all_is_well; } gboolean is_ipc_empty(IPC_Channel *ch) { if(ch == NULL) { return TRUE; } else if(ch->send_queue->current_qlen == 0 && ch->recv_queue->current_qlen == 0) { return TRUE; } return FALSE; } void send_hello_message(IPC_Channel *ipc_client, const char *uuid, const char *client_name, const char *major_version, const char *minor_version) { xmlNode *hello_node = NULL; xmlNode *hello = NULL; if (uuid == NULL || strlen(uuid) == 0 || client_name == NULL || strlen(client_name) == 0 || major_version == NULL || strlen(major_version) == 0 || minor_version == NULL || strlen(minor_version) == 0) { crm_err("Missing fields, Hello message will not be valid."); return; } hello_node = create_xml_node(NULL, XML_TAG_OPTIONS); crm_xml_add(hello_node, "major_version", major_version); crm_xml_add(hello_node, "minor_version", minor_version); crm_xml_add(hello_node, "client_name", client_name); crm_xml_add(hello_node, "client_uuid", uuid); crm_debug_4("creating hello message"); hello = create_request( CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid); send_ipc_message(ipc_client, hello); crm_debug_4("hello message sent"); free_xml(hello_node); free_xml(hello); } gboolean process_hello_message(xmlNode *hello, char **uuid, char **client_name, char **major_version, char **minor_version) { const char *local_uuid; const char *local_client_name; const char *local_major_version; const char *local_minor_version; *uuid = NULL; *client_name = NULL; *major_version = NULL; *minor_version = NULL; if(hello == NULL) { return FALSE; } local_uuid = crm_element_value(hello, "client_uuid"); local_client_name = crm_element_value(hello, "client_name"); local_major_version = crm_element_value(hello, "major_version"); local_minor_version = crm_element_value(hello, "minor_version"); if (local_uuid == NULL || strlen(local_uuid) == 0) { crm_err("Hello message was not valid (field %s not found)", "uuid"); return FALSE; } else if (local_client_name==NULL || strlen(local_client_name)==0){ crm_err("Hello message was not valid (field %s not found)", "client name"); return FALSE; } else if(local_major_version == NULL || strlen(local_major_version) == 0){ crm_err("Hello message was not valid (field %s not found)", "major version"); return FALSE; } else if (local_minor_version == NULL || strlen(local_minor_version) == 0){ crm_err("Hello message was not valid (field %s not found)", "minor version"); return FALSE; } *uuid = crm_strdup(local_uuid); *client_name = crm_strdup(local_client_name); *major_version = crm_strdup(local_major_version); *minor_version = crm_strdup(local_minor_version); crm_debug_3("Hello message ok"); return TRUE; } xmlNode * create_request_adv(const char *task, xmlNode *msg_data, const char *host_to, const char *sys_to, const char *sys_from, const char *uuid_from, const char *origin) { char *true_from = NULL; xmlNode *request = NULL; char *reference = generateReference(task, sys_from); if (uuid_from != NULL) { true_from = generate_hash_key(sys_from, uuid_from); } else if(sys_from != NULL) { true_from = crm_strdup(sys_from); } else { crm_err("No sys from specified"); } /* host_from will get set for us if necessary by CRMd when routed */ request = create_xml_node(NULL, __FUNCTION__); crm_xml_add(request, F_CRM_ORIGIN, origin); crm_xml_add(request, F_TYPE, T_CRM); crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET); crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST); crm_xml_add(request, XML_ATTR_REFERENCE, reference); crm_xml_add(request, F_CRM_TASK, task); crm_xml_add(request, F_CRM_SYS_TO, sys_to); crm_xml_add(request, F_CRM_SYS_FROM, true_from); /* HOSTTO will be ignored if it is to the DC anyway. */ if(host_to != NULL && strlen(host_to) > 0) { crm_xml_add(request, F_CRM_HOST_TO, host_to); } if (msg_data != NULL) { add_message_xml(request, F_CRM_DATA, msg_data); } crm_free(reference); crm_free(true_from); return request; } ha_msg_input_t * new_ha_msg_input(xmlNode *orig) { ha_msg_input_t *input_copy = NULL; crm_malloc0(input_copy, sizeof(ha_msg_input_t)); - input_copy->msg = copy_xml(orig); - input_copy->xml = get_message_xml(input_copy->msg, F_CRM_DATA); - return input_copy; -} - -ha_msg_input_t * -new_ipc_msg_input(xmlNode *orig) -{ - /* HA_Message *msg = NULL; */ - ha_msg_input_t *input_copy = NULL; - - crm_malloc0(input_copy, sizeof(ha_msg_input_t)); - /* msg = ipcmsg2hamsg(orig); */ - input_copy->msg = copy_xml(orig); + input_copy->msg = orig; input_copy->xml = get_message_xml(input_copy->msg, F_CRM_DATA); return input_copy; } void delete_ha_msg_input(ha_msg_input_t *orig) { if(orig == NULL) { return; } free_xml(orig->msg); crm_free(orig); } xmlNode * validate_crm_message( xmlNode *msg, const char *sys, const char *uuid, const char *msg_type) { const char *from = NULL; const char *to = NULL; const char *type = NULL; const char *crm_msg_reference = NULL; xmlNode *action = NULL; const char *true_sys; char *local_sys = NULL; if (msg == NULL) { return NULL; } from = crm_element_value(msg, F_CRM_SYS_FROM); to = crm_element_value(msg, F_CRM_SYS_TO); type = crm_element_value(msg, F_CRM_MSG_TYPE); crm_msg_reference = crm_element_value(msg, XML_ATTR_REFERENCE); action = msg; true_sys = sys; if (uuid != NULL) { local_sys = generate_hash_key(sys, uuid); true_sys = local_sys; } if (to == NULL) { crm_info("No sub-system defined."); action = NULL; } else if (true_sys != NULL && strcasecmp(to, true_sys) != 0) { crm_debug_3("The message is not for this sub-system (%s != %s).", to, true_sys); action = NULL; } crm_free(local_sys); if (type == NULL) { crm_info("No message type defined."); return NULL; } else if (msg_type != NULL && strcasecmp(msg_type, type) != 0) { crm_info("Expecting a (%s) message but received a (%s).", msg_type, type); action = NULL; } if (crm_msg_reference == NULL) { crm_info("No message crm_msg_reference defined."); action = NULL; } /* if(action != NULL) crm_debug_3( "XML is valid and node with message type (%s) found.", type); crm_debug_3("Returning node (%s)", crm_element_name(action)); */ return action; } /* * This method adds a copy of xml_response_data */ xmlNode * create_reply_adv(xmlNode *original_request, xmlNode *xml_response_data, const char *origin) { xmlNode *reply = NULL; const char *host_from= crm_element_value(original_request, F_CRM_HOST_FROM); const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM); const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO); const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE); const char *operation= crm_element_value(original_request, F_CRM_TASK); const char *crm_msg_reference = crm_element_value( original_request, XML_ATTR_REFERENCE); if (type == NULL) { crm_err("Cannot create new_message," " no message type in original message"); CRM_ASSERT(type != NULL); return NULL; #if 0 } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) { crm_err("Cannot create new_message," " original message was not a request"); return NULL; #endif } reply = create_xml_node(NULL, __FUNCTION__); crm_xml_add(reply, F_CRM_ORIGIN, origin); crm_xml_add(reply, F_TYPE, T_CRM); crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET); crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE); crm_xml_add(reply, XML_ATTR_REFERENCE, crm_msg_reference); crm_xml_add(reply, F_CRM_TASK, operation); /* since this is a reply, we reverse the from and to */ crm_xml_add(reply, F_CRM_SYS_TO, sys_from); crm_xml_add(reply, F_CRM_SYS_FROM, sys_to); /* HOSTTO will be ignored if it is to the DC anyway. */ if(host_from != NULL && strlen(host_from) > 0) { crm_xml_add(reply, F_CRM_HOST_TO, host_from); } if (xml_response_data != NULL) { add_message_xml(reply, F_CRM_DATA, xml_response_data); } return reply; } diff --git a/lib/crm/common/xml.c b/lib/crm/common/xml.c index 534b5d604a..6c7844109e 100644 --- a/lib/crm/common/xml.c +++ b/lib/crm/common/xml.c @@ -1,3086 +1,3087 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_BZLIB_H # include #endif #define XML_BUFFER_SIZE 4096 struct schema_s { int type; const char *name; const char *location; const char *transform; }; struct schema_s known_schemas[] = { { 0, "none", NULL, NULL }, { 1, "pacemaker-0.6", DTD_DIRECTORY"/crm.dtd", DTD_DIRECTORY"/upgrade.xsl" }, { 2, "pacemaker-0.7", DTD_DIRECTORY"/pacemaker-0.7.rng", NULL }, { 2, LATEST_SCHEMA_VERSION, DTD_DIRECTORY"/"LATEST_SCHEMA_VERSION".rng", NULL }, /* Just in case I forget */ }; static const char *filter[] = { XML_ATTR_ORIGIN, XML_DIFF_MARKER, XML_CIB_ATTR_WRITTEN, }; static void add_ha_nocopy(HA_Message *parent, HA_Message *child, const char *field) { int next = parent->nfields; if (parent->nfields >= parent->nalloc && ha_msg_expand(parent) != HA_OK ) { crm_err("Parent expansion failed"); return; } parent->names[next] = crm_strdup(field); parent->nlens[next] = strlen(field); parent->values[next] = child; parent->vlens[next] = sizeof(HA_Message); parent->types[next] = FT_UNCOMPRESS; parent->nfields++; } int is_comment_start(const char *input, size_t offset, size_t max); int is_comment_end(const char *input, size_t offset, size_t max); gboolean drop_comments(const char *input, size_t *offset, size_t max); void dump_array( int log_level, const char *message, const char **array, int depth); int print_spaces(char *buffer, int spaces); int log_data_element(const char *function, const char *prefix, int log_level, int depth, xmlNode *data, gboolean formatted); int dump_data_element( int depth, char **buffer, xmlNode *data, gboolean formatted); xmlNode *parse_xml(const char *input, size_t *offset); int get_tag_name(const char *input, size_t offset, size_t max); int get_attr_name(const char *input, size_t offset, size_t max); int get_attr_value(const char *input, size_t offset, size_t max); gboolean can_prune_leaf(xmlNode *xml_node); void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent); int in_upper_context(int depth, int context, xmlNode *xml_node); xmlNode * find_xml_node(xmlNode *root, const char * search_path, gboolean must_find) { const char *name = "NULL"; if(must_find || root != NULL) { crm_validate_data(root); } if(root != NULL) { name = crm_element_name(root); } if(search_path == NULL) { crm_warn("Will never find "); return NULL; } xml_child_iter_filter( root, a_child, search_path, /* crm_debug_5("returning node (%s).", crm_element_name(a_child)); */ crm_log_xml(LOG_DEBUG_5, "found:", a_child); crm_log_xml(LOG_DEBUG_6, "in:", root); crm_validate_data(a_child); return a_child; ); if(must_find) { crm_warn("Could not find %s in %s.", search_path, name); } else if(root != NULL) { crm_debug_3("Could not find %s in %s.", search_path, name); } else { crm_debug_3("Could not find %s in .", search_path); } return NULL; } xmlNode* find_xml_node_nested(xmlNode *root, const char **search_path, int len) { int j; gboolean is_found = TRUE; xmlNode *match = NULL; xmlNode *lastMatch = root; crm_validate_data(root); if(search_path == NULL || search_path[0] == 0) { crm_warn("Will never find NULL"); return NULL; } dump_array(LOG_DEBUG_5, "Looking for.", search_path, len); for (j=0; j < len; ++j) { if (search_path[j] == NULL) { /* a NULL also means stop searching */ break; } match = find_xml_node(lastMatch, search_path[j], FALSE); if(match == NULL) { is_found = FALSE; break; } else { lastMatch = match; } } if (is_found) { crm_debug_5("returning node (%s).", crm_element_name(lastMatch)); crm_log_xml_debug_5(lastMatch, "found\t%s"); crm_log_xml_debug_5(root, "in \t%s"); crm_validate_data(lastMatch); return lastMatch; } dump_array(LOG_DEBUG_2, "Could not find the full path to the node you specified.", search_path, len); crm_debug_2("Closest point was node (%s) starting from %s.", crm_element_name(lastMatch), crm_element_name(root)); return NULL; } const char * get_xml_attr_nested(xmlNode *parent, const char **node_path, int length, const char *attr_name, gboolean error) { const char *attr_value = NULL; xmlNode *attr_parent = NULL; if(error || parent != NULL) { crm_validate_data(parent); } if(parent == NULL) { crm_debug_3("Can not find attribute %s in NULL parent",attr_name); return NULL; } if(attr_name == NULL || attr_name[0] == 0) { crm_err("Can not find attribute with no name in %s", crm_element_name(parent)); return NULL; } if(length == 0) { attr_parent = parent; } else { attr_parent = find_xml_node_nested(parent, node_path, length); if(attr_parent == NULL && error) { crm_err("No node at the path you specified."); return NULL; } } attr_value = crm_element_value(attr_parent, attr_name); if((attr_value == NULL || attr_value[0] == 0) && error) { crm_err("No value present for %s at %s", attr_name, crm_element_name(attr_parent)); return NULL; } return attr_value; } xmlNode* find_entity(xmlNode *parent, const char *node_name, const char *id) { crm_validate_data(parent); xml_child_iter_filter( parent, a_child, node_name, if(id == NULL || crm_str_eq(id, ID(a_child), TRUE)) { crm_debug_4("returning node (%s).", crm_element_name(a_child)); return a_child; } ); crm_debug_3("node <%s id=%s> not found in %s.", node_name, id, crm_element_name(parent)); return NULL; } void copy_in_properties(xmlNode* target, xmlNode *src) { crm_validate_data(src); crm_validate_data(target); if(src == NULL) { crm_warn("No node to copy properties from"); } else if (target == NULL) { crm_err("No node to copy properties into"); } else { xml_prop_iter( src, local_prop_name, local_prop_value, expand_plus_plus(target, local_prop_name, local_prop_value) ); crm_validate_data(target); } return; } void fix_plus_plus_recursive(xmlNode* target) { xml_prop_iter(target, name, value, expand_plus_plus(target, name, value)); xml_child_iter(target, child, fix_plus_plus_recursive(child)); } void expand_plus_plus(xmlNode* target, const char *name, const char *value) { int int_value = 0; const char *old_value = crm_element_value(target, name); int name_len = strlen(name); int value_len = strlen(value); /* if no previous value, set unexpanded */ if(old_value == NULL || value_len < (name_len + 2) || value[name_len] != '+' || (value[name_len+1] != '+' && value[name_len+1] != '=') || strstr(value, name) != value) { crm_xml_add(target, name, value); return; } if(safe_str_eq(value, old_value)) { int_value = 0; } else { int_value = char2score(old_value); } if(value[name_len+1] == '+') { /* if the value is name followed by "++" we need * to increment the existing value */ int_value++; } else { const char *offset_s = value+(name_len+2); int offset = char2score(offset_s); int_value += offset; } if(int_value > INFINITY) { int_value = INFINITY; } crm_xml_add_int(target, name, int_value); return; } xmlNode* add_node_copy(xmlNode *parent, xmlNode *src_node) { const char *name = NULL; xmlNode *child = NULL; CRM_CHECK(src_node != NULL, return NULL); crm_validate_data(src_node); name = crm_element_name(src_node); CRM_CHECK(name != NULL, return NULL); child = copy_xml(src_node); xmlAddChild(parent, child); return child; } int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child) { int next = 0; crm_validate_data(parent); crm_validate_data(child); if(name == NULL) { name = crm_element_name(child); } if(name == NULL || name[0] == 0) { crm_err("Cannot add object with no name"); return HA_FAIL; } next = 0; xmlAddChild(parent, child); return HA_OK; } const char * crm_xml_add(xmlNode* node, const char *name, const char *value) { xmlAttr *attr = NULL; const char *old_value = NULL; CRM_CHECK(node != NULL, return NULL); CRM_CHECK(name != NULL && name[0] != 0, return NULL); /* CRM_CHECK(value != NULL && value[0] != 0, return NULL); */ /* CRM_CHECK(strcasecmp(name, F_XML_TAGNAME) != 0, return NULL); */ old_value = crm_element_value(node, name); #if 1 if (old_value != NULL && (value == NULL || value[0] == 0)) { crm_err("Unsetting %s with crm_xml_add()", name); xml_remove_prop(node, name); return NULL; } #endif if(old_value == value) { return value; } attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value); CRM_CHECK(attr && attr->children && attr->children->content, return NULL); return (char *)attr->children->content; } const char * crm_xml_add_int(xmlNode* node, const char *name, int value) { char *number = crm_itoa(value); const char *added = crm_xml_add(node, name, number); crm_free(number); return added; } xmlNode* create_xml_node(xmlNode *parent, const char *name) { xmlNode *ret_value = NULL; if (name == NULL || name[0] == 0) { ret_value = NULL; } else if(parent == NULL) { ret_value = xmlNewNode(NULL, (const xmlChar*)name); } else { ret_value = xmlNewChild(parent, NULL, (const xmlChar*)name, NULL); } return ret_value; } void free_xml_from_parent(xmlNode *parent, xmlNode *a_node) { CRM_CHECK(parent != NULL, return); CRM_CHECK(a_node != NULL, return); xmlUnlinkNode(a_node); a_node->doc = NULL; free_xml(a_node); } xmlNode* copy_xml(xmlNode *src_node) { return xmlCopyNode(src_node, 1); } xmlNode* string2xml(const char *input) { xmlNode *output = parse_xml(input, NULL); if(output != NULL) { crm_validate_data(output); } return output; } xmlNode * stdin2xml(void) { size_t data_length = 0; size_t read_chars = 0; char *xml_buffer = NULL; xmlNode *xml_obj = NULL; do { crm_realloc(xml_buffer, XML_BUFFER_SIZE + data_length + 1); read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin); data_length += read_chars; } while (read_chars > 0); if(data_length == 0) { crm_warn("No XML supplied on stdin"); return NULL; } xml_buffer[data_length] = '\0'; xml_obj = string2xml(xml_buffer); crm_free(xml_buffer); crm_log_xml_debug_3(xml_obj, "Created fragment"); return xml_obj; } xmlNode* file2xml(FILE *input, gboolean compressed) { char *buffer = NULL; gboolean work_done = FALSE; xmlNode *new_obj = NULL; size_t length = 0, read_len = 0; if(input == NULL) { /* Use perror here as we likely just called fopen() which return NULL */ cl_perror("File open failed, cannot read contents"); return NULL; } if(compressed) { #if HAVE_BZLIB_H int rc = 0; BZFILE *bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0); if ( rc != BZ_OK ) { BZ2_bzReadClose ( &rc, bz_file); return NULL; } rc = BZ_OK; while ( rc == BZ_OK ) { crm_realloc(buffer, XML_BUFFER_SIZE + length + 1); read_len = BZ2_bzRead ( &rc, bz_file, buffer + length, XML_BUFFER_SIZE); crm_debug_5("Read %ld bytes from file: %d", (long)read_len, rc); if ( rc == BZ_OK || rc == BZ_STREAM_END) { length += read_len; } } buffer[length] = '\0'; read_len = length; if ( rc != BZ_STREAM_END ) { crm_err("Couldnt read compressed xml from file"); crm_free(buffer); buffer = NULL; } BZ2_bzReadClose (&rc, bz_file); if(buffer == NULL) { return NULL; } work_done = TRUE; #else crm_err("Cannot read compressed files:" " bzlib was not available at compile time"); #endif } if(work_done == FALSE) { int start = 0; start = ftell(input); fseek(input, 0L, SEEK_END); length = ftell(input); fseek(input, 0L, start); CRM_ASSERT(start == ftell(input)); crm_debug_3("Reading %ld bytes from file", (long)length); crm_malloc0(buffer, (length+1)); read_len = fread(buffer, 1, length, input); } /* see how big the file is */ if(read_len != length) { crm_err("Calculated and read bytes differ: %ld vs. %ld", (long)length, (long)read_len); } else if(length > 0) { new_obj = string2xml(buffer); } else { crm_warn("File contained no XML"); } crm_free(buffer); return new_obj; } void dump_array(int log_level, const char *message, const char **array, int depth) { int j; if(message != NULL) { do_crm_log(log_level, "%s", message); } do_crm_log(log_level, "Contents of the array:"); if(array == NULL || array[0] == NULL || depth == 0) { do_crm_log(log_level, "\t"); return; } for (j=0; j < depth && array[j] != NULL; j++) { if (array[j] == NULL) { break; } do_crm_log(log_level, "\t--> (%s).", array[j]); } } int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress) { int res = 0; time_t now; char *buffer = NULL; char *now_str = NULL; unsigned int out = 0; FILE *file_output_strm = NULL; static mode_t cib_mode = S_IRUSR|S_IWUSR; CRM_CHECK(filename != NULL, return -1); crm_debug_3("Writing XML out to %s", filename); crm_validate_data(xml_node); if (xml_node == NULL) { crm_err("Cannot write NULL to %s", filename); return -1; } file_output_strm = fopen(filename, "w"); if(file_output_strm == NULL) { cl_perror("Cannot open %s for writing", filename); return -1; } /* establish the correct permissions */ fchmod(fileno(file_output_strm), cib_mode); crm_validate_data(xml_node); crm_log_xml_debug_4(xml_node, "Writing out"); crm_validate_data(xml_node); now = time(NULL); now_str = ctime(&now); now_str[24] = EOS; /* replace the newline */ crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str); crm_validate_data(xml_node); buffer = dump_xml_formatted(xml_node); CRM_CHECK(buffer != NULL && strlen(buffer) > 0, goto bail); if(compress) { #if HAVE_BZLIB_H int rc = BZ_OK; unsigned int in = 0; BZFILE *bz_file = NULL; bz_file = BZ2_bzWriteOpen(&rc, file_output_strm, 5, 0, 30); if(rc != BZ_OK) { crm_err("bzWriteOpen failed: %d", rc); } else { BZ2_bzWrite(&rc,bz_file,buffer,strlen(buffer)); if(rc != BZ_OK) { crm_err("bzWrite() failed: %d", rc); } } if(rc == BZ_OK) { BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out); if(rc != BZ_OK) { crm_err("bzWriteClose() failed: %d",rc); out = -1; } else { crm_debug_2("%s: In: %d, out: %d", filename, in, out); } } #else crm_err("Cannot write compressed files:" " bzlib was not available at compile time"); #endif } if(out <= 0) { res = fprintf(file_output_strm, "%s", buffer); if(res < 0) { cl_perror("Cannot write output to %s", filename); goto bail; } } bail: if(fflush(file_output_strm) != 0) { cl_perror("fflush for %s failed:", filename); res = -1; } if(fsync(fileno(file_output_strm)) < 0) { cl_perror("fsync for %s failed:", filename); res = -1; } fclose(file_output_strm); crm_debug_3("Saved %d bytes to the Cib as XML", res); crm_free(buffer); return res; } void print_xml_formatted(int log_level, const char *function, xmlNode *msg, const char *text) { if(msg == NULL) { do_crm_log(log_level, "%s: %s: NULL", function, crm_str(text)); return; } crm_validate_data(msg); log_data_element(function, text, log_level, 0, msg, TRUE); return; } static HA_Message* convert_xml_message_struct(HA_Message *parent, xmlNode *src_node, const char *field) { xmlNode *child = NULL; xmlNode *__crm_xml_iter = src_node->children; xmlAttrPtr prop_iter = src_node->properties; const char *name = NULL; const char *value = NULL; HA_Message *result = ha_msg_new(3); ha_msg_add(result, F_XML_TAGNAME, (const char *)src_node->name); while(prop_iter != NULL) { name = (const char *)prop_iter->name; value = (const char *)xmlGetProp(src_node, prop_iter->name); prop_iter = prop_iter->next; ha_msg_add(result, name, value); } while(__crm_xml_iter != NULL) { child = __crm_xml_iter; __crm_xml_iter = __crm_xml_iter->next; convert_xml_message_struct(result, child, NULL); } if(parent == NULL) { return result; } if(field) { HA_Message *holder = holder = ha_msg_new(3); CRM_ASSERT(holder != NULL); ha_msg_add(holder, F_XML_TAGNAME, field); add_ha_nocopy(holder, result, (const char*)src_node->name); ha_msg_addstruct_compress(parent, field, holder); ha_msg_del(holder); } else { add_ha_nocopy(parent, result, (const char*)src_node->name); } return result; } static void convert_xml_child(HA_Message *msg, xmlNode *xml) { int orig = 0; int rc = BZ_OK; unsigned int len = 0; char *buffer = NULL; char *compressed = NULL; const char *name = NULL; name = (const char *)xml->name; buffer = dump_xml_unformatted(xml); orig = strlen(buffer); if(orig < 512) { ha_msg_add(msg, name, buffer); goto done; } len = (orig * 1.1) + 600; /* recomended size */ crm_malloc0(compressed, len); rc = BZ2_bzBuffToBuffCompress(compressed, &len, buffer, orig, 3, 0, 30); if(rc != BZ_OK) { crm_err("Compression failed: %d", rc); crm_free(compressed); convert_xml_message_struct(msg, xml, name); goto done; } crm_free(buffer); buffer = compressed; crm_debug_2("Compression details: %d -> %d", orig, len); ha_msg_addbin(msg, name, buffer, len); done: crm_free(buffer); # if 0 { unsigned int used = orig; char *uncompressed = NULL; crm_debug("Trying to decompress %d bytes", len); crm_malloc0(uncompressed, orig); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &used, compressed, len, 1, 0); CRM_CHECK(rc == BZ_OK, ;); CRM_CHECK(used == orig, ;); crm_debug("rc=%d, used=%d", rc, used); if(rc != BZ_OK) { exit(100); } crm_debug("Original %s, decompressed %s", buffer, uncompressed); crm_free(uncompressed); } # endif } HA_Message* convert_xml_message(xmlNode *xml) { HA_Message *result = NULL; result = ha_msg_new(3); ha_msg_add(result, F_XML_TAGNAME, (const char *)xml->name); xml_prop_iter(xml, name, value, ha_msg_add(result, name, value)); xml_child_iter(xml, child, convert_xml_child(result, child)); return result; } static void convert_ha_field(xmlNode *parent, HA_Message *msg, int lpc) { int type = 0; const char *name = NULL; const char *value = NULL; xmlNode *xml = NULL; int rc = BZ_OK; size_t orig_len = 0; unsigned int used = 0; char *uncompressed = NULL; char *compressed = NULL; int size = orig_len * 10; CRM_CHECK(parent != NULL, return); CRM_CHECK(msg != NULL, return); name = msg->names[lpc]; type = cl_get_type(msg, name); switch(type) { case FT_COMPRESS: case FT_STRUCT: convert_ha_message(parent, msg->values[lpc], name); break; case FT_UNCOMPRESS: convert_ha_message(parent, cl_get_struct(msg, name), name); break; case FT_STRING: value = cl_get_string(msg, name); if( value == NULL || value[0] != '<' ) { crm_xml_add(parent, name, value); break; } /* unpack xml string */ xml = string2xml(value); if(xml == NULL) { crm_xml_add(parent, name, value); break; } add_node_nocopy(parent, name, xml); break; case FT_BINARY: value = cl_get_binary(msg, name, &orig_len); size = orig_len * 10; if(orig_len < 3 || value[0] != 'B' || value[1] != 'Z' || value[2] != 'h') { if(strstr(name, "uuid") == NULL) { crm_err("Skipping non-bzip binary field: %s", name); } return; } crm_malloc0(compressed, orig_len); memcpy(compressed, value, orig_len); crm_debug_2("Trying to decompress %d bytes", (int)orig_len); retry: crm_realloc(uncompressed, size); memset(uncompressed, 0, size); used = size; rc = BZ2_bzBuffToBuffDecompress( uncompressed, &used, compressed, orig_len, 1, 0); if(rc == BZ_OUTBUFF_FULL) { size = size * 2; /* dont try to allocate more memory than we have */ if(size > 0) { goto retry; } } if(rc != BZ_OK) { crm_err("Decompression of %s (%d bytes) into %d failed: %d", name, (int)orig_len, size, rc); } else { xml = string2xml(uncompressed); } if(xml != NULL) { add_node_nocopy(parent, name, xml); } crm_free(uncompressed); crm_free(compressed); break; } } xmlNode * convert_ha_message(xmlNode *parent, HA_Message *msg, const char *field) { int lpc = 0; xmlNode *child = NULL; const char *tag = NULL; CRM_CHECK(msg != NULL, crm_err("Empty message for %s", field); return parent); tag = cl_get_string(msg, F_XML_TAGNAME); if(tag == NULL) { tag = field; } if(parent == NULL) { parent = create_xml_node(NULL, tag); child = parent; } else { child = create_xml_node(parent, tag); } for (lpc = 0; lpc < msg->nfields; lpc++) { convert_ha_field(child, msg, lpc); } return parent; } xmlNode *convert_ipc_message(IPC_Message *msg, const char *field) { HA_Message *hmsg = wirefmt2msg((char *)msg->msg_body, msg->msg_len, 0); xmlNode *xml = convert_ha_message(NULL, hmsg, __FUNCTION__); crm_msg_del(hmsg); return xml; } xmlNode * get_message_xml(xmlNode *msg, const char *field) { xmlNode *tmp = first_named_child(msg, field); return first_named_child(tmp, NULL); } gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml) { xmlNode *holder = create_xml_node(msg, field); add_node_copy(holder, xml); return TRUE; } char * dump_xml_formatted(xmlNode *an_xml_node) { char *buffer = NULL; char *mutable_ptr = NULL; if(an_xml_node == NULL) { return NULL; } crm_malloc0(buffer, 1024*1024); mutable_ptr = buffer; crm_validate_data(an_xml_node); CRM_CHECK(dump_data_element( 0, &mutable_ptr, an_xml_node, TRUE) >= 0, crm_crit("Could not dump the whole message")); crm_debug_4("Dumped: %s", buffer); return buffer; } char * dump_xml_unformatted(xmlNode *an_xml_node) { char *buffer = NULL; char *mutable_ptr = NULL; crm_malloc0(buffer, 1024*1024); mutable_ptr = buffer; crm_validate_data(an_xml_node); CRM_CHECK(dump_data_element( 0, &mutable_ptr, an_xml_node, FALSE) >= 0, crm_crit("Could not dump the whole message")); crm_debug_4("Dumped: %s", buffer); return buffer; } #define update_buffer_head(buffer, len) if(len < 0) { \ (*buffer) = EOS; return -1; \ } else { \ buffer += len; \ } int print_spaces(char *buffer, int depth) { int lpc = 0; int spaces = 2*depth; /* <= so that we always print 1 space - prevents problems with syslog */ for(lpc = 0; lpc <= spaces; lpc++) { if(sprintf(buffer, "%c", ' ') < 1) { return -1; } buffer += 1; } return lpc; } int log_data_element( const char *function, const char *prefix, int log_level, int depth, xmlNode *data, gboolean formatted) { int printed = 0; int child_result = 0; int has_children = 0; char print_buffer[1000]; char *buffer = print_buffer; const char *name = crm_element_name(data); const char *hidden = NULL; crm_debug_5("Dumping %s...", name); crm_validate_data(data); if(data == NULL) { crm_warn("No data to dump as XML"); return 0; } else if(name == NULL && depth == 0) { xml_child_iter( data, a_child, child_result = log_data_element( function, prefix, log_level, depth, a_child, formatted); if(child_result < 0) { return child_result; } ); return 0; } else if(name == NULL) { crm_err("Cannot dump NULL element at depth %d", depth); return -1; } if(formatted) { printed = print_spaces(buffer, depth); update_buffer_head(buffer, printed); } printed = sprintf(buffer, "<%s", name); update_buffer_head(buffer, printed); hidden = crm_element_value(data, "hidden"); xml_prop_iter( data, prop_name, prop_value, if(prop_name == NULL || safe_str_eq(F_XML_TAGNAME, prop_name)) { continue; } else if(hidden != NULL && prop_name[0] != 0 && strstr(hidden, prop_name) != NULL) { prop_value = "*****"; } crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value); update_buffer_head(buffer, printed); ); xml_child_iter( data, child, if(child != NULL) { has_children++; break; } ); printed = sprintf(buffer, "%s>", has_children==0?"/":""); update_buffer_head(buffer, printed); do_crm_log(log_level, "%s: %s%s", function, prefix?prefix:"", print_buffer); buffer = print_buffer; if(has_children == 0) { return 0; } xml_child_iter( data, a_child, child_result = log_data_element( function, prefix, log_level, depth+1, a_child, formatted); if(child_result < 0) { return -1; } ); if(formatted) { printed = print_spaces(buffer, depth); update_buffer_head(buffer, printed); } do_crm_log(log_level, "%s: %s%s", function, prefix?prefix:"", print_buffer, name); crm_debug_5("Dumped %s...", name); return has_children; } int dump_data_element( int depth, char **buffer, xmlNode *data, gboolean formatted) { int printed = 0; int child_result = 0; int has_children = 0; const char *name = NULL; if(data == NULL) { return 0; } CRM_ASSERT(buffer != NULL && *buffer != NULL); name = crm_element_name(data); if(name == NULL && depth == 0) { name = "__fake__"; } else if(name == NULL) { return 0; } crm_debug_5("Dumping %s...", name); if(formatted) { printed = print_spaces(*buffer, depth); update_buffer_head(*buffer, printed); } printed = sprintf(*buffer, "<%s", name); update_buffer_head(*buffer, printed); has_children = xml_has_children(data); xml_prop_iter(data, prop_name, prop_value, crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = sprintf(*buffer, " %s=\"%s\"", prop_name, prop_value); update_buffer_head(*buffer, printed); ); printed = sprintf(*buffer, "%s>%s", has_children==0?"/":"", formatted?"\n":""); update_buffer_head(*buffer, printed); if(has_children == 0) { return 0; } xml_child_iter( data, child, child_result = dump_data_element( depth+1, buffer, child, formatted); if(child_result < 0) { return -1; } ); if(formatted) { printed = print_spaces(*buffer, depth); update_buffer_head(*buffer, printed); } printed = sprintf(*buffer, "%s", name, formatted?"\n":""); update_buffer_head(*buffer, printed); crm_debug_5("Dumped %s...", name); return has_children; } gboolean xml_has_children(const xmlNode *xml_root) { if(xml_root != NULL && xml_root->children != NULL) { return TRUE; } return FALSE; } void xml_validate(const xmlNode *xml_root) { CRM_ASSERT(xml_root != NULL); } int crm_element_value_int(xmlNode *data, const char *name, int *dest) { const char *value = crm_element_value(data, name); CRM_CHECK(dest != NULL, return -1); if(value) { *dest = crm_parse_int(value, NULL); return 0; } return -1; } const char * crm_element_value(xmlNode *data, const char *name) { xmlAttr *attr = NULL; CRM_CHECK(data != NULL, return NULL); CRM_CHECK(name != NULL, return NULL); attr = xmlHasProp(data, (const xmlChar*)name); if(attr && attr->children) { return (const char*)attr->children->content; } return NULL; } const char * crm_element_value_const(const xmlNode *data, const char *name) { return crm_element_value(data, name); } char * crm_element_value_copy(xmlNode *data, const char *name) { char *value_copy = NULL; const char *value = crm_element_value(data, name); if(value != NULL) { value_copy = crm_strdup(value); } return value_copy; } const char * crm_element_name(const xmlNode *data) { return (data ? (const char *)data->name : NULL); } void xml_remove_prop(xmlNode *obj, const char *name) { xmlUnsetProp(obj, (const xmlChar*)name); } int get_tag_name(const char *input, size_t offset, size_t max) { char ch = 0; size_t lpc = offset; const char *error = NULL; gboolean do_special = FALSE; for(; error == NULL && lpc < max; lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c [%d]", ch, (int)lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '?': if(lpc == 0) { /* weird xml tag that we dont care about */ do_special = TRUE; } else { goto out; } break; case '/': case '>': case '\t': case '\n': case ' ': if(!do_special) { goto out; } break; default: if(do_special) { } else if('a' <= ch && ch <= 'z') { } else if('A' <= ch && ch <= 'Z') { } else if(ch == '_') { } else if(ch == '-') { } else { error = "bad character, not in [a-zA-Z_-]"; } break; } } crm_err("Error parsing token near %.15s: %s", input, crm_str(error)); return -1; out: CRM_ASSERT(lpc > offset); return lpc - offset; } int get_attr_name(const char *input, size_t offset, size_t max) { char ch = 0; size_t lpc = offset; const char *error = NULL; for(; error == NULL && lpc < max; lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c[%d]", ch, (int)lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '\t': case '\n': case ' ': error = "unexpected whitespace"; break; case '=': return lpc - offset; default: if('a' <= ch && ch <= 'z') { } else if('A' <= ch && ch <= 'Z') { } else if('0' <= ch && ch <= '9') { } else if(ch == '_') { } else if(ch == '-') { } else { error = "bad character, not in [a-zA-Z0-9_-]"; } break; } } crm_err("Error parsing token near %.40s: (lpc=%d, ch='%c') %s", input+offset, (int)(lpc-offset), ch, crm_str(error)); return -1; } int get_attr_value(const char *input, size_t offset, size_t max) { char ch = 0; size_t lpc = offset; const char *error = NULL; for(; error == NULL && lpc < max; lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c [%d]", ch, (int)lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '\\': if(input[lpc+1] == '"') { /* skip over the next char */ lpc++; break; } /*fall through*/ case '"': return lpc - offset; default: break; } } crm_err("Error parsing token near %.40s: %s", input+offset, crm_str(error)); return -1; } int is_comment_start(const char *input, size_t offset, size_t max) { size_t remaining = max - offset; CRM_CHECK(input != NULL, return 0); CRM_CHECK(offset < max, return 0); input += offset; if(remaining > 4 && input[0] == '<' && input[1] == '!' && input[2] == '-' && input[3] == '-') { crm_debug_6("Found comment start: "); return 3; } else if(remaining > 1 && input[0] == '?' && input[1] == '>') { crm_debug_6("Found comment end: ?>"); return 2; } if(remaining > 2) { crm_debug_6("Not comment end: %c%c%c", input[0], input[1], input[2]); } else { crm_debug_6("Not comment end"); } return 0; } static gboolean drop_whitespace(const char *input, size_t *offset, size_t max) { char ch = 0; size_t lpc = *offset; gboolean more = TRUE; const char *our_input = input; if(input == NULL) { return FALSE; } while(lpc < max && more) { ch = our_input[lpc]; crm_debug_6("Processing char %c[%d]", ch, (int)lpc); if(isspace(ch)) { lpc++; } else { more = FALSE; } } crm_debug_4("Finished processing whitespace"); if(lpc > *offset) { crm_debug_5("Skipped %d whitespace chars", (int)(lpc - *offset)); } (*offset) = lpc; return FALSE; } gboolean drop_comments(const char *input, size_t *offset, size_t max) { gboolean more = TRUE; gboolean in_directive = FALSE; int in_comment = FALSE; size_t lpc = 0; int tag_len = 0; char ch = 0; if(input == NULL) { return FALSE; } CRM_ASSERT(offset != NULL); lpc = *offset; while(lpc < max && more) { ch = input[lpc]; crm_debug_6("Processing char [%d]: %c ", (int)lpc, ch); switch(ch) { case 0: if(in_comment == FALSE) { more = FALSE; } else { crm_err("unexpected EOS"); crm_warn("Parsing error at or before: %s", input+lpc); } break; case '<': tag_len = is_comment_start(input, lpc, max); if(tag_len > 0) { if(in_comment) { crm_err("Nested XML comments are not supported!"); crm_warn("Parsing error at or before: %s", input+lpc); } in_comment = TRUE; lpc+=tag_len; if(tag_len == 2 && input[lpc-1] == '!') { in_directive = TRUE; } } else if(in_comment == FALSE){ more = FALSE; } else { lpc++; } break; case '>': lpc++; if(in_directive) { in_directive = FALSE; in_comment = FALSE; } break; case '-': case '?': tag_len = is_comment_end(input, lpc, max); if(tag_len > 0) { lpc+=tag_len; in_comment = FALSE; } else { lpc++; } break; case ' ': case '\t': case '\n': case '\r': lpc++; break; default: lpc++; break; } } crm_debug_4("Finished processing comments"); crm_debug_5("Skipped %d comment chars", (int)(lpc - *offset)); *offset = lpc; return FALSE; } xmlNode* parse_xml(const char *input, size_t *offset) { char ch = 0; int len = 0; size_t lpc = 0, max = 0; char *tag_name = NULL; char *attr_name = NULL; char *attr_value = NULL; gboolean more = TRUE; gboolean were_comments = TRUE; const char *error = NULL; const char *our_input = input; xmlNode *new_obj = NULL; if(input == NULL) { return NULL; } if(offset != NULL) { our_input = input + (*offset); } max = strlen(our_input); were_comments = drop_comments(our_input, &lpc, max); CRM_CHECK(our_input[lpc] == '<', return NULL); lpc++; len = get_tag_name(our_input, lpc, max); crm_debug_5("Tag length: %d", (int)len); CRM_CHECK(len > 0, return NULL); crm_malloc0(tag_name, len+1); strncpy(tag_name, our_input + lpc, len+1); tag_name[len] = EOS; crm_debug_4("Processing tag %s", tag_name); new_obj = xmlNewNode(NULL, (xmlChar*)tag_name); lpc += len; for(; more && error == NULL && lpc < max; lpc++) { ch = our_input[lpc]; crm_debug_5("Processing char %c[%d]", ch, (int)lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '/': if(our_input[lpc+1] == '>') { more = FALSE; } break; case '<': if(our_input[lpc+1] == '!') { lpc--; /* allow the '<' to be processed */ drop_comments(our_input, &lpc, max); lpc--; /* allow the '<' to be processed */ } else if(our_input[lpc+1] != '/') { xmlNode *child = NULL; crm_debug_4("Start parsing child at %d...", (int)lpc); lpc--; child = parse_xml(our_input, &lpc); if(child == NULL) { error = "error parsing child"; break; } add_node_nocopy(new_obj, NULL, child); /* ha_msg_addstruct_compress( */ /* new_obj, crm_element_name(child), child); */ crm_debug_4("Finished parsing child: %s", crm_element_name(child)); if(our_input[lpc] == '<') { lpc--; /* allow the '<' to be processed */ } } else { lpc += 2; /* ') { error = "clase tag cannot contain attrs"; } crm_debug_4("Finished parsing ourselves: %s", crm_element_name(new_obj)); } else { error = "Mismatching close tag"; crm_err("Expected: %s", tag_name); } } break; case '=': lpc++; /* = */ /*fall through*/ case '"': lpc++; /* " */ len = get_attr_value(our_input, lpc, max); if(len < 0) { error = "couldnt find attr_value"; } else { crm_malloc0(attr_value, len+1); strncpy(attr_value, our_input+lpc, len+1); attr_value[len] = EOS; lpc += len; crm_debug_4("creating nvpair: <%s %s=\"%s\"...", tag_name, attr_name, attr_value); crm_xml_add(new_obj, attr_name, attr_value); crm_free(attr_name); crm_free(attr_value); } break; case '>': while(lpc < max && our_input[lpc+1] != '<') { lpc++; } break; case ' ': case '\t': case '\n': case '\r': break; default: len = get_attr_name(our_input, lpc, max); if(len < 0) { error = "couldnt find attr_name"; } else { crm_malloc0(attr_name, len+1); strncpy(attr_name, our_input+lpc, len+1); attr_name[len] = EOS; lpc += len; crm_debug_4("found attr name: %s", attr_name); lpc--; /* make sure the '=' is seen next time around */ } break; } } if(error) { crm_err("Error parsing token: %s", error); crm_err("Error at or before: %.40s", our_input+lpc-3); crm_free(tag_name); free_xml(new_obj); return NULL; } if(offset == NULL) { drop_comments(our_input, &lpc, max); drop_whitespace(our_input, &lpc, max); if(lpc < max) { if(crm_log_level < LOG_ERR) { fprintf(stderr, "%s: Ignoring trailing characters in XML input. Supply -V for more details.\n", __PRETTY_FUNCTION__); } else { cl_log(LOG_ERR, "%s: Ignoring trailing characters in XML input.", __PRETTY_FUNCTION__); } cl_log(LOG_ERR, "%s: Parsed %d characters of a possible %d. Trailing text was: \'%.40s\'...", __PRETTY_FUNCTION__, (int)lpc, (int)max, our_input+lpc); } } crm_debug_4("Finished processing %s tag", tag_name); crm_free(tag_name); if(offset != NULL) { (*offset) += lpc; } return new_obj; } void log_xml_diff(unsigned int log_level, xmlNode *diff, const char *function) { xmlNode *added = find_xml_node(diff, "diff-added", FALSE); xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); gboolean is_first = TRUE; if(crm_log_level < log_level) { /* nothing will ever be printed */ return; } xml_child_iter( removed, child, log_data_element(function, "-", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { do_crm_log(log_level, " --- "); } ); is_first = TRUE; xml_child_iter( added, child, log_data_element(function, "+", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { do_crm_log(log_level, " --- "); } ); } void purge_diff_markers(xmlNode *a_node) { CRM_CHECK(a_node != NULL, return); xml_remove_prop(a_node, XML_DIFF_MARKER); xml_child_iter(a_node, child, purge_diff_markers(child); ); } gboolean apply_xml_diff(xmlNode *old, xmlNode *diff, xmlNode **new) { gboolean result = TRUE; const char *digest = crm_element_value(diff, XML_ATTR_DIGEST); xmlNode *added = find_xml_node(diff, "diff-added", FALSE); xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); int root_nodes_seen = 0; CRM_CHECK(new != NULL, return FALSE); crm_debug_2("Substraction Phase"); xml_child_iter(removed, child_diff, CRM_CHECK(root_nodes_seen == 0, result = FALSE); if(root_nodes_seen == 0) { *new = subtract_xml_object(old, child_diff, NULL); } root_nodes_seen++; ); if(root_nodes_seen == 0) { *new = copy_xml(old); } else if(root_nodes_seen > 1) { crm_err("(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen); result = FALSE; } root_nodes_seen = 0; crm_debug_2("Addition Phase"); if(result) { xml_child_iter(added, child_diff, CRM_CHECK(root_nodes_seen == 0, result = FALSE); if(root_nodes_seen == 0) { add_xml_object(NULL, *new, child_diff); } root_nodes_seen++; ); } if(root_nodes_seen > 1) { crm_err("(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen); result = FALSE; } else if(result && digest) { char *new_digest = calculate_xml_digest(*new, FALSE, TRUE); if(safe_str_neq(new_digest, digest)) { crm_info("Digest mis-match: expected %s, calculated %s", digest, new_digest); result = FALSE; } else { crm_debug_2("Digest matched: expected %s, calculated %s", digest, new_digest); } } else if(result) { int lpc = 0; xmlNode *intermediate = NULL; xmlNode *diff_of_diff = NULL; xmlNode *calc_added = NULL; xmlNode *calc_removed = NULL; const char *value = NULL; const char *name = NULL; const char *version_attrs[] = { XML_ATTR_NUMUPDATES, XML_ATTR_GENERATION, XML_ATTR_GENERATION_ADMIN }; crm_debug_2("Verification Phase"); intermediate = diff_xml_object(old, *new, FALSE); calc_added = find_xml_node(intermediate, "diff-added", FALSE); calc_removed = find_xml_node(intermediate, "diff-removed", FALSE); /* add any version details to the diff so they match */ for(lpc = 0; lpc < DIMOF(version_attrs); lpc++) { name = version_attrs[lpc]; value = crm_element_value(added, name); crm_xml_add(calc_added, name, value); value = crm_element_value(removed, name); crm_xml_add(calc_removed, name, value); } diff_of_diff = diff_xml_object(intermediate, diff, TRUE); if(diff_of_diff != NULL) { crm_info("Diff application failed!"); crm_log_xml_debug(old, "diff:original"); crm_log_xml_debug(diff, "diff:input"); result = FALSE; } free_xml(diff_of_diff); free_xml(intermediate); diff_of_diff = NULL; intermediate = NULL; } if(result) { purge_diff_markers(*new); } return result; } xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress) { xmlNode *diff = NULL; xmlNode *tmp1 = NULL; xmlNode *added = NULL; xmlNode *removed = NULL; tmp1 = subtract_xml_object(old, new, "removed:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { free_xml(tmp1); } else { diff = create_xml_node(NULL, "diff"); removed = create_xml_node(diff, "diff-removed"); added = create_xml_node(diff, "diff-added"); add_node_nocopy(removed, NULL, tmp1); } } tmp1 = subtract_xml_object(new, old, "added:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { free_xml(tmp1); return diff; } if(diff == NULL) { diff = create_xml_node(NULL, "diff"); } if(removed == NULL) { removed = create_xml_node(diff, "diff-removed"); } if(added == NULL) { added = create_xml_node(diff, "diff-added"); } add_node_nocopy(added, NULL, tmp1); } return diff; } gboolean can_prune_leaf(xmlNode *xml_node) { gboolean can_prune = TRUE; /* return FALSE; */ xml_prop_iter(xml_node, prop_name, prop_value, if(safe_str_eq(prop_name, XML_ATTR_ID)) { continue; } can_prune = FALSE; ); xml_child_iter(xml_node, child, if(can_prune_leaf(child)) { free_xml(child); } else { can_prune = FALSE; } ); return can_prune; } void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent) { xmlNode *us = NULL; xmlNode *new_parent = parent; const char *name = crm_element_name(xml_node); CRM_CHECK(xml_node != NULL && name != NULL, return); us = create_xml_node(parent, name); xml_prop_iter(xml_node, prop_name, prop_value, lower_bound = context; crm_xml_add(us, prop_name, prop_value); ); if(lower_bound >= 0 || upper_bound >= 0) { crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); new_parent = us; } else { upper_bound = in_upper_context(0, context, xml_node); if(upper_bound >= 0) { crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); new_parent = us; } else { free_xml(us); us = NULL; } } xml_child_iter(us, child, diff_filter_context( context, upper_bound-1, lower_bound-1, child, new_parent); ); } int in_upper_context(int depth, int context, xmlNode *xml_node) { gboolean has_attributes = FALSE; if(context == 0) { return 0; } xml_prop_iter(xml_node, prop_name, prop_value, has_attributes = TRUE; break; ); if(has_attributes) { return depth; } else if(depth < context) { xml_child_iter(xml_node, child, if(in_upper_context(depth+1, context, child)) { return depth; } ); } return 0; } xmlNode * subtract_xml_object(xmlNode *left, xmlNode *right, const char *marker) { gboolean skip = FALSE; gboolean differences = FALSE; xmlNode *diff = NULL; xmlNode *child_diff = NULL; xmlNode *right_child = NULL; const char *id = NULL; const char *name = NULL; const char *value = NULL; const char *right_val = NULL; int lpc = 0; static int filter_len = DIMOF(filter); crm_log_xml(LOG_DEBUG_5, "left:", left); crm_log_xml(LOG_DEBUG_5, "right:", right); if(left == NULL) { return NULL; } id = ID(left); if(right == NULL) { xmlNode *deleted = NULL; crm_debug_5("Processing <%s id=%s> (complete copy)", crm_element_name(left), id); deleted = copy_xml(left); crm_xml_add(deleted, XML_DIFF_MARKER, marker); return deleted; } name = crm_element_name(left); /* sanity checks */ CRM_CHECK(name != NULL, return NULL); /* these checks are costly haven't caught anything for a while */ /* CRM_CHECK(safe_str_eq(crm_element_name(left), */ /* crm_element_name(right)), return NULL); */ /* CRM_CHECK(safe_str_eq(id, ID(right)), return NULL); */ diff = create_xml_node(NULL, name); /* changes to name/value pairs */ crm_debug_5("Processing <%s id=%s>", crm_str(name), id); xml_prop_iter(left, prop_name, left_value, if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) { continue; } skip = FALSE; for(lpc = 0; skip == FALSE && lpc < filter_len; lpc++){ if(crm_str_eq(prop_name, filter[lpc], TRUE)) { skip = TRUE; } } if(skip) { continue; } right_val = crm_element_value(right, prop_name); if(right_val == NULL) { differences = TRUE; crm_xml_add(diff, prop_name, left_value); crm_debug_6("\t%s: %s", crm_str(prop_name), crm_str(left_value)); } else if(safe_str_eq(left_value, right_val)) { crm_debug_5("\t%s: %s (removed)", crm_str(prop_name), crm_str(left_value)); } else { differences = TRUE; crm_xml_add(diff, prop_name, left_value); crm_debug_5("\t%s: %s->%s", crm_str(prop_name), crm_str(left_value), right_val); } ); /* changes to child objects */ crm_debug_3("Processing children of <%s id=%s>",crm_str(name),id); xml_child_iter( left, left_child, right_child = find_entity( right, crm_element_name(left_child), ID(left_child)); child_diff = subtract_xml_object( left_child, right_child, marker); if(child_diff != NULL) { differences = TRUE; add_node_nocopy(diff, NULL, child_diff); } ); if(differences == FALSE) { /* check for XML_DIFF_MARKER in a child */ xml_child_iter( right, right_child, value = crm_element_value(right_child, XML_DIFF_MARKER); if(value != NULL && safe_str_eq(value, "removed:top")) { crm_debug_3("Found the root of the deletion: %s", name); crm_log_xml_debug_3(right_child, "deletion"); differences = TRUE; break; } ); } if(differences == FALSE) { free_xml(diff); crm_debug_5("\tNo changes to <%s id=%s>", crm_str(name), id); return NULL; } crm_xml_add(diff, XML_ATTR_ID, id); return diff; } int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update) { const char *object_id = NULL; const char *object_name = NULL; crm_log_xml(LOG_DEBUG_5, "update:", update); crm_log_xml(LOG_DEBUG_5, "target:", target); CRM_CHECK(update != NULL, return 0); object_name = crm_element_name(update); object_id = ID(update); CRM_CHECK(object_name != NULL, return 0); if(target == NULL && object_id == NULL) { /* placeholder object */ target = find_xml_node(parent, object_name, FALSE); } else if(target == NULL) { target = find_entity(parent, object_name, object_id); } if(target == NULL) { target = create_xml_node(parent, object_name); CRM_CHECK(target != NULL, return 0); crm_debug_2("Added <%s%s%s/>", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); } else { crm_debug_3("Found node <%s%s%s/> to update", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); } copy_in_properties(target, update); xml_child_iter( update, a_child, crm_debug_4("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); add_xml_object(target, NULL, a_child); ); crm_debug_3("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id)); return 0; } gboolean update_xml_child(xmlNode *child, xmlNode *to_update) { gboolean can_update = TRUE; CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(to_update != NULL, return FALSE); if(safe_str_neq(crm_element_name(to_update), crm_element_name(child))) { can_update = FALSE; } else if(safe_str_neq(ID(to_update), ID(child))) { can_update = FALSE; } else if(can_update) { crm_log_xml_debug_2(child, "Update match found..."); add_xml_object(NULL, child, to_update); } xml_child_iter( child, child_of_child, /* only update the first one */ if(can_update) { break; } can_update = update_xml_child(child_of_child, to_update); ); return can_update; } int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches) { int match_found = 0; CRM_CHECK(root != NULL, return FALSE); CRM_CHECK(children != NULL, return FALSE); if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) { } else if(value != NULL && safe_str_neq(value, crm_element_value(root, field))) { } else { if(*children == NULL) { *children = create_xml_node(NULL, __FUNCTION__); } add_node_copy(*children, root); match_found = 1; } if(search_matches || match_found == 0) { xml_child_iter( root, child, match_found += find_xml_children( children, child, tag, field, value, search_matches); ); } return match_found; } gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only) { gboolean can_delete = FALSE; const char *up_id = NULL; const char *child_id = NULL; const char *right_val = NULL; CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(update != NULL, return FALSE); up_id = ID(update); child_id = ID(child); if(up_id == NULL || safe_str_eq(child_id, up_id)) { can_delete = TRUE; } if(safe_str_neq(crm_element_name(update), crm_element_name(child))) { can_delete = FALSE; } if(can_delete && delete_only) { xml_prop_iter(update, prop_name, left_value, right_val = crm_element_value(child, prop_name); if(safe_str_neq(left_value, right_val)) { can_delete = FALSE; } ); } if(can_delete && parent != NULL) { crm_log_xml_debug_4(child, "Delete match found..."); if(delete_only) { free_xml(child); } else { xmlNode *tmp = copy_xml(update); xmlNode *old = xmlReplaceNode(child, tmp); free_xml(old); } child = NULL; return TRUE; } else if(can_delete) { crm_log_xml_debug(child, "Cannot delete the search root"); can_delete = FALSE; } xml_child_iter( child, child_of_child, /* only delete the first one */ if(can_delete) { break; } can_delete = replace_xml_child(child, child_of_child, update, delete_only); ); return can_delete; } void hash2nvpair(gpointer key, gpointer value, gpointer user_data) { const char *name = key; const char *s_value = value; xmlNode *xml_node = user_data; xmlNode *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR); crm_xml_add(xml_child, XML_ATTR_ID, name); crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name); crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value); crm_debug_3("dumped: name=%s value=%s", name, s_value); } void hash2field(gpointer key, gpointer value, gpointer user_data) { const char *name = key; const char *s_value = value; xmlNode *xml_node = user_data; if(crm_element_value(xml_node, name) == NULL) { crm_xml_add(xml_node, name, s_value); crm_debug_3("dumped: %s=%s", name, s_value); } else { crm_debug_2("duplicate: %s=%s", name, s_value); } } void hash2metafield(gpointer key, gpointer value, gpointer user_data) { char *crm_name = NULL; if(key == NULL || value == NULL) { return; } crm_name = crm_concat(CRM_META, key, '_'); hash2field(crm_name, value, user_data); crm_free(crm_name); } #if CRM_DEPRECATED_SINCE_2_0_3 GHashTable * xml2list_202(xmlNode *parent) { xmlNode *nvpair_list = NULL; GHashTable *nvpair_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); CRM_CHECK(parent != NULL, return nvpair_hash); nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); if(nvpair_list == NULL) { crm_debug("No attributes in %s", crm_element_name(parent)); crm_log_xml_debug_2( parent,"No attributes for resource op"); } xml_child_iter_filter( nvpair_list, node_iter, XML_CIB_TAG_NVPAIR, const char *key = crm_element_value( node_iter, XML_NVPAIR_ATTR_NAME); const char *value = crm_element_value( node_iter, XML_NVPAIR_ATTR_VALUE); crm_debug_2("Added %s=%s", key, value); g_hash_table_insert( nvpair_hash, crm_strdup(key), crm_strdup(value)); ); return nvpair_hash; } #endif GHashTable * xml2list(xmlNode *parent) { xmlNode *nvpair_list = NULL; GHashTable *nvpair_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); CRM_CHECK(parent != NULL, return nvpair_hash); nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); if(nvpair_list == NULL) { crm_debug_2("No attributes in %s", crm_element_name(parent)); crm_log_xml_debug_2( parent,"No attributes for resource op"); } crm_log_xml_debug_3(nvpair_list, "Unpacking"); xml_prop_iter( nvpair_list, key, value, crm_debug_4("Added %s=%s", key, value); g_hash_table_insert( nvpair_hash, crm_strdup(key), crm_strdup(value)); ); return nvpair_hash; } static void assign_uuid(xmlNode *xml_obj) { cl_uuid_t new_uuid; char *new_uuid_s = NULL; const char *tag_name = crm_element_name(xml_obj); const char *tag_id = ID(xml_obj); crm_malloc0(new_uuid_s, 38); cl_uuid_generate(&new_uuid); cl_uuid_unparse(&new_uuid, new_uuid_s); crm_warn("Updating object from <%s id=%s/> to <%s id=%s/>", tag_name, tag_id?tag_id:"__empty__", tag_name, new_uuid_s); crm_xml_add(xml_obj, XML_ATTR_ID, new_uuid_s); crm_log_xml_debug(xml_obj, "Updated object"); crm_free(new_uuid_s); } static gboolean tag_needs_id(const char *tag_name) { int lpc = 0; const char *allowed_list[] = { XML_TAG_CIB, XML_TAG_FRAGMENT, XML_CIB_TAG_NODES, XML_CIB_TAG_RESOURCES, XML_CIB_TAG_CONSTRAINTS, XML_CIB_TAG_STATUS, XML_LRM_TAG_RESOURCES, "configuration", "crm_config", "attributes", "operations", "diff", "diff-added", "diff-removed", }; for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) { if(crm_str_eq(tag_name, allowed_list[lpc], TRUE)) { /* this tag is never meant to have an ID */ return FALSE; } } return TRUE; } static gboolean non_unique_allowed(const char *tag_name) { int lpc = 0; const char *non_unique[] = { XML_LRM_TAG_RESOURCE, XML_LRM_TAG_RSC_OP, }; for(lpc = 0; lpc < DIMOF(non_unique); lpc++) { if(safe_str_eq(tag_name, non_unique[lpc])) { /* this tag can have a non-unique ID */ return TRUE; } } return FALSE; } gboolean do_id_check(xmlNode *xml_obj, GHashTable *id_hash, gboolean silent_add, gboolean silent_rename) { char *lookup_id = NULL; gboolean modified = FALSE; char *old_id = NULL; const char *tag_id = NULL; const char *tag_name = NULL; const char *lookup_value = NULL; gboolean created_hash = FALSE; if(xml_obj == NULL) { return FALSE; } else if(id_hash == NULL) { created_hash = TRUE; id_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); } xml_child_iter( xml_obj, xml_child, if(do_id_check(xml_child, id_hash, silent_add, silent_rename)) { modified = TRUE; } ); tag_id = ID(xml_obj); tag_name = TYPE(xml_obj); if(tag_needs_id(tag_name) == FALSE) { crm_debug_5("%s does not need an ID", tag_name); goto finish_id_check; } else if(tag_id != NULL && non_unique_allowed(tag_name)){ crm_debug_5("%s does not need top be unique", tag_name); goto finish_id_check; } lookup_id = NULL; if(tag_id != NULL) { lookup_id = crm_concat(tag_name, tag_id, '-'); lookup_value = g_hash_table_lookup(id_hash, lookup_id); if(lookup_value == NULL) { g_hash_table_insert(id_hash, lookup_id, crm_strdup(tag_id)); goto finish_id_check; } modified |= (!silent_rename); } else { modified |= (!silent_add); } if(tag_id != NULL) { old_id = crm_strdup(tag_id); } crm_free(lookup_id); assign_uuid(xml_obj); tag_id = ID(xml_obj); if(modified == FALSE) { /* nothing to report */ } else if(old_id != NULL && safe_str_neq(tag_id, old_id)) { crm_err("\"id\" collision detected... Multiple '%s' entries" " with id=\"%s\", assigned id=\"%s\"", tag_name, old_id, tag_id); } else if(old_id == NULL && tag_id != NULL) { crm_err("Detected <%s.../> object without an ID. Assigned: %s", tag_name, tag_id); } crm_free(old_id); finish_id_check: if(created_hash) { g_hash_table_destroy(id_hash); } return modified; } typedef struct name_value_s { const char *name; const void *value; } name_value_t; static gint sort_pairs(gconstpointer a, gconstpointer b) { const name_value_t *pair_a = a; const name_value_t *pair_b = b; if(a == NULL && b == NULL) { return 0; } else if(a == NULL) { return 1; } else if(b == NULL) { return -1; } if(pair_a->name == NULL && pair_b->name == NULL) { return 0; } else if(pair_a->name == NULL) { return 1; } else if(pair_b->name == NULL) { return -1; } return strcmp(pair_a->name, pair_b->name); } static void dump_pair(gpointer data, gpointer user_data) { name_value_t *pair = data; xmlNode *parent = user_data; crm_xml_add(parent, pair->name, pair->value); } static void free_pair(gpointer data, gpointer user_data) { name_value_t *pair = data; crm_free(pair); } static xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive) { GListPtr sorted = NULL; GListPtr unsorted = NULL; name_value_t *pair = NULL; xmlNode *result = NULL; const char *name = crm_element_name(input); CRM_CHECK(input != NULL, return NULL); name = crm_element_name(input); CRM_CHECK(name != NULL, return NULL); result = create_xml_node(parent, name); xml_prop_iter(input, p_name, p_value, crm_malloc0(pair, sizeof(name_value_t)); pair->name = p_name; pair->value = p_value; unsorted = g_list_prepend(unsorted, pair); pair = NULL; ); sorted = g_list_sort(unsorted, sort_pairs); g_list_foreach(sorted, dump_pair, result); g_list_foreach(sorted, free_pair, NULL); g_list_free(sorted); if(recursive) { xml_child_iter(input, child, sorted_xml(child, result, recursive)); } else { xml_child_iter(input, child, add_node_copy(result, child)); } return result; } static void filter_xml(xmlNode *data, const char **filter, int filter_len, gboolean recursive) { int lpc = 0; for(lpc = 0; lpc < filter_len; lpc++) { xml_remove_prop(data, filter[lpc]); } if(recursive == FALSE) { return; } xml_child_iter(data, child, filter_xml(child, filter, filter_len, recursive)); } /* "c048eae664dba840e1d2060f00299e9d" */ char * calculate_xml_digest(xmlNode *input, gboolean sort, gboolean do_filter) { int i = 0; int digest_len = 16; char *digest = NULL; unsigned char *raw_digest = NULL; xmlNode *sorted = NULL; char *buffer = NULL; size_t buffer_len = 0; if(sort || do_filter) { sorted = sorted_xml(input, NULL, TRUE); } else { sorted = copy_xml(input); } if(do_filter) { filter_xml(sorted, filter, DIMOF(filter), TRUE); } buffer = dump_xml_formatted(sorted); buffer_len = strlen(buffer); CRM_CHECK(buffer != NULL && buffer_len > 0, free_xml(sorted); return NULL); crm_malloc0(digest, (2 * digest_len + 1)); crm_malloc0(raw_digest, (digest_len + 1)); MD5((unsigned char *)buffer, buffer_len, raw_digest); for(i = 0; i < digest_len; i++) { sprintf(digest+(2*i), "%02x", raw_digest[i]); } crm_debug_2("Digest %s: %s\n", digest, buffer); crm_log_xml(LOG_DEBUG_3, "digest:source", sorted); crm_free(buffer); crm_free(raw_digest); free_xml(sorted); return digest; } #if HAVE_LIBXML2 # include # include # include # include # include #endif static gboolean validate_with_dtd( xmlDocPtr doc, gboolean to_logs, const char *dtd_file) { gboolean valid = TRUE; xmlDtdPtr dtd = NULL; xmlValidCtxtPtr cvp = NULL; CRM_CHECK(doc != NULL, return FALSE); CRM_CHECK(dtd_file != NULL, return FALSE); dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file); CRM_CHECK(dtd != NULL, goto cleanup); cvp = xmlNewValidCtxt(); CRM_CHECK(cvp != NULL, goto cleanup); if(to_logs) { cvp->userData = (void *) LOG_ERR; cvp->error = (xmlValidityErrorFunc) cl_log; cvp->warning = (xmlValidityWarningFunc) cl_log; } else { cvp->userData = (void *) stderr; cvp->error = (xmlValidityErrorFunc) fprintf; cvp->warning = (xmlValidityWarningFunc) fprintf; } if (!xmlValidateDtd(cvp, doc, dtd)) { valid = FALSE; } cleanup: if(cvp) { xmlFreeValidCtxt(cvp); } if(dtd) { xmlFreeDtd(dtd); } return valid; } xmlNode *first_named_child(xmlNode *parent, const char *name) { xml_child_iter_filter(parent, match, name, return match); return NULL; } #if 0 static void relaxng_invalid_stderr(void * userData, xmlErrorPtr error) { /* Structure xmlError struct _xmlError { int domain : What part of the library raised this er int code : The error code, e.g. an xmlParserError char * message : human-readable informative error messag xmlErrorLevel level : how consequent is the error char * file : the filename int line : the line number if available char * str1 : extra string information char * str2 : extra string information char * str3 : extra string information int int1 : extra number information int int2 : column number of the error or 0 if N/A void * ctxt : the parser context if available void * node : the node in the tree } */ crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message); } #endif static gboolean validate_with_relaxng( xmlDocPtr doc, gboolean to_logs, const char *relaxng_file) { gboolean valid = TRUE; #if HAVE_LIBXML2 int rc = 0; xmlRelaxNGPtr rng = NULL; xmlRelaxNGValidCtxtPtr valid_ctx = NULL; xmlRelaxNGParserCtxtPtr parser_ctx = NULL; CRM_CHECK(doc != NULL, return FALSE); CRM_CHECK(relaxng_file != NULL, return FALSE); xmlLoadExtDtdDefaultValue = 1; parser_ctx = xmlRelaxNGNewParserCtxt(relaxng_file); CRM_CHECK(parser_ctx != NULL, goto cleanup); if(to_logs) { xmlRelaxNGSetParserErrors(parser_ctx, (xmlRelaxNGValidityErrorFunc) cl_log, (xmlRelaxNGValidityWarningFunc) cl_log, LOG_ERR); } else { xmlRelaxNGSetParserErrors(parser_ctx, (xmlRelaxNGValidityErrorFunc) fprintf, (xmlRelaxNGValidityWarningFunc) fprintf, stderr); } rng = xmlRelaxNGParse(parser_ctx); CRM_CHECK(rng != NULL, goto cleanup); valid_ctx = xmlRelaxNGNewValidCtxt(rng); CRM_CHECK(valid_ctx != NULL, goto cleanup); if(to_logs) { xmlRelaxNGSetValidErrors(valid_ctx, (xmlRelaxNGValidityErrorFunc) cl_log, (xmlRelaxNGValidityWarningFunc) cl_log, LOG_ERR); } else { xmlRelaxNGSetValidErrors(valid_ctx, (xmlRelaxNGValidityErrorFunc) fprintf, (xmlRelaxNGValidityWarningFunc) fprintf, stderr); } /* xmlRelaxNGSetValidStructuredErrors( */ /* valid_ctx, relaxng_invalid_stderr, valid_ctx); */ xmlLineNumbersDefault(1); rc = xmlRelaxNGValidateDoc(valid_ctx, doc); if (rc > 0) { valid = FALSE; } else if (rc < 0) { crm_err("Internal libxml error during validation\n"); } cleanup: if(parser_ctx != NULL) { xmlRelaxNGFreeParserCtxt(parser_ctx); } if(valid_ctx != NULL) { xmlRelaxNGFreeValidCtxt(valid_ctx); } if (rng != NULL) { xmlRelaxNGFree(rng); } #endif return valid; } static gboolean validate_with(xmlNode *xml, int method, gboolean to_logs) { xmlDocPtr doc = NULL; gboolean valid = FALSE; int type = known_schemas[method].type; const char *file = known_schemas[method].location; CRM_CHECK(xml != NULL, return FALSE); doc = xml->doc; if(xml->doc == NULL) { doc = xmlNewDoc((const xmlChar *)"1.0"); xmlDocSetRootElement(doc, xml); } crm_info("Validating %p with: %s (type=%d)", xml, crm_str(file), type); switch(type) { case 0: valid = TRUE; break; case 1: valid = validate_with_dtd(doc, to_logs, file); break; case 2: valid = validate_with_relaxng(doc, to_logs, file); break; default: crm_err("Unknown validator type: %d", type); break; } return valid; } gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs) { int lpc = 0; static int max = DIMOF(known_schemas); if(validation == NULL) { validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION); } if(validation == NULL) { /* Compatibility for 0.6 */ const char *value = crm_element_value(xml_blob, "ignore_dtd"); if(value != NULL && crm_is_true(value) == FALSE) { validation = "crm.dtd"; } } if(validation == NULL || safe_str_eq(validation, "none")) { return TRUE; } for(; lpc < max; lpc++) { if(safe_str_eq(validation, known_schemas[lpc].name)) { if(to_logs) { crm_info("Validating configuration with %s: %s", known_schemas[lpc].name, known_schemas[lpc].location); } return validate_with(xml_blob, lpc, to_logs); } } crm_err("Unknown validator: %s", validation); return FALSE; } static xmlNode *apply_transformation(xmlNode *xml, const char *transform) { xmlNode *out = NULL; xmlDocPtr res = NULL; xmlDocPtr doc = NULL; xsltStylesheet *xslt = NULL; CRM_CHECK(xml != NULL, return FALSE); doc = xml->doc; if(xml->doc == NULL) { doc = xmlNewDoc((const xmlChar *)"1.0"); xmlDocSetRootElement(doc, xml); } xmlLoadExtDtdDefaultValue = 1; xmlSubstituteEntitiesDefault(1); xslt = xsltParseStylesheetFile((const xmlChar *)transform); CRM_CHECK(xslt != NULL, goto cleanup); res = xsltApplyStylesheet(xslt, doc, NULL); CRM_CHECK(res != NULL, goto cleanup); out = xmlDocGetRootElement(res); cleanup: if(xslt) { xsltFreeStylesheet(xslt); } xsltCleanupGlobals(); xmlCleanupParser(); return out; } /* set which validation to use */ -xmlNode *update_validation(xmlNode *xml_blob, gboolean transform, gboolean to_logs) +xmlNode *update_validation( + xmlNode *xml_blob, gboolean transform, gboolean to_logs) { int lpc = 0, match = 0, best = 0; static int max = DIMOF(known_schemas); const char *value = crm_element_value(xml_blob, XML_ATTR_VALIDATION); if(safe_str_eq(value, "none")) { /* they dont want any */ return xml_blob; } else if(safe_str_eq(value, LATEST_SCHEMA_VERSION)) { return xml_blob; } if(value != NULL) { for(; lpc < max; lpc++) { if(safe_str_eq(value, known_schemas[lpc].name)) { match = lpc; lpc++; break; } } } for(; lpc < max; lpc++) { gboolean valid = TRUE; crm_debug("Testing '%s' validation", known_schemas[lpc].name); valid = validate_with(xml_blob, lpc, to_logs); if(valid) { best = lpc; } if(valid && transform && known_schemas[lpc].transform != NULL) { xmlNode *upgrade = NULL; crm_notice("Upgrading %s-style configuration to %s with %s", known_schemas[lpc].name, known_schemas[lpc+1].name, known_schemas[lpc].transform); upgrade = apply_transformation(xml_blob, known_schemas[lpc].transform); if(upgrade == NULL) { crm_err("Transformation %s failed", known_schemas[lpc].transform); } else if(validate_with(upgrade, lpc+1, to_logs)) { crm_info("Transformation %s successful", known_schemas[lpc].transform); free_xml(xml_blob); xml_blob = upgrade; } else { crm_err("Transformation %s did not produce a valid configuration", known_schemas[lpc].transform); - crm_log_xml_debug(upgrade, "transform:bad"); + crm_log_xml_info(upgrade, "transform:bad"); free_xml(upgrade); } } } if(best > match) { crm_notice("Upgrading from %s to %s validation", value?value:"", known_schemas[best].name); crm_xml_add(xml_blob, XML_ATTR_VALIDATION, known_schemas[best].name); } return xml_blob; }