diff --git a/crm/cib/callbacks.c b/crm/cib/callbacks.c index c5dbb5279e..63d012f7fe 100644 --- a/crm/cib/callbacks.c +++ b/crm/cib/callbacks.c @@ -1,1442 +1,1442 @@ -/* $Id: callbacks.c,v 1.82 2005/10/12 18:28:22 andrew Exp $ */ +/* $Id: callbacks.c,v 1.83 2005/10/17 19:13:47 gshi 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 #include #include #include gint cib_GCompareFunc(gconstpointer a, gconstpointer b); gboolean cib_msg_timeout(gpointer data); void cib_GHFunc(gpointer key, gpointer value, gpointer user_data); gboolean can_write(int flags); -HA_Message *cib_msg_copy(const HA_Message *msg, gboolean with_data); +HA_Message *cib_msg_copy(HA_Message *msg, gboolean with_data); gboolean ccm_manual_check(gpointer data); extern enum cib_errors revision_check(crm_data_t *cib_update, crm_data_t *cib_copy, int flags); void send_cib_replace(const HA_Message *sync_request, const char *host); -void cib_process_request(const HA_Message *request, gboolean privileged, +void cib_process_request(HA_Message *request, gboolean privileged, gboolean from_peer, cib_client_t *cib_client); gboolean syncd_once = FALSE; GHashTable *peer_hash = NULL; int next_client_id = 0; gboolean cib_is_master = FALSE; gboolean cib_have_quorum = FALSE; char * ccm_transition_id = NULL; GHashTable *client_list = NULL; GHashTable *ccm_membership = NULL; extern const char *cib_our_uname; extern ll_cluster_t *hb_conn; extern int set_connected_peers(crm_data_t *xml_obj); /* typedef struct cib_operation_s { const char* operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_quorum; gboolean needs_section; gboolean needs_data; enum cib_errors (*fn)( const char *, int, const char *, crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**); } cib_operation_t; */ /* technically bump does modify the cib... * but we want to split the "bump" from the "sync" */ cib_operation_t cib_server_ops[] = { {NULL, FALSE,FALSE,FALSE,FALSE,FALSE,cib_process_default}, {CRM_OP_NOOP, FALSE,FALSE,FALSE,FALSE,FALSE,cib_process_default}, {CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_diff}, {CIB_OP_SLAVE, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_readwrite}, {CIB_OP_SLAVEALL, TRUE, TRUE, FALSE,FALSE,FALSE,cib_process_readwrite}, {CIB_OP_SYNC_ONE, FALSE,TRUE, FALSE,TRUE, FALSE,cib_process_sync_one}, {CIB_OP_MASTER, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_readwrite}, {CIB_OP_ISMASTER, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_readwrite}, {CIB_OP_BUMP, TRUE, TRUE, TRUE, TRUE, FALSE,cib_process_bump}, {CIB_OP_REPLACE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_replace}, {CIB_OP_CREATE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_change}, {CIB_OP_MODIFY, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_modify}, {CIB_OP_UPDATE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_change}, {CIB_OP_DELETE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_delete}, {CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_change}, {CIB_OP_QUERY, FALSE,FALSE,FALSE,TRUE, FALSE,cib_process_query}, {CIB_OP_SYNC, FALSE,TRUE, FALSE,TRUE, FALSE,cib_process_sync}, {CRM_OP_QUIT, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_quit}, {CRM_OP_PING, FALSE,FALSE,FALSE,FALSE,FALSE,cib_process_ping}, {CIB_OP_ERASE, TRUE, TRUE, TRUE, TRUE, FALSE,cib_process_erase} }; int send_via_callback_channel(HA_Message *msg, const char *token); enum cib_errors cib_process_command( - const HA_Message *request, HA_Message **reply, + HA_Message *request, HA_Message **reply, crm_data_t **cib_diff, gboolean privileged); gboolean cib_common_callback( IPC_Channel *channel, gpointer user_data, gboolean privileged); enum cib_errors cib_get_operation_id(const HA_Message * msg, int *operation); gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client); gboolean cib_client_connect(IPC_Channel *channel, gpointer user_data) { gboolean auth_failed = FALSE; gboolean can_connect = TRUE; gboolean (*client_callback)(IPC_Channel *channel, gpointer user_data) = NULL; cib_client_t *new_client = NULL; crm_debug_3("Connecting channel"); if (channel == NULL) { crm_err("Channel was NULL"); can_connect = FALSE; } else if (channel->ch_status == IPC_DISCONNECT) { crm_err("Channel was disconnected"); can_connect = FALSE; } else if(user_data == NULL) { crm_err("user_data must contain channel name"); can_connect = FALSE; } else { crm_malloc0(new_client, sizeof(cib_client_t)); new_client->id = NULL; new_client->callback_id = NULL; new_client->source = NULL; new_client->channel = channel; new_client->channel_name = user_data; new_client->delegated_calls = NULL; crm_debug_3("Created channel %p for channel %s", new_client, new_client->channel_name); client_callback = NULL; /* choose callback and do auth based on channel_name */ if(safe_str_eq(new_client->channel_name, cib_channel_callback)) { client_callback = cib_null_callback; } else { cl_uuid_t client_id; cl_uuid_generate(&client_id); crm_malloc0(new_client->id, sizeof(char)*36); cl_uuid_unparse(&client_id, new_client->id); new_client->id[35] = EOS; cl_uuid_generate(&client_id); crm_malloc0(new_client->callback_id, sizeof(char)*36); cl_uuid_unparse(&client_id, new_client->callback_id); new_client->callback_id[35] = EOS; client_callback = cib_ro_callback; if(safe_str_eq(new_client->channel_name, cib_channel_rw)) { client_callback = cib_rw_callback; } } } if(auth_failed) { crm_err("Connection to %s channel failed authentication", (char *)user_data); can_connect = FALSE; } if(can_connect == FALSE) { if(new_client) { crm_free(new_client->id); crm_free(new_client->callback_id); } crm_free(new_client); return FALSE; } channel->ops->set_recv_qlen(channel, 100); if(safe_str_eq(new_client->channel_name, cib_channel_callback)) { channel->ops->set_send_qlen(channel, 400); } else { channel->ops->set_send_qlen(channel, 100); } if(client_callback != NULL) { new_client->source = G_main_add_IPC_Channel( G_PRIORITY_DEFAULT, channel, FALSE, client_callback, new_client, default_ipc_connection_destroy); } if(client_callback != cib_null_callback) { /* send msg to client with uuid to use when signing up for * callback channel */ HA_Message *reg_msg = ha_msg_new(3); ha_msg_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); ha_msg_add(reg_msg, F_CIB_CLIENTID, new_client->id); ha_msg_add( reg_msg, F_CIB_CALLBACK_TOKEN, new_client->callback_id); send_ipc_message(channel, reg_msg); /* make sure we can find ourselves later for sync calls * redirected to the master instance */ g_hash_table_insert(client_list, new_client->id, new_client); } crm_debug_3("Channel %s connected for client %s", new_client->channel_name, new_client->id); return TRUE; } gboolean cib_rw_callback(IPC_Channel *channel, gpointer user_data) { return cib_common_callback(channel, user_data, TRUE); } gboolean cib_ro_callback(IPC_Channel *channel, gpointer user_data) { return cib_common_callback(channel, user_data, FALSE); } gboolean cib_null_callback(IPC_Channel *channel, gpointer user_data) { gboolean did_disconnect = TRUE; HA_Message *op_request = NULL; cib_client_t *cib_client = user_data; cib_client_t *hash_client = NULL; const char *type = NULL; const char *uuid_ticket = NULL; const char *client_name = NULL; gboolean register_failed = FALSE; if(cib_client == NULL) { crm_err("Discarding IPC message from unknown source" " on callback channel."); return FALSE; } while(channel->ops->is_message_pending(channel)) { if (channel->ch_status != IPC_CONNECT) { /* The message which was pending for us is that * the channel is no longer fully connected. * * Dont read requests from disconnected clients */ break; } op_request = msgfromIPC_noauth(channel); type = cl_get_string(op_request, F_CIB_OPERATION); if(safe_str_eq(type, T_CIB_NOTIFY) ) { /* Update the notify filters for this client */ int on_off = 0; ha_msg_value_int( op_request, F_CIB_NOTIFY_ACTIVATE, &on_off); type = cl_get_string(op_request, F_CIB_NOTIFY_TYPE); crm_info("Setting %s callbacks for %s: %s", type, cib_client->name, on_off?"on":"off"); if(safe_str_eq(type, T_CIB_POST_NOTIFY)) { cib_client->post_notify = on_off; } else if(safe_str_eq(type, T_CIB_PRE_NOTIFY)) { cib_client->pre_notify = on_off; } else if(safe_str_eq(type, T_CIB_UPDATE_CONFIRM)) { cib_client->confirmations = on_off; } else if(safe_str_eq(type, T_CIB_DIFF_NOTIFY)) { cib_client->diffs = on_off; } continue; } else if(safe_str_neq(type, CRM_OP_REGISTER) ) { crm_warn("Discarding IPC message from %s on callback channel", cib_client->id); crm_msg_del(op_request); continue; } uuid_ticket = cl_get_string(op_request, F_CIB_CALLBACK_TOKEN); client_name = cl_get_string(op_request, F_CIB_CLIENTNAME); CRM_DEV_ASSERT(uuid_ticket != NULL); if(crm_assert_failed) { register_failed = crm_assert_failed; } CRM_DEV_ASSERT(client_name != NULL); if(crm_assert_failed) { register_failed = crm_assert_failed; } if(register_failed == FALSE) { hash_client = g_hash_table_lookup(client_list, uuid_ticket); if(hash_client != NULL) { crm_err("Duplicate registration request..." " disconnecting"); register_failed = TRUE; } } if(register_failed) { crm_err("Registration request failed... disconnecting"); crm_msg_del(op_request); return FALSE; } cib_client->id = crm_strdup(uuid_ticket); cib_client->name = crm_strdup(client_name); g_hash_table_insert(client_list, cib_client->id, cib_client); crm_debug_2("Registered %s on %s channel", cib_client->id, cib_client->channel_name); if(safe_str_eq(cib_client->name, CRM_SYSTEM_TENGINE)) { /* The TE is _always_ interested in these * Enable now to avoid timing issues */ cib_client->diffs = TRUE; } crm_msg_del(op_request); op_request = ha_msg_new(2); ha_msg_add(op_request, F_CIB_OPERATION, CRM_OP_REGISTER); ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id); send_ipc_message(channel, op_request); } did_disconnect = cib_process_disconnect(channel, cib_client); if(did_disconnect) { crm_debug_2("Client disconnected"); } return did_disconnect; } gboolean cib_common_callback( IPC_Channel *channel, gpointer user_data, gboolean privileged) { int rc = cib_ok; int lpc = 0; const char *op = NULL; HA_Message *op_request = NULL; cib_client_t *cib_client = user_data; if(cib_client == NULL) { crm_err("Receieved call from unknown source. Discarding."); return FALSE; } crm_debug_2("Callback for %s on %s channel", cib_client->id, cib_client->channel_name); while(channel->ops->is_message_pending(channel)) { if (channel->ch_status != IPC_CONNECT) { /* The message which was pending for us is that * the channel is no longer fully connected. * * Dont read requests from disconnected clients */ break; } op_request = msgfromIPC(channel, 0); if (op_request == NULL) { perror("Receive failure:"); break; } op = cl_get_string(op_request, F_CIB_OPERATION); crm_info("Operation %s from client %s/%s", op, cib_client->id, cib_client->channel_name); crm_log_message_adv(LOG_MSG, "Client[inbound]", op_request); lpc++; rc = cib_ok; CRM_DEV_ASSERT( ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id) == HA_OK); if(crm_assert_failed == FALSE) { cib_process_request( op_request, privileged, FALSE, cib_client); } crm_debug_3("Cleaning up request"); crm_msg_del(op_request); op_request = NULL; } crm_debug_2("Processed %d messages", lpc); return cib_process_disconnect(channel, cib_client); } void -cib_process_request(const HA_Message *request, gboolean privileged, +cib_process_request(HA_Message *request, gboolean privileged, gboolean from_peer, cib_client_t *cib_client) { int call_type = 0; int call_options = 0; gboolean process = TRUE; gboolean needs_reply = TRUE; gboolean local_notify = FALSE; gboolean needs_forward = FALSE; crm_data_t *result_diff = NULL; enum cib_errors rc = cib_ok; HA_Message *op_reply = NULL; const char *op = cl_get_string(request, F_CIB_OPERATION); const char *originator = cl_get_string(request, F_ORIG); const char *host = cl_get_string(request, F_CIB_HOST); const char *reply_to = cl_get_string(request, F_CIB_ISREPLY); const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE); const char *delegated = cl_get_string(request, F_CIB_DELEGATED); const char *client_id = NULL; crm_debug_4("%s Processing msg %s", cib_our_uname, cl_get_string(request, F_SEQ)); if(host != NULL && strlen(host) == 0) { host = NULL; } ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options); crm_debug_4("Retrieved call options: %d", call_options); crm_debug_2("Processing %s message (%s) to %s...", from_peer?"peer":"local",originator, host?host:"master"); rc = cib_get_operation_id(request, &call_type); if(rc != cib_ok) { /* TODO: construct error reply */ crm_err("Pre-processing of command failed: %s", cib_error2string(rc)); } else if(from_peer == FALSE) { if(cib_server_ops[call_type].modifies_cib && !(call_options & cib_inhibit_bcast)) { /* we need to send an update anyway */ needs_reply = TRUE; } else { needs_reply = FALSE; } if(host == NULL && (call_options & cib_scope_local)) { crm_debug("Processing locally scoped %s op from %s", op, cib_client->name); local_notify = TRUE; } else if(host == NULL && cib_is_master) { crm_debug("Processing master %s op locally from %s", op, cib_client->name); local_notify = TRUE; } else if(safe_str_eq(host, cib_our_uname)) { crm_debug("Processing locally addressed %s op from %s", op, cib_client->name); local_notify = TRUE; } else { crm_debug("%s op from %s needs to be forwarded to %s", op, cib_client->name, host?host:"the master instance"); needs_forward = TRUE; process = FALSE; } } else if(crm_is_true(update) && safe_str_eq(reply_to, cib_our_uname)) { crm_debug("Processing global/peer update from %s" " that originated from us", originator); needs_reply = FALSE; local_notify = TRUE; } else if(crm_is_true(update)) { crm_debug("Processing global/peer update from %s", originator); needs_reply = FALSE; } else if(host != NULL && safe_str_eq(host, cib_our_uname)) { crm_debug("Processing request sent to us from %s", originator); } else if(delegated != NULL && cib_is_master == TRUE) { crm_debug("Processing request sent to master instance from %s", originator); } else if(reply_to != NULL && safe_str_eq(reply_to, cib_our_uname)) { crm_debug("Forward reply sent from %s to local clients", originator); process = FALSE; needs_reply = FALSE; local_notify = TRUE; } else if(delegated != NULL) { crm_debug("Ignoring msg for master instance"); return; } else if(host != NULL) { /* this is for a specific instance and we're not it */ crm_debug("Ignoring msg for instance on %s", crm_str(host)); return; } else if(reply_to == NULL && cib_is_master == FALSE) { /* this is for the master instance and we're not it */ crm_debug("Ignoring reply to %s", crm_str(reply_to)); return; } else { crm_err("Nothing for us to do?"); crm_log_message_adv(LOG_ERR, "Peer[inbound]", request); return; } crm_debug_3("Finished determining processing actions"); if(call_options & cib_discard_reply) { needs_reply = cib_server_ops[call_type].modifies_cib; local_notify = FALSE; } if(needs_forward) { HA_Message *forward_msg = cib_msg_copy(request, TRUE); ha_msg_add(forward_msg, F_CIB_DELEGATED, cib_our_uname); if(host != NULL) { crm_debug("Forwarding %s op to %s", op, host); send_ha_message(hb_conn, forward_msg, host, FALSE); } else { crm_debug("Forwarding %s op to master instance", op); send_ha_message(hb_conn, forward_msg, NULL, FALSE); } if(call_options & cib_discard_reply) { crm_debug_2("Client not interested in reply"); crm_msg_del(forward_msg); } else if(call_options & cib_sync_call) { /* keep track of the request so we can time it * out if required */ crm_debug("Registering delegated call from %s", cib_client->id); cib_client->delegated_calls = g_list_append( cib_client->delegated_calls, forward_msg); } else { crm_msg_del(forward_msg); } return; } if(process) { crm_debug_2("Performing local processing:" " op=%s origin=%s/%s,%s (update=%s)", cl_get_string(request, F_CIB_OPERATION), originator, cl_get_string(request, F_CIB_CLIENTID), cl_get_string(request, F_CIB_CALLID), update); rc = cib_process_command(request, &op_reply, &result_diff, TRUE); crm_debug_2("Processing complete"); if(rc == cib_diff_resync || rc == cib_diff_failed || rc == cib_old_data) { crm_warn("%s operation failed: %s", crm_str(op), cib_error2string(rc)); } else if(rc != cib_ok) { crm_err("%s operation failed: %s", crm_str(op), cib_error2string(rc)); crm_log_message_adv(LOG_DEBUG, "CIB[output]", op_reply); crm_log_message_adv(LOG_INFO, "Input message", request); } if(op_reply == NULL && (needs_reply || local_notify)) { crm_err("Unexpected NULL reply to message"); crm_log_message(LOG_ERR, request); needs_reply = FALSE; local_notify = FALSE; } } crm_debug_3("processing response cases"); if(local_notify) { /* send callback to originating child */ cib_client_t *client_obj = NULL; HA_Message *client_reply = NULL; enum cib_errors local_rc = cib_ok; crm_debug_2("Performing notification"); if(process == FALSE) { client_reply = cib_msg_copy(request, TRUE); } else { client_reply = cib_msg_copy(op_reply, TRUE); } client_id = cl_get_string(request, F_CIB_CLIENTID); if(client_id != NULL) { client_obj = g_hash_table_lookup( client_list, client_id); } else { crm_debug("No client to sent the response to." " F_CIB_CLIENTID not set."); } crm_debug_3("Sending callback to request originator"); if(client_obj != NULL) { crm_debug_2("Sending %ssync response to %s %s", (call_options & cib_sync_call)?"":"an a-", client_obj->id, from_peer?"(originator of delegated request)":""); if(call_options & cib_sync_call) { send_via_callback_channel( client_reply, client_obj->id); } else { send_via_callback_channel( client_reply, client_obj->callback_id); } } else { crm_warn("Client %s may have left us", crm_str(client_id)); crm_msg_del(client_reply); } if(local_rc != cib_ok) { crm_warn("%sSync reply failed: %s", (call_options & cib_sync_call)?"":"A-", cib_error2string(local_rc)); crm_log_message(LOG_DEBUG, op_reply); } } if(needs_reply == FALSE) { /* nothing more to do... * this was a non-originating slave update */ crm_debug_2("Completed slave update"); crm_msg_del(op_reply); free_xml(result_diff); return; } crm_debug_4("add the originator to message"); CRM_DEV_ASSERT(op_reply != NULL); CRM_DEV_ASSERT(cib_server_ops[call_type].modifies_cib == FALSE || result_diff != NULL || rc != cib_ok); /* from now on we are the server */ if(rc == cib_ok && result_diff != NULL && cib_server_ops[call_type].modifies_cib && !(call_options & cib_inhibit_bcast)) { /* this (successful) call modified the CIB _and_ the * change needs to be broadcast... * send via HA to other nodes */ HA_Message *op_bcast = cib_msg_copy(request, FALSE); int diff_add_updates = 0; int diff_add_epoch = 0; int diff_add_admin_epoch = 0; int diff_del_updates = 0; int diff_del_epoch = 0; int diff_del_admin_epoch = 0; cib_diff_version_details( result_diff, &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); crm_debug("Sending update diff %d.%d.%d -> %d.%d.%d", diff_del_admin_epoch,diff_del_epoch,diff_del_updates, diff_add_admin_epoch,diff_add_epoch,diff_add_updates); ha_msg_add(op_bcast, F_CIB_ISREPLY, originator); ha_msg_add(op_bcast, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); ha_msg_mod(op_bcast, F_CIB_OPERATION, CIB_OP_APPLY_DIFF); add_message_xml(op_bcast, F_CIB_UPDATE_DIFF, result_diff); crm_log_message(LOG_DEBUG_3, op_bcast); send_ha_message(hb_conn, op_bcast, NULL, TRUE); crm_msg_del(op_bcast); } else if((call_options & cib_discard_reply) == 0) { crm_debug_2("Sending replies"); if(from_peer && originator != NULL) { /* send reply via HA to originating node */ crm_debug("Sending request result to originator only"); ha_msg_add(op_reply, F_CIB_ISREPLY, originator); send_ha_message(hb_conn, op_reply, originator, FALSE); } if(call_options & cib_inhibit_bcast ) { crm_debug("Request not broadcast: inhibited"); } if(cib_server_ops[call_type].modifies_cib == FALSE) { crm_debug_2("Request not broadcast: R/O call"); } if(rc != cib_ok) { crm_warn("Request not broadcast: call failed: %s", cib_error2string(rc)); } } crm_msg_del(op_reply); free_xml(result_diff); return; } enum cib_errors -cib_process_command(const HA_Message *request, HA_Message **reply, +cib_process_command(HA_Message *request, HA_Message **reply, crm_data_t **cib_diff, gboolean privileged) { crm_data_t *output = NULL; crm_data_t *input = NULL; crm_data_t *input_fragment = NULL; crm_data_t *current_cib = the_cib; crm_data_t *result_cib = NULL; crm_data_t *local_diff = NULL; int call_type = 0; int call_options = 0; enum cib_errors rc = cib_ok; const char *op = NULL; const char *call_id = NULL; const char *section = NULL; const char *tmp = NULL; gboolean global_update = crm_is_true( cl_get_string(request, F_CIB_GLOBAL_UPDATE)); CRM_DEV_ASSERT(reply != NULL); if(reply) { *reply = NULL; } /* Start processing the request... */ op = cl_get_string(request, F_CIB_OPERATION); call_id = cl_get_string(request, F_CIB_CALLID); ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options); crm_debug_4("Processing call id: %s", call_id); rc = cib_get_operation_id(request, &call_type); if(rc == cib_ok && cib_server_ops[call_type].needs_privileges && privileged == FALSE) { /* abort */ rc = cib_not_authorized; } if(rc == cib_ok && global_update == FALSE && cib_server_ops[call_type].needs_quorum && can_write(call_options) == FALSE) { rc = cib_no_quorum; } /* prevent NUMUPDATES from being incrimented - apply the change as-is */ if(global_update) { call_options |= cib_inhibit_bcast; } if(rc == cib_ok && cib_server_ops[call_type].needs_section) { section = cl_get_string(request, F_CIB_SECTION); } if(rc == cib_ok && safe_str_eq(op, CIB_OP_APPLY_DIFF)) { crm_debug_4("Unpacking diff data in %s", F_CIB_UPDATE_DIFF); input_fragment = get_message_xml(request, F_CIB_UPDATE_DIFF); if(global_update) { call_options |= cib_force_diff; } else { do_id_check(input_fragment, NULL); } } else if(rc == cib_ok && safe_str_eq(op, CIB_OP_SYNC)) { input_fragment = ha_msg_copy(request); } else if(rc == cib_ok && safe_str_eq(op, CIB_OP_SYNC_ONE)) { input_fragment = ha_msg_copy(request); } else if(rc == cib_ok && cib_server_ops[call_type].needs_data) { crm_debug_4("Unpacking data in %s", F_CIB_CALLDATA); input_fragment = get_message_xml(request, F_CIB_CALLDATA); } /* extract the CIB from the fragment */ if(input_fragment != NULL && safe_str_eq(crm_element_name(input_fragment), XML_TAG_FRAGMENT)) { input = find_xml_node(input_fragment, XML_TAG_CIB, TRUE); } else { input = input_fragment; } /* grab the section specified for the command */ if(input != NULL && safe_str_eq(crm_element_name(input), XML_TAG_CIB)){ rc = revision_check(input, current_cib, call_options); if(rc == cib_ok) { input = get_object_root(section, input); do_id_check(input, NULL); } } if(cib_server_ops[call_type].modifies_cib) { cib_pre_notify( call_options, op, get_object_root(section, current_cib), input); } if(rc == cib_ok) { rc = cib_server_ops[call_type].fn( op, call_options, section, input, current_cib, &result_cib, &output); } if(cib_server_ops[call_type].modifies_cib) { if(safe_str_eq(op, CIB_OP_APPLY_DIFF)) { local_diff = copy_xml(input); } else if(result_cib != NULL && current_cib != result_cib) { local_diff = diff_cib_object( current_cib, result_cib, FALSE); } if(rc != cib_ok) { free_xml(result_cib); } else if(activateCibXml(result_cib,CIB_FILENAME) < 0){ crm_warn("Activation failed"); rc = cib_ACTIVATION; } cib_post_notify(call_options, op, input, rc, the_cib); cib_diff_notify(call_options, op, input, rc, local_diff); } else if(result_cib != NULL) { crm_err("%s call modified the CIB", op); free_xml(result_cib); } crm_debug_4("Processing reply cases"); if((call_options & cib_discard_reply) && cib_server_ops[call_type].modifies_cib == FALSE) { crm_debug_3("No reply needed for call %s", call_id); return rc; } else if(reply == NULL) { crm_debug("No reply possible for call %s", call_id); return rc; } crm_debug_4("Creating a basic reply"); *reply = ha_msg_new(8); ha_msg_add(*reply, F_TYPE, T_CIB); ha_msg_add(*reply, F_CIB_OPERATION, op); ha_msg_add(*reply, F_CIB_CALLID, call_id); ha_msg_add_int(*reply, F_CIB_RC, rc); tmp = cl_get_string(request, F_CIB_CLIENTID); ha_msg_add(*reply, F_CIB_CLIENTID, tmp); tmp = cl_get_string(request, F_CIB_CALLOPTS); ha_msg_add(*reply, F_CIB_CALLOPTS, tmp); crm_debug_4("Attaching output if necessary"); if(output != NULL) { add_message_xml(*reply, F_CIB_CALLDATA, output); } else { crm_debug_3("No output for call %s", call_id); } crm_debug_4("Cleaning up"); if(cib_diff != NULL) { *cib_diff = local_diff; } else { free_xml(local_diff); } free_xml(output); free_xml(input_fragment); return rc; } int send_via_callback_channel(HA_Message *msg, const char *token) { cib_client_t *hash_client = NULL; GList *list_item = NULL; enum cib_errors rc = cib_ok; crm_debug_3("Delivering msg %p to client %s", msg, token); if(token == NULL) { crm_err("No client id token, cant send message"); if(rc == cib_ok) { rc = cib_missing; } } else { /* A client that left before we could reply is not really * _our_ error. Warn instead. */ hash_client = g_hash_table_lookup(client_list, token); if(hash_client == NULL) { crm_warn("Cannot find client for token %s", token); rc = cib_client_gone; } else if(hash_client->channel == NULL) { crm_err("Cannot find channel for client %s", token); rc = cib_client_corrupt; } else if(hash_client->channel->ops->get_chan_status( hash_client->channel) != IPC_CONNECT) { crm_warn("Client %s has disconnected", token); rc = cib_client_gone; } } /* this is a more important error so overwriting rc is warrented */ if(msg == NULL) { crm_err("No message to send"); rc = cib_reply_failed; } if(rc == cib_ok) { list_item = g_list_find_custom( hash_client->delegated_calls, msg, cib_GCompareFunc); } if(list_item != NULL) { /* remove it - no need to time it out */ HA_Message *orig_msg = list_item->data; crm_debug_3("Removing msg from delegated list"); hash_client->delegated_calls = g_list_remove( hash_client->delegated_calls, orig_msg); CRM_DEV_ASSERT(orig_msg != msg); crm_msg_del(orig_msg); } if(rc == cib_ok) { crm_debug_3("Delivering reply to client %s", token); if(send_ipc_message(hash_client->channel, msg) == FALSE) { crm_warn("Delivery of reply to client %s/%s failed", hash_client->name, token); rc = cib_reply_failed; } } else { /* be consistent... * send_ipc_message() will free the message, so we should do * so manually if we dont try to send it. */ crm_msg_del(msg); } return rc; } gint cib_GCompareFunc(gconstpointer a, gconstpointer b) { const HA_Message *a_msg = a; const HA_Message *b_msg = b; int msg_a_id = 0; int msg_b_id = 0; ha_msg_value_int(a_msg, F_CIB_CALLID, &msg_a_id); ha_msg_value_int(b_msg, F_CIB_CALLID, &msg_b_id); if(msg_a_id == msg_b_id) { return 0; } else if(msg_a_id < msg_b_id) { return -1; } return 1; } gboolean cib_msg_timeout(gpointer data) { crm_debug_4("Checking if any clients have timed out messages"); g_hash_table_foreach(client_list, cib_GHFunc, NULL); return TRUE; } void cib_GHFunc(gpointer key, gpointer value, gpointer user_data) { cib_client_t *client = value; GListPtr list = client->delegated_calls; HA_Message *msg = NULL; while(list != NULL) { int seen = 0; int timeout = 5; /* 1 iteration == 1 seconds */ HA_Message *reply = NULL; const char *host_to = NULL; msg = list->data; ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen); ha_msg_value_int(msg, F_CIB_TIMEOUT, &timeout); host_to = cl_get_string(msg, F_CIB_HOST); crm_debug_4("Timeout %d, seen %d", timeout, seen); if(timeout > 0 && seen < timeout) { int seen2 = 0; crm_debug_4("Updating seen count for msg from client %s", client->id); seen++; ha_msg_mod_int(msg, F_CIB_SEENCOUNT, seen); ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen2); list = list->next; continue; } crm_warn("Sending operation timeout msg to client %s", client->id); reply = ha_msg_new(4); ha_msg_add(reply, F_TYPE, T_CIB); ha_msg_add(reply, F_CIB_OPERATION, cl_get_string(msg, F_CIB_OPERATION)); ha_msg_add(reply, F_CIB_CALLID, cl_get_string(msg, F_CIB_CALLID)); if(host_to == NULL) { ha_msg_add_int(reply, F_CIB_RC, cib_master_timeout); } else { ha_msg_add_int(reply, F_CIB_RC, cib_remote_timeout); } send_ipc_message(client->channel, reply); list = list->next; client->delegated_calls = g_list_remove( client->delegated_calls, msg); crm_msg_del(msg); } } gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client) { if (channel->ch_status != IPC_CONNECT && cib_client != NULL) { crm_info("Cleaning up after %s channel disconnect from client (%p) %s/%s", cib_client->channel_name, cib_client, crm_str(cib_client->id), crm_str(cib_client->name)); if(cib_client->id != NULL) { g_hash_table_remove(client_list, cib_client->id); } if(cib_client->source != NULL) { crm_debug_3("deleting the IPC Channel"); G_main_del_IPC_Channel(cib_client->source); cib_client->source = NULL; } crm_debug_3("Freeing the cib client %s", crm_str(cib_client->id)); #if 0 /* todo - put this back in once i recheck its safe */ crm_free(cib_client->callback_id); crm_free(cib_client->name); crm_free(cib_client->id); #endif crm_free(cib_client); crm_debug_3("Freed the cib client"); return FALSE; } else if (channel->ch_status != IPC_CONNECT) { crm_warn("Unknown client disconnected"); return FALSE; } return TRUE; } gboolean cib_ha_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; ll_cluster_t *hb_cluster = (ll_cluster_t*)user_data; while(lpc < 2 && hb_cluster->llc_ops->msgready(hb_cluster)) { lpc++; /* invoke the callbacks but dont block */ hb_cluster->llc_ops->rcvmsg(hb_cluster, 0); } crm_debug_4("%d HA messages dispatched", lpc); if (channel && (channel->ch_status != IPC_CONNECT)) { crm_crit("Lost connection to heartbeat service... exiting"); exit(100); return FALSE; } return TRUE; } void -cib_peer_callback(const HA_Message * msg, void* private_data) +cib_peer_callback(HA_Message * msg, void* private_data) { int call_type = 0; const char *originator = cl_get_string(msg, F_ORIG); const char *seq = cl_get_string(msg, F_SEQ); const char *op = cl_get_string(msg, F_CIB_OPERATION); crm_log_message_adv(LOG_MSG, "Peer[inbound]", msg); if(originator == NULL || safe_str_eq(originator, cib_our_uname)) { crm_debug_3("Discarding %s message from ourselves", op); return; } else if(ccm_membership == NULL) { crm_info("Discarding %s message (%s) from %s:" " membership not established", op, seq, originator); return; } else if(g_hash_table_lookup(ccm_membership, originator) == NULL) { crm_warn("Discarding %s message (%s) from %s:" " not in our membership", op, seq, originator); return; } else if(cib_get_operation_id(msg, &call_type) != cib_ok) { crm_err("Discarding %s message (%s) from %s:" " Invalid operation", op, seq, originator); return; } crm_info("Processing %s msg (%s) from %s", op, seq, originator); cib_process_request(msg, TRUE, TRUE, NULL); return; } HA_Message * -cib_msg_copy(const HA_Message *msg, gboolean with_data) +cib_msg_copy(HA_Message *msg, gboolean with_data) { int lpc = 0; const char *field = NULL; const char *value = NULL; const HA_Message *value_struct = NULL; const char *field_list[] = { F_TYPE , F_CIB_CLIENTID , F_CIB_CALLOPTS , F_CIB_CALLID , F_CIB_OPERATION , F_CIB_ISREPLY , F_CIB_SECTION , F_CIB_HOST , F_CIB_RC , F_CIB_DELEGATED , F_CIB_OBJID , F_CIB_OBJTYPE , F_CIB_EXISTING , F_CIB_SEENCOUNT , F_CIB_TIMEOUT , F_CIB_CALLBACK_TOKEN , F_CIB_GLOBAL_UPDATE , F_CIB_CLIENTNAME , F_CIB_NOTIFY_TYPE , F_CIB_NOTIFY_ACTIVATE }; const char *data_list[] = { F_CIB_CALLDATA , F_CIB_UPDATE , F_CIB_UPDATE_RESULT }; HA_Message *copy = ha_msg_new(10); if(copy == NULL) { return copy; } for(lpc = 0; lpc < DIMOF(field_list); lpc++) { field = field_list[lpc]; value = cl_get_string(msg, field); if(value != NULL) { ha_msg_add(copy, field, value); } } for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) { field = data_list[lpc]; value_struct = cl_get_struct(msg, field); if(value_struct != NULL) { ha_msg_addstruct(copy, field, value_struct); } } return copy; } enum cib_errors cib_get_operation_id(const HA_Message * msg, int *operation) { int lpc = 0; int max_msg_types = DIMOF(cib_server_ops); const char *op = cl_get_string(msg, F_CIB_OPERATION); for (lpc = 0; lpc < max_msg_types; lpc++) { if (safe_str_eq(op, cib_server_ops[lpc].operation)) { *operation = lpc; return cib_ok; } } crm_err("Operation %s is not valid", op); *operation = -1; return cib_operation; } void cib_client_status_callback(const char * node, const char * client, const char * status, void * private) { if(safe_str_eq(client, CRM_SYSTEM_CIB)) { crm_info("Status update: Client %s/%s now has status [%s]", node, client, status); g_hash_table_replace(peer_hash, crm_strdup(node), crm_strdup(status)); set_connected_peers(the_cib); } return; } extern oc_ev_t *cib_ev_token; gboolean ccm_manual_check(gpointer data) { int rc = 0; oc_ev_t *ccm_token = cib_ev_token; crm_debug("manual check"); rc = oc_ev_handle_event(ccm_token); if(0 == rc) { return TRUE; } else { crm_err("CCM connection appears to have failed: rc=%d.", rc); return FALSE; } } gboolean cib_ccm_dispatch(int fd, gpointer user_data) { int rc = 0; oc_ev_t *ccm_token = (oc_ev_t*)user_data; crm_debug("received callback"); rc = oc_ev_handle_event(ccm_token); if(0 == rc) { return TRUE; } else { crm_err("CCM connection appears to have failed: rc=%d.", rc); return FALSE; } } static void crm_ghash_clfree(gpointer data) { crm_free(data); } void cib_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data) { int instance = -1; gboolean update_id = FALSE; gboolean update_quorum = FALSE; const oc_ev_membership_t *membership = data; if(membership != NULL) { instance = membership->m_instance; } crm_info("Process CCM event=%s (id=%d)", ccm_event_name(event), instance); switch(event) { case OC_EV_MS_NEW_MEMBERSHIP: case OC_EV_MS_INVALID: update_id = TRUE; update_quorum = TRUE; break; case OC_EV_MS_PRIMARY_RESTORED: update_id = TRUE; break; case OC_EV_MS_NOT_PRIMARY: crm_debug("Ignoring transitional CCM event: %s", ccm_event_name(event)); break; case OC_EV_MS_EVICTED: crm_err("Evicted from CCM: %s", ccm_event_name(event)); update_quorum = TRUE; break; default: crm_err("Unknown CCM event: %d", event); } if(update_id) { CRM_DEV_ASSERT(membership != NULL); if(crm_assert_failed) { return; } if(ccm_transition_id != NULL) { crm_free(ccm_transition_id); ccm_transition_id = NULL; } ccm_transition_id = crm_itoa(instance); set_transition(the_cib); } if(update_quorum) { unsigned int members = 0; int offset = 0; unsigned int lpc = 0; cib_have_quorum = ccm_have_quorum(event); if(cib_have_quorum) { crm_xml_add( the_cib,XML_ATTR_HAVE_QUORUM,XML_BOOLEAN_TRUE); } else { crm_xml_add( the_cib,XML_ATTR_HAVE_QUORUM,XML_BOOLEAN_FALSE); } crm_info("Quorum %s after event=%s (id=%d)", cib_have_quorum?"(re)attained":"lost", ccm_event_name(event), instance); if(ccm_membership == NULL) { ccm_membership = g_hash_table_new_full( g_str_hash, g_str_equal, crm_ghash_clfree, NULL); } if(membership != NULL && membership->m_n_in != 0) { members = membership->m_n_in; offset = membership->m_in_idx; for(lpc = 0; lpc < members; lpc++) { oc_node_t a_node = membership->m_array[lpc+offset]; char *uname = crm_strdup(a_node.node_uname); crm_info("Added: %s", uname); g_hash_table_replace( ccm_membership, uname, uname); } } if(membership != NULL && membership->m_n_out != 0) { members = membership->m_n_out; offset = membership->m_out_idx; for(lpc = 0; lpc < members; lpc++) { oc_node_t a_node = membership->m_array[lpc+offset]; crm_info("LOST: %s", a_node.node_uname); g_hash_table_remove( ccm_membership, a_node.node_uname); } } } oc_ev_callback_done(cookie); set_connected_peers(the_cib); return; } gboolean can_write(int flags) { const char *value = NULL; if(cib_have_quorum) { return TRUE; } value = get_crm_option(the_cib, "no_quorum_policy", TRUE); if(safe_str_eq(value, "ignore")) { return TRUE; } if((flags & cib_quorum_override) != 0) { crm_debug_2("Overriding \"no quorum\" condition"); return TRUE; } return FALSE; } diff --git a/crm/cib/callbacks.h b/crm/cib/callbacks.h index d12b0584f7..b865bfa6f0 100644 --- a/crm/cib/callbacks.h +++ b/crm/cib/callbacks.h @@ -1,87 +1,87 @@ -/* $Id: callbacks.h,v 1.10 2005/06/13 11:54:53 andrew Exp $ */ +/* $Id: callbacks.h,v 1.11 2005/10/17 19:13:47 gshi 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 extern gboolean cib_is_master; extern gboolean cib_have_quorum; extern GHashTable *client_list; extern GHashTable *peer_hash; typedef struct cib_client_s { char *id; char *name; char *callback_id; const char *channel_name; IPC_Channel *channel; GCHSource *source; int pre_notify; int post_notify; int confirmations; int diffs; GList *delegated_calls; } cib_client_t; typedef struct cib_operation_s { const char* operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_quorum; gboolean needs_section; gboolean needs_data; enum cib_errors (*fn)( const char *, int, const char *, crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**); } cib_operation_t; extern cib_operation_t cib_server_ops[]; extern gboolean cib_client_connect(IPC_Channel *channel, gpointer user_data); extern gboolean cib_null_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_rw_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_ro_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_ha_dispatch (IPC_Channel *channel, gpointer user_data); -extern void cib_peer_callback(const HA_Message * msg, void* private_data); +extern void cib_peer_callback(HA_Message * msg, void* private_data); extern void cib_client_status_callback(const char * node, const char * client, const char * status, void * private); extern gboolean cib_ccm_dispatch(int fd, gpointer user_data); extern void cib_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data); diff --git a/crm/crmd/callbacks.c b/crm/crmd/callbacks.c index 23f8c38734..2edbc30abb 100644 --- a/crm/crmd/callbacks.c +++ b/crm/crmd/callbacks.c @@ -1,619 +1,619 @@ /* * 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 GHashTable *crmd_peer_state = NULL; crm_data_t *find_xml_in_hamessage(const HA_Message * msg); void crmd_ha_connection_destroy(gpointer user_data); /* From join_dc... */ extern gboolean check_join_state( enum crmd_fsa_state cur_state, const char *source); /* #define MAX_EMPTY_CALLBACKS 20 */ /* int empty_callbacks = 0; */ gboolean crmd_ha_msg_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; ll_cluster_t *hb_cluster = (ll_cluster_t*)user_data; while(lpc < 2 && hb_cluster->llc_ops->msgready(hb_cluster)) { if(channel->ch_status != IPC_CONNECT) { /* there really is no point continuing */ break; } lpc++; /* invoke the callbacks but dont block */ hb_cluster->llc_ops->rcvmsg(hb_cluster, 0); } crm_debug_3("%d HA messages dispatched", lpc); G_main_set_trigger(fsa_source); if (channel && (channel->ch_status != IPC_CONNECT)) { crm_crit("Lost connection to heartbeat service."); return FALSE; } return TRUE; } void -crmd_ha_msg_callback(const HA_Message * msg, void* private_data) +crmd_ha_msg_callback(HA_Message * msg, void* private_data) { ha_msg_input_t *new_input = NULL; oc_node_t *from_node = NULL; const char *from = ha_msg_value(msg, F_ORIG); const char *seq = ha_msg_value(msg, F_SEQ); const char *op = ha_msg_value(msg, F_CRM_TASK); const char *sys_to = ha_msg_value(msg, F_CRM_SYS_TO); const char *sys_from = ha_msg_value(msg, F_CRM_SYS_FROM); CRM_DEV_ASSERT(from != NULL); if(fsa_membership_copy == NULL) { crm_debug("Ignoring HA messages until we are" " connected to the CCM (%s op from %s)", op, from); crm_log_message_adv( LOG_MSG, "HA[inbound]: Ignore (No CCM)", msg); return; } from_node = g_hash_table_lookup(fsa_membership_copy->members, from); if(from_node == NULL) { int level = LOG_DEBUG; if(safe_str_eq(op, CRM_OP_VOTE)) { level = LOG_WARNING; } else if(AM_I_DC && safe_str_eq(op, CRM_OP_JOIN_ANNOUNCE)) { level = LOG_WARNING; } else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) { level = LOG_WARNING; } do_crm_log(level, __FILE__, __FUNCTION__, "Ignoring HA message (op=%s) from %s: not in our" " membership list (size=%d)", op, from, g_hash_table_size(fsa_membership_copy->members)); crm_log_message_adv(LOG_MSG, "HA[inbound]: CCM Discard", msg); } else if(AM_I_DC && safe_str_eq(sys_from, CRM_SYSTEM_DC) && safe_str_neq(from, fsa_our_uname)) { crm_err("Another DC detected: %s (op=%s)", from, op); crm_log_message_adv( LOG_WARNING, "HA[inbound]: Duplicate DC", msg); new_input = new_ha_msg_input(msg); /* make sure the election happens NOW */ register_fsa_error_adv(C_FSA_INTERNAL, I_ELECTION, NULL, new_input, __FUNCTION__); #if 0 /* still thinking about this one... * could create a timing issue if we dont notice the * election before a new DC is elected. */ } else if(fsa_our_dc != NULL && safe_str_eq(sys_from, CRM_SYSTEM_DC) && safe_str_neq(from, fsa_our_dc)) { crm_warn("Ignoring message from wrong DC: %s vs. %s ", from, fsa_our_dc); crm_log_message_adv(LOG_WARNING, "HA[inbound]: wrong DC", msg); #endif } else if(safe_str_eq(sys_to, CRM_SYSTEM_DC) && AM_I_DC == FALSE) { crm_debug_2("Ignoring message for the DC [F_SEQ=%s]", seq); crm_log_message_adv(LOG_DEBUG_4, "HA[inbound]: ignore", msg); return; } else if(safe_str_eq(from, fsa_our_uname) && safe_str_eq(op, CRM_OP_VOTE)) { crm_log_message_adv(LOG_DEBUG_4, "HA[inbound]", msg); crm_debug_2("Ignoring our own vote [F_SEQ=%s]: own vote", seq); return; } else if(AM_I_DC && safe_str_eq(op, CRM_OP_HBEAT)) { crm_debug_2("Ignoring our own heartbeat [F_SEQ=%s]", seq); crm_log_message_adv(LOG_DEBUG_4, "HA[inbound]: own heartbeat", msg); return; } else { crm_debug_3("Processing message"); crm_log_message_adv(LOG_MSG, "HA[inbound]", msg); new_input = new_ha_msg_input(msg); register_fsa_input(C_HA_MESSAGE, I_ROUTER, new_input); } #if 0 if(ha_msg_value(msg, XML_ATTR_REFERENCE) == NULL) { ha_msg_add(new_input->msg, XML_ATTR_REFERENCE, seq); } #endif delete_ha_msg_input(new_input); return; } /* * Apparently returning TRUE means "stay connected, keep doing stuff". * Returning FALSE means "we're all done, close the connection" */ gboolean crmd_ipc_msg_callback(IPC_Channel *client, gpointer user_data) { int lpc = 0; IPC_Message *msg = NULL; ha_msg_input_t *new_input = NULL; crmd_client_t *curr_client = (crmd_client_t*)user_data; gboolean stay_connected = TRUE; crm_debug_2("Processing IPC message from %s", curr_client->table_key); while(lpc == 0 && client->ops->is_message_pending(client)) { if (client->ch_status == IPC_DISCONNECT) { /* The message which was pending for us is that * the IPC status is now IPC_DISCONNECT */ break; } if (client->ops->recv(client, &msg) != IPC_OK) { perror("Receive failure:"); crm_err("[%s] [receive failure]", curr_client->table_key); stay_connected = FALSE; break; } else if (msg == NULL) { crm_err("[%s] [no message this time]", curr_client->table_key); continue; } lpc++; new_input = new_ipc_msg_input(msg); msg->msg_done(msg); crm_debug_2("Processing msg from %s", curr_client->table_key); crm_log_message_adv(LOG_MSG, "CRMd[inbound]", new_input->msg); if(crmd_authorize_message(new_input, curr_client)) { register_fsa_input(C_IPC_MESSAGE, I_ROUTER, new_input); } delete_ha_msg_input(new_input); msg = NULL; new_input = NULL; } crm_debug_2("Processed %d messages", lpc); if (client->ch_status == IPC_DISCONNECT) { stay_connected = FALSE; process_client_disconnect(curr_client); } G_main_set_trigger(fsa_source); return stay_connected; } gboolean lrm_dispatch(IPC_Channel*src_not_used, gpointer user_data) { int num_msgs = 0; ll_lrm_t *lrm = (ll_lrm_t*)user_data; crm_debug_3("received callback"); num_msgs = lrm->lrm_ops->rcvmsg(lrm, FALSE); if(num_msgs < 1) { crm_err("lrm->lrm_ops->rcvmsg() failed, connection lost?"); clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); return FALSE; } return TRUE; } void lrm_op_callback(lrm_op_t* op) { CRM_DEV_ASSERT(op != NULL); if(crm_assert_failed) { return; } crm_debug("received callback: %s/%s (%s)", op->op_type, op->rsc_id, op_status2text(op->op_status)); /* Make sure the LRM events are received in order */ register_fsa_input_later(C_LRM_OP_CALLBACK, I_LRM_EVENT, op); } void crmd_ha_status_callback( const char *node, const char * status, void* private_data) { crm_data_t *update = NULL; crm_debug_3("received callback"); crm_notice("Status update: Node %s now has status [%s]",node,status); if(safe_str_neq(status, DEADSTATUS)) { crm_debug_3("nstatus callback was not for a dead node"); return; } /* this node is taost */ update = create_node_state( node, node, status, NULL, NULL, NULL, NULL, __FUNCTION__); crm_xml_add(update, XML_CIB_ATTR_CLEAR_SHUTDOWN, XML_BOOLEAN_TRUE); crm_xml_add(update, XML_CIB_ATTR_REPLACE, XML_TAG_TRANSIENT_NODEATTRS); /* this change should not be broadcast */ update_local_cib(create_cib_fragment(update, XML_CIB_TAG_STATUS)); G_main_set_trigger(fsa_source); free_xml(update); } void crmd_client_status_callback(const char * node, const char * client, const char * status, void * private) { const char *join = NULL; const char *extra = NULL; crm_data_t * update = NULL; crm_debug_3("received callback"); if(safe_str_neq(client, CRM_SYSTEM_CRMD)) { return; } if(safe_str_eq(status, JOINSTATUS)){ status = ONLINESTATUS; /* extra = XML_CIB_ATTR_CLEAR_SHUTDOWN; */ } else if(safe_str_eq(status, LEAVESTATUS)){ status = OFFLINESTATUS; join = CRMD_STATE_INACTIVE; extra = XML_CIB_ATTR_CLEAR_SHUTDOWN; } set_bit_inplace(fsa_input_register, R_PEER_DATA); g_hash_table_replace( crmd_peer_state, crm_strdup(node), crm_strdup(status)); if(is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) { return; } else if(fsa_state == S_STOPPING) { return; } crm_notice("Status update: Client %s/%s now has status [%s]", node, client, status); if(safe_str_eq(node, fsa_our_dc) && safe_str_eq(status, OFFLINESTATUS)) { /* did our DC leave us */ crm_info("Got client status callback - our DC is dead"); register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ELECTION, NULL); } else { crm_data_t *fragment = NULL; crm_debug_3("Got client status callback"); update = create_node_state( node, node, NULL, NULL, status, join, NULL, __FUNCTION__); crm_xml_add(update, extra, XML_BOOLEAN_TRUE); if(safe_str_eq(extra, XML_CIB_ATTR_CLEAR_SHUTDOWN)) { crm_xml_add(update, XML_CIB_ATTR_REPLACE, XML_TAG_TRANSIENT_NODEATTRS); } fragment = create_cib_fragment(update, XML_CIB_TAG_STATUS); /* it is safe to keep these updates on the local node * each node updates their own CIB */ fsa_cib_conn->cmds->update( fsa_cib_conn, XML_CIB_TAG_STATUS, fragment, NULL, cib_inhibit_bcast|cib_scope_local|cib_quorum_override); free_xml(fragment); free_xml(update); if(AM_I_DC && safe_str_eq(status, OFFLINESTATUS)) { g_hash_table_remove(confirmed_nodes, node); g_hash_table_remove(finalized_nodes, node); g_hash_table_remove(integrated_nodes, node); g_hash_table_remove(welcomed_nodes, node); check_join_state(fsa_state, __FUNCTION__); } } G_main_set_trigger(fsa_source); } void crmd_ha_connection_destroy(gpointer user_data) { if(is_set(fsa_input_register, R_HA_DISCONNECTED)) { /* we signed out, so this is expected */ return; } crm_crit("Heartbeat has left us"); /* this is always an error */ /* feed this back into the FSA */ register_fsa_input(C_HA_DISCONNECT, I_ERROR, NULL); } gboolean crmd_client_connect(IPC_Channel *client_channel, gpointer user_data) { if (client_channel == NULL) { crm_err("Channel was NULL"); } else if (client_channel->ch_status == IPC_DISCONNECT) { crm_err("Channel was disconnected"); } else { crmd_client_t *blank_client = NULL; crm_debug_3("Channel connected"); crm_malloc0(blank_client, sizeof(crmd_client_t)); if (blank_client == NULL) { return FALSE; } client_channel->ops->set_recv_qlen(client_channel, 100); client_channel->ops->set_send_qlen(client_channel, 100); blank_client->client_channel = client_channel; blank_client->sub_sys = NULL; blank_client->uuid = NULL; blank_client->table_key = NULL; blank_client->client_source = G_main_add_IPC_Channel( G_PRIORITY_LOW, client_channel, FALSE, crmd_ipc_msg_callback, blank_client, default_ipc_connection_destroy); } return TRUE; } gboolean ccm_dispatch(int fd, gpointer user_data) { int rc = 0; oc_ev_t *ccm_token = (oc_ev_t*)user_data; gboolean was_error = FALSE; crm_debug_3("received callback"); rc = oc_ev_handle_event(ccm_token); if(rc != 0) { if(is_set(fsa_input_register, R_CCM_DISCONNECTED) == FALSE) { /* we signed out, so this is expected */ register_fsa_input(C_CCM_CALLBACK, I_ERROR, NULL); crm_err("CCM connection appears to have failed: rc=%d.", rc); } was_error = TRUE; } G_main_set_trigger(fsa_source); return !was_error; } static gboolean fsa_have_quorum = FALSE; void crmd_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data) { int instance = -1; gboolean update_cache = FALSE; struct crmd_ccm_data_s *event_data = NULL; const oc_ev_membership_t *membership = data; gboolean update_quorum = FALSE; gboolean trigger_transition = FALSE; crm_debug_3("received callback"); if(data != NULL) { instance = membership->m_instance; } crm_info("Quorum %s after event=%s (id=%d)", ccm_have_quorum(event)?"(re)attained":"lost", ccm_event_name(event), instance); switch(event) { case OC_EV_MS_NEW_MEMBERSHIP: case OC_EV_MS_INVALID:/* fall through */ update_cache = TRUE; update_quorum = TRUE; break; case OC_EV_MS_NOT_PRIMARY: #if UNTESTED if(AM_I_DC == FALSE) { break; } /* tell the TE to pretend it had completed and stop */ /* side effect: we'll end up in S_IDLE */ register_fsa_action(A_TE_HALT, TRUE); #endif break; case OC_EV_MS_PRIMARY_RESTORED: fsa_membership_copy->id = instance; if(AM_I_DC && need_transition(fsa_state)) { trigger_transition = TRUE; } break; case OC_EV_MS_EVICTED: update_quorum = TRUE; register_fsa_input(C_FSA_INTERNAL, I_STOP, NULL); break; default: crm_err("Unknown CCM event: %d", event); } if(update_quorum && ccm_have_quorum(event) == FALSE) { /* did we just loose quorum? */ if(fsa_have_quorum && need_transition(fsa_state)) { crm_info("Quorum lost: triggering transition (%s)", ccm_event_name(event)); trigger_transition = TRUE; } fsa_have_quorum = FALSE; } else if(update_quorum) { crm_debug_2("Updating quorum after event %s", ccm_event_name(event)); fsa_have_quorum = TRUE; } if(trigger_transition) { crm_debug_2("Scheduling transition after event %s", ccm_event_name(event)); /* make sure that when we query the CIB that it has * the changes that triggered the transition */ switch(event) { case OC_EV_MS_NEW_MEMBERSHIP: case OC_EV_MS_INVALID: case OC_EV_MS_PRIMARY_RESTORED: fsa_membership_copy->id = instance; break; default: break; } if(update_cache == FALSE) { /* a stand-alone transition */ register_fsa_action(A_TE_CANCEL); } } if(update_cache) { crm_debug_2("Updating cache after event %s", ccm_event_name(event)); crm_malloc0(event_data, sizeof(struct crmd_ccm_data_s)); if(event_data == NULL) { return; } event_data->event = event; if(data != NULL) { event_data->oc = copy_ccm_oc_data(data); } register_fsa_input_adv( C_CCM_CALLBACK, I_CCM_EVENT, event_data, trigger_transition?A_TE_CANCEL:A_NOTHING, FALSE, __FUNCTION__); if (event_data->oc) { crm_free(event_data->oc); event_data->oc = NULL; } crm_free(event_data); } oc_ev_callback_done(cookie); return; } void crmd_cib_connection_destroy(gpointer user_data) { if(is_set(fsa_input_register, R_SHUTDOWN) || is_set(fsa_input_register, R_CIB_CONNECTED)) { crm_info("Connection to the CIB terminated..."); return; } /* eventually this will trigger a reconnect, not a shutdown */ crm_err("Connection to the CIB terminated..."); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); clear_bit_inplace(fsa_input_register, R_CIB_CONNECTED); return; } longclock_t fsa_start = 0; longclock_t fsa_stop = 0; longclock_t fsa_diff = 0; gboolean crm_fsa_trigger(gpointer user_data) { unsigned int fsa_diff_ms = 0; if(fsa_diff_max_ms > 0) { fsa_start = time_longclock(); } s_crmd_fsa(C_FSA_INTERNAL); if(fsa_diff_max_ms > 0) { fsa_stop = time_longclock(); fsa_diff = sub_longclock(fsa_stop, fsa_start); fsa_diff_ms = longclockto_ms(fsa_diff); if(fsa_diff_ms > fsa_diff_max_ms) { crm_warn("FSA took %dms to complete", fsa_diff_ms); } else if(fsa_diff_ms > fsa_diff_warn_ms) { crm_warn("FSA took %dms to complete", fsa_diff_ms); } } return TRUE; } diff --git a/crm/crmd/crmd_callbacks.h b/crm/crmd/crmd_callbacks.h index f6e4b9d72c..50b107a52c 100644 --- a/crm/crmd/crmd_callbacks.h +++ b/crm/crmd/crmd_callbacks.h @@ -1,61 +1,61 @@ /* * 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 extern void ccm_event_detail(const oc_ev_membership_t *oc, oc_ed_t event); extern gboolean ccm_dispatch(int fd, gpointer user_data); extern void crmd_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data); /* * Apparently returning TRUE means "stay connected, keep doing stuff". * Returning FALSE means "we're all done, close the connection" */ extern gboolean crmd_ha_msg_dispatch( IPC_Channel *channel, gpointer user_data); extern void crmd_ha_msg_callback( - const HA_Message * msg, void* private_data); + HA_Message * msg, void* private_data); extern gboolean crmd_ipc_msg_callback( IPC_Channel *client, gpointer user_data); extern gboolean crmd_ipc_msg_callback( IPC_Channel *client, gpointer user_data); extern gboolean lrm_dispatch(IPC_Channel*src, gpointer user_data); extern void lrm_op_callback (lrm_op_t* op); extern void crmd_ha_status_callback( const char *node, const char * status, void* private_data); extern void crmd_client_status_callback( const char * node, const char * client, const char * status, void * private); extern void msg_ccm_join(const HA_Message *msg, void *foo); extern gboolean crmd_client_connect( IPC_Channel *newclient, gpointer user_data); extern void crmd_cib_connection_destroy(gpointer user_data); extern gboolean crm_fsa_trigger(gpointer user_data); diff --git a/crm/crmd/crmd_messages.h b/crm/crmd/crmd_messages.h index 1aa5d7ccfd..6b1323733c 100644 --- a/crm/crmd/crmd_messages.h +++ b/crm/crmd/crmd_messages.h @@ -1,119 +1,119 @@ /* * 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 XML_CRM_MESSAGES__H #define XML_CRM_MESSAGES__H #include #include #include #include extern void *fsa_typed_data_adv( fsa_data_t *fsa_data, enum fsa_data_type a_type, const char *caller); #define fsa_typed_data(x) fsa_typed_data_adv(msg_data, x, __FUNCTION__) extern void register_fsa_error_adv( enum crmd_fsa_cause cause, enum crmd_fsa_input input, fsa_data_t *cur_data, void *new_data, const char *raised_from); #define register_fsa_error(cause, input, new_data) register_fsa_error_adv(cause, input, msg_data, new_data, __FUNCTION__) extern void register_fsa_input_adv( enum crmd_fsa_cause cause, enum crmd_fsa_input input, void *data, long long with_actions, gboolean prepend, const char *raised_from); extern void fsa_dump_queue(int log_level); #define crmd_fsa_stall(cur_input) if(cur_input != NULL) { \ register_fsa_input_adv( \ ((fsa_data_t*)cur_input)->fsa_cause, I_WAIT_FOR_EVENT, \ ((fsa_data_t*)cur_input)->data, action, TRUE, __FUNCTION__); \ } else { \ register_fsa_input_adv( \ C_FSA_INTERNAL, I_WAIT_FOR_EVENT, \ NULL, action, TRUE, __FUNCTION__); \ } \ #define register_fsa_input(cause, input, data) register_fsa_input_adv(cause, input, data, A_NOTHING, FALSE, __FUNCTION__) #define register_fsa_action(action) { \ fsa_actions |= action; \ if(fsa_source) { \ G_main_set_trigger(fsa_source); \ } \ crm_debug("%s added action %s to the FSA", \ __FUNCTION__, fsa_action2string(action)); \ } #define register_fsa_input_before(cause, input, data) register_fsa_input_adv(cause, input, data, A_NOTHING, TRUE, __FUNCTION__) #define register_fsa_input_later(cause, input, data) register_fsa_input_adv(cause, input, data, A_NOTHING, FALSE, __FUNCTION__) void delete_fsa_input(fsa_data_t *fsa_data); GListPtr put_message(fsa_data_t *new_message); fsa_data_t *get_message(void); gboolean is_message(void); gboolean have_wait_message(void); extern gboolean relay_message( HA_Message *relay_message, gboolean originated_locally); -extern void crmd_ha_msg_callback(const HA_Message * msg, void* private_data); +extern void crmd_ha_msg_callback(HA_Message * msg, void* private_data); extern gboolean crmd_ipc_msg_callback(IPC_Channel *client, gpointer user_data); extern void process_message( HA_Message *msg, gboolean originated_locally,const char *src_node_name); extern gboolean crm_dc_process_message(crm_data_t *whole_message, crm_data_t *action, const char *host_from, const char *sys_from, const char *sys_to, const char *op, gboolean dc_mode); extern gboolean send_msg_via_ha(ll_cluster_t *hb_fd, HA_Message *msg); extern gboolean send_msg_via_ipc(HA_Message *msg, const char *sys); extern gboolean add_pending_outgoing_reply(const char *originating_node_name, const char *crm_msg_reference, const char *sys_to, const char *sys_from); extern gboolean crmd_authorize_message( ha_msg_input_t *client_msg, crmd_client_t *curr_client); extern gboolean send_request(HA_Message *msg, char **msg_reference); extern enum crmd_fsa_input handle_message(ha_msg_input_t *stored_msg); extern gboolean send_ha_reply(ll_cluster_t *hb_cluster, crm_data_t *xml_request, crm_data_t *xml_response_data); extern void lrm_op_callback(lrm_op_t* op); extern void msg_queue_helper(void); #endif diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h index 96b7cb1e13..07a39c391f 100644 --- a/include/crm/common/xml.h +++ b/include/crm/common/xml.h @@ -1,289 +1,289 @@ -/* $Id: xml.h,v 1.33 2005/10/14 08:27:34 andrew Exp $ */ +/* $Id: xml.h,v 1.34 2005/10/17 19:13:48 gshi 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 */ #define XML_PARANOIA_CHECKS 1 #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(const HA_Message *msg, const char *field); +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 set_node_tstamp(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 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, gboolean suppress); 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 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 crm_validate_data(const crm_data_t *root); extern void crm_update_parents(crm_data_t *root); extern gboolean xml_has_children(crm_data_t *root); # define xmlGetNodePath(data) crm_element_value(data, XML_ATTR_TAGNAME) # define xml_child_iter(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) { \ continue; \ } \ child = parent->values[__counter]; \ if(child == NULL) { \ crm_debug_4("Skipping %s == NULL", \ parent->names[__counter]); \ } else if(filter == NULL \ || 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 1381516c59..c654df144e 100644 --- a/lib/crm/common/xml.c +++ b/lib/crm/common/xml.c @@ -1,2240 +1,2240 @@ -/* $Id: xml.c,v 1.41 2005/10/14 08:27:34 andrew Exp $ */ +/* $Id: xml.c,v 1.42 2005/10/17 19:13:48 gshi 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 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( root, a_child, search_path, /* crm_debug_5("returning node (%s).", xmlGetNodePath(a_child)); */ crm_log_xml_debug_5(a_child, "contents\t%s"); crm_log_xml_debug_5(root, "found in\t%s"); 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( 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) { 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, crm_xml_add(target, local_prop_name, local_prop_value); ); crm_validate_data(target); } return; } crm_data_t* add_node_copy(crm_data_t *new_parent, const crm_data_t *xml_node) { crm_data_t *node_copy = NULL; crm_validate_data(new_parent); crm_validate_data(xml_node); if(xml_node != NULL && new_parent != NULL) { const char *name = crm_element_name(xml_node); CRM_DEV_ASSERT( HA_OK == ha_msg_addstruct(new_parent, name, xml_node)); node_copy = find_entity( new_parent, crm_element_name(xml_node), ID(xml_node)); crm_validate_data(node_copy); crm_update_parents(new_parent); crm_validate_data(new_parent); } else if(xml_node == NULL) { crm_err("Could not add copy of NULL node"); } else { crm_err("Could not add copy of node to NULL parent"); } crm_validate_data(node_copy); return node_copy; } 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); CRM_DEV_ASSERT(cl_is_allocated(new_value)); 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_DEV_ASSERT(ret_value != NULL); crm_xml_add(ret_value, F_XML_TAGNAME, name); 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_DEV_ASSERT(HA_OK == ha_msg_addstruct( parent, name, ret_value)); crm_msg_del(ret_value); crm_update_parents(parent); crm_validate_data(parent); ret_value = parent->values[parent->nfields-1]; crm_validate_data(ret_value); } } crm_debug_5("Created node [%s [%s]]", crm_str(parent_name), crm_str(local_name)); /* set_node_tstamp(ret_value); */ return ret_value; } void free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node) { CRM_DEV_ASSERT(parent != NULL); if(parent == NULL) { return; } else if(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_DEV_ASSERT(has_parent == 0); if(has_parent == 0) { crm_validate_data(a_node); crm_msg_del(a_node); } } return; } void set_node_tstamp(crm_data_t *a_node) { #if 0 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); } #endif } crm_data_t* copy_xml(const crm_data_t *src_node) { crm_data_t *new_xml = NULL; CRM_DEV_ASSERT(src_node != NULL); CRM_DEV_ASSERT(crm_element_name(src_node) != NULL); if(src_node == NULL) { crm_warn("Attempt to dup NULL XML"); return NULL; } else if(crm_element_name(src_node) == NULL) { crm_log_xml_err(src_node, "Attempt to dup XML with no name"); return NULL; } crm_validate_data(src_node); new_xml = ha_msg_copy(src_node); crm_set_element_parent(new_xml, NULL); crm_update_parents(new_xml); crm_validate_data(new_xml); return new_xml; } 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); if(start != ftell(input)) { crm_err("fseek not behaving"); return NULL; } 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) { int res = 0; char *now_str = NULL; time_t now; CRM_DEV_ASSERT(filename != NULL); 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, "last_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 { char *buffer = dump_xml_formatted(xml_node); CRM_DEV_ASSERT(buffer != NULL && strlen(buffer) > 0); if(buffer != NULL && strlen(buffer) > 0) { res = fprintf(file_output_strm, "%s", buffer); if(res < 0) { cl_perror("Cannot write output to %s", filename); } } 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(const HA_Message *msg, const char *field) +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); ha_msg_addstruct(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; crm_malloc0(buffer, 3*get_stringlen(an_xml_node)); mutable_ptr = buffer; crm_validate_data(an_xml_node); CRM_DEV_ASSERT(dump_data_element( 0, &mutable_ptr, an_xml_node, TRUE) >= 0); if(crm_assert_failed) { 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_DEV_ASSERT(dump_data_element( 0, &mutable_ptr, an_xml_node, TRUE) >= 0); if(crm_assert_failed) { 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, return TRUE; ); return FALSE; } void crm_validate_data(const crm_data_t *xml_root) { #ifndef XML_PARANOIA_CHECKS CRM_DEV_ASSERT(xml_root != NULL); #else int lpc = 0; CRM_ASSERT(xml_root != NULL); CRM_ASSERT(crm_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(crm_is_allocated(xml_root->names[lpc]) == 1); if(child == NULL) { } else if(xml_root->types[lpc] == FT_STRUCT) { crm_validate_data(child); } else if(xml_root->types[lpc] == FT_STRING) { CRM_ASSERT(crm_is_allocated(child) == 1); /* } else { */ /* CRM_DEV_ASSERT(FALSE); */ } } #endif } 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(value != NULL) { CRM_DEV_ASSERT(crm_is_allocated(value) == 1); } 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(value != NULL) { CRM_DEV_ASSERT(crm_is_allocated(value) == 1); } CRM_DEV_ASSERT(value != NULL); if(value != NULL) { value_copy = crm_strdup(value); } return value_copy; } const char * crm_element_name(const crm_data_t *data) { crm_validate_data(data); 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, NULL, 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(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_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_DEV_ASSERT(input != NULL); if(crm_assert_failed) { 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_DEV_ASSERT(our_input[lpc] == '<'); if(crm_assert_failed) { 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); CRM_DEV_ASSERT(crm_is_allocated(new_obj) == 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 { CRM_DEV_ASSERT(crm_is_allocated(child) == 1); ha_msg_addstruct( 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; } if(new_obj != NULL) { CRM_DEV_ASSERT(crm_is_allocated(new_obj) == 1); } 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, NULL, 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, NULL, log_data_element(function, "+", log_level, 0, child, TRUE); if(is_first) { is_first = FALSE; } else { crm_log_maybe(log_level, " --- "); } ); } 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); crm_data_t *intermediate = NULL; crm_data_t *diff_of_diff = NULL; int root_nodes_seen = 0; CRM_DEV_ASSERT(new != NULL); if(crm_assert_failed) { return FALSE; } crm_debug_2("Substraction Phase"); xml_child_iter(removed, child_diff, NULL, CRM_DEV_ASSERT(root_nodes_seen == 0); if(root_nodes_seen == 0) { *new = subtract_xml_object(old, child_diff, FALSE); } 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, NULL, CRM_DEV_ASSERT(root_nodes_seen == 0); 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_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_DEBUG, intermediate, "diff:actual_diff"); result = FALSE; } crm_free(diff_of_diff); crm_free(intermediate); #endif diff_of_diff = NULL; intermediate = NULL; } if(result == FALSE) { log_xml_diff(LOG_DEBUG, diff, "diff:input_diff"); log_data_element("diff:input", NULL, LOG_DEBUG_2, 0, old, TRUE); /* CRM_DEV_ASSERT(diff_of_diff != NULL); */ result = FALSE; } 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, suppress); if(tmp1 != NULL) { diff = create_xml_node(NULL, "diff"); if(can_prune_leaf(tmp1)) { ha_msg_del(tmp1); tmp1 = NULL; } else { 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, suppress); if(tmp1 != NULL) { if(diff == NULL) { diff = create_xml_node(NULL, "diff"); } if(can_prune_leaf(tmp1)) { ha_msg_del(tmp1); tmp1 = NULL; } else { 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; } else if(safe_str_eq(prop_name, XML_ATTR_TSTAMP)) { continue; } can_prune = FALSE; ); xml_child_iter(xml_node, child, NULL, 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_DEV_ASSERT(xml_node != NULL && name != NULL); if(crm_assert_failed) { 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, NULL, 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, NULL, 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, gboolean suppress) { 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_TSTAMP, "last_written", "debug_source", "origin", "te-allow-fail" }; if(left == NULL) { return NULL; } else if(right == NULL) { crm_debug_4("Processing <%s id=%s> (complete copy)", crm_element_name(left), ID(left)); return copy_xml(left); } name = crm_element_name(left); /* sanity check */ CRM_DEV_ASSERT(name != NULL); if(crm_assert_failed) { return NULL; } CRM_DEV_ASSERT(safe_str_eq(crm_element_name(left), crm_element_name(right))); if(crm_assert_failed) { return NULL; } CRM_DEV_ASSERT(safe_str_eq(ID(left), ID(right))); if(crm_assert_failed) { return NULL; } diff = create_xml_node(NULL, name); /* changes to name/value pairs */ crm_debug_4("Processing <%s id=%s>", crm_str(name), ID(left)); xml_prop_iter(left, prop_name, left_value, skip = FALSE; if(safe_str_eq(prop_name, XML_ATTR_ID)) { skip = TRUE; } for(lpc = 0; skip == FALSE && suppress && 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_5("\t%s: %s", crm_str(prop_name), crm_str(left_value)); } else if(safe_str_eq(left_value, right_val)) { crm_debug_4("\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_4("\t%s: %s->%s", crm_str(prop_name), crm_str(left_value), right_val); } ); /* changes to child objects */ xml_child_iter( left, left_child, NULL, right_child = find_entity( right, crm_element_name(left_child), ID(left_child)); child_diff = subtract_xml_object( left_child, right_child, suppress); if(child_diff != NULL) { differences = TRUE; add_node_copy(diff, child_diff); free_xml(child_diff); } ); if(differences == FALSE) { free_xml(diff); crm_debug_4("\tNo changes"); 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_DEV_ASSERT(update != NULL); if(crm_assert_failed) { return 0; } object_name = crm_element_name(update); object_id = ID(update); CRM_DEV_ASSERT(object_name != NULL); if(crm_assert_failed) { 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 = add_node_copy(parent, update); crm_debug_2("Added <%s id=%s>", crm_str(object_name), crm_str(object_id)); CRM_DEV_ASSERT(target != NULL); return 0; } crm_debug_2("Found node <%s id=%s> to update", crm_str(object_name), crm_str(object_id)); xml_prop_iter(update, prop_name, left_value, right_val = crm_element_value(target, prop_name); 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_DEV_ASSERT(cl_is_allocated(object_name)); if(object_id != NULL) { CRM_DEV_ASSERT(cl_is_allocated(object_id)); } crm_debug_3("Processing children of <%s id=%s>", crm_str(object_name), crm_str(object_id)); xml_child_iter( update, a_child, NULL, 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_DEV_ASSERT(child != NULL); if(crm_assert_failed) { return FALSE; } CRM_DEV_ASSERT(to_update != NULL); if(crm_assert_failed) { 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, NULL, /* only update the first one */ if(can_update) { break; } can_update = update_xml_child(child_of_child, to_update); ); return can_update; } 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_DEV_ASSERT(child != NULL); if(crm_assert_failed) { return FALSE; } CRM_DEV_ASSERT(to_delete != NULL); if(crm_assert_failed) { 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, NULL, /* 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_DEV_ASSERT(parent != NULL); if(parent != NULL) { 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( 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_DEV_ASSERT(parent != NULL); if(parent != NULL) { 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"); } } crm_log_xml_debug_3(nvpair_list, "Unpacking"); xml_prop_iter( nvpair_list, key, value, crm_debug_2("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, NULL, 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, 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 is never meant to have an 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) { modified = TRUE; crm_err("\"id\" collision detected."); crm_err(" Multiple %s entries with id=\"%s\"," " assigned id=\"%s\"", tag_name, lookup_value, tag_id); assign_uuid(xml_obj); tag_id = ID(xml_obj); } 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; }