diff --git a/crm/admin/crm_verify.c b/crm/admin/crm_verify.c index a595369df2..6ce94e83bc 100644 --- a/crm/admin/crm_verify.c +++ b/crm/admin/crm_verify.c @@ -1,281 +1,281 @@ -/* $Id: crm_verify.c,v 1.5 2006/03/29 06:11:52 andrew Exp $ */ +/* $Id: crm_verify.c,v 1.6 2006/04/03 09:41:36 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OPTARGS "V?X:LS:" #ifdef HAVE_GETOPT_H # include #endif #include #include #include gboolean USE_LIVE_CIB = FALSE; char *cib_save = NULL; const char *crm_system_name = NULL; void usage(const char *cmd, int exit_status); extern void cleanup_calculations(pe_working_set_t *data_set); int main(int argc, char **argv) { crm_data_t * cib_object = NULL; int argerr = 0; int flag; pe_working_set_t data_set; cib_t * cib_conn = NULL; enum cib_errors rc = cib_ok; const char *xml_file = NULL; crm_system_name = basename(argv[0]); g_log_set_handler(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, cl_glib_msg_handler, NULL); /* and for good measure... - this enum is a bit field (!) */ g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/ cl_log_set_entity(crm_system_name); cl_log_set_facility(LOG_LOCAL7); cl_log_enable_stderr(TRUE); set_crm_log_level(LOG_ERR); CL_SIGNAL(DEBUG_INC, alter_debug); CL_SIGNAL(DEBUG_DEC, alter_debug); while (1) { #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { /* Top-level Options */ {"xml-file", 1, 0, 'X'}, {"save-xml", 1, 0, 'S'}, {"live-check", 0, 0, 'L'}, {"help", 0, 0, '?'}, {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 = crm_strdup(optarg); break; case 'S': cib_save = crm_strdup(optarg); break; case 'V': alter_debug(DEBUG_INC); break; case 'L': USE_LIVE_CIB = TRUE; break; case '?': usage(crm_system_name, LSB_EXIT_GENERIC); break; default: printf("?? getopt returned character code 0%o ??\n", 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) { crm_err("%d errors in option parsing", argerr); usage(crm_system_name, LSB_EXIT_GENERIC); } crm_info("=#=#=#=#= Getting XML =#=#=#=#="); if(USE_LIVE_CIB) { cib_conn = cib_new(); rc = cib_conn->cmds->signon( cib_conn, crm_system_name, cib_command_synchronous); } crm_zero_mem_stats(NULL); if(USE_LIVE_CIB) { if(rc == cib_ok) { int options = cib_scope_local|cib_sync_call; crm_info("Reading XML from: live cluster"); rc = cib_conn->cmds->query( cib_conn, NULL, &cib_object, options); } if(rc != cib_ok) { fprintf(stderr, "Live CIB query failed: %s\n", cib_error2string(rc)); return 3; } if(cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); return 3; } } else if(xml_file != NULL) { FILE *xml_strm = fopen(xml_file, "r"); crm_info("Reading XML from: %s", xml_file); cib_object = file2xml(xml_strm); } else { fprintf(stderr, "Reading XML from: stdin"); cib_object = stdin2xml(); } CRM_CHECK(cib_object != NULL, fprintf(stderr, "No config supplied\n"); return 3; ); if(cib_save != NULL) { - write_xml_file(cib_object, cib_save); + write_xml_file(cib_object, cib_save, FALSE); } #if 0 status = get_object_root(XML_CIB_TAG_STATUS, cib_object); xml_child_iter(status, node_state, XML_CIB_TAG_STATE, xml_remove_prop(node_state, XML_CIB_TAG_LRM); ); #endif crm_notice("Required feature set: %s", feature_set(cib_object)); if(do_id_check(cib_object, NULL)) { pe_config_err("ID Check failed"); } set_working_set_defaults(&data_set); data_set.input = cib_object; data_set.now = new_ha_date(TRUE); stage0(&data_set); cleanup_calculations(&data_set); if(USE_LIVE_CIB) { /* Calling msg2ipcchan() seems to initialize something * which isn't free'd when we disconnect and free the * CIB connection. * Fake this extra free and move along. */ volatile cl_mem_stats_t *active_stats = cl_malloc_getstats(); active_stats->numfree++; } CRM_CHECK(crm_mem_stats(NULL) == FALSE, ; ); if(was_config_error) { fprintf(stderr, "Errors found during check: config not valid\n"); if(crm_log_level < LOG_WARNING) { fprintf(stderr, " -V may provide more details\n"); } return 2; } else if(was_config_warning) { fprintf(stderr, "Warnings found during check: config may not be valid\n"); if(crm_log_level < LOG_WARNING) { fprintf(stderr, " Use -V for more details\n"); } return 1; } if(USE_LIVE_CIB) { cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); } return 0; } void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-V] -(?|L|X)\n", cmd); fprintf(stream, "\t--%s (-%c)\t: this help message\n", "help", '?'); fprintf(stream, "\t--%s (-%c)\t: " "turn on debug info. additional instances increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\t: Connect to the running cluster\n", "live-check", 'L'); fprintf(stream, "\t--%s (-%c) \t: Use the configuration in the named file\n", "xml-file", 'X'); fflush(stream); exit(exit_status); } diff --git a/crm/admin/crmadmin.c b/crm/admin/crmadmin.c index 06bb954e1d..04fa093788 100644 --- a/crm/admin/crmadmin.c +++ b/crm/admin/crmadmin.c @@ -1,733 +1,734 @@ -/* $Id: crmadmin.c,v 1.62 2006/01/26 11:36:58 andrew Exp $ */ +/* $Id: crmadmin.c,v 1.63 2006/04/03 09:41:36 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H # include #endif #include int message_timer_id = -1; int message_timeout_ms = 30*1000; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; char *admin_uuid = NULL; void usage(const char *cmd, int exit_status); ll_cluster_t *do_init(void); int do_work(ll_cluster_t * hb_cluster); void crmd_ipc_connection_destroy(gpointer user_data); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); char *pluralSection(const char *a_section); crm_data_t *handleCibMod(void); int do_find_node_list(crm_data_t *xml_node); gboolean admin_message_timeout(gpointer data); gboolean is_node_online(crm_data_t *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; crm_data_t *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; const char *crm_system_name = NULL; #define OPTARGS "V?K:S:HE:Dd:i:RNqt:B" int main(int argc, char **argv) { int argerr = 0; int flag; ll_cluster_t *hb_cluster = NULL; #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 */ {"crm_debug_inc", 1, 0, 'i'}, {"crm_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'}, {0, 0, 0, 0} }; #endif crm_system_name = basename(argv[0]); crm_log_init(crm_system_name); 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: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); if (strcmp("reference", long_options[option_index].name) == 0) { this_msg_reference = crm_strdup(optarg); } else if (strcmp("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': 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); } hb_cluster = do_init(); if (hb_cluster != NULL) { int res = do_work(hb_cluster); if (res >= 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); expected_responses++; if(res == 0) { crm_debug_2("no reply expected," " wait for the hello message only"); } else { crm_debug_2("Waiting for reply from the local CRM"); } message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); g_main_run(mainloop); return_to_orig_privs(); } else { crm_err("No message to send"); operation_status = -1; } } else { crm_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(ll_cluster_t * hb_cluster) { int ret = 1; /* construct the request */ crm_data_t *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;/* wait until timeout instead */ } 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(); crm_data_t *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; } { HA_Message *cmd = create_request( crmd_operation, msg_data, dest_node, sys_to, crm_system_name, admin_uuid); if(this_msg_reference != NULL) { ha_msg_mod(cmd, XML_ATTR_REFERENCE, this_msg_reference); } send_ipc_message(crmd_channel, cmd); } return ret; } void crmd_ipc_connection_destroy(gpointer user_data) { crm_info("Connection to CRMd was terminated"); exit(1); } ll_cluster_t * do_init(void) { int facility; GCHSource *src = NULL; ll_cluster_t *hb_cluster = NULL; /* change the logging facility to the one used by heartbeat daemon */ hb_cluster = ll_cluster_new("heartbeat"); crm_debug_2("Switching to Heartbeat logger"); if (( facility = hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) { cl_log_set_facility(facility); } crm_malloc0(admin_uuid, sizeof(char) * 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 hb_cluster; } else if(crmd_channel != NULL) { send_hello_message( crmd_channel, admin_uuid, crm_system_name,"0", "1"); set_IPC_Channel_dnotify(src, crmd_ipc_connection_destroy); return hb_cluster; } return NULL; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { int lpc = 0; 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); } if (server->ops->recv(server, &msg) != IPC_OK) { perror("Receive failure:"); return !hack_return_good; } if (msg == NULL) { crm_debug_4("No message this time"); continue; } lpc++; received_responses++; new_input = new_ipc_msg_input(msg); crm_log_message(LOG_MSG, new_input->msg); msg->msg_done(msg); if (new_input->xml == NULL) { crm_info("XML in IPC message was not valid... " "discarding."); continue; } else if (validate_crm_message( new_input->msg, crm_system_name, admin_uuid, XML_ATTR_RESPONSE) == FALSE) { crm_info("Message was not a CRM response. Discarding."); continue; } result = cl_get_string(new_input->msg, XML_ATTR_RESULT); if(result == NULL || strcmp(result, "ok") == 0) { result = "pass"; } else { result = "fail"; } if(DO_HEALTH) { const char *state = crm_element_value( new_input->xml, "crmd_state"); printf("Status of %s@%s: %s (%s)\n", crm_element_value(new_input->xml,XML_PING_ATTR_SYSFROM), cl_get_string(new_input->msg, F_CRM_HOST_FROM), state, crm_element_value(new_input->xml,XML_PING_ATTR_STATUS)); if(BE_SILENT && state != NULL) { fprintf(stderr, "%s\n", state); } } else if(DO_WHOIS_DC) { const char *dc = cl_get_string( new_input->msg, 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' */ filename_len = 31 + strlen(this_msg_reference); crm_malloc0(filename, sizeof(char) * filename_len); if(filename != NULL) { sprintf(filename, "%s-%s_%d.xml", result, this_msg_reference, received_responses); filename[filename_len - 1] = '\0'; - if (0 > write_xml_file(new_input->xml, filename)) { + if (0 > write_xml_file( + new_input->xml, filename, FALSE)) { crm_crit("Could not save response to" " %s", filename); } } } } 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); g_main_quit(mainloop); return !hack_return_good; } message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); return hack_return_good; } gboolean admin_message_timeout(gpointer data) { fprintf(stderr, "No messages received in %d seconds.. aborting\n", (int)message_timeout_ms/1000); crm_err("No messages received in %d seconds", (int)message_timeout_ms/1000); g_main_quit(mainloop); return FALSE; } gboolean is_node_online(crm_data_t *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, ACTIVESTATUS)) && crm_is_true(ccm_state) && safe_str_eq(crm_state, ONLINESTATUS)) { 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(crm_data_t *xml_node) { int found = 0; crm_data_t *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: " "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'); fprintf(stream, "\t--%s (-%c)\t\t: " "request the status of all nodes\n", "health", 'H'); fprintf(stream, "\t--%s (-%c) \t: " "initiate an election from \n", "election", 'E'); fprintf(stream, "\t--%s (-%c)\t: " "request the uname of the DC\n", "dc_lookup", 'D'); fprintf(stream, "\t--%s (-%c)\t\t: " "request the uname of all member nodes\n", "nodes", 'N'); fprintf(stream, "\t--%s (-%c)\t: " "request the names of all resources\n", "resources", 'R'); fprintf(stream, "\t--%s (-%c) \t: " "request the location of \n", "whereis", 'W'); fprintf(stream, "\t--%s (-%c) \t: " "Tell the node to enter \"standby\" mode\n", "standby", 's'); fprintf(stream, "\t--%s (-%c) \t: " "Tell the node to exit \"standby\" mode\n", "active", 'a'); /* fprintf(stream, "\t--%s (-%c)\t\n", "disconnect", 'D'); */ fflush(stream); exit(exit_status); } diff --git a/crm/cib/io.c b/crm/cib/io.c index ba0ab0fb44..e99ace903f 100644 --- a/crm/cib/io.c +++ b/crm/cib/io.c @@ -1,683 +1,683 @@ -/* $Id: io.c,v 1.54 2006/03/31 12:45:02 andrew Exp $ */ +/* $Id: io.c,v 1.55 2006/04/03 09:41:36 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char * local_resource_path[] = { XML_CIB_TAG_STATUS, }; const char * resource_path[] = { XML_CIB_TAG_RESOURCES, }; const char * node_path[] = { XML_CIB_TAG_NODES, }; const char * constraint_path[] = { XML_CIB_TAG_CONSTRAINTS, }; gboolean initialized = FALSE; crm_data_t *the_cib = NULL; crm_data_t *node_search = NULL; crm_data_t *resource_search = NULL; crm_data_t *constraint_search = NULL; crm_data_t *status_search = NULL; extern gboolean cib_writes_enabled; extern char *ccm_transition_id; extern gboolean cib_have_quorum; extern GHashTable *peer_hash; extern GHashTable *ccm_membership; extern GTRIGSource *cib_writer; int set_connected_peers(crm_data_t *xml_obj); void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data); int write_cib_contents(gpointer p); #include #include #include static gboolean validate_cib_digest(crm_data_t *local_cib) { int s_res = -1; struct stat buf; char *digest = NULL; char *expected = NULL; gboolean passed = FALSE; FILE *expected_strm = NULL; int start = 0, length = 0, read_len = 0; if(local_cib != NULL) { digest = calculate_xml_digest(local_cib, FALSE); } s_res = stat(CIB_FILENAME ".sig", &buf); if (s_res != 0) { crm_warn("No on-disk digest present"); return TRUE; } expected_strm = fopen(CIB_FILENAME ".sig", "r"); start = ftell(expected_strm); fseek(expected_strm, 0L, SEEK_END); length = ftell(expected_strm); fseek(expected_strm, 0L, start); CRM_ASSERT(start == ftell(expected_strm)); crm_debug_3("Reading %d bytes from file", length); crm_malloc0(expected, sizeof(char) * (length+1)); read_len = fread(expected, sizeof(char), length, expected_strm); CRM_ASSERT(read_len == length); if(expected == NULL) { crm_err("On-disk digest is empty"); } else if(safe_str_eq(expected, digest)) { crm_debug("Digest comparision passed: %s", digest); passed = TRUE; } else { crm_err("Digest comparision failed: %s vs. %s", expected, digest); } crm_free(digest); crm_free(expected); return passed; } static int write_cib_digest(crm_data_t *local_cib, char *digest) { int rc = 0; FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w"); char *local_digest = NULL; CRM_ASSERT(digest_strm != NULL); if(digest == NULL) { digest = calculate_xml_digest(local_cib, FALSE); CRM_ASSERT(digest != NULL); local_digest = digest; } rc = fprintf(digest_strm, "%s", digest); if(rc < 0) { cl_perror("Cannot write output to %s.sig", CIB_FILENAME); } fflush(digest_strm); fclose(digest_strm); crm_free(local_digest); return rc; } static gboolean validate_on_disk_cib(const char *filename, crm_data_t **on_disk_cib) { int s_res = -1; struct stat buf; FILE *cib_file = NULL; gboolean passed = TRUE; crm_data_t *root = NULL; if(filename != NULL) { s_res = stat(filename, &buf); } if (s_res == 0) { cib_file = fopen(filename, "r"); crm_debug_2("Reading cluster configuration from: %s", filename); root = file2xml(cib_file); fclose(cib_file); if(validate_cib_digest(root) == FALSE) { passed = FALSE; } } if(on_disk_cib != NULL) { *on_disk_cib = root; } else { free_xml(root); } return passed; } /* * It is the callers responsibility to free the output of this function */ crm_data_t* readCibXmlFile(const char *filename) { int s_res = -1; struct stat buf; gboolean valid = FALSE; const char *name = NULL; const char *value = NULL; crm_data_t *root = NULL; crm_data_t *status = NULL; struct passwd *cib_user = NULL; gboolean user_readwritable = FALSE; if(filename != NULL) { s_res = stat(filename, &buf); } if (s_res != 0) { return NULL; } cib_user = getpwnam(HA_CCMUSER); user_readwritable = (cib_user != NULL && buf.st_uid == cib_user->pw_uid && (buf.st_mode & (S_IRUSR|S_IWUSR))); if( S_ISREG(buf.st_mode) == FALSE ) { crm_err("%s must be a regular file", filename); cl_flush_logs(); exit(100); } else if( user_readwritable == FALSE ) { struct group *cib_grp = getgrnam(HA_APIGROUP); gboolean group_readwritable = ( cib_grp != NULL && buf.st_gid == cib_grp->gr_gid && (buf.st_mode & (S_IRGRP|S_IWGRP))); if( group_readwritable == FALSE ) { crm_err("%s must be owned and read/writeable by user %s," " or owned and read/writable by group %s", filename, HA_CCMUSER, HA_APIGROUP); cl_flush_logs(); exit(100); } crm_warn("%s should be owned and read/writeable by user %s", filename, HA_CCMUSER); } crm_info("Reading cluster configuration from: %s", filename); valid = validate_on_disk_cib(filename, &root); crm_log_xml_info(root, "[on-disk]"); if(root == NULL) { crm_crit("Parse ERROR reading %s.", filename); crm_crit("Inhibiting respawn by Heartbeat to avoid loss" " of configuration data."); cl_flush_logs(); exit(100); } else if(valid == FALSE) { crm_err("%s has been manually changed" " - please update the md5 digest in %s.sig", filename, filename); cl_flush_logs(); exit(100); } crm_xml_add(root, "generated", XML_BOOLEAN_FALSE); /* strip out the status section if there is one */ status = find_xml_node(root, XML_CIB_TAG_STATUS, TRUE); if(status != NULL) { free_xml_from_parent(root, status); } create_xml_node(root, XML_CIB_TAG_STATUS); /* fill in some defaults */ name = XML_ATTR_GENERATION_ADMIN; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } name = XML_ATTR_GENERATION; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } name = XML_ATTR_NUMUPDATES; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } do_id_check(root, NULL); if (verifyCibXml(root) == FALSE) { crm_crit("%s does not contain a vaild configuration.", filename); crm_crit("Inhibiting respawn by Heartbeat to avoid loss" " of configuration data."); cl_flush_logs(); exit(100); } return root; } /* * The caller should never free the return value */ crm_data_t* get_the_CIB(void) { return the_cib; } gboolean uninitializeCib(void) { crm_data_t *tmp_cib = the_cib; if(tmp_cib == NULL) { crm_err("The CIB has already been deallocated."); return FALSE; } initialized = FALSE; the_cib = NULL; node_search = NULL; resource_search = NULL; constraint_search = NULL; status_search = NULL; crm_err("Deallocating the CIB."); free_xml(tmp_cib); crm_err("The CIB has been deallocated."); return TRUE; } /* * This method will not free the old CIB pointer or the new one. * We rely on the caller to have saved a pointer to the old CIB * and to free the old/bad one depending on what is appropriate. */ gboolean initializeCib(crm_data_t *new_cib) { gboolean is_valid = TRUE; crm_data_t *tmp_node = NULL; if(new_cib == NULL) { return FALSE; } xml_validate(new_cib); tmp_node = get_object_root(XML_CIB_TAG_NODES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_STATUS, new_cib); if (is_valid && tmp_node == NULL) { create_xml_node(new_cib, XML_CIB_TAG_STATUS); } if(is_valid == FALSE) { crm_warn("CIB Verification failed"); return FALSE; } update_counters(__FILE__, __FUNCTION__, new_cib); the_cib = new_cib; initialized = TRUE; return TRUE; } static int archive_file(const char *oldname, const char *newname, const char *ext) { /* move 'oldname' to 'newname' by creating a hard link to it * and then removing the original hard link */ int rc = 0; int res = 0; struct stat tmp; int s_res = 0; char *backup_file = NULL; static const char *back_ext = "bak"; /* calculate the backup name if required */ if(newname != NULL) { backup_file = crm_strdup(newname); } else { int max_name_len = 1024; crm_malloc0(backup_file, sizeof(char) * max_name_len); if (ext == NULL) { ext = back_ext; } snprintf(backup_file, max_name_len - 1, "%s.%s", oldname, ext); } if(backup_file == NULL || strlen(backup_file) == 0) { crm_err("%s backup filename was %s", newname == NULL?"calculated":"supplied", backup_file == NULL?"null":"empty"); rc = -4; } s_res = stat(backup_file, &tmp); /* unlink the old backup */ if (rc == 0 && s_res >= 0) { res = unlink(backup_file); if (res < 0) { cl_perror("Could not unlink %s", backup_file); rc = -1; } } s_res = stat(oldname, &tmp); /* copy */ if (rc == 0 && s_res >= 0) { res = link(oldname, backup_file); if (res < 0) { cl_perror("Could not create backup %s from %s", backup_file, oldname); rc = -2; } } /* unlink the original */ if (rc == 0 && s_res >= 0) { res = unlink(oldname); if (res < 0) { cl_perror("Could not unlink %s", oldname); rc = -3; } } crm_free(backup_file); return rc; } /* * This method will free the old CIB pointer on success and the new one * on failure. */ int activateCibXml(crm_data_t *new_cib, const char *ignored) { int error_code = cib_ok; crm_data_t *saved_cib = get_the_CIB(); crm_log_xml_debug_4(new_cib, "Attempting to activate CIB"); CRM_ASSERT(new_cib != saved_cib); if(saved_cib != NULL) { crm_validate_data(saved_cib); } if (initializeCib(new_cib) == FALSE) { crm_err("Ignoring invalid or NULL CIB"); error_code = -1; if(saved_cib != NULL) { crm_warn("Reverting to last known CIB"); if (initializeCib(saved_cib)) { /* oh we are so dead */ crm_crit("Couldn't re-initialize the old CIB!"); cl_flush_logs(); exit(1); } } else if(error_code != cib_ok) { crm_crit("Could not write out new CIB and no saved" " version to revert to"); } } else if(cib_writes_enabled) { crm_debug_2("Triggering CIB write"); G_main_set_trigger(cib_writer); } if(the_cib != saved_cib && the_cib != new_cib) { CRM_DEV_ASSERT(error_code != cib_ok); CRM_DEV_ASSERT(the_cib == NULL); } if(the_cib != new_cib) { free_xml(new_cib); CRM_DEV_ASSERT(error_code != cib_ok); } if(the_cib != saved_cib) { free_xml(saved_cib); } return error_code; } int write_cib_contents(gpointer p) { int rc = 0; char *digest = NULL; crm_data_t *cib_status_root = NULL; const char *digest_filename = CIB_FILENAME ".sig"; /* we can scribble on "the_cib" here and not affect the parent */ const char *epoch = crm_element_value(the_cib, XML_ATTR_GENERATION); const char *updates = crm_element_value(the_cib, XML_ATTR_NUMUPDATES); const char *admin_epoch = crm_element_value( the_cib, XML_ATTR_GENERATION_ADMIN); /* check the admin didnt modify it underneath us */ if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) { crm_err("%s was manually modified while Heartbeat was active!", CIB_FILENAME); exit(LSB_EXIT_GENERIC); } rc = archive_file(CIB_FILENAME, NULL, "last"); if(rc != 0) { crm_err("Could not make backup of the existing CIB: %d", rc); exit(LSB_EXIT_GENERIC); } rc = archive_file(digest_filename, NULL, "last"); if(rc != 0) { crm_warn("Could not make backup of the existing CIB digest: %d", rc); } /* Given that we discard the status section on startup * there is no point writing it out in the first place * since users just get confused by it * * Although, it does help me once in a while * * So delete the status section before we write it out */ cib_status_root = find_xml_node(the_cib, XML_CIB_TAG_STATUS, TRUE); CRM_DEV_ASSERT(cib_status_root != NULL); if(cib_status_root != NULL) { free_xml_from_parent(the_cib, cib_status_root); } - rc = write_xml_file(the_cib, CIB_FILENAME); + rc = write_xml_file(the_cib, CIB_FILENAME, FALSE); if(rc <= 0) { crm_err("Changes couldn't be written to disk"); exit(LSB_EXIT_GENERIC); } digest = calculate_xml_digest(the_cib, FALSE); crm_info("Wrote version %s.%s.%s of the CIB to disk (digest: %s)", admin_epoch?admin_epoch:"0", epoch?epoch:"0", updates?updates:"0", digest); rc = write_cib_digest(the_cib, digest); if(rc <= 0) { crm_err("Digest couldn't be written to disk"); exit(LSB_EXIT_GENERIC); } #if 0 if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) { crm_err("wrote incorrect digest"); exit(LSB_EXIT_GENERIC); } #endif exit(LSB_EXIT_OK); return HA_OK; } gboolean set_transition(crm_data_t *xml_obj) { const char *current = crm_element_value( xml_obj, XML_ATTR_CCM_TRANSITION); if(safe_str_neq(current, ccm_transition_id)) { crm_debug("CCM transition: old=%s, new=%s", current, ccm_transition_id); crm_xml_add(xml_obj, XML_ATTR_CCM_TRANSITION,ccm_transition_id); return TRUE; } return FALSE; } gboolean set_connected_peers(crm_data_t *xml_obj) { int active = 0; int current = 0; char *peers_s = NULL; const char *current_s = crm_element_value(xml_obj, XML_ATTR_NUMPEERS); g_hash_table_foreach(peer_hash, GHFunc_count_peers, &active); current = crm_parse_int(current_s, "0"); if(current != active) { peers_s = crm_itoa(active); crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s); crm_debug("We now have %s active peers", peers_s); crm_free(peers_s); return TRUE; } return FALSE; } gboolean update_quorum(crm_data_t *xml_obj) { const char *quorum_value = XML_BOOLEAN_FALSE; const char *current = crm_element_value(xml_obj, XML_ATTR_HAVE_QUORUM); if(cib_have_quorum) { quorum_value = XML_BOOLEAN_TRUE; } if(safe_str_neq(current, quorum_value)) { crm_debug("CCM quorum: old=%s, new=%s", current, quorum_value); crm_xml_add(xml_obj, XML_ATTR_HAVE_QUORUM, quorum_value); return TRUE; } return FALSE; } gboolean update_counters(const char *file, const char *fn, crm_data_t *xml_obj) { gboolean did_update = FALSE; did_update = did_update || update_quorum(xml_obj); did_update = did_update || set_transition(xml_obj); did_update = did_update || set_connected_peers(xml_obj); if(did_update) { do_crm_log(LOG_DEBUG, file, fn, "Counters updated"); } return did_update; } void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data) { int *active = user_data; if(safe_str_eq(value, ONLINESTATUS)) { (*active)++; } else if(safe_str_eq(value, JOINSTATUS)) { (*active)++; } } diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h index bfe18e1694..172d64d257 100644 --- a/include/crm/common/xml.h +++ b/include/crm/common/xml.h @@ -1,331 +1,332 @@ -/* $Id: xml.h,v 1.42 2006/03/31 12:45:02 andrew Exp $ */ +/* $Id: xml.h,v 1.43 2006/04/03 09:41:36 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM_COMMON_XML__H #define CRM_COMMON_XML__H #include #include #include #include #include #include #include #include #include /* #define USE_LIBXML 1 */ #if CRM_DEV_BUILD # define XML_PARANOIA_CHECKS 1 #else # define XML_PARANOIA_CHECKS 0 #endif #ifdef USE_LIBXML # include typedef xmlNode crm_data_t; #else typedef struct ha_msg crm_data_t; #endif extern gboolean add_message_xml( HA_Message *msg, const char *field, const crm_data_t *xml); extern crm_data_t *get_message_xml(HA_Message *msg, const char *field); extern GHashTable *xml2list(crm_data_t *parent); #if CRM_DEPRECATED_SINCE_2_0_3 extern GHashTable *xml2list_202(crm_data_t *parent); #endif extern void hash2nvpair(gpointer key, gpointer value, gpointer user_data); extern void hash2field(gpointer key, gpointer value, gpointer user_data); extern gboolean do_id_check(crm_data_t *xml_obj, GHashTable *id_hash); /* * Replacement function for xmlCopyPropList which at the very least, * doesnt work the way *I* would expect it to. * * Copy all the attributes/properties from src into target. * * Not recursive, does not return anything. * */ extern void copy_in_properties(crm_data_t *target, const crm_data_t *src); /* * Find a child named search_path[i] at level i in the XML fragment where i=0 * is an immediate child of root. * * Terminate with success if i == len, or search_path[i] == NULL. * * On success, returns the sub-fragment described by search_path. * On failure, returns NULL. */ extern crm_data_t *find_xml_node_nested( crm_data_t *root, const char **search_path, int len); /* * Find a child named search_path[i] at level i in the XML fragment where i=0 * is an immediate child of root. * * Once the last child specified by node_path is found, find the value * of attr_name. * * If error is set to TRUE, then it is an error for the attribute not * to be found and the function will log accordingly. * * On success, returns the value of attr_name. * On failure, returns NULL. */ extern const char *get_xml_attr_nested(crm_data_t *parent, const char **node_path, int length, const char *attr_name, gboolean error); /* * Free the XML "stuff" associated with a_node * * If a_node is part of another XML blob, barf. * (Should be using free_xml_from_parent) * * Otherwise, free everything recursivly * * Wont barf on NULL. * */ extern void free_xml_fn(crm_data_t *a_node); #if 1 # define free_xml(xml_obj) free_xml_fn(xml_obj); xml_obj = NULL #else # define free_xml(xml_obj) xml_obj = NULL #endif void free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node); #define zap_xml_from_parent(parent, xml_obj) free_xml_from_parent(parent, xml_obj); xml_obj = NULL /* * Create a node named "name" as a child of "parent" * If parent is NULL, creates an unconnected node. * * Returns the created node * */ extern crm_data_t *create_xml_node(crm_data_t *parent, const char *name); /* * Make a copy of name and value and use the copied memory to create * an attribute for node. * * If node, name or value are NULL, nothing is done. * * If name or value are an empty string, nothing is done. * * Returns FALSE on failure and TRUE on success. * */ extern const char *crm_xml_add( crm_data_t *node, const char *name, const char *value); extern const char *crm_xml_add_int( crm_data_t* node, const char *name, int value); /* * Unlink the node and set its doc pointer to NULL so free_xml() * will act appropriately */ extern void unlink_xml_node(crm_data_t *node); /* * Set a timestamp attribute on a_node */ extern void add_xml_tstamp(crm_data_t *a_node); /* * */ extern void purge_diff_markers(crm_data_t *a_node); /* * Returns a deep copy of src_node * */ extern crm_data_t *copy_xml(const crm_data_t *src_node); /* * Add a copy of xml_node to new_parent */ extern crm_data_t *add_node_copy( crm_data_t *new_parent, const crm_data_t *xml_node); /* * XML I/O Functions * * Whitespace between tags is discarded. */ extern crm_data_t *file2xml(FILE *input); extern crm_data_t *stdin2xml(void); extern crm_data_t *string2xml(const char *input); -extern int write_xml_file(crm_data_t *xml_node, const char *filename); +extern int write_xml_file( + crm_data_t *xml_node, const char *filename, gboolean compress); extern char *dump_xml_formatted(const crm_data_t *msg); extern char *dump_xml_unformatted(const crm_data_t *msg); extern void print_xml_formatted( int log_level, const char *function, const crm_data_t *an_xml_node, const char *text); /* * Diff related Functions */ extern crm_data_t *diff_xml_object( crm_data_t *left, crm_data_t *right, gboolean suppress); extern void log_xml_diff(unsigned int log_level, crm_data_t *diff, const char *function); extern gboolean apply_xml_diff( crm_data_t *old, crm_data_t *diff, crm_data_t **new); /* * Searching & Modifying */ extern crm_data_t *find_xml_node( crm_data_t *cib, const char * node_path, gboolean must_find); extern crm_data_t *find_entity( crm_data_t *parent, const char *node_name, const char *id); extern crm_data_t *subtract_xml_object( crm_data_t *left, crm_data_t *right, const char *marker); extern int add_xml_object( crm_data_t *parent, crm_data_t *target, const crm_data_t *update); extern void xml_remove_prop(crm_data_t *obj, const char *name); extern void crm_set_element_parent(crm_data_t *data, crm_data_t *parent); extern gboolean delete_xml_child( crm_data_t *parent, crm_data_t *child, crm_data_t *to_delete); extern gboolean update_xml_child(crm_data_t *child, crm_data_t *to_update); extern gboolean find_xml_child(crm_data_t *child, const char *tag, const char *id); /* * */ extern const char *crm_element_value(const crm_data_t *data, const char *name); extern char *crm_element_value_copy(const crm_data_t *data, const char *name); extern const char *crm_element_name(const crm_data_t *data); extern void xml_validate(const crm_data_t *root); extern void crm_update_parents(crm_data_t *root); extern gboolean xml_has_children(crm_data_t *root); extern char *calculate_xml_digest(crm_data_t *local_cib, gboolean sort); #if XML_PARANOIA_CHECKS # define crm_validate_data(obj) xml_validate(obj) #else # define crm_validate_data(obj) CRM_DEV_ASSERT(obj != NULL) #endif #define xmlGetNodePath(data) crm_element_value(data, XML_ATTR_TAGNAME) #define xml_child_iter(parent, child, loop_code) \ if(parent != NULL) { \ int __counter = 0; \ crm_data_t *child = NULL; \ crm_validate_data(parent); \ for (__counter = 0; __counter < parent->nfields; __counter++) { \ if(parent->types[__counter] != FT_STRUCT \ && parent->types[__counter] != FT_UNCOMPRESS) { \ continue; \ } \ child = parent->values[__counter]; \ if(child == NULL) { \ crm_debug_4("Skipping %s == NULL", \ parent->names[__counter]); \ } else { \ loop_code; \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } #define xml_child_iter_filter(parent, child, filter, loop_code) \ if(parent != NULL) { \ int __counter = 0; \ crm_data_t *child = NULL; \ crm_validate_data(parent); \ for (__counter = 0; __counter < parent->nfields; __counter++) { \ if(parent->types[__counter] != FT_STRUCT \ && parent->types[__counter] != FT_UNCOMPRESS) { \ continue; \ } \ child = parent->values[__counter]; \ if(child == NULL) { \ crm_debug_4("Skipping %s == NULL", \ parent->names[__counter]); \ } else if(filter == NULL/*constant condition*/ \ || safe_str_eq(filter, parent->names[__counter])) { \ loop_code; \ } else { \ crm_debug_4("Skipping <%s../>", \ parent->names[__counter]); \ } \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } #define xml_prop_iter(parent, prop_name, prop_value, code) if(parent != NULL) { \ const char *prop_name = NULL; \ const char *prop_value = NULL; \ int __counter = 0; \ crm_validate_data(parent); \ crm_debug_5("Searching %d fields", parent->nfields); \ for (__counter = 0; __counter < parent->nfields; __counter++) { \ crm_debug_5("Searching field %d", __counter); \ if(parent->types[__counter] != FT_STRING) { \ continue; \ } else if(safe_str_eq(parent->names[__counter], F_XML_TAGNAME)) { \ continue; \ } else if(safe_str_eq(parent->names[__counter], F_XML_PARENT)) { \ continue; \ } \ prop_name = parent->names[__counter]; \ prop_value = parent->values[__counter]; \ code; \ } \ } else { \ crm_debug_4("Parent of loop was NULL"); \ } #endif diff --git a/lib/crm/common/xml.c b/lib/crm/common/xml.c index 4f77fb5d90..3a004ddfec 100644 --- a/lib/crm/common/xml.c +++ b/lib/crm/common/xml.c @@ -1,2397 +1,2442 @@ -/* $Id: xml.c,v 1.63 2006/03/31 12:45:02 andrew Exp $ */ +/* $Id: xml.c,v 1.64 2006/04/03 09:41:36 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include - +#if HAVE_BZLIB_H +# include +#endif #include int is_comment_start(const char *input); int is_comment_end(const char *input); gboolean drop_comments(const char *input, int *offset); 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, const crm_data_t *data, gboolean formatted); int dump_data_element( int depth, char **buffer, const crm_data_t *data, gboolean formatted); crm_data_t *parse_xml(const char *input, int *offset); int get_tag_name(const char *input); int get_attr_name(const char *input); int get_attr_value(const char *input); gboolean can_prune_leaf(crm_data_t *xml_node); void diff_filter_context(int context, int upper_bound, int lower_bound, crm_data_t *xml_node, crm_data_t *parent); int in_upper_context(int depth, int context, crm_data_t *xml_node); crm_data_t * find_xml_node(crm_data_t *root, const char * search_path, gboolean must_find) { if(must_find || root != NULL) { crm_validate_data(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).", xmlGetNodePath(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, xmlGetNodePath(root)); } else if(root != NULL) { crm_debug_3("Could not find %s in %s.", search_path, xmlGetNodePath(root)); } else { crm_debug_3("Could not find %s in .", search_path); } return NULL; } crm_data_t* find_xml_node_nested(crm_data_t *root, const char **search_path, int len) { int j; gboolean is_found = TRUE; crm_data_t *match = NULL; crm_data_t *lastMatch = root; crm_validate_data(root); if(search_path == NULL || search_path[0] == NULL) { 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).", xmlGetNodePath(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.", xmlGetNodePath(lastMatch), crm_element_name(root)); return NULL; } const char * get_xml_attr_nested(crm_data_t *parent, const char **node_path, int length, const char *attr_name, gboolean error) { const char *attr_value = NULL; crm_data_t *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 || strlen(attr_name) == 0) { crm_err("Can not find attribute with no name in %s", xmlGetNodePath(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 || strlen(attr_value) == 0) && error) { crm_err("No value present for %s at %s", attr_name, xmlGetNodePath(attr_parent)); return NULL; } return attr_value; } crm_data_t* find_entity(crm_data_t *parent, const char *node_name, const char *id) { crm_validate_data(parent); xml_child_iter_filter( parent, a_child, node_name, if(id == NULL || safe_str_eq(id, ID(a_child))) { crm_debug_4("returning node (%s).", xmlGetNodePath(a_child)); return a_child; } ); crm_debug_3("node <%s id=%s> not found in %s.", node_name, id, xmlGetNodePath(parent)); return NULL; } void copy_in_properties(crm_data_t* target, const crm_data_t *src) { int value_len = 0; char *incr_value = NULL; char *new_value = NULL; 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, /* if the value is name followed by "++" we need * to increment the existing value */ new_value = NULL; incr_value = NULL; /* do a quick check to decide if its worth * constructing the various strings and * performing string comparisions */ value_len = strlen(local_prop_value); if(value_len > 2 && local_prop_value[0] == local_prop_value[0] && local_prop_value[value_len-1] == '+' && local_prop_value[value_len-2] == '+') { int old_int = 0; const char *old_value = NULL; incr_value=crm_concat(local_prop_name,"+",'+'); if(safe_str_eq(local_prop_value, incr_value)) { old_value = crm_element_value( target, local_prop_name); old_int = crm_parse_int(old_value, "0"); new_value = crm_itoa(old_int + 1); local_prop_value = new_value; } } crm_xml_add(target, local_prop_name, local_prop_value); crm_free(incr_value); crm_free(new_value); ); crm_validate_data(target); } return; } crm_data_t* add_node_copy(crm_data_t *parent, const crm_data_t *src_node) { const char *name = NULL; crm_data_t *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 = create_xml_node(parent, name); copy_in_properties(child, src_node); xml_child_iter(src_node, src_child, add_node_copy(child, src_child); ); crm_set_element_parent(child, parent); return child; } const char * crm_xml_add(crm_data_t* node, const char *name, const char *value) { const char *parent_name = NULL; if(node != NULL) { parent_name = crm_element_name(node); } crm_debug_5("[%s] Setting %s to %s", crm_str(parent_name), name, value); if (name == NULL || strlen(name) <= 0) { } else if(node == NULL) { } else if(parent_name == NULL && strcmp(name, F_XML_TAGNAME) != 0) { } else if (value == NULL || strlen(value) <= 0) { xml_remove_prop(node, name); return NULL; } else { const char *new_value = NULL; crm_validate_data(node); ha_msg_mod(node, name, value); new_value = crm_element_value(node, name); return new_value; } return NULL; } const char * crm_xml_add_int(crm_data_t* node, const char *name, int value) { const char *parent_name = NULL; if(node != NULL) { parent_name = crm_element_name(node); } crm_debug_5("[%s] Setting %s to %d", crm_str(parent_name), name, value); if (name == NULL || strlen(name) <= 0) { } else if(node == NULL) { } else if(parent_name == NULL && strcmp(name, F_XML_TAGNAME) != 0) { } else { crm_validate_data(node); ha_msg_mod_int(node, name, value); return crm_element_value(node, name); } return NULL; } crm_data_t* create_xml_node(crm_data_t *parent, const char *name) { const char *local_name = NULL; const char *parent_name = NULL; crm_data_t *ret_value = NULL; if (name == NULL || strlen(name) < 1) { ret_value = NULL; } else { local_name = name; ret_value = ha_msg_new(1); CRM_CHECK(ret_value != NULL, return NULL); crm_xml_add(ret_value, F_XML_TAGNAME, name); crm_set_element_parent(ret_value, parent); crm_validate_data(ret_value); if(parent) { crm_validate_data(parent); parent_name = crm_element_name(parent); crm_debug_5("Attaching %s to parent %s", local_name, parent_name); CRM_CHECK(HA_OK == ha_msg_addstruct( parent, name, ret_value), return NULL); crm_msg_del(ret_value); crm_validate_data(parent); ret_value = parent->values[parent->nfields-1]; } } crm_debug_5("Created node [%s [%s]]", crm_str(parent_name), crm_str(local_name)); return ret_value; } void free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node) { CRM_CHECK(parent != NULL, return); CRM_CHECK(a_node != NULL, return); crm_validate_data(parent); cl_msg_remove_value(parent, a_node); crm_validate_data(parent); } void free_xml_fn(crm_data_t *a_node) { if(a_node == NULL) { ; /* nothing to do */ } else { int has_parent = 0; crm_validate_data(a_node); ha_msg_value_int(a_node, F_XML_PARENT, &has_parent); /* there is no way in hell we should be deleting anything * with a parent and without the parent knowning */ CRM_CHECK(has_parent == 0, return); if(has_parent == 0) { crm_validate_data(a_node); crm_msg_del(a_node); } } return; } void add_xml_tstamp(crm_data_t *a_node) { char *since_epoch = NULL; time_t a_time = time(NULL); crm_validate_data(a_node); if(a_time == (time_t)-1) { cl_perror("set_node_tstamp(): Invalid time returned"); return; } crm_malloc0(since_epoch, 128*(sizeof(char))); if(since_epoch != NULL) { sprintf(since_epoch, "%ld", (unsigned long)a_time); ha_msg_mod(a_node, XML_ATTR_TSTAMP, since_epoch); crm_validate_data(a_node); crm_free(since_epoch); } } crm_data_t* copy_xml(const crm_data_t *src_node) { return add_node_copy(NULL, src_node); } crm_data_t* string2xml(const char *input) { crm_data_t *output = parse_xml(input, NULL); if(output != NULL) { crm_update_parents(output); crm_validate_data(output); } return output; } crm_data_t * stdin2xml(void) { int lpc = 0; int MAX_XML_BUFFER = 20000; int ch = 0; gboolean more = TRUE; gboolean inTag = FALSE; FILE *input = stdin; char *xml_buffer = NULL; crm_data_t *xml_obj = NULL; crm_malloc0(xml_buffer, sizeof(char)*(MAX_XML_BUFFER+1)); while (more && lpc < MAX_XML_BUFFER) { ch = fgetc(input); /* crm_debug_3("Got [%c]", ch); */ switch(ch) { case EOF: case 0: ch = 0; more = FALSE; xml_buffer[lpc++] = ch; break; case '>': case '<': inTag = TRUE; if(ch == '>') { inTag = FALSE; } xml_buffer[lpc++] = ch; break; case '\n': case '\r': case '\t': case ' ': ch = ' '; if(inTag) { xml_buffer[lpc++] = ch; } break; default: xml_buffer[lpc++] = ch; break; } } xml_buffer[MAX_XML_BUFFER] = 0; xml_obj = string2xml(xml_buffer); crm_free(xml_buffer); crm_log_xml_debug_3(xml_obj, "Created fragment"); return xml_obj; } crm_data_t* file2xml(FILE *input) { char *buffer = NULL; crm_data_t *new_obj = NULL; int start = 0, length = 0, read_len = 0; if(input == NULL) { crm_err("No file to read"); return NULL; } /* see how big the file is */ start = ftell(input); fseek(input, 0L, SEEK_END); length = ftell(input); fseek(input, 0L, start); CRM_ASSERT(start == ftell(input)); crm_debug_3("Reading %d bytes from file", length); crm_malloc0(buffer, sizeof(char) * (length+1)); read_len = fread(buffer, sizeof(char), length, input); if(read_len != length) { crm_err("Calculated and read bytes differ: %d vs. %d", length, 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, __FILE__, __FUNCTION__, "%s", message); } do_crm_log(log_level, __FILE__, __FUNCTION__, "Contents of the array:"); if(array == NULL || array[0] == NULL || depth == 0) { do_crm_log(log_level, __FILE__, __FUNCTION__, "\t"); return; } for (j=0; j < depth && array[j] != NULL; j++) { if (array[j] == NULL) { break; } do_crm_log(log_level, __FILE__, __FUNCTION__, "\t--> (%s).", array[j]); } } int -write_xml_file(crm_data_t *xml_node, const char *filename) +write_xml_file(crm_data_t *xml_node, const char *filename, gboolean compress) { int res = 0; char *now_str = NULL; time_t now; 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; } 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); { FILE *file_output_strm = fopen(filename, "w"); if(file_output_strm == NULL) { res = -1; cl_perror("Cannot write to %s", filename); } else { + gboolean is_done = FALSE; char *buffer = dump_xml_formatted(xml_node); CRM_CHECK(buffer != NULL && strlen(buffer) > 0, return -1); - if(buffer != NULL && strlen(buffer) > 0) { + if(buffer == NULL || strlen(buffer) <= 0) { + is_done = TRUE; + } +#if HAVE_BZLIB_H + if(compress && is_done == FALSE) { + int rc = BZ_OK; + BZFILE *bz_file = NULL; + unsigned int in = 0, out = 0; + is_done = TRUE; + bz_file = BZ2_bzWriteOpen( + &rc, file_output_strm, 5, 0, 0); + if(rc != BZ_OK) { + is_done = FALSE; + crm_err("bzWriteOpen failed: %d", rc); + } + if(is_done) { + BZ2_bzWrite(&rc, bz_file, + buffer, strlen(buffer)); + if(rc != BZ_OK) { + crm_err("bzWrite() failed: %d", rc); + is_done = FALSE; + } + } + if(is_done) { + BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out); + if(rc != BZ_OK) { + crm_err("bzWriteClose() failed: %d",rc); + is_done = FALSE; + } else { + crm_debug("%s: In: %d, out: %d", + filename, in, out); + } + } + } +#endif + if(compress && is_done == FALSE) { + compress = FALSE; + crm_warn("bzlib failed or not installed." + " Writing uncompressed data to: %s", + filename); + } + if(is_done == FALSE) { res = fprintf(file_output_strm, "%s", buffer); if(res < 0) { cl_perror("Cannot write output to %s", filename); } + fflush(file_output_strm); } - fflush(file_output_strm); fclose(file_output_strm); crm_free(buffer); } } crm_debug_3("Saved %d bytes to the Cib as XML", res); return res; } void print_xml_formatted(int log_level, const char *function, const crm_data_t *msg, const char *text) { if(msg == NULL) { do_crm_log(log_level,NULL,function, "%s: NULL", crm_str(text)); return; } crm_validate_data(msg); log_data_element(function, text, log_level, 0, msg, TRUE); return; } crm_data_t * get_message_xml(HA_Message *msg, const char *field) { crm_data_t *xml_node = NULL; crm_data_t *tmp_node = NULL; crm_validate_data(msg); tmp_node = cl_get_struct(msg, field); if(tmp_node != NULL) { xml_node = copy_xml(tmp_node); } return xml_node; } gboolean add_message_xml(HA_Message *msg, const char *field, const crm_data_t *xml) { crm_validate_data(xml); crm_validate_data(msg); CRM_CHECK(field != NULL, return FALSE); ha_msg_addstruct_compress(msg, field, xml); crm_update_parents(msg); return TRUE; } char * dump_xml_formatted(const crm_data_t *an_xml_node) { char *buffer = NULL; char *mutable_ptr = NULL; if(an_xml_node == NULL) { return NULL; } crm_malloc0(buffer, 3*get_stringlen(an_xml_node)); 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(const crm_data_t *an_xml_node) { char *buffer = NULL; char *mutable_ptr = NULL; crm_malloc0(buffer, 2*get_stringlen(an_xml_node)); 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; } #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, const crm_data_t *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); 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); xml_prop_iter( data, prop_name, prop_value, if(safe_str_eq(F_XML_TAGNAME, prop_name)) { continue; } else if(safe_str_eq(F_XML_PARENT, prop_name)) { continue; } 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, function, NULL, "%s%s", 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, function, NULL, "%s%s", prefix?prefix:"", print_buffer, name); crm_debug_5("Dumped %s...", name); return has_children; } int dump_data_element( int depth, char **buffer, const crm_data_t *data, gboolean formatted) { int printed = 0; int child_result = 0; int has_children = 0; const char *name = crm_element_name(data); crm_debug_5("Dumping %s...", name); crm_validate_data(data); if(buffer == NULL || *buffer == NULL) { crm_err("No buffer supplied to dump XML into"); return -1; } else if(data == NULL) { crm_warn("No data to dump as XML"); (*buffer)[0] = EOS; return 0; } else if(name == NULL && depth == 0) { xml_child_iter( data, a_child, child_result = dump_data_element( depth, buffer, 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); xml_prop_iter(data, prop_name, prop_value, if(safe_str_eq(F_XML_TAGNAME, prop_name)) { continue; } else if(safe_str_eq(F_XML_PARENT, prop_name)) { continue; } 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>%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(crm_data_t *xml_root) { crm_validate_data(xml_root); xml_child_iter( xml_root, a_child, return TRUE; ); return FALSE; } void xml_validate(const crm_data_t *xml_root) { int lpc = 0; CRM_ASSERT(xml_root != NULL); CRM_ASSERT(cl_is_allocated(xml_root) == 1); CRM_ASSERT(xml_root->nfields < 500); for (lpc = 0; lpc < xml_root->nfields; lpc++) { void *child = xml_root->values[lpc]; CRM_ASSERT(cl_is_allocated(xml_root->names[lpc]) == 1); if(child == NULL) { } else if(xml_root->types[lpc] == FT_STRUCT || xml_root->types[lpc] == FT_UNCOMPRESS) { crm_validate_data(child); } else if(xml_root->types[lpc] == FT_STRING) { CRM_ASSERT(cl_is_allocated(child) == 1); /* } else { */ /* CRM_CHECK(FALSE); */ } } } void crm_set_element_parent(crm_data_t *data, crm_data_t *parent) { crm_validate_data(data); if(parent != NULL) { ha_msg_mod_int(data, F_XML_PARENT, 1); } else { ha_msg_mod_int(data, F_XML_PARENT, 0); } } const char * crm_element_value(const crm_data_t *data, const char *name) { const char *value = NULL; crm_validate_data(data); value = cl_get_string(data, name); #if XML_PARANOIA_CHECKS CRM_CHECK(value == NULL || cl_is_allocated(value) == 1, return NULL); #endif return value; } char * crm_element_value_copy(const crm_data_t *data, const char *name) { const char *value = NULL; char *value_copy = NULL; crm_validate_data(data); value = cl_get_string(data, name); #if XML_PARANOIA_CHECKS CRM_CHECK(cl_is_allocated(value) == 1, return NULL); #endif if(value != NULL) { value_copy = crm_strdup(value); } return value_copy; } const char * crm_element_name(const crm_data_t *data) { #if CRM_DEV_BUILD crm_validate_data(data); #endif return cl_get_string(data, F_XML_TAGNAME); } void xml_remove_prop(crm_data_t *obj, const char *name) { if(crm_element_value(obj, name) != NULL) { cl_msg_remove(obj, name); } } void crm_update_parents(crm_data_t *xml_root) { crm_validate_data(xml_root); xml_child_iter( xml_root, a_child, crm_set_element_parent(a_child, xml_root); crm_update_parents(a_child); ); } int get_tag_name(const char *input) { int lpc = 0; char ch = 0; const char *error = NULL; gboolean do_special = FALSE; for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c [%d]", ch, 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 { return lpc; } break; case '/': case '>': case '\t': case '\n': case ' ': if(!do_special) { return lpc; } 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; } int get_attr_name(const char *input) { int lpc = 0; char ch = 0; const char *error = NULL; for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c[%d]", ch, lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '\t': case '\n': case ' ': error = "unexpected whitespace"; break; case '=': return lpc; default: if('a' <= ch && ch <= 'z') { } else if('A' <= ch && ch <= 'Z') { } else if(isdigit((int) ch)) { } else if(ch == '_') { } else if(ch == '-') { } else { error = "bad character, not in [a-zA-Z0-9_-]"; } break; } } crm_err("Error parsing token near %.15s: %s", input, crm_str(error)); return -1; } int get_attr_value(const char *input) { int lpc = 0; char ch = 0; const char *error = NULL; for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = input[lpc]; crm_debug_5("Processing char %c [%d]", ch, 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; default: break; } } crm_err("Error parsing token near %.15s: %s", input, crm_str(error)); return -1; } int is_comment_start(const char *input) { CRM_CHECK(input != NULL, return 0); if(strlen(input) > 4 && input[0] == '<' && input[1] == '!' && input[2] == '-' && input[3] == '-') { crm_debug_6("Found comment start: "); return 3; } else if(strlen(input) > 1 && input[0] == '?' && input[1] == '>') { crm_debug_6("Found comment end: ?>"); return 2; } if(strlen(input) > 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; } gboolean drop_comments(const char *input, int *offset) { gboolean more = TRUE; gboolean in_directive = FALSE; int in_comment = FALSE; const char *our_input = input; int len = 0, lpc = 0; int tag_len = 0; char ch = 0; if(input == NULL) { return FALSE; } if(offset != NULL) { our_input = input + (*offset); } len = strlen(our_input); while(lpc < len && more) { ch = our_input[lpc]; crm_debug_6("Processing char %c[%d]", ch, lpc); switch(ch) { case 0: if(in_comment == FALSE) { more = FALSE; } else { crm_err("unexpected EOS"); crm_warn("Parsing error at or before: %s", our_input); } break; case '<': tag_len = is_comment_start(our_input + lpc); if(tag_len > 0) { if(in_comment) { crm_err("Nested XML comments are not supported!"); crm_warn("Parsing error at or before: %s", our_input); crm_warn("Netsed comment found at: %s", our_input + lpc + tag_len); } in_comment = TRUE; lpc+=tag_len; if(tag_len == 2) { #if 1 if(our_input[lpc-1] == '!') { in_directive = TRUE; } #else tag_len = get_tag_name(our_input + lpc); crm_debug_2("Tag length: %d", len); if(strncmp("DOCTYPE", our_input+lpc, tag_len) == 0) { in_directive = TRUE; } #endif } } else if(in_comment == FALSE){ more = FALSE; } else { lpc++; crm_debug_6("Skipping comment char %c", our_input[lpc]); } break; case '>': lpc++; if(in_directive) { in_directive = FALSE; in_comment = FALSE; } break; case '-': case '?': tag_len = is_comment_end(our_input + lpc); if(tag_len > 0) { lpc+=tag_len; in_comment = FALSE; } else { lpc++; crm_debug_6("Skipping comment char %c", our_input[lpc]); } break; case ' ': case '\t': case '\n': case '\r': lpc++; crm_debug_6("Skipping whitespace char %d", our_input[lpc]); break; default: lpc++; crm_debug_6("Skipping comment char %c", our_input[lpc]); break; } } crm_debug_4("Finished processing comments"); if(offset != NULL) { (*offset) += lpc; } if(lpc > 0) { crm_debug_5("Skipped %d comment chars", lpc); return TRUE; } return FALSE; } crm_data_t* parse_xml(const char *input, int *offset) { int len = 0, lpc = 0; char ch = 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; crm_data_t *new_obj = NULL; if(input == NULL) { return NULL; } if(offset != NULL) { our_input = input + (*offset); } len = strlen(our_input); while(lpc < len && were_comments) { were_comments = drop_comments(our_input, &lpc); } CRM_CHECK(our_input[lpc] == '<', return NULL); lpc++; len = get_tag_name(our_input + lpc); crm_debug_5("Tag length: %d", len); if(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 = ha_msg_new(1); ha_msg_add(new_obj, F_XML_TAGNAME, tag_name); lpc += len; for(; more && error == NULL && lpc < (ssize_t)strlen(input); lpc++) { ch = our_input[lpc]; crm_debug_5("Processing char %c[%d]", ch, lpc); switch(ch) { case 0: error = "unexpected EOS"; break; case '/': if(our_input[lpc+1] == '>') { more = FALSE; } break; case '<': if(our_input[lpc+1] != '/') { crm_data_t *child = NULL; gboolean any_comments = FALSE; do { were_comments = drop_comments(our_input, &lpc); any_comments = any_comments || were_comments; } while(lpc < len && were_comments); if(any_comments) { lpc--; break; } crm_debug_4("Start parsing child..."); child = parse_xml(our_input, &lpc); if(child == NULL) { error = "error parsing child"; } else { ha_msg_addstruct_compress( new_obj, crm_element_name(child), child); crm_debug_4("Finished parsing child: %s", crm_element_name(child)); ha_msg_del(child); if(our_input[lpc] == '<') { /* lpc is about to get incrimented * make sure we process the '<' that * we're currently looking at */ lpc--; } /* lpc++; /\* > *\/ */ } } else { lpc += 2; /* *\/ */ if(our_input[lpc] != '>') { 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); 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; /* lpc++; /\* " *\/ */ crm_debug_4("creating nvpair: <%s %s=\"%s\"...", tag_name, crm_str(attr_name), crm_str(attr_value)); ha_msg_add(new_obj, attr_name, attr_value); crm_free(attr_name); crm_free(attr_value); } break; case '>': case ' ': case '\t': case '\n': case '\r': break; default: len = get_attr_name(our_input+lpc); 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: %s", our_input+lpc-3); return NULL; } 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, crm_data_t *diff, const char *function) { crm_data_t *added = find_xml_node(diff, "diff-added", FALSE); crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE); gboolean is_first = TRUE; xml_child_iter( removed, child, log_data_element(function, "-", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { crm_log_maybe(log_level, " --- "); } ); /* crm_log_maybe(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 { crm_log_maybe(log_level, " --- "); } ); } void purge_diff_markers(crm_data_t *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(crm_data_t *old, crm_data_t *diff, crm_data_t **new) { gboolean result = TRUE; crm_data_t *added = find_xml_node(diff, "diff-added", FALSE); crm_data_t *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; #if CRM_DEV_BUILD } else if(result) { crm_data_t *intermediate = NULL; crm_data_t *diff_of_diff = NULL; crm_debug_2("Verification Phase"); intermediate = diff_xml_object(old, *new, FALSE); diff_of_diff = diff_xml_object(intermediate, diff, TRUE); if(diff_of_diff != NULL) { crm_warn("Diff application failed!"); log_xml_diff(LOG_DEBUG, diff_of_diff, "diff:diff_of_diff"); log_xml_diff(LOG_INFO, intermediate, "diff:actual_diff"); result = FALSE; } free_xml(diff_of_diff); free_xml(intermediate); diff_of_diff = NULL; intermediate = NULL; #endif } if(result == FALSE) { log_xml_diff(LOG_INFO, diff, "diff:input_diff"); log_data_element("diff:input", NULL, LOG_DEBUG_2, 0, old, TRUE); /* CRM_CHECK(diff_of_diff != NULL); */ result = FALSE; } else { purge_diff_markers(*new); } return result; } crm_data_t * diff_xml_object(crm_data_t *old, crm_data_t *new, gboolean suppress) { crm_data_t *diff = NULL; crm_data_t *tmp1 = NULL; crm_data_t *added = NULL; crm_data_t *removed = NULL; tmp1 = subtract_xml_object(old, new, "removed:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { ha_msg_del(tmp1); tmp1 = NULL; } else { diff = create_xml_node(NULL, "diff"); removed = create_xml_node(diff, "diff-removed"); added = create_xml_node(diff, "diff-added"); add_node_copy(removed, tmp1); } free_xml(tmp1); } tmp1 = subtract_xml_object(new, old, "added:top"); if(tmp1 != NULL) { if(suppress && can_prune_leaf(tmp1)) { ha_msg_del(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_copy(added, tmp1); free_xml(tmp1); } return diff; } gboolean can_prune_leaf(crm_data_t *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)) { cl_msg_remove_value(xml_node, child); __counter--; } else { can_prune = FALSE; } ); return can_prune; } void diff_filter_context(int context, int upper_bound, int lower_bound, crm_data_t *xml_node, crm_data_t *parent) { crm_data_t *us = NULL; crm_data_t *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, crm_data_t *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; } crm_data_t * subtract_xml_object(crm_data_t *left, crm_data_t *right, const char *marker) { gboolean skip = FALSE; gboolean differences = FALSE; crm_data_t *diff = NULL; crm_data_t *child_diff = NULL; crm_data_t *right_child = NULL; const char *right_val = NULL; const char *name = NULL; int lpc = 0; const char *filter[] = { XML_ATTR_ORIGIN, XML_DIFF_MARKER, XML_CIB_ATTR_WRITTEN, }; crm_log_xml(LOG_DEBUG_5, "left:", left); crm_log_xml(LOG_DEBUG_5, "right:", right); if(left == NULL) { return NULL; } else if(right == NULL) { crm_data_t *deleted = NULL; crm_debug_5("Processing <%s id=%s> (complete copy)", crm_element_name(left), ID(left)); 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); CRM_CHECK(safe_str_eq(crm_element_name(left), crm_element_name(right)), return NULL); CRM_CHECK(safe_str_eq(ID(left), 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(left)); xml_prop_iter(left, prop_name, left_value, if(safe_str_eq(prop_name, XML_ATTR_ID)) { continue; } skip = FALSE; for(lpc = 0; skip == FALSE && lpc < DIMOF(filter); lpc++){ if(safe_str_eq(prop_name, filter[lpc])) { 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(left)); 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_copy(diff, child_diff); free_xml(child_diff); } ); if(differences == FALSE) { /* check for XML_DIFF_MARKER in a child */ xml_child_iter( right, right_child, if(crm_element_value(right_child, XML_DIFF_MARKER)) { crm_debug_2("Found the root of the deletion: %s", name); differences = TRUE; break; } ); } if(differences == FALSE) { free_xml(diff); crm_debug_5("\tNo changes to <%s id=%s>", crm_str(name), ID(left)); return NULL; } crm_xml_add(diff, XML_ATTR_ID, ID(left)); return diff; } int add_xml_object(crm_data_t *parent, crm_data_t *target, const crm_data_t *update) { const char *object_id = NULL; const char *object_name = NULL; const char *right_val = NULL; int result = 0; 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_debug_2("Added <%s%s%s/>", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); CRM_CHECK(target != NULL, return 0); } else { crm_debug_2("Found node <%s%s%s/> to update", crm_str(object_name), object_id?" id=":"", object_id?object_id:""); } xml_prop_iter(update, prop_name, left_value, right_val = crm_element_value(target, prop_name); if(safe_str_eq(prop_name, XML_DIFF_MARKER)) { continue; } else if(right_val == NULL) { crm_xml_add(target, prop_name, left_value); crm_debug_2("\t%s: %s (added)", crm_str(prop_name), crm_str(left_value)); } else if(safe_str_neq(left_value, right_val)) { crm_xml_add(target, prop_name, left_value); crm_debug_2("\t%s: %s->%s", crm_str(prop_name), crm_str(left_value), right_val); } ); crm_debug_3("Processing children of <%s id=%s>", crm_str(object_name), crm_str(object_id)); xml_child_iter( update, a_child, int tmp_result = 0; crm_debug_3("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); tmp_result = add_xml_object(target, NULL, a_child); if(tmp_result < 0) { crm_err("Error updating child <%s id=%s>", crm_element_name(a_child), ID(a_child)); /* only the first error is likely to be interesting */ if(result >= 0) { result = tmp_result; } } ); crm_debug_3("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id)); return result; } gboolean update_xml_child(crm_data_t *child, crm_data_t *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(child, "Update match found..."); copy_in_properties(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; } gboolean find_xml_child(crm_data_t *child, const char *tag, const char *id) { gboolean match_found = TRUE; CRM_CHECK(child != NULL, return FALSE); if(safe_str_neq(tag, crm_element_name(child))) { match_found = FALSE; } else if(safe_str_neq(id, ID(child))) { match_found = FALSE; } else if(match_found) { crm_err("Update match found for <%s id=%s.../>", tag, id); crm_log_xml_debug(child, __FUNCTION__); } xml_child_iter( child, child_of_child, /* only find the first one */ if(match_found) { break; } match_found = find_xml_child(child_of_child, tag, id); ); return match_found; } gboolean delete_xml_child(crm_data_t *parent, crm_data_t *child, crm_data_t *to_delete) { gboolean can_delete = FALSE; const char *right_val = NULL; CRM_CHECK(child != NULL, return FALSE); CRM_CHECK(to_delete != NULL, return FALSE); if(safe_str_eq(crm_element_name(to_delete), crm_element_name(child))) { can_delete = TRUE; } xml_prop_iter(to_delete, prop_name, left_value, if(can_delete == FALSE) { break; } 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(child, "Delete match found..."); cl_msg_remove_value(parent, child); child = NULL; } else if(can_delete) { crm_log_xml_debug(child, "Cannot delete the search root"); } xml_child_iter( child, child_of_child, /* only delete the first one */ if(can_delete) { break; } can_delete = delete_xml_child(child, child_of_child, to_delete); ); return can_delete; } void hash2nvpair(gpointer key, gpointer value, gpointer user_data) { const char *name = key; const char *s_value = value; crm_data_t *xml_node = user_data; crm_data_t *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; crm_data_t *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); } } #if CRM_DEPRECATED_SINCE_2_0_3 GHashTable * xml2list_202(crm_data_t *parent) { crm_data_t *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(crm_data_t *parent) { crm_data_t *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(crm_data_t *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, sizeof(char)*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); } gboolean do_id_check(crm_data_t *xml_obj, GHashTable *id_hash) { int lpc = 0; char *lookup_id = NULL; gboolean modified = FALSE; const char *tag_id = NULL; const char *tag_name = NULL; const char *lookup_value = NULL; gboolean created_hash = FALSE; 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_CIB_TAG_LRM, XML_LRM_TAG_RESOURCES, XML_TAG_PARAMS, "operations", }; const char *non_unique[] = { XML_LRM_TAG_RESOURCE, XML_LRM_TAG_RSC_OP, }; 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)) { modified = TRUE; } ); tag_id = ID(xml_obj); tag_name = TYPE(xml_obj); xml_prop_iter( xml_obj, local_prop_name, local_prop_value, if(safe_str_eq(local_prop_name, XML_DIFF_MARKER)) { crm_err("Detected "XML_DIFF_MARKER" attribute"); continue; } lookup_id = NULL; if(ID(xml_obj) != NULL) { 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 */ break; } } if(lpc < DIMOF(non_unique)) { break; } lookup_id = crm_concat(tag_name, tag_id, '-'); lookup_value = g_hash_table_lookup(id_hash, lookup_id); if(lookup_value != NULL) { char *old_id = crm_strdup(tag_id); modified = TRUE; assign_uuid(xml_obj); tag_id = ID(xml_obj); crm_err("\"id\" collision detected..." " Multiple '%s' entries with id=\"%s\"," " assigned id=\"%s\"", tag_name, old_id, tag_id); } g_hash_table_insert( id_hash, lookup_id, crm_strdup(tag_id)); break; } for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) { if(safe_str_eq(tag_name, allowed_list[lpc])) { /* this tag is never meant to have an ID */ break; } } if(lpc < DIMOF(allowed_list)) { break; } modified = TRUE; crm_err("%s object with attributes but no ID field detected.", tag_name); assign_uuid(xml_obj); break; ); 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; crm_data_t *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 crm_data_t * sorted_xml(const crm_data_t *input) { GListPtr sorted = NULL; GListPtr unsorted = NULL; name_value_t *pair = NULL; crm_data_t *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(NULL, 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) ); 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); return result; } /* "c048eae664dba840e1d2060f00299e9d" */ char * calculate_xml_digest(crm_data_t *input, gboolean sort) { int i = 0; int digest_len = 16; char *digest = NULL; unsigned char *raw_digest = NULL; crm_data_t *sorted = NULL; char *buffer = NULL; if(sort) { sorted = sorted_xml(input); } else { sorted = copy_xml(input); } buffer = dump_xml_formatted(sorted); CRM_CHECK(buffer != NULL && strlen(buffer) > 0, free_xml(sorted); return NULL); crm_malloc0(digest, sizeof(char) * (2 * digest_len + 1)); crm_malloc0(raw_digest, sizeof(char) * (digest_len + 1)); MD5((unsigned char *)buffer, strlen(buffer), raw_digest); for(i = 0; i < digest_len; i++) { sprintf(digest+(2*i), "%02x", raw_digest[i]); } crm_debug_2("Digest is: %s\n", digest); crm_log_xml(LOG_DEBUG_3, "digest:source", sorted); crm_free(buffer); crm_free(raw_digest); free_xml(sorted); return digest; }