diff --git a/cib/Makefile.am b/cib/Makefile.am index 790614f35c..c12fa0136b 100644 --- a/cib/Makefile.am +++ b/cib/Makefile.am @@ -1,67 +1,67 @@ # # 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 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(AISPREFIX)/include/openais EXTRA_DIST = cib.pam hadir = $(sysconfdir)/ha.d halibdir = $(libdir)/@HB_PKG@ commmoddir = $(halibdir)/modules/comm COMMONLIBS = $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(top_builddir)/lib/crm/cib/libcib.la \ $(GLIBLIB) \ $(LIBRT) ## binary progs halib_PROGRAMS = cib cibmon sbin_PROGRAMS = cibpipe ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = cibio.h cibmessages.h cibprimatives.h notify.h \ callbacks.h cib_SOURCES = io.c primatives.c messages.c cib.c notify.c \ callbacks.c main.c remote.c common.c cib_LDADD = $(COMMONLIBS) $(CRYPTOLIB) $(CLUSTERLIBS) \ $(top_builddir)/lib/crm/common/libcrmcluster.la cib_LDFLAGS = $(GNUTLSLIBS) cibmon_SOURCES = cibmon.c cibmon_LDADD = $(COMMONLIBS) -cibpipe_SOURCES = primatives.c messages.c common.c cibpipe.c +cibpipe_SOURCES = primatives.c cibpipe.c cibpipe_LDADD = $(COMMONLIBS) $(CRYPTOLIB) cibpipe_CFLAGS = -DCIBPIPE=1 clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: # cp -f $(top_srcdir)/crm/cib/cib.pam $(DESTDIR)/etc/pam.d/cib uninstall-local: diff --git a/cib/callbacks.c b/cib/callbacks.c index b996000ef6..307e93c2d4 100644 --- a/cib/callbacks.c +++ b/cib/callbacks.c @@ -1,1605 +1,1606 @@ /* * 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 #include #include #include "common.h" extern GMainLoop* mainloop; extern gboolean cib_shutdown_flag; extern gboolean stand_alone; extern const char* cib_root; #if SUPPORT_HEARTBEAT extern ll_cluster_t *hb_conn; #endif extern void cib_ha_connection_destroy(gpointer user_data); extern enum cib_errors cib_update_counter( xmlNode *xml_obj, const char *field, gboolean reset); extern void GHFunc_count_peers( gpointer key, gpointer value, gpointer user_data); extern enum cib_errors revision_check( xmlNode *cib_update, xmlNode *cib_copy, int flags); void initiate_exit(void); void terminate_cib(const char *caller); 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); void send_cib_replace(const xmlNode *sync_request, const char *host); void cib_process_request( xmlNode *request, gboolean privileged, gboolean force_synchronous, gboolean from_peer, cib_client_t *cib_client); extern GHashTable *client_list; int next_client_id = 0; extern const char *cib_our_uname; extern unsigned long cib_num_ops, cib_num_local, cib_num_updates, cib_num_fail; extern unsigned long cib_bad_connects, cib_num_timeouts; extern longclock_t cib_call_time; extern enum cib_errors cib_status; int send_via_callback_channel(xmlNode *msg, const char *token); enum cib_errors cib_process_command( xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged); gboolean cib_common_callback(IPC_Channel *channel, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client); int num_clients = 0; static void cib_ipc_connection_destroy(gpointer user_data) { cib_client_t *cib_client = user_data; /* cib_process_disconnect */ if(cib_client == NULL) { crm_debug_4("Destroying %p", user_data); return; } if(cib_client->source != NULL) { crm_debug_4("Deleting %s (%p) from mainloop", cib_client->name, cib_client->source); G_main_del_IPC_Channel(cib_client->source); cib_client->source = NULL; } crm_debug_3("Destroying %s (%p)", cib_client->name, user_data); num_clients--; crm_debug_2("Num unfree'd clients: %d", num_clients); crm_free(cib_client->name); crm_free(cib_client->callback_id); crm_free(cib_client->id); crm_free(cib_client); crm_debug_4("Freed the cib client"); return; } static cib_client_t * cib_client_connect_common( IPC_Channel *channel, const char *channel_name, gboolean (*callback)(IPC_Channel *channel, gpointer user_data)) { gboolean can_connect = TRUE; cib_client_t *new_client = NULL; crm_debug_3("Connecting channel"); if (channel == NULL) { crm_err("Channel was NULL"); can_connect = FALSE; cib_bad_connects++; } else if (channel->ch_status != IPC_CONNECT) { crm_err("Channel was disconnected"); can_connect = FALSE; cib_bad_connects++; } else if(channel_name == NULL) { crm_err("user_data must contain channel name"); can_connect = FALSE; cib_bad_connects++; } else if(cib_shutdown_flag) { crm_info("Ignoring new client [%d] during shutdown", channel->farside_pid); return NULL; } else { crm_malloc0(new_client, sizeof(cib_client_t)); num_clients++; new_client->channel = channel; new_client->channel_name = channel_name; crm_debug_3("Created channel %p for channel %s", new_client, new_client->channel_name); channel->ops->set_recv_qlen(channel, 1024); channel->ops->set_send_qlen(channel, 1024); if(callback != NULL) { new_client->source = G_main_add_IPC_Channel( G_PRIORITY_DEFAULT, channel, FALSE, callback, new_client, cib_ipc_connection_destroy); } crm_debug_3("Channel %s connected for client %s", new_client->channel_name, new_client->id); } return new_client; } gboolean cib_client_connect_rw_synch(IPC_Channel *channel, gpointer user_data) { cib_client_t *new_client = NULL; new_client = cib_client_connect_common( channel, cib_channel_rw_synchronous, cib_rw_synchronous_callback); if(new_client == NULL) { return FALSE; } return TRUE; } gboolean cib_client_connect_ro_synch(IPC_Channel *channel, gpointer user_data) { cib_client_t *new_client = NULL; new_client = cib_client_connect_common( channel, cib_channel_ro_synchronous, cib_ro_synchronous_callback); if(new_client == NULL) { return FALSE; } return TRUE; } gboolean cib_client_connect_rw_ro(IPC_Channel *channel, gpointer user_data) { cl_uuid_t client_id; xmlNode *reg_msg = NULL; cib_client_t *new_client = NULL; char uuid_str[UU_UNPARSE_SIZEOF]; gboolean (*callback)(IPC_Channel *channel, gpointer user_data); callback = cib_ro_callback; if(safe_str_eq(user_data, cib_channel_rw)) { callback = cib_rw_callback; } new_client = cib_client_connect_common( channel, callback==cib_ro_callback?cib_channel_ro:cib_channel_rw, callback); if(new_client == NULL) { return FALSE; } cl_uuid_generate(&client_id); cl_uuid_unparse(&client_id, uuid_str); CRM_CHECK(new_client->id == NULL, crm_free(new_client->id)); new_client->id = crm_strdup(uuid_str); cl_uuid_generate(&client_id); cl_uuid_unparse(&client_id, uuid_str); CRM_CHECK(new_client->callback_id == NULL, crm_free(new_client->callback_id)); new_client->callback_id = crm_strdup(uuid_str); /* 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); reg_msg = create_xml_node(NULL, "callback"); crm_xml_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); crm_xml_add(reg_msg, F_CIB_CLIENTID, new_client->id); crm_xml_add(reg_msg, F_CIB_CALLBACK_TOKEN, new_client->callback_id); send_ipc_message(channel, reg_msg); free_xml(reg_msg); return TRUE; } gboolean cib_client_connect_null(IPC_Channel *channel, gpointer user_data) { cib_client_t *new_client = NULL; new_client = cib_client_connect_common( channel, cib_channel_callback, cib_null_callback); if(new_client == NULL) { return FALSE; } return TRUE; } gboolean cib_rw_callback(IPC_Channel *channel, gpointer user_data) { gboolean result = FALSE; result = cib_common_callback(channel, user_data, FALSE, TRUE); return result; } gboolean cib_ro_synchronous_callback(IPC_Channel *channel, gpointer user_data) { gboolean result = FALSE; result = cib_common_callback(channel, user_data, TRUE, FALSE); return result; } gboolean cib_rw_synchronous_callback(IPC_Channel *channel, gpointer user_data) { gboolean result = FALSE; result = cib_common_callback(channel, user_data, TRUE, TRUE); return result; } gboolean cib_ro_callback(IPC_Channel *channel, gpointer user_data) { gboolean result = FALSE; result = cib_common_callback(channel, user_data, FALSE, FALSE); return result; } gboolean cib_null_callback(IPC_Channel *channel, gpointer user_data) { gboolean keep_connection = TRUE; xmlNode *op_request = NULL; xmlNode *registered = 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(IPC_ISRCONN(channel)) { free_xml(op_request); op_request = NULL; if(channel->ops->is_message_pending(channel) == 0) { break; } op_request = xmlfromIPC(channel, 0); if(op_request == NULL) { break; } type = crm_element_value(op_request, F_CIB_OPERATION); if(safe_str_eq(type, T_CIB_NOTIFY) ) { /* Update the notify filters for this client */ int on_off = 0; crm_element_value_int( op_request, F_CIB_NOTIFY_ACTIVATE, &on_off); type = crm_element_value(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; } else if(safe_str_eq(type, T_CIB_REPLACE_NOTIFY)) { cib_client->replace = 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); continue; } uuid_ticket = crm_element_value(op_request, F_CIB_CALLBACK_TOKEN); client_name = crm_element_value(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"); free_xml(op_request); return FALSE; } CRM_CHECK(cib_client->id == NULL, crm_free(cib_client->id)); CRM_CHECK(cib_client->name == NULL, crm_free(cib_client->name)); 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; } registered = create_xml_node(NULL, "registered"); crm_xml_add(registered, F_CIB_OPERATION, CRM_OP_REGISTER); crm_xml_add(registered, F_CIB_CLIENTID, cib_client->id); send_ipc_message(channel, registered); free_xml(registered); if(channel->ch_status == IPC_CONNECT) { break; } } free_xml(op_request); if(channel->ch_status != IPC_CONNECT) { crm_debug_2("Client disconnected"); keep_connection = cib_process_disconnect(channel, cib_client); } return keep_connection; } void cib_common_callback_worker(xmlNode *op_request, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); void cib_common_callback_worker(xmlNode *op_request, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged) { int rc = cib_ok; int call_type = 0; const char *op = NULL; longclock_t call_stop = 0; longclock_t call_start = 0; call_start = time_longclock(); cib_client->num_calls++; op = crm_element_value(op_request, F_CIB_OPERATION); rc = cib_get_operation_id(op, &call_type); if(rc != cib_ok) { crm_debug("Invalid operation %s from %s/%s", op, cib_client->name, cib_client->channel_name); } else { crm_debug_2("Processing %s operation from %s/%s", op, cib_client->name, cib_client->channel_name); } if(rc == cib_ok) { cib_process_request( op_request, force_synchronous, privileged, FALSE, cib_client); } call_stop = time_longclock(); cib_call_time += (call_stop - call_start); } gboolean cib_common_callback(IPC_Channel *channel, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged) { int lpc = 0; xmlNode *op_request = NULL; gboolean keep_channel = TRUE; if(cib_client == NULL) { crm_err("Receieved call from unknown source. Discarding."); return FALSE; } if(cib_client->name == NULL) { cib_client->name = crm_itoa(channel->farside_pid); } if(cib_client->id == NULL) { cib_client->id = crm_strdup(cib_client->name); g_hash_table_insert(client_list, cib_client->id, cib_client); } crm_debug_2("Callback for %s on %s channel", cib_client->id, cib_client->channel_name); while(IPC_ISRCONN(channel)) { if(channel->ops->is_message_pending(channel) == 0) { break; } op_request = xmlfromIPC(channel, 0); if (op_request == NULL) { perror("Receive failure:"); break; } lpc++; crm_assert_failed = FALSE; crm_log_xml(LOG_MSG, "Client[inbound]", op_request); crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id); crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name); cib_common_callback_worker( op_request, cib_client, force_synchronous, privileged); free_xml(op_request); if(channel->ch_status == IPC_CONNECT) { break; } } crm_debug_2("Processed %d messages", lpc); if(channel->ch_status != IPC_CONNECT) { crm_debug_2("Client disconnected"); keep_channel = cib_process_disconnect(channel, cib_client); } return keep_channel; } extern void cib_send_remote_msg(void *session, xmlNode *msg); static void do_local_notify(xmlNode *notify_src, const char *client_id, gboolean sync_reply, gboolean from_peer) { /* send callback to originating child */ cib_client_t *client_obj = NULL; xmlNode *client_reply = NULL; enum cib_errors local_rc = cib_ok; crm_debug_2("Performing notification"); client_reply = cib_msg_copy(notify_src, TRUE); if(client_id != NULL) { client_obj = g_hash_table_lookup( client_list, client_id); } else { crm_debug_2("No client to sent the response to." " F_CIB_CLIENTID not set."); } crm_debug_3("Sending callback to request originator"); if(client_obj == NULL) { local_rc = cib_reply_failed; } else if (crm_str_eq(client_obj->channel_name, "remote", FALSE)) { crm_debug("Send message over TLS connection"); cib_send_remote_msg(client_obj->channel, client_reply); } else { const char *client_id = client_obj->callback_id; crm_debug_2("Sending %ssync response to %s %s", sync_reply?"":"an a-", client_obj->name, from_peer?"(originator of delegated request)":""); if(sync_reply) { client_id = client_obj->id; } local_rc = send_via_callback_channel(client_reply, client_id); } if(local_rc != cib_ok && client_obj != NULL) { crm_warn("%sSync reply to %s failed: %s", sync_reply?"":"A-", client_obj?client_obj->name:"", cib_error2string(local_rc)); } free_xml(client_reply); } static void parse_local_options( cib_client_t *cib_client, int call_type, int call_options, const char *host, const char *op, gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward) { if(cib_op_modifies(call_type) && !(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_2("Processing locally scoped %s op from %s", op, cib_client->name); *local_notify = TRUE; } else if(host == NULL && cib_is_master) { crm_debug_2("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_2("Processing locally addressed %s op from %s", op, cib_client->name); *local_notify = TRUE; } else if(stand_alone) { *needs_forward = FALSE; *local_notify = TRUE; *process = TRUE; } else { crm_debug_2("%s op from %s needs to be forwarded to %s", op, cib_client->name, host?host:"the master instance"); *needs_forward = TRUE; *process = FALSE; } } static gboolean parse_peer_options( int call_type, xmlNode *request, gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward) { const char *op = crm_element_value(request, F_CIB_OPERATION); const char *originator = crm_element_value(request, F_ORIG); const char *host = crm_element_value(request, F_CIB_HOST); const char *reply_to = crm_element_value(request, F_CIB_ISREPLY); const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); const char *delegated = crm_element_value(request, F_CIB_DELEGATED); if(safe_str_eq(op, "cib_shutdown_req")) { if(reply_to != NULL) { crm_debug("Processing %s from %s", op, host); *needs_reply = FALSE; } else { crm_debug("Processing %s reply from %s", op, host); } return TRUE; } else if(crm_is_true(update) && safe_str_eq(reply_to, cib_our_uname)) { crm_debug_2("Processing global/peer update from %s" " that originated from us", originator); *needs_reply = FALSE; if(crm_element_value(request, F_CIB_CLIENTID) != NULL) { *local_notify = TRUE; } return TRUE; } else if(crm_is_true(update)) { crm_debug_2("Processing global/peer update from %s", originator); *needs_reply = FALSE; return TRUE; } else if(host != NULL && safe_str_eq(host, cib_our_uname)) { crm_debug_2("Processing request sent to us from %s", originator); return TRUE; } else if(delegated != NULL && cib_is_master == TRUE) { crm_debug_2("Processing request sent to master instance from %s", originator); return TRUE; } else if(reply_to != NULL && safe_str_eq(reply_to, cib_our_uname)) { crm_debug_2("Forward reply sent from %s to local clients", originator); *process = FALSE; *needs_reply = FALSE; *local_notify = TRUE; return TRUE; } else if(delegated != NULL) { crm_debug_2("Ignoring msg for master instance"); } else if(host != NULL) { /* this is for a specific instance and we're not it */ crm_debug_2("Ignoring msg for instance on %s", crm_str(host)); } else if(reply_to == NULL && cib_is_master == FALSE) { /* this is for the master instance and we're not it */ crm_debug_2("Ignoring reply to %s", crm_str(reply_to)); } else { crm_err("Nothing for us to do?"); crm_log_xml(LOG_ERR, "Peer[inbound]", request); } return FALSE; } static void forward_request(xmlNode *request, cib_client_t *cib_client, int call_options) { xmlNode *forward_msg = NULL; const char *op = crm_element_value(request, F_CIB_OPERATION); const char *host = crm_element_value(request, F_CIB_HOST); forward_msg = cib_msg_copy(request, TRUE); crm_xml_add(forward_msg, F_CIB_DELEGATED, cib_our_uname); if(host != NULL) { crm_debug_2("Forwarding %s op to %s", op, host); send_cluster_message(host, crm_msg_cib, forward_msg, FALSE); } else { crm_debug_2("Forwarding %s op to master instance", op); send_cluster_message(NULL, crm_msg_cib, forward_msg, FALSE); } if(call_options & cib_discard_reply) { crm_debug_2("Client not interested in reply"); } else if(call_options & cib_sync_call) { /* keep track of the request so we can time it * out if required */ crm_debug_2("Registering delegated call from %s", cib_client->id); cib_client->delegated_calls = g_list_append( cib_client->delegated_calls, forward_msg); forward_msg = NULL; } free_xml(forward_msg); } static void send_peer_reply( xmlNode *msg, xmlNode *result_diff, const char *originator, gboolean broadcast) { xmlNode *reply_copy = NULL; CRM_ASSERT(msg != NULL); reply_copy = cib_msg_copy(msg, TRUE); if(broadcast) { /* this (successful) call modified the CIB _and_ the * change needs to be broadcast... * send via HA to other nodes */ 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; char *digest = NULL; 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); crm_xml_add(reply_copy, F_CIB_ISREPLY, originator); crm_xml_add(reply_copy, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); crm_xml_add(reply_copy, F_CIB_OPERATION, CIB_OP_APPLY_DIFF); digest = calculate_xml_digest(the_cib, FALSE, TRUE); crm_xml_add(result_diff, XML_ATTR_DIGEST, digest); /* crm_log_xml_debug(the_cib, digest); */ crm_free(digest); add_message_xml(reply_copy, F_CIB_UPDATE_DIFF, result_diff); crm_log_xml(LOG_DEBUG_3, "copy", reply_copy); send_cluster_message(NULL, crm_msg_cib, reply_copy, TRUE); } else if(originator != NULL) { /* send reply via HA to originating node */ crm_debug_2("Sending request result to originator only"); crm_xml_add(reply_copy, F_CIB_ISREPLY, originator); send_cluster_message(originator, crm_msg_cib, reply_copy, FALSE); } free_xml(reply_copy); } void cib_process_request( xmlNode *request, gboolean force_synchronous, 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; xmlNode *result_diff = NULL; enum cib_errors rc = cib_ok; xmlNode *op_reply = NULL; const char *op = crm_element_value(request, F_CIB_OPERATION); const char *originator = crm_element_value(request, F_ORIG); const char *host = crm_element_value(request, F_CIB_HOST); const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); crm_debug_4("%s Processing msg %s", cib_our_uname, crm_element_value(request, F_SEQ)); cib_num_ops++; if(cib_num_ops == 0) { cib_num_fail = 0; cib_num_local = 0; cib_num_updates = 0; crm_info("Stats wrapped around"); } if(host != NULL && strlen(host) == 0) { host = NULL; } crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); crm_debug_4("Retrieved call options: %d", call_options); if(force_synchronous) { call_options |= cib_sync_call; } crm_debug_2("Processing %s message (%s) for %s...", from_peer?"peer":"local", from_peer?originator:cib_our_uname, host?host:"master"); rc = cib_get_operation_id(op, &call_type); if(cib_op_modifies(call_type)) { cib_num_updates++; } if(rc != cib_ok) { /* TODO: construct error reply */ crm_err("Pre-processing of command failed: %s", cib_error2string(rc)); } else if(from_peer == FALSE) { parse_local_options(cib_client, call_type, call_options, host, op, &local_notify, &needs_reply, &process, &needs_forward); } else if(parse_peer_options(call_type, request, &local_notify, &needs_reply, &process, &needs_forward) == FALSE) { return; } crm_debug_3("Finished determining processing actions"); if(call_options & cib_discard_reply) { needs_reply = cib_op_modifies(call_type); local_notify = FALSE; } if(needs_forward) { forward_request(request, cib_client, call_options); return; } if(cib_status != cib_ok) { rc = cib_status; crm_err("Operation ignored, cluster configuration is invalid." " Please repair and restart: %s", cib_error2string(cib_status)); op_reply = cib_construct_reply(request, the_cib, cib_status); } else if(process) { cib_num_local++; crm_debug_2("Performing local processing:" " op=%s origin=%s/%s,%s (update=%s)", crm_element_value(request, F_CIB_OPERATION), originator, crm_element_value(request, F_CIB_CLIENTID), crm_element_value(request, F_CIB_CALLID), update); rc = cib_process_command( request, &op_reply, &result_diff, privileged); 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) { cib_num_fail++; crm_err("%s operation failed: %s", crm_str(op), cib_error2string(rc)); crm_log_xml(LOG_DEBUG, "CIB[output]", op_reply); crm_log_xml(LOG_INFO, "Input message", request); } if(op_reply == NULL && (needs_reply || local_notify)) { crm_err("Unexpected NULL reply to message"); crm_log_xml(LOG_ERR, "null reply", request); needs_reply = FALSE; local_notify = FALSE; } } crm_debug_3("processing response cases"); crm_debug("Notify: %d", local_notify); if(local_notify) { const char *client_id = crm_element_value(request, F_CIB_CLIENTID); if(process == FALSE) { do_local_notify(request, client_id, call_options & cib_sync_call, from_peer); } else { do_local_notify(op_reply, client_id, call_options & cib_sync_call, from_peer); } } /* from now on we are the server */ if(needs_reply == FALSE || stand_alone) { /* nothing more to do... * this was a non-originating slave update */ crm_debug_2("Completed slave update"); } else if(rc == cib_ok && result_diff != NULL && !(call_options & cib_inhibit_bcast)) { send_peer_reply(request, result_diff, originator, TRUE); } else if(call_options & cib_discard_reply) { crm_debug_4("Caller isn't interested in reply"); } else if (from_peer) { crm_debug_2("Directing reply to %s", originator); if(call_options & cib_inhibit_bcast) { crm_debug_3("Request not broadcast: inhibited"); } if(cib_op_modifies(call_type) == FALSE || result_diff == NULL) { crm_debug_3("Request not broadcast: R/O call"); } if(rc != cib_ok) { crm_debug_3("Request not broadcast: call failed: %s", cib_error2string(rc)); } send_peer_reply(op_reply, result_diff, originator, FALSE); } free_xml(op_reply); free_xml(result_diff); return; } xmlNode * cib_construct_reply(xmlNode *request, xmlNode *output, int rc) { int lpc = 0; xmlNode *reply = NULL; const char *name = NULL; const char *value = NULL; const char *names[] = { F_CIB_OPERATION, F_CIB_CALLID, F_CIB_CLIENTID, F_CIB_CALLOPTS }; crm_debug_4("Creating a basic reply"); reply = create_xml_node(NULL, "cib-reply"); crm_xml_add(reply, F_TYPE, T_CIB); for(lpc = 0; lpc < DIMOF(names); lpc++) { name = names[lpc]; value = crm_element_value(request, name); crm_xml_add(reply, name, value); } crm_xml_add_int(reply, F_CIB_RC, rc); if(output != NULL) { crm_debug_4("Attaching reply output"); add_message_xml(reply, F_CIB_CALLDATA, output); } return reply; } enum cib_errors cib_process_command(xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged) { gboolean send_r_notify = FALSE; xmlNode *output = NULL; xmlNode *input = NULL; xmlNode *current_cib = NULL; xmlNode *result_cib = NULL; int call_type = 0; int call_options = 0; enum cib_errors rc = cib_ok; enum cib_errors rc2 = cib_ok; int log_level = LOG_DEBUG_3; xmlNode *filtered = NULL; const char *op = NULL; const char *section = NULL; gboolean config_changed = FALSE; gboolean global_update = crm_is_true(crm_element_value(request, F_CIB_GLOBAL_UPDATE)); CRM_ASSERT(cib_status == cib_ok); *reply = NULL; *cib_diff = NULL; if(per_action_cib) { CRM_CHECK(the_cib == NULL, free_xml(the_cib)); the_cib = readCibXmlFile(cib_root, "cib.xml", FALSE); CRM_CHECK(the_cib != NULL, return cib_NOOBJECT); } current_cib = the_cib; /* Start processing the request... */ op = crm_element_value(request, F_CIB_OPERATION); crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); rc = cib_get_operation_id(op, &call_type); if(rc == cib_ok) { rc = cib_op_can_run(call_type, call_options, privileged, global_update); } /* prevent NUMUPDATES from being incrimented - apply the change as-is */ if(global_update) { call_options |= cib_inhibit_bcast; call_options |= cib_force_diff; } rc2 = cib_op_prepare(call_type, request, &input, §ion); if(rc == cib_ok) { rc = rc2; } if(rc != cib_ok) { crm_debug_2("Call setup failed: %s", cib_error2string(rc)); goto done; } else if(cib_op_modifies(call_type) == FALSE) { - rc = cib_perform_op(op, call_options, section, request, input, FALSE, &config_changed, + rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE, + section, request, input, FALSE, &config_changed, current_cib, &result_cib, &output); CRM_CHECK(result_cib == NULL, free_xml(result_cib)); goto done; } /* Handle a valid write action */ if((call_options & cib_inhibit_notify) == 0) { cib_pre_notify(call_options, op, get_object_root(section, current_cib), input); } if(rc == cib_ok) { gboolean manage_counters = TRUE; if(global_update) { /* skip */ CRM_CHECK(call_type == 4 || call_type == 11, crm_err("Call type: %d", call_type); crm_log_xml(LOG_ERR, "bad op", request)); crm_debug_2("Skipping update: global replace"); manage_counters = FALSE; } else if(call_options & cib_inhibit_bcast) { /* skip */ crm_debug_2("Skipping update: inhibit broadcast"); manage_counters = FALSE; } - rc = cib_perform_op( - op, call_options, section, request, input, manage_counters, &config_changed, - current_cib, &result_cib, &output); + rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE, + section, request, input, manage_counters, &config_changed, + current_cib, &result_cib, &output); *cib_diff = diff_cib_object(current_cib, result_cib, FALSE); } if(rc != cib_ok) { free_xml(result_cib); } else { rc = activateCibXml(result_cib, config_changed); if(rc != cib_ok) { crm_warn("Activation failed"); } } if((call_options & cib_inhibit_notify) == 0) { const char *call_id = crm_element_value(request, F_CIB_CALLID); const char *client = crm_element_value(request, F_CIB_CLIENTNAME); cib_post_notify(call_options, op, input, rc, the_cib); cib_diff_notify(call_options, client, call_id, op, input, rc, *cib_diff); } if(rc == cib_ok && safe_str_eq(CIB_OP_ERASE, op)) { send_r_notify = TRUE; } else if(rc == cib_ok && safe_str_eq(CIB_OP_REPLACE, op)) { if(section == NULL) { send_r_notify = TRUE; } else if(safe_str_eq(section, XML_TAG_CIB)) { send_r_notify = TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_NODES)) { send_r_notify = TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { send_r_notify = TRUE; } } if(send_r_notify) { cib_replace_notify(the_cib, rc, *cib_diff); } if(rc == cib_dtd_validation && global_update) { log_level = LOG_WARNING; crm_log_xml_info(input, "cib:global_update"); } else if(rc != cib_ok) { log_level = LOG_DEBUG_4; } else if(cib_is_master && config_changed) { log_level = LOG_INFO; } else if(cib_is_master) { log_level = LOG_DEBUG; log_xml_diff(LOG_DEBUG_2, filtered, "cib:diff:filtered"); } else if(config_changed) { log_level = LOG_DEBUG_2; } else { log_level = LOG_DEBUG_3; } log_xml_diff(log_level, *cib_diff, "cib:diff"); free_xml(filtered); done: if((call_options & cib_discard_reply) == 0) { *reply = cib_construct_reply(request, output, rc); crm_log_xml_info(*reply, "cib:reply"); } if(call_type >= 0) { cib_op_cleanup(call_type, op, &input, &output); } if(per_action_cib) { uninitializeCib(); } return rc; } int send_via_callback_channel(xmlNode *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_DISCONNECT) { crm_warn("Client %s has disconnected", token); rc = cib_client_gone; cib_num_timeouts++; } } /* 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 */ xmlNode *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); free_xml(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; } } return rc; } gint cib_GCompareFunc(gconstpointer a, gconstpointer b) { const xmlNode *a_msg = a; const xmlNode *b_msg = b; int msg_a_id = 0; int msg_b_id = 0; const char *value = NULL; value = crm_element_value_const(a_msg, F_CIB_CALLID); msg_a_id = crm_parse_int(value, NULL); value = crm_element_value_const(b_msg, F_CIB_CALLID); msg_b_id = crm_parse_int(value, NULL); 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) { int timeout = 0; /* 1 iteration == 10 seconds */ xmlNode *msg = NULL; xmlNode *reply = NULL; const char *host_to = NULL; cib_client_t *client = value; GListPtr list = client->delegated_calls; while(list != NULL) { msg = list->data; crm_element_value_int(msg, F_CIB_TIMEOUT, &timeout); if(timeout <= 0) { list = list->next; continue; } else { int seen = 0; crm_element_value_int(msg, F_CIB_SEENCOUNT, &seen); crm_debug_4("Timeout %d, seen %d", timeout, seen); if(seen < timeout) { crm_debug_4("Updating seen count for msg from client %s", client->id); seen += 10; crm_xml_add_int(msg, F_CIB_SEENCOUNT, seen); list = list->next; continue; } } cib_num_timeouts++; host_to = crm_element_value(msg, F_CIB_HOST); crm_warn("Sending operation timeout msg to client %s", client->id); reply = create_xml_node(NULL, "cib-reply"); crm_xml_add(reply, F_TYPE, T_CIB); crm_xml_add(reply, F_CIB_OPERATION, crm_element_value(msg, F_CIB_OPERATION)); crm_xml_add(reply, F_CIB_CALLID, crm_element_value(msg, F_CIB_CALLID)); if(host_to == NULL) { crm_xml_add_int(reply, F_CIB_RC, cib_master_timeout); } else { crm_xml_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); free_xml(msg); free_xml(reply); } } gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client) { if (channel == NULL) { CRM_DEV_ASSERT(cib_client == NULL); } else if (cib_client == NULL) { crm_err("No client"); } else { CRM_DEV_ASSERT(channel->ch_status != IPC_CONNECT); crm_debug_2("Cleaning up after client disconnect: %s/%s/%s", crm_str(cib_client->name), cib_client->channel_name, cib_client->id); if(cib_client->id != NULL) { if(!g_hash_table_remove(client_list, cib_client->id)) { crm_err("Client %s not found in the hashtable", cib_client->name); } } } if(cib_shutdown_flag && g_hash_table_size(client_list) == 0) { crm_info("All clients disconnected..."); initiate_exit(); } return FALSE; } void cib_peer_callback(xmlNode * msg, void* private_data) { int call_type = 0; int call_options = 0; const char *originator = crm_element_value(msg, F_ORIG); const char *seq = crm_element_value(msg, F_SEQ); const char *op = crm_element_value(msg, F_CIB_OPERATION); crm_node_t *node = NULL; crm_log_xml(LOG_MSG, "Peer[inbound]", msg); crm_debug_2("Peer %s message (%s) from %s", op, seq, originator); if(originator == NULL || safe_str_eq(originator, cib_our_uname)) { crm_debug_2("Discarding %s message %s from ourselves", op, seq); return; } if(crm_peer_cache == NULL) { crm_info("Discarding %s message (%s) from %s:" " membership not established", op, seq, originator); return; } node = g_hash_table_lookup(crm_peer_cache, originator); if(node == NULL || crm_is_member_active(node) == FALSE) { crm_warn("Discarding %s message (%s) from %s:" " not in our membership", op, seq, originator); return; } if(cib_get_operation_id(op, &call_type) != cib_ok) { crm_debug("Discarding %s message (%s) from %s:" " Invalid operation", op, seq, originator); return; } crm_debug_2("Processing %s msg (%s) from %s",op, seq, originator); crm_element_value_int(msg, F_CIB_CALLOPTS, &call_options); crm_debug_4("Retrieved call options: %d", call_options); if(crm_element_value(msg, F_CIB_CLIENTNAME) == NULL) { crm_xml_add(msg, F_CIB_CLIENTNAME, originator); } cib_process_request(msg, FALSE, TRUE, TRUE, NULL); return; } void cib_client_status_callback(const char * node, const char * client, const char * status, void * private) { crm_node_t *member = NULL; if(safe_str_eq(client, CRM_SYSTEM_CIB)) { crm_info("Status update: Client %s/%s now has status [%s]", node, client, status); if(safe_str_eq(status, JOINSTATUS)){ status = ONLINESTATUS; } else if(safe_str_eq(status, LEAVESTATUS)){ status = OFFLINESTATUS; } member = g_hash_table_lookup(crm_peer_cache, node); if(member == NULL) { /* Make sure it gets created */ const char *uuid = get_uuid(node); member = crm_update_peer(0, 0, -1, 0, uuid, node, NULL, NULL); } crm_update_peer_proc(node, crm_proc_cib, status); set_connected_peers(the_cib); } return; } #if SUPPORT_HEARTBEAT extern oc_ev_t *cib_ev_token; gboolean cib_ccm_dispatch(int fd, gpointer user_data) { int rc = 0; oc_ev_t *ccm_token = (oc_ev_t*)user_data; crm_debug_2("received callback"); rc = oc_ev_handle_event(ccm_token); if(0 == rc) { return TRUE; } crm_err("CCM connection appears to have failed: rc=%d.", rc); /* eventually it might be nice to recover and reconnect... but until then... */ crm_err("Exiting to recover from CCM connection failure"); exit(2); return FALSE; } int current_instance = 0; void cib_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data) { gboolean update_id = FALSE; const oc_ev_membership_t *membership = data; CRM_ASSERT(membership != NULL); crm_info("Processing CCM event=%s (id=%d)", ccm_event_name(event), membership->m_instance); if(current_instance > membership->m_instance) { crm_err("Membership instance ID went backwards! %d->%d", current_instance, membership->m_instance); CRM_ASSERT(current_instance <= membership->m_instance); } switch(event) { case OC_EV_MS_NEW_MEMBERSHIP: case OC_EV_MS_INVALID: update_id = TRUE; break; case OC_EV_MS_PRIMARY_RESTORED: update_id = TRUE; break; case OC_EV_MS_NOT_PRIMARY: crm_debug_2("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)); break; default: crm_err("Unknown CCM event: %d", event); } if(update_id) { unsigned int lpc = 0; CRM_CHECK(membership != NULL, return); current_instance = membership->m_instance; for(lpc=0; lpc < membership->m_n_out; lpc++) { crm_update_ccm_node( membership, lpc+membership->m_out_idx, CRM_NODE_LOST); } for(lpc=0; lpc < membership->m_n_member; lpc++) { crm_update_ccm_node( membership, lpc+membership->m_memb_idx,CRM_NODE_ACTIVE); } } oc_ev_callback_done(cookie); set_connected_peers(the_cib); return; } #endif gboolean can_write(int flags) { return TRUE; } static gboolean cib_force_exit(gpointer data) { crm_notice("Forcing exit!"); terminate_cib(__FUNCTION__); return FALSE; } void initiate_exit(void) { int active = 0; xmlNode *leaving = NULL; active = crm_active_peers(crm_proc_cib); if(active < 2) { terminate_cib(__FUNCTION__); return; } crm_info("Sending disconnect notification to %d peers...", active); leaving = create_xml_node(NULL, "exit-notification"); crm_xml_add(leaving, F_TYPE, "cib"); crm_xml_add(leaving, F_CIB_OPERATION, "cib_shutdown_req"); send_cluster_message(NULL, crm_msg_cib, leaving, TRUE); free_xml(leaving); Gmain_timeout_add(crm_get_msec("5s"), cib_force_exit, NULL); } void terminate_cib(const char *caller) { #if SUPPORT_AIS if(is_openais_cluster()) { cib_ha_connection_destroy(NULL); return; } #endif #if SUPPORT_HEARTBEAT if(hb_conn != NULL) { crm_info("%s: Disconnecting heartbeat", caller); hb_conn->llc_ops->signoff(hb_conn, FALSE); } else { crm_err("%s: No heartbeat connection", caller); } #endif uninitializeCib(); crm_info("Exiting..."); if (mainloop != NULL && g_main_is_running(mainloop)) { g_main_quit(mainloop); } else { exit(LSB_EXIT_OK); } } diff --git a/cib/cibmessages.h b/cib/cibmessages.h index ddbd60ef54..dc595cfa41 100644 --- a/cib/cibmessages.h +++ b/cib/cibmessages.h @@ -1,87 +1,63 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB_MESSAGES__H #define CIB_MESSAGES__H - +#include extern xmlNode *createCibRequest( gboolean isLocal, const char *operation, const char *section, const char *verbose, xmlNode *data); extern enum cib_errors cib_process_shutdown_req( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); extern enum cib_errors cib_process_default( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); extern enum cib_errors cib_process_quit( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); extern enum cib_errors cib_process_ping( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -extern enum cib_errors cib_process_query( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); - -extern enum cib_errors cib_process_erase( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); - -extern enum cib_errors cib_process_bump( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); - -extern enum cib_errors cib_process_replace( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); - -extern enum cib_errors cib_process_modify( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); - extern enum cib_errors cib_process_readwrite( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -extern enum cib_errors cib_process_diff( +extern enum cib_errors cib_server_process_diff( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); extern enum cib_errors cib_process_sync( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); extern enum cib_errors cib_process_sync_one( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); -extern enum cib_errors cib_process_delete( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); - extern enum cib_errors cib_process_change( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); #endif diff --git a/cib/cibpipe.c b/cib/cibpipe.c index 20cc0f9de3..504a584bfb 100644 --- a/cib/cibpipe.c +++ b/cib/cibpipe.c @@ -1,333 +1,374 @@ /* * 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 "common.h" #ifdef HAVE_GETOPT_H # include #endif void usage(const char* cmd, int exit_status); +struct cib_func_entry +{ + const char *op; + gboolean read_only; + cib_op_t fn; +}; + +static struct cib_func_entry cib_pipe_ops[] = { + {CIB_OP_QUERY, TRUE, cib_process_query}, + {CIB_OP_MODIFY, FALSE, cib_process_modify}, + /* {CIB_OP_UPDATE, FALSE, cib_process_change}, */ + {CIB_OP_APPLY_DIFF, FALSE, cib_process_diff}, + {CIB_OP_BUMP, FALSE, cib_process_bump}, + {CIB_OP_REPLACE, FALSE, cib_process_replace}, + /* {CIB_OP_CREATE, FALSE, cib_process_change}, */ + {CIB_OP_DELETE, FALSE, cib_process_delete}, + {CIB_OP_ERASE, FALSE, cib_process_erase}, +}; + #define OPTARGS "V?o:QDUCEX:t:MBfRx:P5S" int main(int argc, char ** argv) { + int lpc; int flag; int rc = 0; int argerr = 0; + int max_msg_types = DIMOF(cib_pipe_ops); int command_options = 0; gboolean changed = FALSE; gboolean force_flag = FALSE; gboolean dangerous_cmd = FALSE; char *buffer = NULL; const char *section = NULL; const char *input_xml = NULL; const char *input_file = NULL; const char *output_file = NULL; const char *cib_action = NULL; xmlNode *input = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; xmlNode *current_cib = NULL; + gboolean query = FALSE; + cib_op_t *fn = NULL; + #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { {CIB_OP_ERASE, 0, 0, 'E'}, {CIB_OP_QUERY, 0, 0, 'Q'}, {CIB_OP_CREATE, 0, 0, 'C'}, {CIB_OP_REPLACE, 0, 0, 'R'}, {CIB_OP_UPDATE, 0, 0, 'U'}, {CIB_OP_MODIFY, 0, 0, 'M'}, {"patch", 0, 0, 'P'}, {CIB_OP_DELETE, 0, 0, 'D'}, {CIB_OP_BUMP, 0, 0, 'B'}, {"md5-sum", 0, 0, '5'}, {"force", 0, 0, 'f'}, {"xml-file", 1, 0, 'x'}, {"xml-text", 1, 0, 'X'}, {"xml-save", 1, 0, 'S'}, {"obj_type", 1, 0, 'o'}, {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {0, 0, 0, 0} }; #endif crm_log_init("cibpipe", LOG_ERR, FALSE, FALSE, argc, argv); while (1) { #ifdef HAVE_GETOPT_H flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); #else flag = getopt(argc, argv, OPTARGS); #endif if (flag == -1) break; switch(flag) { case 'E': cib_action = CIB_OP_ERASE; dangerous_cmd = TRUE; break; case 'Q': cib_action = CIB_OP_QUERY; break; case 'P': cib_action = CIB_OP_APPLY_DIFF; break; case 'S': cib_action = CIB_OP_SYNC; break; case 'U': case 'M': cib_action = CIB_OP_MODIFY; break; case 'R': cib_action = CIB_OP_REPLACE; break; case 'C': cib_action = CIB_OP_CREATE; break; case 'D': cib_action = CIB_OP_DELETE; break; case '5': cib_action = "md5-sum"; break; case 'd': cib_action = CIB_OP_DELETE_ALT; break; case 'm': cib_action = CIB_OP_ISMASTER; command_options |= cib_scope_local; break; case 'B': cib_action = CIB_OP_BUMP; break; case 'o': crm_debug_2("Option %c => %s", flag, optarg); section = crm_strdup(optarg); break; case 'x': crm_debug_2("Option %c => %s", flag, optarg); input_file = crm_strdup(optarg); break; case 'X': crm_debug_2("Option %c => %s", flag, optarg); input_xml = crm_strdup(optarg); break; case 'f': force_flag = TRUE; command_options |= cib_quorum_override; break; case 'V': alter_debug(DEBUG_INC); cl_log_enable_stderr(1); break; case '?': /* Help message */ usage(crm_system_name, LSB_EXIT_OK); break; default: ++argerr; break; } } if (cib_action == NULL) { ++argerr; } if (optind > argc) { ++argerr; } if (argerr) { usage(crm_system_name, LSB_EXIT_GENERIC); } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); usage(crm_system_name, LSB_EXIT_GENERIC); } if(input_file != NULL) { FILE *xml_strm = fopen(input_file, "r"); input = file2xml(xml_strm, FALSE); if(input == NULL) { fprintf(stderr, "Couldn't parse input file: %s\n", input_file); return 1; } fclose(xml_strm); } else if(input_xml != NULL) { input = string2xml(input_xml); if(input == NULL) { fprintf(stderr, "Couldn't parse input string: %s\n", input_xml); return 1; } } if(input && safe_str_eq(cib_action, CIB_OP_QUERY)) { current_cib = copy_xml(input); } else { current_cib = stdin2xml(); if(current_cib == NULL && safe_str_neq(cib_action, CIB_OP_ERASE)) { fprintf(stderr, "Couldn't parse existing CIB from STDIN.\n"); return 1; } } if(current_cib == NULL) { current_cib = createEmptyCib(); } result_cib = copy_xml(current_cib); if(safe_str_eq(cib_action, "md5-sum")) { char *digest = NULL; digest = calculate_xml_digest(current_cib, FALSE, FALSE); fprintf(stdout, "%s\n", crm_str(digest)); crm_free(digest); return 0; } /* read local config file */ - rc = cib_perform_op( - cib_action, command_options, section, NULL, input, TRUE, &changed, - current_cib, &result_cib, &output); + if(cib_action == NULL) { + crm_err("No operation specified"); + return cib_operation; + } + + for (lpc = 0; lpc < max_msg_types; lpc++) { + if (safe_str_eq(cib_action, cib_pipe_ops[lpc].op)) { + fn = &(cib_pipe_ops[lpc].fn); + query = cib_pipe_ops[lpc].read_only; + break; + } + } + + if(fn == NULL) { + rc = cib_NOTSUPPORTED; + } else { + rc = cib_perform_op(cib_action, command_options, fn, query, + section, NULL, input, TRUE, &changed, + current_cib, &result_cib, &output); + } if(rc != cib_ok) { fprintf(stderr, "Call failed: %s\n", cib_error2string(rc)); fprintf(stdout, "%c", 0); return -rc; } cl_log_args(argc, argv); if(output) { buffer = dump_xml_formatted(output); } else { buffer = dump_xml_formatted(result_cib); } fprintf(stdout, "%s\n", buffer); fflush(stdout); if(output_file != NULL) { FILE *output_strm = fopen(output_file, "w"); if(output_strm == NULL) { cl_perror("Could not open %s for writing", output_file); } else { if(fprintf(output_strm, "%s\n", buffer) < 0) { cl_perror("Write to %s failed", output_file); } fflush(output_strm); fclose(output_strm); } } crm_info("Done"); return 0; } void usage(const char* cmd, int exit_status) { FILE* stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s -Q -(x|X)\n", cmd); fprintf(stream, "usage: %s -Q -(x|X) | %s [-%s] | %s [-%s] | ...\n", cmd, cmd, OPTARGS, cmd, OPTARGS); fprintf(stream, "usage: cibadmin -Q | %s [-%s] | %s [-%s] | ...\n", cmd, OPTARGS, cmd, OPTARGS); fprintf(stream, "\nOptions\n"); fprintf(stream, "\t--%s (-%c) \tobject type being operated on\n", "obj_type", 'o'); fprintf(stream, "\t\tValid values are:" " nodes, resources, constraints, crm_config, status\n"); fprintf(stream, "\t--%s (-%c)\tturn on debug info." " additional instance increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help", '?'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c)\tErase the contents of the whole CIB\n", CIB_OP_ERASE, 'E'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_QUERY, 'Q'); fprintf(stream, "\t--%s (-%c)\tCreate an object that does not yet exist\n", CIB_OP_CREATE, 'C'); fprintf(stream, "\t--%s (-%c)\tRecursivly update an object in the CIB\n", CIB_OP_UPDATE, 'U'); fprintf(stream, "\t--%s (-%c)\tFind the object somewhere in the CIB's XML tree and update it as --"CIB_OP_UPDATE" would\n", CIB_OP_MODIFY, 'M'); fprintf(stream, "\t--%s (-%c)\tRecursivly replace an object in the CIB\n", CIB_OP_REPLACE,'R'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE, 'D'); fprintf(stream, "\t\t\tDelete the first object matching the supplied criteria\n"); fprintf(stream, "\t\t\tEg. \n"); fprintf(stream, "\t\t\tThe tagname and all attributes must match in order for the element to be deleted\n"); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_BUMP, 'B'); fprintf(stream, "\t--%s (-%c)\t\tCalculate the configuration's digest.\n", "md5-sum", '5'); fprintf(stream, "\nXML data\n"); fprintf(stream, "\t--%s (-%c) \tRetrieve XML from the named file\n", "xml-file", 'x'); fprintf(stream, "\t--%s (-%c) \tRetrieve XML from the supplied string\n", "xml-text", 'X'); fprintf(stream, "\t--%s (-%c) \tSave the XML output to the named file\n", "xml-save", 'S'); fprintf(stream, "\nNOTE: The current CIB is assumed to be passed in via stdin," " unless -Q is used in which case -x or -X are also acceptable\n"); fflush(stream); exit(exit_status); } diff --git a/cib/common.c b/cib/common.c index 1a328a952f..0483290d24 100644 --- a/cib/common.c +++ b/cib/common.c @@ -1,440 +1,380 @@ /* * Copyright (C) 2008 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 #include "common.h" extern gboolean cib_is_master; extern const char* cib_root; gboolean stand_alone = FALSE; extern enum cib_errors cib_status; extern gboolean can_write(int flags); extern enum cib_errors cib_perform_command( xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged); static xmlNode * cib_prepare_common(xmlNode *root, const char *section) { xmlNode *data = NULL; /* extract the CIB from the fragment */ if(root == NULL) { return NULL; } else if(safe_str_eq(crm_element_name(root), XML_TAG_FRAGMENT) || safe_str_eq(crm_element_name(root), F_CIB_CALLDATA)) { data = find_xml_node(root, XML_TAG_CIB, TRUE); if(data != NULL) { crm_debug_3("Extracted CIB from %s", TYPE(root)); } else { crm_log_xml_debug_4(root, "No CIB"); } } else { data = root; } /* grab the section specified for the command */ if(section != NULL && data != NULL && safe_str_eq(crm_element_name(data), XML_TAG_CIB)){ int rc = revision_check(data, the_cib, 0/* call_options */); if(rc == cib_ok) { data = get_object_root(section, data); if(data != NULL) { crm_debug_3("Extracted %s from CIB", section); } else { crm_log_xml_debug_4(root, "No Section"); } } else { crm_debug_2("Revision check failed"); } } crm_log_xml_debug_4(root, "cib:input"); return copy_xml(data); } static gboolean verify_section(const char *section) { if(section == NULL) { return TRUE; } else if(safe_str_eq(section, XML_TAG_CIB)) { return TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { return TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { return TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_NODES)) { return TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_RESOURCES)) { return TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_CONSTRAINTS)) { return TRUE; } return FALSE; } static enum cib_errors cib_prepare_none(xmlNode *request, xmlNode **data, const char **section) { *data = NULL; *section = crm_element_value(request, F_CIB_SECTION); if(verify_section(*section) == FALSE) { return cib_bad_section; } return cib_ok; } static enum cib_errors cib_prepare_data(xmlNode *request, xmlNode **data, const char **section) { xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA); *section = crm_element_value(request, F_CIB_SECTION); *data = cib_prepare_common(input_fragment, *section); crm_log_xml_debug(*data, "data"); if(verify_section(*section) == FALSE) { return cib_bad_section; } return cib_ok; } static enum cib_errors cib_prepare_sync(xmlNode *request, xmlNode **data, const char **section) { *section = crm_element_value(request, F_CIB_SECTION); *data = NULL; if(verify_section(*section) == FALSE) { return cib_bad_section; } return cib_ok; } static enum cib_errors cib_prepare_diff(xmlNode *request, xmlNode **data, const char **section) { xmlNode *input_fragment = NULL; const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); *data = NULL; *section = NULL; if(crm_is_true(update)) { input_fragment = get_message_xml(request,F_CIB_UPDATE_DIFF); } else { input_fragment = get_message_xml(request, F_CIB_CALLDATA); } CRM_CHECK(input_fragment != NULL,crm_log_xml(LOG_WARNING, "no input", request)); *data = cib_prepare_common(input_fragment, NULL); return cib_ok; } static enum cib_errors cib_cleanup_query(const char *op, xmlNode **data, xmlNode **output) { CRM_DEV_ASSERT(*data == NULL); return cib_ok; } static enum cib_errors cib_cleanup_data(const char *op, xmlNode **data, xmlNode **output) { free_xml(*output); free_xml(*data); return cib_ok; } static enum cib_errors cib_cleanup_output(const char *op, xmlNode **data, xmlNode **output) { free_xml(*output); return cib_ok; } static enum cib_errors cib_cleanup_none(const char *op, xmlNode **data, xmlNode **output) { CRM_DEV_ASSERT(*data == NULL); CRM_DEV_ASSERT(*output == NULL); return cib_ok; } static enum cib_errors cib_cleanup_sync(const char *op, xmlNode **data, xmlNode **output) { /* data is non-NULL but doesnt need to be free'd */ CRM_DEV_ASSERT(*data == NULL); CRM_DEV_ASSERT(*output == NULL); return cib_ok; } /* typedef struct cib_operation_s { const char* operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_quorum; enum cib_errors (*prepare)(xmlNode *, xmlNode**, const char **); enum cib_errors (*cleanup)(xmlNode**, xmlNode**); enum cib_errors (*fn)( const char *, int, const char *, xmlNode*, xmlNode*, xmlNode**, xmlNode**); } cib_operation_t; */ /* technically bump does modify the cib... * but we want to split the "bump" from the "sync" */ static cib_operation_t cib_server_ops[] = { {NULL, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default}, {CIB_OP_QUERY, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_query, cib_process_query}, {CIB_OP_MODIFY, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_modify}, {CIB_OP_UPDATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_change}, - {CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_process_diff}, + {CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_server_process_diff}, {CIB_OP_SLAVE, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {CIB_OP_SLAVEALL, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {CIB_OP_SYNC_ONE, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync_one}, {CIB_OP_MASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {CIB_OP_ISMASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite}, {CIB_OP_BUMP, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_bump}, {CIB_OP_REPLACE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_replace}, {CIB_OP_CREATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_change}, {CIB_OP_DELETE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete}, {CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_change}, {CIB_OP_SYNC, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync}, {CRM_OP_QUIT, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_quit}, {CRM_OP_PING, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_output, cib_process_ping}, {CIB_OP_ERASE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_erase}, {CRM_OP_NOOP, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default}, {"cib_shutdown_req",FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_shutdown_req}, }; enum cib_errors cib_get_operation_id(const char *op, int *operation) { int lpc = 0; int max_msg_types = DIMOF(cib_server_ops); 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; } xmlNode * cib_msg_copy(xmlNode *msg, gboolean with_data) { int lpc = 0; const char *field = NULL; const char *value = NULL; xmlNode *value_struct = NULL; static const char *field_list[] = { F_XML_TAGNAME , 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 }; static const char *data_list[] = { F_CIB_CALLDATA , F_CIB_UPDATE , F_CIB_UPDATE_RESULT }; xmlNode *copy = create_xml_node(NULL, "copy"); CRM_ASSERT(copy != NULL); for(lpc = 0; lpc < DIMOF(field_list); lpc++) { field = field_list[lpc]; value = crm_element_value(msg, field); if(value != NULL) { crm_xml_add(copy, field, value); } } for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) { field = data_list[lpc]; value_struct = get_message_xml(msg, field); if(value_struct != NULL) { add_message_xml(copy, field, value_struct); } } return copy; } -enum cib_errors -cib_perform_op( - const char *op, int call_options, const char *section, xmlNode *req, xmlNode *input, - gboolean manage_counters, gboolean *config_changed, - xmlNode *current_cib, xmlNode **result_cib, xmlNode **output) +cib_op_t *cib_op_func(int call_type) { - int rc = cib_ok; - int call_type = 0; - xmlNode *scratch = NULL; - - CRM_CHECK(output != NULL && result_cib != NULL && config_changed != NULL, - return cib_output_data); - - *output = NULL; - *result_cib = NULL; - rc = cib_get_operation_id(op, &call_type); - *config_changed = FALSE; - - if(rc != cib_ok) { - return rc; - } - - if(cib_server_ops[call_type].modifies_cib == FALSE) { - rc = cib_server_ops[call_type].fn( - op, call_options, section, req, input, current_cib, result_cib, output); - /* FIXME: Free scratch */ - return rc; - } - - scratch = copy_xml(current_cib); - rc = cib_server_ops[call_type].fn( - op, call_options, section, req, input, current_cib, &scratch, output); - - crm_log_xml_debug(*output, "output"); - - CRM_CHECK(current_cib != scratch, return cib_unknown); - - if(rc == cib_ok) { - - CRM_CHECK(scratch != NULL, return cib_unknown); - - if(do_id_check(scratch, NULL, TRUE, FALSE)) { - rc = cib_id_check; - if(call_options & cib_force_diff) { - crm_err("Global update introduces id collision!"); - } - } - - if(rc == cib_ok) { - fix_plus_plus_recursive(scratch); - *config_changed = cib_config_changed(current_cib, scratch, NULL); - - if(manage_counters && *config_changed) { - cib_update_counter(scratch, XML_ATTR_NUMUPDATES, TRUE); - cib_update_counter(scratch, XML_ATTR_GENERATION, FALSE); - } else if(manage_counters) { - cib_update_counter(scratch, XML_ATTR_NUMUPDATES, FALSE); - } - } - } - - *result_cib = scratch; - return rc; + return &(cib_server_ops[call_type].fn); } gboolean cib_op_modifies(int call_type) { return cib_server_ops[call_type].modifies_cib; } int cib_op_can_run( int call_type, int call_options, gboolean privileged, gboolean global_update) { int rc = cib_ok; if(rc == cib_ok && cib_server_ops[call_type].needs_privileges && privileged == FALSE) { /* abort */ return cib_not_authorized; } if(rc == cib_ok && stand_alone == FALSE && global_update == FALSE && (call_options & cib_quorum_override) == 0 && cib_server_ops[call_type].needs_quorum) { return cib_no_quorum; } return cib_ok; } int cib_op_prepare( int call_type, xmlNode *request, xmlNode **input, const char **section) { return cib_server_ops[call_type].prepare(request, input, section); } int cib_op_cleanup( int call_type, const char *op, xmlNode **input, xmlNode **output) { return cib_server_ops[call_type].cleanup(op, input, output); } diff --git a/cib/common.h b/cib/common.h index 67b656b022..c2663803ef 100644 --- a/cib/common.h +++ b/cib/common.h @@ -1,39 +1,44 @@ /* * 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 "../lib/crm/cib/cib_private.h" + extern xmlNode *cib_msg_copy(xmlNode *msg, gboolean with_data); extern xmlNode *cib_construct_reply(xmlNode *request, xmlNode *output, int rc); extern enum cib_errors revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags); extern enum cib_errors cib_get_operation_id(const char *op, int *operation); extern enum cib_errors cib_perform_op( - const char *op, int call_options, const char *section, xmlNode *req, xmlNode *input, + const char *op, int call_options, cib_op_t *fn, gboolean is_query, + const char *section, xmlNode *req, xmlNode *input, gboolean manage_counters, gboolean *config_changed, xmlNode *current_cib, xmlNode **result_cib, xmlNode **output); extern enum cib_errors cib_update_counter( xmlNode *xml_obj, const char *field, gboolean reset); +extern cib_op_t *cib_op_func(int call_type); + extern gboolean cib_op_modifies(int call_type); extern int cib_op_prepare( int call_type, xmlNode *request, xmlNode **input, const char **section); extern int cib_op_cleanup( int call_type, const char *op, xmlNode **input, xmlNode **output); extern int cib_op_can_run( int call_type, int call_options, gboolean privileged, gboolean global_update); diff --git a/cib/io.c b/cib/io.c index baa676e72a..e768ef2c96 100644 --- a/cib/io.c +++ b/cib/io.c @@ -1,857 +1,836 @@ /* * 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 int archive_file(const char *oldname, const char *newname, const char *ext, gboolean preserve); const char * local_resource_path[] = { XML_CIB_TAG_STATUS, }; const char * resource_path[] = { XML_CIB_TAG_RESOURCES, }; const char * node_path[] = { XML_CIB_TAG_NODES, }; const char * constraint_path[] = { XML_CIB_TAG_CONSTRAINTS, }; gboolean initialized = FALSE; xmlNode *node_search = NULL; xmlNode *resource_search = NULL; xmlNode *constraint_search = NULL; xmlNode *status_search = NULL; extern gboolean cib_writes_enabled; extern GTRIGSource *cib_writer; extern enum cib_errors cib_status; int set_connected_peers(xmlNode *xml_obj); void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data); int write_cib_contents(gpointer p); extern void cib_cleanup(void); static gboolean validate_cib_digest(xmlNode *local_cib, const char *sigfile) { int s_res = -1; struct stat buf; char *digest = NULL; char *expected = NULL; gboolean passed = FALSE; FILE *expected_strm = NULL; int start = 0, length = 0, read_len = 0; CRM_ASSERT(sigfile != NULL); s_res = stat(sigfile, &buf); if (s_res != 0) { crm_warn("No on-disk digest present"); return TRUE; } if(local_cib != NULL) { digest = calculate_xml_digest(local_cib, FALSE, FALSE); } expected_strm = fopen(sigfile, "r"); if(expected_strm == NULL) { cl_perror("Could not open signature file %s for reading", sigfile); goto bail; } start = ftell(expected_strm); fseek(expected_strm, 0L, SEEK_END); length = ftell(expected_strm); fseek(expected_strm, 0L, start); CRM_ASSERT(start == ftell(expected_strm)); crm_debug_3("Reading %d bytes from file", length); crm_malloc0(expected, (length+1)); read_len = fread(expected, 1, length, expected_strm); CRM_ASSERT(read_len == length); fclose(expected_strm); bail: if(expected == NULL) { crm_err("On-disk digest is empty"); } else if(safe_str_eq(expected, digest)) { crm_debug_2("Digest comparision passed: %s", digest); passed = TRUE; } else { crm_err("Digest comparision failed: expected %s (%s), calculated %s", expected, sigfile, digest); } crm_free(digest); crm_free(expected); return passed; } static int write_cib_digest(xmlNode *local_cib, char *digest) { int rc = 0; char *local_digest = NULL; FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w"); if(digest_strm == NULL) { cl_perror("Cannot open signature file "CIB_FILENAME ".sig for writing"); return -1; } if(digest == NULL) { local_digest = calculate_xml_digest(local_cib, FALSE, FALSE); CRM_ASSERT(digest != NULL); digest = local_digest; } rc = fprintf(digest_strm, "%s", digest); if(rc < 0) { cl_perror("Cannot write to signature file "CIB_FILENAME ".sig"); } CRM_ASSERT(digest_strm != NULL); if(fflush(digest_strm) != 0) { cl_perror("fflush for %s failed:", digest); rc = -1; } if(fsync(fileno(digest_strm)) < 0) { cl_perror("fsync for %s failed:", digest); rc = -1; } fclose(digest_strm); crm_free(local_digest); return rc; } static gboolean validate_on_disk_cib(const char *filename, xmlNode **on_disk_cib) { int s_res = -1; struct stat buf; FILE *cib_file = NULL; gboolean passed = TRUE; xmlNode *root = NULL; CRM_ASSERT(filename != NULL); s_res = stat(filename, &buf); if (s_res == 0) { char *sigfile = NULL; size_t fnsize; cib_file = fopen(filename, "r"); if(cib_file == NULL) { cl_perror("Couldn't open config file %s for reading", filename); return FALSE; } crm_debug_2("Reading cluster configuration from: %s", filename); root = file2xml(cib_file, FALSE); fclose(cib_file); fnsize = strlen(filename) + 5; crm_malloc0(sigfile, fnsize); snprintf(sigfile, fnsize, "%s.sig", filename); if(validate_cib_digest(root, sigfile) == FALSE) { passed = FALSE; } crm_free(sigfile); } if(on_disk_cib != NULL) { *on_disk_cib = root; } else { free_xml(root); } return passed; } static int cib_unlink(const char *file) { int rc = unlink(file); if (rc < 0) { cl_perror("Could not unlink %s - Disabling disk writes and continuing", file); cib_writes_enabled = FALSE; } return rc; } /* * It is the callers responsibility to free the output of this function */ static xmlNode* retrieveCib(const char *filename, const char *sigfile, gboolean archive_invalid) { struct stat buf; FILE *cib_file = NULL; xmlNode *root = NULL; crm_info("Reading cluster configuration from: %s (digest: %s)", filename, sigfile); if(stat(filename, &buf) != 0) { crm_warn("Cluster configuration not found: %s", filename); return NULL; } cib_file = fopen(filename, "r"); if(cib_file == NULL) { cl_perror("Could not open config file %s for reading", filename); } else { root = file2xml(cib_file, FALSE); fclose(cib_file); } if(root == NULL) { crm_err("%s exists but does NOT contain valid XML. ", filename); crm_warn("Continuing but %s will NOT used.", filename); } else if(validate_cib_digest(root, sigfile) == FALSE) { crm_err("Checksum of %s failed! Configuration contents ignored!", filename); crm_err("Usually this is caused by manual changes, " "please refer to http://linux-ha.org/v2/faq/cib_changes_detected"); crm_warn("Continuing but %s will NOT used.", filename); free_xml(root); root = NULL; if(archive_invalid) { int rc = 0; char *suffix = crm_itoa(getpid()); /* Archive the original files so the contents are not lost */ crm_err("Archiving corrupt or unusable configuration to %s.%s", filename, suffix); rc = archive_file(filename, NULL, suffix, TRUE); if(rc < 0) { crm_err("Archival of %s failed - Disabling disk writes and continuing", filename); cib_writes_enabled = FALSE; } rc = archive_file(sigfile, NULL, suffix, TRUE); if(rc < 0) { crm_err("Archival of %s failed - Disabling disk writes and continuing", sigfile); cib_writes_enabled = FALSE; } /* Unlink the original files so they dont get in the way later */ cib_unlink(filename); cib_unlink(sigfile); crm_free(suffix); } } return root; } xmlNode* readCibXmlFile(const char *dir, const char *file, gboolean discard_status) { gboolean dtd_ok = TRUE; char *filename = NULL, *sigfile = NULL; const char *name = NULL; const char *value = NULL; const char *ignore_dtd = NULL; const char *use_valgrind = getenv("HA_VALGRIND_ENABLED"); xmlNode *root = NULL; xmlNode *status = NULL; if(!crm_is_writable(dir, file, HA_CCMUSER, NULL, FALSE)) { cib_status = cib_bad_permissions; return NULL; } filename = crm_concat(dir, file, '/'); sigfile = crm_concat(filename, "sig", '.'); cib_status = cib_ok; root = retrieveCib(filename, sigfile, TRUE); if(root == NULL) { char *tmp = NULL; /* Try the backups */ tmp = filename; filename = crm_concat(tmp, "last", '.'); crm_free(tmp); tmp = sigfile; sigfile = crm_concat(tmp, "last", '.'); crm_free(tmp); crm_warn("Primary configuration corrupt or unusable, trying backup..."); root = retrieveCib(filename, sigfile, FALSE); } if(root == NULL) { root = createEmptyCib(); crm_warn("Continuing with an empty configuration."); } else { crm_xml_add(root, "generated", XML_BOOLEAN_FALSE); } if(cib_writes_enabled && crm_is_true(use_valgrind)) { cib_writes_enabled = FALSE; crm_err("HA_VALGRIND_ENABLED: %s", getenv("HA_VALGRIND_ENABLED")); crm_err("*********************************************************"); crm_err("*** Disabling disk writes to avoid confusing Valgrind ***"); crm_err("*********************************************************"); } status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE); if(discard_status && status != NULL) { /* strip out the status section if there is one */ free_xml_from_parent(root, status); status = NULL; } if(status == NULL) { create_xml_node(root, XML_CIB_TAG_STATUS); } /* Do this before DTD validation happens */ /* fill in some defaults */ name = XML_ATTR_GENERATION_ADMIN; value = crm_element_value(root, name); if(value == NULL) { crm_warn("No value for %s was specified in the configuration.", name); crm_warn("The reccomended course of action is to shutdown," " run crm_verify and fix any errors it reports."); crm_warn("We will default to zero and continue but may get" " confused about which configuration to use if" " multiple nodes are powered up at the same time."); crm_xml_add_int(root, name, 0); } name = XML_ATTR_GENERATION; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add_int(root, name, 0); } name = XML_ATTR_NUMUPDATES; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add_int(root, name, 0); } /* unset these and require the DC/CCM to update as needed */ update_counters(__FILE__, __PRETTY_FUNCTION__, root); xml_remove_prop(root, XML_ATTR_DC_UUID); if(discard_status) { crm_log_xml_info(root, "[on-disk]"); } ignore_dtd = crm_element_value(root, "ignore_dtd"); dtd_ok = validate_with_dtd(root, TRUE, DTD_DIRECTORY"/crm.dtd"); if(dtd_ok == FALSE) { crm_err("CIB does not validate against "DTD_DIRECTORY"/crm.dtd"); - if(ignore_dtd == NULL - && crm_is_true(ignore_dtd) == FALSE) { + if(ignore_dtd != NULL && crm_is_true(ignore_dtd) == FALSE) { cib_status = cib_dtd_validation; } } else if(ignore_dtd == NULL) { crm_notice("Enabling DTD validation on" " the existing (sane) configuration"); crm_xml_add(root, "ignore_dtd", XML_BOOLEAN_FALSE); } if(do_id_check(root, NULL, TRUE, FALSE)) { crm_err("%s does not contain a vaild configuration:" " ID check failed", filename); cib_status = cib_id_check; } - if (verifyCibXml(root) == FALSE) { - crm_err("%s does not contain a vaild configuration:" - " structure test failed", - filename); - cib_status = cib_bad_config; - } - crm_free(filename); crm_free(sigfile); return root; } /* * The caller should never free the return value */ xmlNode* get_the_CIB(void) { return the_cib; } gboolean uninitializeCib(void) { xmlNode *tmp_cib = the_cib; if(tmp_cib == NULL) { crm_debug("The CIB has already been deallocated."); return FALSE; } initialized = FALSE; the_cib = NULL; node_search = NULL; resource_search = NULL; constraint_search = NULL; status_search = NULL; crm_debug("Deallocating the CIB."); free_xml(tmp_cib); crm_debug("The CIB has been deallocated."); return TRUE; } /* * This method will not free the old CIB pointer or the new one. * We rely on the caller to have saved a pointer to the old CIB * and to free the old/bad one depending on what is appropriate. */ gboolean initializeCib(xmlNode *new_cib) { gboolean is_valid = TRUE; xmlNode *tmp_node = NULL; if(new_cib == NULL) { return FALSE; } xml_validate(new_cib); tmp_node = get_object_root(XML_CIB_TAG_NODES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_STATUS, new_cib); if (is_valid && tmp_node == NULL) { create_xml_node(new_cib, XML_CIB_TAG_STATUS); } if(is_valid == FALSE) { crm_warn("CIB Verification failed"); return FALSE; } update_counters(__FILE__, __PRETTY_FUNCTION__, new_cib); the_cib = new_cib; initialized = TRUE; return TRUE; } static void sync_file(const char *file) { FILE *syncme = fopen(file, "a"); if(syncme == NULL) { cl_perror("Cannot open file %s for syncing", file); return; } if(fsync(fileno(syncme)) < 0) { cl_perror("fsync for %s failed:", file); } fclose(syncme); } int archive_file(const char *oldname, const char *newname, const char *ext, gboolean preserve) { /* move 'oldname' to 'newname' by creating a hard link to it * and then removing the original hard link */ int rc = 0; int res = 0; struct stat tmp; int s_res = 0; char *backup_file = NULL; static const char *back_ext = "bak"; /* calculate the backup name if required */ if(newname != NULL) { backup_file = crm_strdup(newname); } else { int max_name_len = 1024; crm_malloc0(backup_file, max_name_len); if (ext == NULL) { ext = back_ext; } snprintf(backup_file, max_name_len - 1, "%s.%s", oldname, ext); } if(backup_file == NULL || strlen(backup_file) == 0) { crm_err("%s backup filename was %s", newname == NULL?"calculated":"supplied", backup_file == NULL?"null":"empty"); rc = -4; } s_res = stat(backup_file, &tmp); /* move the old backup */ if (rc == 0 && s_res >= 0) { if(preserve == FALSE) { res = unlink(backup_file); if (res < 0) { cl_perror("Could not unlink %s", backup_file); rc = -1; } } else { crm_info("Archive file %s exists... backing it up first", backup_file); res = archive_file(backup_file, NULL, NULL, preserve); if (res < 0) { return res; } } } s_res = stat(oldname, &tmp); /* copy */ if (rc == 0 && s_res >= 0) { res = link(oldname, backup_file); if (res < 0) { cl_perror("Could not create backup %s from %s", backup_file, oldname); rc = -2; } else if(preserve) { crm_info("%s archived as %s", oldname, backup_file); } else { crm_debug("%s archived as %s", oldname, backup_file); } sync_file(backup_file); } crm_free(backup_file); return rc; } /* * This method will free the old CIB pointer on success and the new one * on failure. */ int activateCibXml(xmlNode *new_cib, gboolean to_disk) { int error_code = cib_ok; xmlNode *saved_cib = the_cib; - const char *ignore_dtd = NULL; crm_debug("Activating new CIB"); crm_log_xml_debug_4(new_cib, "Attempting to activate CIB"); update_counters(__FILE__, __FUNCTION__, new_cib); CRM_ASSERT(new_cib != saved_cib); if(saved_cib != NULL) { crm_validate_data(saved_cib); } - ignore_dtd = crm_element_value(new_cib, "ignore_dtd"); - if( -#if CRM_DEPRECATED_SINCE_2_0_4 - ignore_dtd != NULL && -#endif - crm_is_true(ignore_dtd) == FALSE - && validate_with_dtd( - new_cib, TRUE, DTD_DIRECTORY"/crm.dtd") == FALSE) { - crm_err("Updated CIB does not validate against "DTD_DIRECTORY"/crm.dtd... ignoring"); - error_code = cib_dtd_validation; - } - - if(error_code == cib_ok && initializeCib(new_cib) == FALSE) { + if(initializeCib(new_cib) == FALSE) { error_code = cib_ACTIVATION; crm_err("Ignoring invalid or NULL CIB"); } if(error_code != cib_ok) { if(saved_cib != NULL) { crm_warn("Reverting to last known CIB"); if (initializeCib(saved_cib) == FALSE) { /* oh we are so dead */ crm_crit("Couldn't re-initialize the old CIB!"); cl_flush_logs(); exit(1); } } else { crm_crit("Could not write out new CIB and no saved" " version to revert to"); } } else if(per_action_cib && cib_writes_enabled && cib_status == cib_ok) { crm_err("Per-action CIB"); write_cib_contents(the_cib); } else if(cib_writes_enabled && cib_status == cib_ok && to_disk) { crm_debug("Triggering CIB write"); G_main_set_trigger(cib_writer); } else { crm_debug("disk: %d, writes: %d", to_disk, cib_writes_enabled); } if(the_cib != saved_cib && the_cib != new_cib) { CRM_DEV_ASSERT(error_code != cib_ok); CRM_DEV_ASSERT(the_cib == NULL); } if(the_cib != new_cib) { free_xml(new_cib); CRM_DEV_ASSERT(error_code != cib_ok); } if(the_cib != saved_cib) { free_xml(saved_cib); } return error_code; } int write_cib_contents(gpointer p) { int rc = 0; gboolean need_archive = FALSE; struct stat buf; char *digest = NULL; int exit_rc = LSB_EXIT_OK; xmlNode *cib_status_root = NULL; /* we can scribble on "the_cib" here and not affect the parent */ const char *epoch = crm_element_value(the_cib, XML_ATTR_GENERATION); const char *updates = crm_element_value(the_cib, XML_ATTR_NUMUPDATES); const char *admin_epoch = crm_element_value( the_cib, XML_ATTR_GENERATION_ADMIN); need_archive = (stat(CIB_FILENAME, &buf) == 0); if (need_archive) { crm_debug("Archiving current version"); /* check the admin didnt modify it underneath us */ if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) { crm_err("%s was manually modified while Heartbeat was active!", CIB_FILENAME); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } /* These calls leak, but we're in a separate process that will exit * when the function does... so it's of no consequence */ CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL); rc = archive_file(CIB_FILENAME, NULL, "last", FALSE); if(rc != 0) { crm_err("Could not make backup of the existing CIB: %d", rc); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } rc = archive_file(CIB_FILENAME".sig", NULL, "last", FALSE); if(rc != 0) { crm_warn("Could not make backup of the existing CIB digest: %d", rc); } CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL); CRM_ASSERT(retrieveCib(CIB_FILENAME".last", CIB_FILENAME".sig.last", FALSE) != NULL); crm_debug("Verified CIB archive"); } /* Given that we discard the status section on startup * there is no point writing it out in the first place * since users just get confused by it * * Although, it does help me once in a while * * So delete the status section before we write it out */ if(p == NULL) { cib_status_root = find_xml_node(the_cib, XML_CIB_TAG_STATUS, TRUE); CRM_DEV_ASSERT(cib_status_root != NULL); if(cib_status_root != NULL) { free_xml_from_parent(the_cib, cib_status_root); } } rc = write_xml_file(the_cib, CIB_FILENAME, FALSE); crm_debug("Wrote CIB to disk"); if(rc <= 0) { crm_err("Changes couldn't be written to disk"); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } digest = calculate_xml_digest(the_cib, FALSE, FALSE); crm_info("Wrote version %s.%s.%s of the CIB to disk (digest: %s)", admin_epoch?admin_epoch:"0", epoch?epoch:"0", updates?updates:"0", digest); rc = write_cib_digest(the_cib, digest); crm_debug("Wrote digest to disk"); if(rc <= 0) { crm_err("Digest couldn't be written to disk"); exit_rc = LSB_EXIT_GENERIC; goto cleanup; } CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL); if(need_archive) { CRM_ASSERT(retrieveCib(CIB_FILENAME".last", CIB_FILENAME".sig.last", FALSE) != NULL); } crm_debug("Wrote and verified CIB"); cleanup: crm_free(digest); if(p == NULL) { /* fork-and-write mode */ exit(exit_rc); } /* stand-alone mode */ return exit_rc; } gboolean set_connected_peers(xmlNode *xml_obj) { guint active = 0; int current = 0; char *peers_s = NULL; const char *current_s = NULL; if(xml_obj == NULL) { return FALSE; } current_s = crm_element_value(xml_obj, XML_ATTR_NUMPEERS); current = crm_parse_int(current_s, "0"); active = crm_active_peers(crm_proc_cib); if(current != active) { crm_malloc0(peers_s, 32); snprintf(peers_s, 32, "%u", active); crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s); crm_debug("We now have %s (%u) active peers", peers_s, active); crm_free(peers_s); return TRUE; } return FALSE; } gboolean update_counters(const char *file, const char *fn, xmlNode *xml_obj) { gboolean did_update = FALSE; did_update = did_update || set_connected_peers(xml_obj); if(did_update) { do_crm_log(LOG_DEBUG, "Counters updated by %s", fn); } return did_update; } void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data) { int *active = user_data; if(safe_str_eq(value, ONLINESTATUS)) { (*active)++; } else if(safe_str_eq(value, JOINSTATUS)) { (*active)++; } } diff --git a/cib/messages.c b/cib/messages.c index 4699cc1b3c..92ae7d5a2e 100644 --- a/cib/messages.c +++ b/cib/messages.c @@ -1,932 +1,608 @@ /* * 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 #define MAX_DIFF_RETRY 5 #ifdef CIBPIPE gboolean cib_is_master = TRUE; #else gboolean cib_is_master = FALSE; #endif gboolean syncd_once = FALSE; xmlNode *the_cib = NULL; extern const char *cib_our_uname; enum cib_errors revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags); int get_revision(xmlNode *xml_obj, int cur_revision); enum cib_errors updateList( xmlNode *local_cib, xmlNode *update_command, xmlNode *failed, int operation, const char *section); gboolean check_generation(xmlNode *newCib, xmlNode *oldCib); gboolean update_results( xmlNode *failed, xmlNode *target, int operation, int return_code); enum cib_errors cib_update_counter( xmlNode *xml_obj, const char *field, gboolean reset); enum cib_errors sync_our_cib(xmlNode *request, gboolean all); extern xmlNode *cib_msg_copy(const xmlNode *msg, gboolean with_data); extern gboolean cib_shutdown_flag; extern void terminate_cib(const char *caller); enum cib_errors cib_process_shutdown_req( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else enum cib_errors result = cib_ok; const char *host = crm_element_value(req, F_ORIG); *answer = NULL; if(crm_element_value(req, F_CIB_ISREPLY) == NULL) { crm_info("Shutdown REQ from %s", host); return cib_ok; } else if(cib_shutdown_flag) { crm_info("Shutdown ACK from %s", host); terminate_cib(__FUNCTION__); return cib_ok; } else { crm_err("Shutdown ACK from %s - not shutting down",host); result = cib_unknown; } return result; #endif } enum cib_errors cib_process_default( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { enum cib_errors result = cib_ok; crm_debug_2("Processing \"%s\" event", op); *answer = NULL; if(op == NULL) { result = cib_operation; crm_err("No operation specified"); } else if(strcasecmp(CRM_OP_NOOP, op) == 0) { ; } else { result = cib_NOTSUPPORTED; crm_err("Action [%s] is not supported by the CIB", op); } return result; } enum cib_errors cib_process_quit( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { enum cib_errors result = cib_ok; crm_debug_2("Processing \"%s\" event", op); crm_warn("The CRMd has asked us to exit... complying"); exit(0); return result; } enum cib_errors cib_process_readwrite( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else enum cib_errors result = cib_ok; crm_debug_2("Processing \"%s\" event", op); if(safe_str_eq(op, CIB_OP_ISMASTER)) { if(cib_is_master == TRUE) { result = cib_ok; } else { result = cib_not_master; } return result; } if(safe_str_eq(op, CIB_OP_MASTER)) { if(cib_is_master == FALSE) { crm_info("We are now in R/W mode"); cib_is_master = TRUE; syncd_once = TRUE; } else { crm_debug("We are still in R/W mode"); } } else if(cib_is_master) { crm_info("We are now in R/O mode"); cib_is_master = FALSE; } return result; #endif } enum cib_errors cib_process_ping( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else enum cib_errors result = cib_ok; crm_debug_2("Processing \"%s\" event", op); *answer = createPingAnswerFragment(CRM_SYSTEM_CIB, "ok"); return result; #endif } -enum cib_errors -cib_process_query( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) -{ - xmlNode *obj_root = NULL; - enum cib_errors result = cib_ok; - - crm_debug_2("Processing \"%s\" event for section=%s", - op, crm_str(section)); - - CRM_CHECK(*answer == NULL, free_xml(*answer)); - *answer = NULL; - - if (safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { - section = NULL; - } - - obj_root = get_object_root(section, existing_cib); - - if(obj_root == NULL) { - result = cib_NOTEXISTS; - - } else { - *answer = obj_root; - } - - if(result == cib_ok && *answer == NULL) { - crm_err("Error creating query response"); - result = cib_output_data; - } - - return result; -} - -enum cib_errors -cib_process_erase( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) -{ - enum cib_errors result = cib_ok; - - crm_debug_2("Processing \"%s\" event", op); - *answer = NULL; - free_xml(*result_cib); - *result_cib = createEmptyCib(); - - copy_in_properties(*result_cib, existing_cib); - cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); - - return result; -} - -enum cib_errors -cib_process_bump( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) -{ - enum cib_errors result = cib_ok; - - crm_debug_2("Processing \"%s\" event for epoch=%s", - op, crm_str(crm_element_value(the_cib, XML_ATTR_GENERATION))); - - *answer = NULL; - cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); - - return result; -} - enum cib_errors cib_process_sync( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else return sync_our_cib(req, TRUE); #endif } enum cib_errors cib_process_sync_one( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else return sync_our_cib(req, FALSE); #endif } -enum cib_errors -cib_update_counter(xmlNode *xml_obj, const char *field, gboolean reset) -{ - char *new_value = NULL; - char *old_value = NULL; - int int_value = -1; - - if(reset == FALSE && crm_element_value(xml_obj, field) != NULL) { - old_value = crm_element_value_copy(xml_obj, field); - } - if(old_value != NULL) { - crm_malloc0(new_value, 128); - int_value = atoi(old_value); - sprintf(new_value, "%d", ++int_value); - } else { - new_value = crm_strdup("1"); - } - - crm_debug_4("%s %d(%s)->%s", - field, int_value, crm_str(old_value), crm_str(new_value)); - crm_xml_add(xml_obj, field, new_value); - - crm_free(new_value); - crm_free(old_value); - - return cib_ok; -} - int sync_in_progress = 0; enum cib_errors -cib_process_diff( +cib_server_process_diff( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - unsigned int log_level = LOG_DEBUG; - const char *value = NULL; - const char *reason = NULL; - gboolean apply_diff = TRUE; - gboolean do_resync = FALSE; - enum cib_errors result = cib_ok; - - int this_updates = 0; - int this_epoch = 0; - int this_admin_epoch = 0; - - 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; - - crm_debug_2("Processing \"%s\" event", op); + int rc = cib_ok; if(cib_is_master) { /* the master is never waiting for a resync */ sync_in_progress = 0; } - cib_diff_version_details( - input, - &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, - &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); - if(sync_in_progress > MAX_DIFF_RETRY) { /* request another full-sync, * the last request may have been lost */ sync_in_progress = 0; } - if(sync_in_progress) { - sync_in_progress++; - crm_warn("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)", - diff_del_admin_epoch,diff_del_epoch,diff_del_updates, - diff_add_admin_epoch,diff_add_epoch,diff_add_updates); - return cib_diff_resync; - } - - value = crm_element_value(existing_cib, XML_ATTR_GENERATION); - this_epoch = atoi(value?value:"0"); - - value = crm_element_value(existing_cib, XML_ATTR_NUMUPDATES); - this_updates = atoi(value?value:"0"); - - value = crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN); - this_admin_epoch = atoi(value?value:"0"); - - if(diff_del_admin_epoch == diff_add_admin_epoch - && diff_del_epoch == diff_add_epoch - && diff_del_updates == diff_add_updates) { - if(diff_add_admin_epoch == -1 && diff_add_epoch == -1 && diff_add_updates == -1) { - diff_add_epoch = this_epoch; - diff_add_updates = this_updates + 1; - diff_add_admin_epoch = this_admin_epoch; - diff_del_epoch = this_epoch; - diff_del_updates = this_updates; - diff_del_admin_epoch = this_admin_epoch; - } else { - apply_diff = FALSE; - log_level = LOG_ERR; - reason = "+ and - versions in the diff did not change"; - log_cib_diff(LOG_ERR, input, __FUNCTION__); - } - } - - if(apply_diff && diff_del_admin_epoch > this_admin_epoch) { - do_resync = TRUE; - apply_diff = FALSE; - log_level = LOG_INFO; - reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is less than required"; - - } else if(apply_diff && diff_del_admin_epoch < this_admin_epoch) { - apply_diff = FALSE; - log_level = LOG_WARNING; - reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is greater than required"; - } - - if(apply_diff && diff_del_epoch > this_epoch) { - do_resync = TRUE; - apply_diff = FALSE; - log_level = LOG_INFO; - reason = "current \""XML_ATTR_GENERATION"\" is less than required"; - - } else if(apply_diff && diff_del_epoch < this_epoch) { - apply_diff = FALSE; - log_level = LOG_WARNING; - reason = "current \""XML_ATTR_GENERATION"\" is greater than required"; - } - if(apply_diff && diff_del_updates > this_updates) { - do_resync = TRUE; - apply_diff = FALSE; - log_level = LOG_INFO; - reason = "current \""XML_ATTR_NUMUPDATES"\" is less than required"; - - } else if(apply_diff && diff_del_updates < this_updates) { - apply_diff = FALSE; - log_level = LOG_WARNING; - reason = "current \""XML_ATTR_NUMUPDATES"\" is greater than required"; + if(sync_in_progress) { + 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( + input, + &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, + &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + + sync_in_progress++; + crm_warn("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)", + diff_del_admin_epoch,diff_del_epoch,diff_del_updates, + diff_add_admin_epoch,diff_add_epoch,diff_add_updates); + return cib_diff_resync; } - if(apply_diff) { - free_xml(*result_cib); - *result_cib = NULL; - if(apply_xml_diff(existing_cib, input, result_cib) == FALSE) { - log_level = LOG_WARNING; - reason = "Failed application of an update diff"; - - if(options & cib_force_diff) { - if(cib_is_master == FALSE) { - log_level = LOG_INFO; - reason = "Failed application of a global update." - " Requesting full refresh."; - do_resync = TRUE; - - } else { - reason = "Failed application of a global update." - " Not requesting full refresh."; - } - } - - } else if((options & cib_force_diff) && !validate_with_dtd( - *result_cib, FALSE, DTD_DIRECTORY"/crm.dtd")) { - - if(cib_is_master == FALSE) { - log_level = LOG_INFO; - reason = "Failed DTD validation of a global update." - " Requesting full refresh."; - do_resync = TRUE; - } else { - log_level = LOG_WARNING; - reason = "Failed DTD validation of a global update." - " Not requesting full refresh."; - } - } - } + + rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); - if(reason != NULL) { - do_crm_log( - log_level, - "Diff %d.%d.%d -> %d.%d.%d not applied to %d.%d.%d: %s", - diff_del_admin_epoch,diff_del_epoch,diff_del_updates, - diff_add_admin_epoch,diff_add_epoch,diff_add_updates, - this_admin_epoch,this_epoch,this_updates, reason); - - result = cib_diff_failed; - - } else if(apply_diff) { - crm_debug_2("Diff %d.%d.%d -> %d.%d.%d was applied", - diff_del_admin_epoch,diff_del_epoch,diff_del_updates, - diff_add_admin_epoch,diff_add_epoch,diff_add_updates); - } -#ifdef CIBPIPE - do_resync = FALSE; -#else - if(do_resync && cib_is_master == FALSE) { + if(rc == cib_diff_resync && cib_is_master == FALSE) { xmlNode *sync_me = create_xml_node(NULL, "sync-me"); free_xml(*result_cib); *result_cib = NULL; - result = cib_diff_resync; - crm_info("Requesting re-sync from peer: %s", reason); + crm_info("Requesting re-sync from peer"); sync_in_progress++; crm_xml_add(sync_me, F_TYPE, "cib"); crm_xml_add(sync_me, F_CIB_OPERATION, CIB_OP_SYNC_ONE); crm_xml_add(sync_me, F_CIB_DELEGATED, cib_our_uname); if(send_cluster_message(NULL, crm_msg_cib, sync_me, FALSE) == FALSE) { - result = cib_not_connected; + rc = cib_not_connected; } free_xml(sync_me); - } else if(do_resync) { - crm_warn("Not resyncing in master mode"); + } else if(rc == cib_diff_resync) { + rc = cib_diff_failed; + if(options & cib_force_diff) { + crm_warn("Not requesting full refresh in slave mode."); + } } -#endif - return result; -} - -enum cib_errors -cib_process_replace( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) -{ - const char *tag = NULL; - gboolean verbose = FALSE; - enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event for section=%s", - op, crm_str(section)); - *answer = NULL; - - if (input == NULL) { - return cib_NOOBJECT; - } - - tag = crm_element_name(input); - - if (options & cib_verbose) { - verbose = TRUE; - } - if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { - section = NULL; - - } else if(safe_str_eq(tag, section)) { - section = NULL; - } - - if(safe_str_eq(tag, XML_TAG_CIB)) { - int updates = 0; - int epoch = 0; - int admin_epoch = 0; - - int replace_updates = 0; - int replace_epoch = 0; - int replace_admin_epoch = 0; - const char *reason = NULL; - - cib_version_details( - existing_cib, &admin_epoch, &epoch, &updates); - cib_version_details(input, &replace_admin_epoch, - &replace_epoch, &replace_updates); - - if(replace_admin_epoch < admin_epoch) { - reason = XML_ATTR_GENERATION_ADMIN; - - } else if(replace_admin_epoch > admin_epoch) { - /* no more checks */ - - } else if(replace_epoch < epoch) { - reason = XML_ATTR_GENERATION; - - } else if(replace_epoch > epoch) { - /* no more checks */ - - } else if(replace_updates < updates) { - reason = XML_ATTR_NUMUPDATES; - } - - if(reason != NULL) { - crm_warn("Replacement %d.%d.%d not applied to %d.%d.%d:" - " current %s is greater than the replacement", - replace_admin_epoch, replace_epoch, - replace_updates, admin_epoch, epoch, updates, - reason); - result = cib_old_data; - } - sync_in_progress = 0; - free_xml(*result_cib); - *result_cib = copy_xml(input); - - } else { - xmlNode *obj_root = NULL; - gboolean ok = TRUE; - obj_root = get_object_root(section, *result_cib); - ok = replace_xml_child(NULL, obj_root, input, FALSE); - if(ok == FALSE) { - crm_debug_2("No matching object to replace"); - result = cib_NOTEXISTS; - } - } - - return result; -} - -enum cib_errors -cib_process_delete( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) -{ - xmlNode *obj_root = NULL; - crm_debug_2("Processing \"%s\" event", op); - - if(input == NULL) { - crm_err("Cannot perform modification with no data"); - return cib_NOOBJECT; - } - - obj_root = get_object_root(section, *result_cib); - - crm_validate_data(input); - crm_validate_data(*result_cib); - - if(replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) { - crm_debug_2("No matching object to delete"); - } - - return cib_ok; -} - - -enum cib_errors -cib_process_modify( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) -{ - xmlNode *obj_root = NULL; - crm_debug_2("Processing \"%s\" event", op); - - if(input == NULL) { - crm_err("Cannot perform modification with no data"); - return cib_NOOBJECT; - } - - obj_root = get_object_root(section, *result_cib); - - crm_validate_data(input); - crm_validate_data(*result_cib); - - if(update_xml_child(obj_root, input) == FALSE) { - return cib_NOTEXISTS; - } - - return cib_ok; + return rc; } enum cib_errors cib_process_change( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { gboolean verbose = FALSE; xmlNode *failed = NULL; enum cib_errors result = cib_ok; int cib_update_op = CIB_UPDATE_OP_NONE; crm_debug_2("Processing \"%s\" event for section=%s", op, crm_str(section)); if (strcasecmp(CIB_OP_CREATE, op) == 0) { cib_update_op = CIB_UPDATE_OP_ADD; } else if (strcasecmp(CIB_OP_UPDATE, op) == 0) { cib_update_op = CIB_UPDATE_OP_MODIFY; } else if (strcasecmp(CIB_OP_DELETE_ALT, op) == 0) { cib_update_op = CIB_UPDATE_OP_DELETE; } else { crm_err("Incorrect request handler invoked for \"%s\" op", crm_str(op)); return cib_operation; } result = cib_ok; if (options & cib_verbose) { verbose = TRUE; } if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { section = NULL; } else if(safe_str_eq(XML_TAG_CIB, section)) { section = NULL; } if(input == NULL) { crm_err("Cannot perform modification with no data"); return cib_NOOBJECT; } crm_validate_data(input); crm_validate_data(*result_cib); failed = create_xml_node(NULL, XML_TAG_FAILED); /* make changes to a temp copy then activate */ if(section == NULL) { int lpc = 0; const char *type = NULL; xmlNode *sub_input = NULL; /* order is no longer important here */ const char *type_list[] = { XML_CIB_TAG_NODES, XML_CIB_TAG_CONSTRAINTS, XML_CIB_TAG_RESOURCES, XML_CIB_TAG_STATUS, XML_CIB_TAG_CRMCONFIG }; copy_in_properties(*result_cib, input); for(lpc = 0; lpc < DIMOF(type_list); lpc++) { type = type_list[lpc]; if(result == cib_ok) { crm_debug_2("Processing section=%s", type); sub_input = get_object_root(type, input); if(sub_input) { result = updateList( *result_cib, sub_input, failed, cib_update_op, type); } } } } else { result = updateList( *result_cib, input, failed, cib_update_op, section); } if (result != cib_ok || xml_has_children(failed)) { if(result == cib_ok) { result = cib_unknown; } crm_log_xml_err(failed, "CIB Update failures"); *answer = failed; } else { free_xml(failed); } return result; } #define cib_update_xml_macro(parent, xml_update) \ if(operation == CIB_UPDATE_OP_DELETE) { \ rc = delete_cib_object(parent, xml_update); \ update_results(failed, xml_update, operation, rc); \ \ } else if(operation == CIB_UPDATE_OP_MODIFY) { \ rc = update_cib_object(parent, xml_update); \ update_results(failed, xml_update, operation, rc); \ \ } else { \ rc = add_cib_object(parent, xml_update); \ update_results(failed, xml_update, operation, rc); \ } \ enum cib_errors updateList(xmlNode *local_cib, xmlNode *xml_section, xmlNode *failed, int operation, const char *section) { int rc = cib_ok; xmlNode *this_section = get_object_root(section, local_cib); if (section == NULL || xml_section == NULL) { crm_err("Section %s not found in message." " CIB update is corrupt, ignoring.", crm_str(section)); return cib_NOSECTION; } if((CIB_UPDATE_OP_NONE > operation) || (operation > CIB_UPDATE_OP_MAX)){ crm_err("Invalid operation on section %s", crm_str(section)); return cib_operation; } if(safe_str_eq(crm_element_name(xml_section), section)) { xml_child_iter(xml_section, a_child, rc = cib_ok; cib_update_xml_macro(this_section, a_child); ); } else { cib_update_xml_macro(this_section, xml_section); } if(rc == cib_ok && xml_has_children(failed)) { rc = cib_unknown; } return rc; } gboolean check_generation(xmlNode *newCib, xmlNode *oldCib) { if(cib_compare_generation(newCib, oldCib) >= 0) { return TRUE; } crm_warn("Generation from update is older than the existing one"); return FALSE; } - + +static const char * +cib_op2string(enum cib_update_op operation) +{ + const char *operation_msg = NULL; + switch(operation) { + case 0: + operation_msg = "none"; + break; + case 1: + operation_msg = "add"; + break; + case 2: + operation_msg = "modify"; + break; + case 3: + operation_msg = "delete"; + break; + case CIB_UPDATE_OP_MAX: + operation_msg = "invalid operation"; + break; + + } + + if(operation_msg == NULL) { + crm_err("Unknown CIB operation %d", operation); + operation_msg = ""; + } + + return operation_msg; +} + + gboolean update_results( xmlNode *failed, xmlNode *target, int operation, int return_code) { gboolean was_error = FALSE; const char *error_msg = NULL; const char *operation_msg = NULL; xmlNode *xml_node = NULL; if (return_code != cib_ok) { operation_msg = cib_op2string(operation); error_msg = cib_error2string(return_code); xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB); was_error = TRUE; add_node_copy(xml_node, target); crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target)); crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE, TYPE(target)); crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation_msg); crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg); crm_warn("Action %s failed: %s (cde=%d)", operation_msg, error_msg, return_code); } return was_error; } enum cib_errors revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags) { int cmp = 0; enum cib_errors rc = cib_ok; char *new_revision = NULL; const char *cur_revision = crm_element_value( cib_copy, XML_ATTR_CIB_REVISION); crm_validate_data(cib_update); crm_validate_data(cib_copy); if(crm_element_value(cib_update, XML_ATTR_CIB_REVISION) == NULL) { return cib_ok; } new_revision = crm_element_value_copy(cib_update,XML_ATTR_CIB_REVISION); cmp = compare_version(new_revision, CIB_FEATURE_SET); if(cmp > 0) { CRM_DEV_ASSERT(cib_is_master == FALSE); CRM_DEV_ASSERT((flags & cib_scope_local) == 0); if(cib_is_master) { crm_err("Update uses an unsupported tag/feature:" " %s vs %s", new_revision,CIB_FEATURE_SET); rc = cib_revision_unsupported; } else if(flags & cib_scope_local) { /* an admin has forced a local change using a tag we * dont understand... ERROR */ crm_err("Local update uses an unsupported tag/feature:" " %s vs %s", new_revision,CIB_FEATURE_SET); rc = cib_revision_unsupported; } } else if(cur_revision == NULL) { crm_info("Updating CIB revision to %s", new_revision); crm_xml_add(cib_copy, XML_ATTR_CIB_REVISION, new_revision); } else { /* make sure we end up with the right value in the end */ crm_xml_add(cib_update, XML_ATTR_CIB_REVISION, cur_revision); } crm_free(new_revision); return rc; } #ifndef CIBPIPE enum cib_errors sync_our_cib(xmlNode *request, gboolean all) { enum cib_errors result = cib_ok; const char *host = crm_element_value(request, F_ORIG); const char *op = crm_element_value(request, F_CIB_OPERATION); xmlNode *replace_request = cib_msg_copy(request, FALSE); CRM_CHECK(the_cib != NULL, ;); CRM_CHECK(replace_request != NULL, ;); crm_info("Syncing CIB to %s", all?"all peers":host); if(all == FALSE && host == NULL) { crm_log_xml(LOG_ERR, "bad sync", request); } /* remove the "all == FALSE" condition * * sync_from was failing, the local client wasnt being notified * because it didnt know it was a reply * setting this does not prevent the other nodes from applying it * if all == TRUE */ if(host != NULL) { crm_xml_add(replace_request, F_CIB_ISREPLY, host); } crm_xml_add(replace_request, F_CIB_OPERATION, CIB_OP_REPLACE); crm_xml_add(replace_request, "original_"F_CIB_OPERATION, op); crm_xml_add(replace_request, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); add_message_xml(replace_request, F_CIB_CALLDATA, the_cib); if(send_cluster_message(all?NULL:host, crm_msg_cib, replace_request, FALSE) == FALSE) { result = cib_not_connected; } free_xml(replace_request); return result; } #endif diff --git a/crm/admin/cibadmin.c b/crm/admin/cibadmin.c index c7bd09d9b1..6b912650b7 100644 --- a/crm/admin/cibadmin.c +++ b/crm/admin/cibadmin.c @@ -1,572 +1,566 @@ /* * 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 #ifdef HAVE_GETOPT_H # include #endif #include /* someone complaining about _ha_msg_mod not being found */ int exit_code = cib_ok; int message_timer_id = -1; int message_timeout_ms = 30; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; const char *host = NULL; void usage(const char *cmd, int exit_status); enum cib_errors do_init(void); int do_work(xmlNode *input, int command_options, xmlNode **output); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); gboolean admin_message_timeout(gpointer data); void cib_connection_destroy(gpointer user_data); void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); int command_options = 0; const char *cib_action = NULL; typedef struct str_list_s { int num_items; char *value; struct str_list_s *next; } str_list_t; char *this_msg_reference = NULL; char *obj_type = NULL; char *status = NULL; char *migrate_from = NULL; char *migrate_res = NULL; char *subtype = NULL; char *reset = NULL; int request_id = 0; int operation_status = 0; cib_t *the_cib = NULL; gboolean force_flag = FALSE; -#define OPTARGS "V?o:QDUCEX:t:Srwlsh:MmBfbdRx:pP5" +#define OPTARGS "V?o:QDUCEX:t:Srwlsh:MmBfbRx:pP5" + int main(int argc, char **argv) { int argerr = 0; int flag; char *admin_input_xml = NULL; char *admin_input_file = NULL; gboolean dangerous_cmd = FALSE; gboolean admin_input_stdin = FALSE; xmlNode *output = NULL; xmlNode *input = NULL; #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { /* Top-level Options */ {CIB_OP_ERASE, 0, 0, 'E'}, {CIB_OP_QUERY, 0, 0, 'Q'}, {CIB_OP_CREATE, 0, 0, 'C'}, {CIB_OP_REPLACE, 0, 0, 'R'}, {CIB_OP_UPDATE, 0, 0, 'U'}, {CIB_OP_MODIFY, 0, 0, 'M'}, {"patch", 0, 0, 'P'}, {CIB_OP_DELETE, 0, 0, 'D'}, - {CIB_OP_DELETE_ALT, 0, 0, 'd'}, {CIB_OP_BUMP, 0, 0, 'B'}, {CIB_OP_SYNC, 0, 0, 'S'}, {CIB_OP_SLAVE, 0, 0, 'r'}, {CIB_OP_MASTER, 0, 0, 'w'}, {CIB_OP_ISMASTER,0, 0, 'm'}, {"md5-sum", 0, 0, '5'}, + {"file-mode", 1, 0, 0}, + {"force-quorum",0, 0, 'f'}, {"force", 0, 0, 'f'}, {"local", 0, 0, 'l'}, {"sync-call", 0, 0, 's'}, {"no-bcast", 0, 0, 'b'}, {"host", 0, 0, 'h'}, {F_CRM_DATA, 1, 0, 'X'}, {"xml-file", 1, 0, 'x'}, {"xml-pipe", 0, 0, 'p'}, {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"reference", 1, 0, 0}, {"timeout", 1, 0, 't'}, /* common options */ {"obj_type", 1, 0, 'o'}, {0, 0, 0, 0} }; #endif crm_log_init("cibadmin", LOG_CRIT, FALSE, FALSE, argc, argv); if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } while (1) { #ifdef HAVE_GETOPT_H flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); #else flag = getopt(argc, argv, OPTARGS); #endif if (flag == -1) break; switch(flag) { #ifdef HAVE_GETOPT_H case 0: - printf("option %s", - long_options[option_index].name); - if (optarg) - printf(" with arg %s", optarg); - printf("\n"); if (safe_str_eq("reference", long_options[option_index].name)) { this_msg_reference = crm_strdup(optarg); + } else if (safe_str_eq("file-mode", long_options[option_index].name)) { + setenv("CIB_file", optarg, 1); + } else { printf("Long option (--%s) is not (yet?) properly supported\n", long_options[option_index].name); ++argerr; } break; #endif case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30; } break; case 'E': cib_action = CIB_OP_ERASE; dangerous_cmd = TRUE; break; case 'Q': cib_action = CIB_OP_QUERY; break; case 'P': cib_action = CIB_OP_APPLY_DIFF; break; case 'S': cib_action = CIB_OP_SYNC; break; case 'U': case 'M': cib_action = CIB_OP_MODIFY; break; case 'R': cib_action = CIB_OP_REPLACE; break; case 'C': cib_action = CIB_OP_CREATE; break; case 'D': cib_action = CIB_OP_DELETE; break; case '5': cib_action = "md5-sum"; break; - case 'd': - cib_action = CIB_OP_DELETE_ALT; - break; case 'm': cib_action = CIB_OP_ISMASTER; command_options |= cib_scope_local; break; case 'B': cib_action = CIB_OP_BUMP; break; case 'r': dangerous_cmd = TRUE; cib_action = CIB_OP_SLAVE; break; case 'w': dangerous_cmd = TRUE; cib_action = CIB_OP_MASTER; command_options |= cib_scope_local; break; case 'V': command_options = command_options | cib_verbose; cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '?': usage(crm_system_name, LSB_EXIT_OK); break; case 'o': crm_debug_2("Option %c => %s", flag, optarg); obj_type = crm_strdup(optarg); break; case 'X': crm_debug_2("Option %c => %s", flag, optarg); admin_input_xml = crm_strdup(optarg); break; case 'x': crm_debug_2("Option %c => %s", flag, optarg); admin_input_file = crm_strdup(optarg); break; case 'p': admin_input_stdin = TRUE; break; case 'h': host = crm_strdup(optarg); break; case 'l': command_options |= cib_scope_local; break; case 'b': dangerous_cmd = TRUE; command_options |= cib_inhibit_bcast; command_options |= cib_scope_local; break; case 's': command_options |= cib_sync_call; break; case 'f': force_flag = TRUE; command_options |= cib_quorum_override; break; default: printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); usage(crm_system_name, LSB_EXIT_EINVAL); } if (optind > argc || cib_action == NULL) { ++argerr; } if (argerr) { usage(crm_system_name, LSB_EXIT_GENERIC); } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); usage(crm_system_name, LSB_EXIT_GENERIC); } if(admin_input_file != NULL) { FILE *xml_strm = fopen(admin_input_file, "r"); input = file2xml(xml_strm, FALSE); if(input == NULL) { fprintf(stderr, "Couldn't parse input file: %s\n", admin_input_file); return 1; } fclose(xml_strm); } else if(admin_input_xml != NULL) { input = string2xml(admin_input_xml); if(input == NULL) { fprintf(stderr, "Couldn't parse input string: %s\n", admin_input_xml); return 1; } } else if(admin_input_stdin) { input = stdin2xml(); if(input == NULL) { fprintf(stderr, "Couldn't parse input from STDIN.\n"); return 1; } } if(input != NULL) { crm_log_xml_debug(input, "[admin input]"); } if(safe_str_eq(cib_action, "md5-sum")) { char *digest = NULL; if(input == NULL) { fprintf(stderr, "Please supply XML to process with -X, -x or -p\n"); exit(1); } digest = calculate_xml_digest(input, FALSE, FALSE); fprintf(stderr, "Digest: "); fprintf(stdout, "%s\n", crm_str(digest)); crm_free(digest); exit(0); } exit_code = do_init(); if(exit_code != cib_ok) { crm_err("Init failed, could not perform requested operations"); fprintf(stderr, "Init failed, could not perform requested operations\n"); return -exit_code; } exit_code = do_work(input, command_options, &output); if (exit_code > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ request_id = exit_code; add_cib_op_callback_timeout( request_id, message_timeout_ms, FALSE, NULL, cibadmin_op_callback); mainloop = g_main_new(FALSE); crm_debug_3("%s waiting for reply from the local CIB", crm_system_name); crm_info("Starting mainloop"); g_main_run(mainloop); } else if(exit_code < 0) { crm_err("Call failed: %s", cib_error2string(exit_code)); fprintf(stderr, "Call failed: %s\n", cib_error2string(exit_code)); operation_status = exit_code; } if(output != NULL) { char *buffer = dump_xml_formatted(output); fprintf(stdout, "%s", crm_str(buffer)); crm_free(buffer); } + + the_cib->cmds->signoff(the_cib); crm_debug_3("%s exiting normally", crm_system_name); return -exit_code; } int do_work(xmlNode *input, int call_options, xmlNode **output) { /* construct the request */ the_cib->call_timeout = message_timeout_ms; if (strcasecmp(CIB_OP_SYNC, cib_action) == 0) { crm_debug_4("Performing %s op...", cib_action); return the_cib->cmds->sync_from( the_cib, host, obj_type, call_options); } else if (strcasecmp(CIB_OP_SLAVE, cib_action) == 0 && (call_options ^ cib_scope_local) ) { crm_debug_4("Performing %s op on all nodes...", cib_action); return the_cib->cmds->set_slave_all(the_cib, call_options); } else if (strcasecmp(CIB_OP_MASTER, cib_action) == 0) { crm_debug_4("Performing %s op on all nodes...", cib_action); return the_cib->cmds->set_master(the_cib, call_options); } else if(cib_action != NULL) { crm_debug_4("Passing \"%s\" to variant_op...", cib_action); if(strcasecmp(CIB_OP_APPLY_DIFF, cib_action) != 0 && input != NULL && do_id_check(input, NULL, TRUE, FALSE)) { crm_err("ID Check failed."); return cib_id_check; } return the_cib->cmds->variant_op( the_cib, cib_action, host, obj_type, input, output, call_options); } else { crm_err("You must specify an operation"); } return cib_operation; } enum cib_errors do_init(void) { enum cib_errors rc = cib_ok; - + the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); if(rc != cib_ok) { crm_err("Signon to CIB failed: %s", cib_error2string(rc)); fprintf(stderr, "Signon to CIB failed: %s\n", cib_error2string(rc)); } return rc; } void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status != 0 ? stderr : stdout; fprintf(stream, "usage: %s [%s] command\n" "\twhere necessary, XML data will be obtained using -X," " -x, or -p options\n", cmd, OPTARGS); fprintf(stream, "Options\n"); fprintf(stream, "\t--%s (-%c) \tobject type being operated on\n", "obj_type", 'o'); fprintf(stream, "\t\tValid values are: nodes, resources, constraints, crm_config, status\n"); fprintf(stream, "\t--%s (-%c)\tturn on debug info." " additional instance increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help", '?'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c)\tErase the contents of the whole CIB\n", CIB_OP_ERASE, 'E'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_QUERY, 'Q'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_CREATE, 'C'); fprintf(stream, "\t--%s (-%c)\tCalculate an XML file's digest." " Requires either -X, -x or -p\n", "md5-sum", '5'); fprintf(stream, "\t--%s (-%c)\tRecursivly replace an object in the CIB\n", CIB_OP_REPLACE,'R'); fprintf(stream, "\t--%s (-%c)\tRecursivly update an object in the CIB\n", CIB_OP_UPDATE, 'U'); fprintf(stream, "\t--%s (-%c)\tFind the object somewhere in the CIB's XML tree and update is as --"CIB_OP_UPDATE" would\n", CIB_OP_MODIFY, 'M'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE, 'D'); fprintf(stream, "\t\t\tDelete the first object matching the supplied criteria\n"); fprintf(stream, "\t\t\tEg. \n"); fprintf(stream, "\t\t\tThe tagname and all attributes must match in order for the element to be deleted\n"); - fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE_ALT, 'd'); - fprintf(stream, "\t\t\tDelete the object at specified fully qualified location\n"); - fprintf(stream, "\t\t\tEg. ...\n"); - fprintf(stream, "\t\t\tRequires -o\n"); - fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_BUMP, 'B'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_ISMASTER,'m'); fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_SYNC, 'S'); fprintf(stream, "\nXML data\n"); fprintf(stream, "\t--%s (-%c) \t\tRetrieve XML from the supplied string\n", F_CRM_DATA, 'X'); fprintf(stream, "\t--%s (-%c) \tRetrieve XML from the named file\n", "xml-file", 'x'); fprintf(stream, "\t--%s (-%c)\t\t\tRetrieve XML from STDIN\n", "xml-pipe", 'p'); fprintf(stream, "\nAdvanced Options\n"); fprintf(stream, "\t--%s (-%c)\tsend command to specified host." " Applies to %s and %s commands only\n", "host", 'h', CIB_OP_QUERY, CIB_OP_SYNC); fprintf(stream, "\t--%s (-%c)\tcommand takes effect locally" " on the specified host\n", "local", 'l'); fprintf(stream, "\t--%s (-%c)\tcommand will not be broadcast even if" " it altered the CIB\n", "no-bcast", 'b'); fprintf(stream, "\t--%s (-%c)\twait for call to complete before" " returning\n", "sync-call", 's'); fflush(stream); exit(exit_status); } void cib_connection_destroy(gpointer user_data) { crm_err("Connection to the CIB terminated... exiting"); g_main_quit(mainloop); return; } void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { char *admin_input_xml = NULL; exit_code = rc; if(output != NULL) { admin_input_xml = dump_xml_formatted(output); } if(safe_str_eq(cib_action, CIB_OP_ISMASTER) && rc != cib_ok) { crm_info("CIB on %s is _not_ the master instance", host?host:"localhost"); fprintf(stderr, "CIB on %s is _not_ the master instance\n", host?host:"localhost"); } else if(safe_str_eq(cib_action, CIB_OP_ISMASTER)) { crm_info("CIB on %s _is_ the master instance", host?host:"localhost"); fprintf(stderr, "CIB on %s _is_ the master instance\n", host?host:"localhost"); } else if(rc != 0) { crm_warn("Call %s failed (%d): %s", cib_action, rc, cib_error2string(rc)); fprintf(stderr, "Call %s failed (%d): %s\n", cib_action, rc, cib_error2string(rc)); fprintf(stdout, "%s\n", crm_str(admin_input_xml)); } else if(safe_str_eq(cib_action, CIB_OP_QUERY) && output==NULL) { crm_err("Output expected in query response"); crm_log_xml(LOG_ERR, "no output", msg); } else if(output == NULL) { crm_info("Call passed"); } else { crm_info("Call passed"); fprintf(stdout, "%s\n", crm_str(admin_input_xml)); } crm_free(admin_input_xml); if(call_id == request_id) { g_main_quit(mainloop); } else { crm_info("Message was not the response we were looking for (%d vs. %d", call_id, request_id); } } diff --git a/crm/ais/plugin.c b/crm/ais/plugin.c index 3e7e6810f5..16aaaafa6c 100644 --- a/crm/ais/plugin.c +++ b/crm/ais/plugin.c @@ -1,1162 +1,1162 @@ /* * 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 "plugin.h" #include "utils.h" #include #define OPENAIS_EXTERNAL_SERVICE insane_ais_header_hack_in__totem_h #include #include #include #include #include #include #include #include #include #include #include #include int plugin_log_level = LOG_DEBUG; char *local_uname = NULL; int local_uname_len = 0; unsigned int local_nodeid = 0; char *ipc_channel_name = NULL; unsigned long long membership_seq = 0; pthread_t crm_wait_thread; gboolean wait_active = TRUE; GHashTable *membership_list = NULL; #define MAX_RESPAWN 100 #define crm_flag_none 0x00000000 #define crm_flag_members 0x00000001 struct crm_identify_msg_s { mar_req_header_t header __attribute__((aligned(8))); uint32_t id; uint32_t pid; int32_t votes; uint32_t processes; char uname[256]; char version[256]; } __attribute__((packed)); static crm_child_t crm_children[] = { { 0, crm_proc_none, crm_flag_none, 0, FALSE, "none", 0, NULL, NULL }, { 0, crm_proc_ais, crm_flag_none, 0, FALSE, "ais", 0, NULL, NULL }, { 0, crm_proc_lrmd, crm_flag_none, 0, TRUE, "lrmd", 0, HA_LIBHBDIR"/lrmd", NULL }, { 0, crm_proc_cib, crm_flag_members, 0, TRUE, "cib", HA_CCMUID, HA_LIBHBDIR"/cib", NULL }, { 0, crm_proc_crmd, crm_flag_members, 0, TRUE, "crmd", HA_CCMUID, HA_LIBHBDIR"/crmd", NULL }, { 0, crm_proc_attrd,crm_flag_none, 0, TRUE, "attrd", HA_CCMUID, HA_LIBHBDIR"/attrd", NULL }, }; void send_cluster_id(void); int send_cluster_msg_raw(AIS_Message *ais_msg); char *ais_generate_membership_data(void); extern totempg_groups_handle openais_group_handle; void global_confchg_fn ( enum totem_configuration_type configuration_type, unsigned int *member_list, int member_list_entries, unsigned int *left_list, int left_list_entries, unsigned int *joined_list, int joined_list_entries, struct memb_ring_id *ring_id); int crm_exec_exit_fn (struct objdb_iface_ver0 *objdb); int crm_exec_init_fn (struct objdb_iface_ver0 *objdb); int crm_config_init_fn(struct objdb_iface_ver0 *objdb); int ais_ipc_client_connect_callback (void *conn); int ais_ipc_client_exit_callback (void *conn); void ais_cluster_message_swab(void *msg); void ais_cluster_message_callback(void *message, unsigned int nodeid); void ais_ipc_message_callback(void *conn, void *msg); void ais_quorum_query(void *conn, void *msg); void ais_node_list_query(void *conn, void *msg); void ais_manage_notification(void *conn, void *msg); void ais_cluster_id_swab(void *msg); void ais_cluster_id_callback(void *message, unsigned int nodeid); static struct openais_lib_handler crm_lib_service[] = { { /* 0 */ .lib_handler_fn = ais_ipc_message_callback, .response_size = sizeof (mar_res_header_t), .response_id = CRM_MESSAGE_IPC_ACK, .flow_control = OPENAIS_FLOW_CONTROL_NOT_REQUIRED }, { /* 1 */ .lib_handler_fn = ais_node_list_query, .response_size = sizeof (mar_res_header_t), .response_id = CRM_MESSAGE_IPC_ACK, .flow_control = OPENAIS_FLOW_CONTROL_NOT_REQUIRED }, { /* 2 */ .lib_handler_fn = ais_manage_notification, .response_size = sizeof (mar_res_header_t), .response_id = CRM_MESSAGE_IPC_ACK, .flow_control = OPENAIS_FLOW_CONTROL_NOT_REQUIRED }, }; static struct openais_exec_handler crm_exec_service[] = { { /* 0 */ .exec_handler_fn = ais_cluster_message_callback, .exec_endian_convert_fn = ais_cluster_message_swab }, { /* 1 */ .exec_handler_fn = ais_cluster_id_callback, .exec_endian_convert_fn = ais_cluster_id_swab } }; static void crm_exec_dump_fn(void) { ENTER(""); ais_err("Called after SIG_USR2"); LEAVE(""); } /* * Exports the interface for the service */ struct openais_service_handler crm_service_handler = { .name = "LHA Cluster Manager", .id = CRM_SERVICE, .private_data_size = 0, .flow_control = OPENAIS_FLOW_CONTROL_NOT_REQUIRED, .lib_init_fn = ais_ipc_client_connect_callback, .lib_exit_fn = ais_ipc_client_exit_callback, .lib_service = crm_lib_service, .lib_service_count = sizeof (crm_lib_service) / sizeof (struct openais_lib_handler), .exec_init_fn = crm_exec_init_fn, .exec_exit_fn = crm_exec_exit_fn, .exec_service = crm_exec_service, .exec_service_count = sizeof (crm_exec_service) / sizeof (struct openais_exec_handler), .config_init_fn = crm_config_init_fn, .confchg_fn = global_confchg_fn, .exec_dump_fn = crm_exec_dump_fn, /* void (*sync_init) (void); */ /* int (*sync_process) (void); */ /* void (*sync_activate) (void); */ /* void (*sync_abort) (void); */ }; /* * Dynamic Loader definition */ struct openais_service_handler *crm_get_handler_ver0 (void); static struct openais_service_handler_iface_ver0 crm_service_handler_iface = { .openais_get_service_handler_ver0 = crm_get_handler_ver0 }; static struct lcr_iface openais_crm_ver0[1] = { { .name = "lha_crm", .version = 0, .versions_replace = 0, .versions_replace_count = 0, .dependencies = 0, .dependency_count = 0, .constructor = NULL, .destructor = NULL, .interfaces = NULL } }; static struct lcr_comp crm_comp_ver0 = { .iface_count = 1, .ifaces = openais_crm_ver0 }; struct openais_service_handler *crm_get_handler_ver0 (void) { return (&crm_service_handler); } __attribute__ ((constructor)) static void register_this_component (void) { lcr_interfaces_set (&openais_crm_ver0[0], &crm_service_handler_iface); lcr_component_register (&crm_comp_ver0); } /* IMPL */ int crm_config_init_fn(struct objdb_iface_ver0 *objdb) { int rc = 0; struct utsname us; char *value = NULL; unsigned int object_service_handle; membership_list = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, destroy_ais_node); setenv("HA_COMPRESSION", "bz2", 1); setenv("HA_cluster_type", "openais", 1); objdb->object_find_reset (OBJECT_PARENT_HANDLE); if (objdb->object_find ( OBJECT_PARENT_HANDLE, "pacemaker", strlen ("pacemaker"), &object_service_handle) != 0) { object_service_handle = 0; ais_info("No configuration suplpied for pacemaker"); } objdb_get_string( objdb, object_service_handle, "logfacility", &value, "daemon"); setenv("HA_logfacility", value, 1); objdb_get_string(objdb, object_service_handle, "initdead", &value, "20"); setenv("HA_initdead", value, 1); objdb_get_string(objdb, object_service_handle, "debug", &value, "1"); setenv("HA_debug", value, 1); rc = atoi(value); plugin_log_level = LOG_INFO+rc; if(system("echo 1 > /proc/sys/kernel/core_uses_pid") != 0) { ais_perror("Could not enable /proc/sys/kernel/core_uses_pid"); } ais_info("CRM: Initialized"); log_printf(LOG_INFO, "Logging: Initialized %s\n", __PRETTY_FUNCTION__); rc = uname(&us); AIS_ASSERT(rc == 0); local_uname = ais_strdup(us.nodename); local_uname_len = strlen(local_uname); ais_info("Local hostname: %s", local_uname); local_nodeid = totempg_my_nodeid_get(); update_member(local_nodeid, 0, 1, 0, local_uname, CRM_NODE_LOST); LEAVE(""); return 0; } static void *crm_wait_dispatch (void *arg) { struct timespec waitsleep = { .tv_sec = 0, .tv_nsec = 100000 /* 100 msec */ }; while(wait_active) { int lpc = 0; for (; lpc < SIZEOF(crm_children); lpc++) { if(crm_children[lpc].pid > 0) { int status; pid_t pid = wait4( crm_children[lpc].pid, &status, WNOHANG, NULL); if(pid == 0) { continue; } else if(pid < 0) { ais_perror("crm_wait_dispatch: Call to wait4(%s) failed", crm_children[lpc].name); continue; } /* cleanup */ crm_children[lpc].pid = 0; crm_children[lpc].conn = NULL; crm_children[lpc].async_conn = NULL; if(WIFSIGNALED(status)) { int sig = WTERMSIG(status); ais_warn("Child process %s terminated with signal %d" " (pid=%d, core=%s)", crm_children[lpc].name, sig, pid, WCOREDUMP(status)?"true":"false"); } else if (WIFEXITED(status)) { int rc = WEXITSTATUS(status); ais_notice("Child process %s exited (pid=%d, rc=%d)", crm_children[lpc].name, pid, rc); if(rc == 100) { ais_notice("Child process %s no longer wishes" " to be respawned", crm_children[lpc].name); crm_children[lpc].respawn = FALSE; } } crm_children[lpc].respawn_count += 1; if(crm_children[lpc].respawn_count > MAX_RESPAWN) { ais_notice("Child respawn count exceeded by %s", crm_children[lpc].name); crm_children[lpc].respawn = FALSE; } if(crm_children[lpc].respawn) { ais_info("Respawning failed child process: %s", crm_children[lpc].name); spawn_child(&(crm_children[lpc])); } else { send_cluster_id(); } } } sched_yield (); nanosleep (&waitsleep, 0); } return 0; } int crm_exec_init_fn (struct objdb_iface_ver0 *objdb) { int lpc = 0; ENTER(""); pthread_create (&crm_wait_thread, NULL, crm_wait_dispatch, NULL); for (; lpc < SIZEOF(crm_children); lpc++) { spawn_child(&(crm_children[lpc])); } ais_info("CRM: Initialized"); LEAVE(""); return 0; } /* static void ais_print_node(const char *prefix, struct totem_ip_address *host) { int len = 0; char *buffer = NULL; ais_malloc0(buffer, INET6_ADDRSTRLEN+1); inet_ntop(host->family, host->addr, buffer, INET6_ADDRSTRLEN); len = strlen(buffer); ais_info("%s: %.*s", prefix, len, buffer); ais_free(buffer); } */ #if 0 /* copied here for reference from exec/totempg.c */ char *totempg_ifaces_print (unsigned int nodeid) { static char iface_string[256 * INTERFACE_MAX]; char one_iface[64]; struct totem_ip_address interfaces[INTERFACE_MAX]; char **status; unsigned int iface_count; unsigned int i; int res; iface_string[0] = '\0'; res = totempg_ifaces_get (nodeid, interfaces, &status, &iface_count); if (res == -1) { return ("no interface found for nodeid"); } for (i = 0; i < iface_count; i++) { sprintf (one_iface, "r(%d) ip(%s) ", i, totemip_print (&interfaces[i])); strcat (iface_string, one_iface); } return (iface_string); } #endif static void ais_mark_unseen_peer_dead( gpointer key, gpointer value, gpointer user_data) { int *changed = user_data; crm_node_t *node = value; if(node->last_seen != membership_seq && ais_str_eq(CRM_NODE_LOST, node->state) == FALSE) { ais_info("Node %s was not seen in the previous transition", node->uname); *changed += update_member(node->id, membership_seq, node->votes, node->processes, node->uname, CRM_NODE_LOST); ais_info("Node %s marked dead", node->uname); } } void global_confchg_fn ( enum totem_configuration_type configuration_type, unsigned int *member_list, int member_list_entries, unsigned int *left_list, int left_list_entries, unsigned int *joined_list, int joined_list_entries, struct memb_ring_id *ring_id) { int lpc = 0; int changed = 0; int do_update = 0; ENTER(""); AIS_ASSERT(ring_id != NULL); switch(configuration_type) { case TOTEM_CONFIGURATION_REGULAR: do_update = 1; break; case TOTEM_CONFIGURATION_TRANSITIONAL: break; } membership_seq = ring_id->seq; ais_notice("%s membership event on ring %lld: memb=%d, new=%d, lost=%d", do_update?"Stable":"Transitional", ring_id->seq, member_list_entries, joined_list_entries, left_list_entries); if(do_update == 0) { for(lpc = 0; lpc < joined_list_entries; lpc++) { const char *prefix = "new: "; uint32_t nodeid = joined_list[lpc]; ais_info("%s %s %u", prefix, member_uname(nodeid), nodeid); } for(lpc = 0; lpc < member_list_entries; lpc++) { const char *prefix = "memb:"; uint32_t nodeid = member_list[lpc]; ais_info("%s %s %u", prefix, member_uname(nodeid), nodeid); } for(lpc = 0; lpc < left_list_entries; lpc++) { const char *prefix = "lost:"; uint32_t nodeid = left_list[lpc]; ais_info("%s %s %u", prefix, member_uname(nodeid), nodeid); } return; } for(lpc = 0; lpc < joined_list_entries; lpc++) { const char *prefix = "NEW: "; uint32_t nodeid = joined_list[lpc]; crm_node_t *node = NULL; changed += update_member( nodeid, membership_seq, -1, 0, NULL, CRM_NODE_MEMBER); ais_info("%s %s %u", prefix, member_uname(nodeid), nodeid); node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(nodeid)); if(node->addr == NULL) { const char *addr = totempg_ifaces_print(nodeid); node->addr = ais_strdup(addr); ais_debug("Node %u has address %s", nodeid, node->addr); } } for(lpc = 0; lpc < member_list_entries; lpc++) { const char *prefix = "MEMB:"; uint32_t nodeid = member_list[lpc]; changed += update_member( nodeid, membership_seq, -1, 0, NULL, CRM_NODE_MEMBER); ais_info("%s %s %u", prefix, member_uname(nodeid), nodeid); } for(lpc = 0; lpc < left_list_entries; lpc++) { const char *prefix = "LOST:"; uint32_t nodeid = left_list[lpc]; changed += update_member( nodeid, membership_seq, -1, 0, NULL, CRM_NODE_LOST); ais_info("%s %s %u", prefix, member_uname(nodeid), nodeid); } if(do_update) { ais_debug_2("Reaping unseen nodes..."); g_hash_table_foreach( membership_list, ais_mark_unseen_peer_dead, &changed); } if(changed) { ais_debug("%d nodes changed", changed); send_member_notification(); } send_cluster_id(); LEAVE(""); } int ais_ipc_client_exit_callback (void *conn) { int lpc = 0; const char *client = NULL; ENTER("Client=%p", conn); for (; lpc < SIZEOF(crm_children); lpc++) { if(crm_children[lpc].conn == conn) { crm_children[lpc].conn = NULL; crm_children[lpc].async_conn = NULL; client = crm_children[lpc].name; break; } } ais_info("Client %p/%s left", conn, client?client:"unknown-transient"); LEAVE(""); return (0); } int ais_ipc_client_connect_callback (void *conn) { void *async_conn = openais_conn_partner_get(conn); ENTER("Client=%p", conn); ais_debug("Client %p/%p joined", conn, async_conn); if(async_conn) { send_client_msg(async_conn, crm_class_cluster, crm_msg_none, "identify"); } else { ais_err("No async connection"); } LEAVE(""); return (0); } /* * Executive message handlers */ void ais_cluster_message_swab(void *msg) { AIS_Message *ais_msg = msg; ENTER(""); ais_debug_3("Performing endian conversion..."); ais_msg->id = swab32 (ais_msg->id); + ais_msg->size = swab32 (ais_msg->size); ais_msg->is_compressed = swab32 (ais_msg->is_compressed); ais_msg->compressed_size = swab32 (ais_msg->compressed_size); - ais_msg->size = swab32 (ais_msg->size); ais_msg->host.id = swab32 (ais_msg->host.id); ais_msg->host.pid = swab32 (ais_msg->host.pid); ais_msg->host.type = swab32 (ais_msg->host.type); ais_msg->host.size = swab32 (ais_msg->host.size); ais_msg->host.local = swab32 (ais_msg->host.local); ais_msg->sender.id = swab32 (ais_msg->sender.id); ais_msg->sender.pid = swab32 (ais_msg->sender.pid); ais_msg->sender.type = swab32 (ais_msg->sender.type); ais_msg->sender.size = swab32 (ais_msg->sender.size); ais_msg->sender.local = swab32 (ais_msg->sender.local); LEAVE(""); } void ais_cluster_message_callback ( void *message, unsigned int nodeid) { AIS_Message *ais_msg = message; ENTER("Node=%u (%s)", nodeid, nodeid==local_nodeid?"local":"remote"); ais_debug_2("Message from node %u (%s)", nodeid, nodeid==local_nodeid?"local":"remote"); /* Shouldn't be required... update_member( ais_msg->sender.id, membership_seq, -1, 0, ais_msg->sender.uname, NULL); */ if(ais_msg->host.size == 0 || ais_str_eq(ais_msg->host.uname, local_uname)) { route_ais_message(ais_msg, FALSE); } else { ais_debug_3("Discarding Msg[%d] (dest=%s:%s, from=%s:%s)", ais_msg->id, ais_dest(&(ais_msg->host)), msg_type2text(ais_msg->host.type), ais_dest(&(ais_msg->sender)), msg_type2text(ais_msg->sender.type)); } LEAVE(""); } void ais_cluster_id_swab(void *msg) { struct crm_identify_msg_s *ais_msg = msg; ENTER(""); ais_debug_3("Performing endian conversion..."); ais_msg->id = swab32 (ais_msg->id); ais_msg->pid = swab32 (ais_msg->pid); ais_msg->votes = swab32 (ais_msg->votes); ais_msg->processes = swab32 (ais_msg->processes); LEAVE(""); } void ais_cluster_id_callback (void *message, unsigned int nodeid) { int changed = 0; struct crm_identify_msg_s *msg = message; if(nodeid != msg->id) { ais_err("Invalid message: Node %u claimed to be node %d", nodeid, msg->id); return; } ais_debug("Node update: %s (%s)", msg->uname, msg->version); changed = update_member( nodeid, membership_seq, msg->votes, msg->processes, msg->uname, NULL); if(changed) { send_member_notification(); } } struct res_overlay { mar_res_header_t header __attribute((aligned(8))); char buf[4096]; }; struct res_overlay *res_overlay = NULL; static void send_ipc_ack(void *conn, int class) { if(res_overlay == NULL) { ais_malloc0(res_overlay, sizeof(struct res_overlay)); } res_overlay->header.size = crm_lib_service[class].response_size; res_overlay->header.id = crm_lib_service[class].response_id; res_overlay->header.error = 0; openais_conn_send_response (conn, res_overlay, res_overlay->header.size); } /* local callbacks */ void ais_ipc_message_callback(void *conn, void *msg) { AIS_Message *ais_msg = msg; int type = ais_msg->sender.type; void *async_conn = openais_conn_partner_get(conn); ENTER("Client=%p", conn); ais_debug_2("Message from client %p", conn); if(type > 0 && ais_msg->host.local && crm_children[type].conn == NULL && ais_msg->host.type == crm_msg_ais && ais_msg->sender.pid == crm_children[type].pid && type < SIZEOF(crm_children)) { ais_info("Recorded connection %p for %s/%d", conn, crm_children[type].name, crm_children[type].pid); crm_children[type].conn = conn; crm_children[type].async_conn = async_conn; /* Make sure they have the latest membership */ if(crm_children[type].flags & crm_flag_members) { char *update = ais_generate_membership_data(); ais_info("Sending membership update %llu to %s", membership_seq, crm_children[type].name); send_client_msg(async_conn, crm_class_members, crm_msg_none,update); } } ais_msg->sender.id = local_nodeid; ais_msg->sender.size = local_uname_len; memset(ais_msg->sender.uname, 0, MAX_NAME); memcpy(ais_msg->sender.uname, local_uname, ais_msg->sender.size); route_ais_message(msg, TRUE); send_ipc_ack(conn, 0); LEAVE(""); } int crm_exec_exit_fn (struct objdb_iface_ver0 *objdb) { int lpc = 0; struct timespec waitsleep = { .tv_sec = 1, .tv_nsec = 0 }; ENTER(""); ais_notice("Begining shutdown"); in_shutdown = TRUE; wait_active = FALSE; /* stop the wait loop */ for (lpc = SIZEOF(crm_children) - 1; lpc > 0; lpc--) { crm_children[lpc].respawn = FALSE; stop_child(&(crm_children[lpc]), SIGTERM); while(crm_children[lpc].command && crm_children[lpc].pid) { int status; pid_t pid = 0; pid = wait4( crm_children[lpc].pid, &status, WNOHANG, NULL); if(pid == 0) { sched_yield (); nanosleep (&waitsleep, 0); continue; } else if(pid < 0) { ais_perror("crm_wait_dispatch: Call to wait4(%s) failed", crm_children[lpc].name); } ais_notice("%s (pid=%d) confirmed dead", crm_children[lpc].name, crm_children[lpc].pid); /* cleanup */ crm_children[lpc].pid = 0; crm_children[lpc].conn = NULL; crm_children[lpc].async_conn = NULL; break; } } send_cluster_id(); ais_notice("Shutdown complete"); LEAVE(""); logsys_flush (); return 0; } struct member_loop_data { char *string; }; void member_loop_fn(gpointer key, gpointer value, gpointer user_data) { crm_node_t *node = value; struct member_loop_data *data = user_data; ais_debug_2("Dumping node %u", node->id); data->string = append_member(data->string, node); } char *ais_generate_membership_data(void) { int size = 0; struct member_loop_data data; size = 14 + 32; /* + int */ ais_malloc0(data.string, size); sprintf(data.string, "", membership_seq); g_hash_table_foreach(membership_list, member_loop_fn, &data); size = strlen(data.string); data.string = realloc(data.string, size + 9) ;/* 9 = + nul */ sprintf(data.string + size, ""); return data.string; } void ais_node_list_query(void *conn, void *msg) { char *data = ais_generate_membership_data(); void *async_conn = openais_conn_partner_get(conn); ais_debug_4("members: %s", data); if(async_conn) { send_client_msg(async_conn, crm_class_members, crm_msg_none, data); } ais_free(data); send_ipc_ack(conn, 1); } void ais_manage_notification(void *conn, void *msg) { int lpc = 0; int enable = 0; AIS_Message *ais_msg = msg; char *data = get_ais_data(ais_msg); if(ais_str_eq("true", data)) { enable = 1; } for (; lpc < SIZEOF(crm_children); lpc++) { if(crm_children[lpc].conn == conn) { ais_info("%s node notifications for %s", enable?"Enabling":"Disabling", crm_children[lpc].name); if(enable) { crm_children[lpc].flags |= crm_flag_members; } else { crm_children[lpc].flags |= crm_flag_members; crm_children[lpc].flags ^= crm_flag_members; } break; } } send_ipc_ack(conn, 2); } void send_member_notification(void) { int lpc = 0; char *update = ais_generate_membership_data(); for (; lpc < SIZEOF(crm_children); lpc++) { if(crm_children[lpc].flags & crm_flag_members) { if(crm_children[lpc].async_conn == NULL) { continue; } ais_info("Sending membership update %llu to %s", membership_seq, crm_children[lpc].name); send_client_msg(crm_children[lpc].async_conn, crm_class_members, crm_msg_none, update); } } ais_free(update); } static gboolean check_message_sanity(AIS_Message *msg, char *data) { gboolean sane = TRUE; gboolean repaired = FALSE; int dest = msg->host.type; int tmp_size = msg->header.size - sizeof(AIS_Message); if(sane && msg->header.size == 0) { ais_warn("Message with no size"); sane = FALSE; } if(sane && msg->header.error != 0) { ais_warn("Message header contains an error: %d", msg->header.error); sane = FALSE; } if(sane && ais_data_len(msg) != tmp_size) { int cur_size = ais_data_len(msg); repaired = TRUE; if(msg->is_compressed) { msg->compressed_size = tmp_size; } else { msg->size = tmp_size; } ais_warn("Repaired message payload size %d -> %d", cur_size, tmp_size); } if(sane && ais_data_len(msg) == 0) { ais_warn("Message with no payload"); sane = FALSE; } if(sane && data && msg->is_compressed == FALSE) { int str_size = strlen(data) + 1; if(ais_data_len(msg) != str_size) { int lpc = 0; ais_warn("Message payload is corrupted: expected %d bytes, got %d", ais_data_len(msg), str_size); sane = FALSE; for(lpc = (str_size - 10); lpc < msg->size; lpc++) { if(lpc < 0) { lpc = 0; } ais_debug_2("bad_data[%d]: %d / '%c'", lpc, data[lpc], data[lpc]); } } } if(sane == FALSE) { ais_err("Invalid message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } else if(repaired) { ais_err("Repaired message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } else { ais_debug_3("Verified message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } return sane; } gboolean route_ais_message(AIS_Message *msg, gboolean local_origin) { int rc = 0; int level = LOG_WARNING; int dest = msg->host.type; ais_debug_3("Msg[%d] (dest=%s:%s, from=%s:%s.%d, remote=%s, size=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, local_origin?"false":"true", ais_data_len(msg)); if(local_origin == FALSE) { if(msg->host.size == 0 || ais_str_eq(local_uname, msg->host.uname)) { msg->host.local = TRUE; } } if(check_message_sanity(msg, msg->data) == FALSE) { /* Dont send this message to anyone */ return FALSE; } if(msg->host.local) { void *conn = NULL; const char *lookup = NULL; if(dest == crm_msg_ais) { process_ais_message(msg); return TRUE; } else if(dest == crm_msg_lrmd) { /* lrmd messages are routed via the crm */ dest = crm_msg_crmd; } else if(dest == crm_msg_te) { /* te messages are routed via the crm - for now */ dest = crm_msg_crmd; } if(in_shutdown) { level = LOG_INFO; } AIS_CHECK(dest > 0 && dest < SIZEOF(crm_children), ais_err("Invalid destination: %d", dest); log_ais_message(LOG_ERR, msg); return FALSE; ); rc = 1; lookup = msg_type2text(dest); conn = crm_children[dest].async_conn; /* the cluster fails in weird and wonderfully obscure ways when this is not true */ AIS_ASSERT(ais_str_eq(lookup, crm_children[dest].name)); if (conn == NULL) { do_ais_log(level, "No connection to %s", crm_children[dest].name); } else if (!libais_connection_active(conn)) { do_ais_log(level, "Connection to %s is no longer active", crm_children[dest].name); crm_children[dest].async_conn = NULL; /* } else if ((queue->size - 1) == queue->used) { */ /* ais_err("Connection is throttled: %d", queue->size); */ } else { level = LOG_ERR; ais_debug_3("Delivering locally to %s (size=%d)", crm_children[dest].name, msg->header.size); rc = openais_conn_send_response(conn, msg, msg->header.size); } } else if(local_origin) { /* forward to other hosts */ ais_debug_3("Forwarding to cluster"); rc = send_cluster_msg_raw(msg); } else { ais_debug_3("Ignoring..."); } if(rc != 0) { do_ais_log(level, "Sending message to %s.%s failed (rc=%d)", ais_dest(&(msg->host)), msg_type2text(dest), rc); log_ais_message(level, msg); return FALSE; } return TRUE; } int send_cluster_msg_raw(AIS_Message *ais_msg) { int rc = 0; struct iovec iovec; static uint32_t msg_id = 0; AIS_Message *bz2_msg = NULL; ENTER(""); AIS_ASSERT(local_nodeid != 0); if(ais_msg->header.size != (sizeof(AIS_Message) + ais_data_len(ais_msg))) { ais_err("Repairing size mismatch: %u + %d = %d", (unsigned int)sizeof(AIS_Message), ais_data_len(ais_msg), ais_msg->header.size); ais_msg->header.size = sizeof(AIS_Message) + ais_data_len(ais_msg); } if(ais_msg->id == 0) { msg_id++; AIS_CHECK(msg_id != 0 /* detect wrap-around */, msg_id++; ais_err("Message ID wrapped around")); ais_msg->id = msg_id; } ais_msg->header.id = SERVICE_ID_MAKE(CRM_SERVICE, 0); ais_msg->sender.id = local_nodeid; ais_msg->sender.size = local_uname_len; memset(ais_msg->sender.uname, 0, MAX_NAME); memcpy(ais_msg->sender.uname, local_uname, ais_msg->sender.size); iovec.iov_base = (char *)ais_msg; iovec.iov_len = ais_msg->header.size; #if 0 if(ais_msg->is_compressed == FALSE && ais_msg->size > 1024) { char *compressed = NULL; unsigned int len = (ais_msg->size * 1.1) + 600; /* recomended size */ ais_debug_2("Creating compressed message"); ais_malloc0(compressed, len); rc = BZ2_bzBuffToBuffCompress( compressed, &len, ais_msg->data, ais_msg->size, 3, 0, 30); if(rc != BZ_OK) { ais_err("Compression failed: %d", rc); ais_free(compressed); goto send; } ais_malloc0(bz2_msg, sizeof(AIS_Message) + len + 1); memcpy(bz2_msg, ais_msg, sizeof(AIS_Message)); memcpy(bz2_msg->data, compressed, len); ais_free(compressed); bz2_msg->is_compressed = TRUE; bz2_msg->compressed_size = len; bz2_msg->header.size = sizeof(AIS_Message) + ais_data_len(bz2_msg); ais_debug("Compression details: %d -> %d", bz2_msg->size, ais_data_len(bz2_msg)); iovec.iov_base = (char *)bz2_msg; iovec.iov_len = bz2_msg->header.size; } send: #endif ais_debug_3("Sending message (size=%u)", (unsigned int)iovec.iov_len); rc = totempg_groups_mcast_joined ( openais_group_handle, &iovec, 1, TOTEMPG_SAFE); if(rc == 0 && ais_msg->is_compressed == FALSE) { ais_debug_2("Message sent: %.80s", ais_msg->data); } AIS_CHECK(rc == 0, ais_err("Message not sent (%d)", rc)); ais_free(bz2_msg); LEAVE(""); return rc; } #define min(x,y) (x)<(y)?(x):(y) void send_cluster_id(void) { int rc = 0; int lpc = 0; int len = 0; struct iovec iovec; struct crm_identify_msg_s *msg = NULL; ENTER(""); AIS_ASSERT(local_nodeid != 0); ais_malloc0(msg, sizeof(struct crm_identify_msg_s)); msg->header.size = sizeof(struct crm_identify_msg_s); msg->id = local_nodeid; msg->header.id = SERVICE_ID_MAKE(CRM_SERVICE, 1); len = min(local_uname_len, MAX_NAME-1); memset(msg->uname, 0, MAX_NAME); memcpy(msg->uname, local_uname, len); len = min(strlen(VERSION), MAX_NAME-1); memset(msg->version, 0, MAX_NAME); memcpy(msg->version, VERSION, len); msg->votes = 1; msg->pid = getpid(); msg->processes = crm_proc_ais; for (lpc = 0; lpc < SIZEOF(crm_children); lpc++) { if(crm_children[lpc].pid != 0) { msg->processes |= crm_children[lpc].flag; } } ais_debug("Local update: %u", local_nodeid); update_member( local_nodeid, membership_seq, msg->votes, msg->processes, NULL, NULL); iovec.iov_base = (char *)msg; iovec.iov_len = msg->header.size; rc = totempg_groups_mcast_joined ( openais_group_handle, &iovec, 1, TOTEMPG_SAFE); AIS_CHECK(rc == 0, ais_err("Message not sent (%d)", rc)); ais_free(msg); LEAVE(""); } diff --git a/include/crm/cib.h b/include/crm/cib.h index 75f635ed32..820ef1769a 100644 --- a/include/crm/cib.h +++ b/include/crm/cib.h @@ -1,412 +1,315 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB__H #define CIB__H #include #include #include #include #define CIB_FEATURE_SET "2.0" #define USE_PESKY_FRAGMENTS 1 /* use compare_version() for doing comparisons */ enum cib_variant { cib_native, + cib_file, + cib_remote, cib_database, cib_edir }; enum cib_state { cib_connected_command, cib_connected_query, cib_disconnected }; enum cib_conn_type { cib_command, cib_query, cib_query_synchronous, cib_command_synchronous, cib_no_connection }; enum cib_call_options { cib_none = 0x00000000, cib_verbose = 0x00000001, cib_discard_reply = 0x00000010, cib_scope_local = 0x00000100, cib_sync_call = 0x00001000, cib_inhibit_notify = 0x00010000, cib_quorum_override = 0x00100000, cib_inhibit_bcast = 0x01000000, cib_force_diff = 0x10000000 }; #define cib_default_options = cib_none enum cib_errors { cib_ok = 0, cib_operation = -1, cib_create_msg = -2, cib_not_connected = -3, cib_not_authorized = -4, cib_send_failed = -5, cib_reply_failed = -6, cib_return_code = -7, cib_output_ptr = -8, cib_output_data = -9, cib_connection = -10, cib_authentication = -11, cib_missing = -12, cib_variant = -28, CIBRES_MISSING_ID = -13, CIBRES_MISSING_TYPE = -14, CIBRES_MISSING_FIELD = -15, CIBRES_OBJTYPE_MISMATCH = -16, CIBRES_CORRUPT = -17, CIBRES_OTHER = -18, cib_unknown = -19, cib_STALE = -20, cib_EXISTS = -21, cib_NOTEXISTS = -22, cib_ACTIVATION = -23, cib_NOSECTION = -24, cib_NOOBJECT = -25, cib_NOPARENT = -26, cib_NODECOPY = -27, cib_NOTSUPPORTED = -29, cib_registration_msg = -30, cib_callback_token = -31, cib_callback_register = -32, cib_msg_field_add = -33, cib_client_gone = -34, cib_not_master = -35, cib_client_corrupt = -36, cib_master_timeout = -37, cib_revision_unsupported= -38, cib_revision_unknown = -39, cib_missing_data = -40, cib_remote_timeout = -41, cib_no_quorum = -42, cib_diff_failed = -43, cib_diff_resync = -44, cib_old_data = -45, cib_id_check = -46, cib_dtd_validation = -47, cib_bad_section = -48, cib_bad_digest = -49, cib_bad_permissions = -50, cib_bad_config = -51, cib_invalid_argument = -52 }; enum cib_update_op { CIB_UPDATE_OP_NONE = 0, CIB_UPDATE_OP_ADD, CIB_UPDATE_OP_MODIFY, CIB_UPDATE_OP_DELETE, CIB_UPDATE_OP_MAX }; enum cib_section { cib_section_none, cib_section_all, cib_section_nodes, cib_section_constraints, cib_section_resources, cib_section_crmconfig, cib_section_status }; #define CIB_OP_SLAVE "cib_slave" #define CIB_OP_SLAVEALL "cib_slave_all" #define CIB_OP_MASTER "cib_master" #define CIB_OP_SYNC "cib_sync" #define CIB_OP_SYNC_ONE "cib_sync_one" #define CIB_OP_ISMASTER "cib_ismaster" #define CIB_OP_BUMP "cib_bump" #define CIB_OP_QUERY "cib_query" #define CIB_OP_CREATE "cib_create" #define CIB_OP_UPDATE "cib_update" #define CIB_OP_MODIFY "cib_modify" #define CIB_OP_DELETE "cib_delete" #define CIB_OP_DELETE_ALT "cib_delete_alt" #define CIB_OP_ERASE "cib_erase" #define CIB_OP_REPLACE "cib_replace" #define CIB_OP_NOTIFY "cib_notify" #define CIB_OP_APPLY_DIFF "cib_apply_diff" #define F_CIB_CLIENTID "cib_clientid" #define F_CIB_CALLOPTS "cib_callopt" #define F_CIB_CALLID "cib_callid" #define F_CIB_CALLDATA "cib_calldata" #define F_CIB_OPERATION "cib_op" #define F_CIB_ISREPLY "cib_isreplyto" #define F_CIB_SECTION "cib_section" #define F_CIB_HOST "cib_host" #define F_CIB_RC "cib_rc" #define F_CIB_DELEGATED "cib_delegated_from" #define F_CIB_OBJID "cib_object" #define F_CIB_OBJTYPE "cib_object_type" #define F_CIB_EXISTING "cib_existing_object" #define F_CIB_SEENCOUNT "cib_seen" #define F_CIB_TIMEOUT "cib_timeout" #define F_CIB_UPDATE "cib_update" #define F_CIB_CALLBACK_TOKEN "cib_callback_token" #define F_CIB_GLOBAL_UPDATE "cib_update" #define F_CIB_UPDATE_RESULT "cib_update_result" #define F_CIB_CLIENTNAME "cib_clientname" #define F_CIB_NOTIFY_TYPE "cib_notify_type" #define F_CIB_NOTIFY_ACTIVATE "cib_notify_activate" #define F_CIB_UPDATE_DIFF "cib_update_diff" #define T_CIB "cib" #define T_CIB_NOTIFY "cib_notify" /* notify sub-types */ #define T_CIB_PRE_NOTIFY "cib_pre_notify" #define T_CIB_POST_NOTIFY "cib_post_notify" #define T_CIB_UPDATE_CONFIRM "cib_update_confirmation" #define T_CIB_DIFF_NOTIFY "cib_diff_notify" #define T_CIB_REPLACE_NOTIFY "cib_refresh_notify" #define cib_channel_ro "cib_ro" #define cib_channel_rw "cib_rw" #define cib_channel_callback "cib_callback" #define cib_channel_ro_synchronous "cib_ro_syncronous" #define cib_channel_rw_synchronous "cib_rw_syncronous" typedef struct cib_s cib_t; typedef struct cib_api_operations_s { int (*variant_op)( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*signon) ( cib_t *cib, const char *name, enum cib_conn_type type); int (*signoff)(cib_t *cib); int (*free) (cib_t *cib); int (*set_op_callback)( cib_t *cib, void (*callback)( const xmlNode *msg, int callid , int rc, xmlNode *output)); int (*add_notify_callback)( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); int (*del_notify_callback)( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); int (*set_connection_dnotify)( cib_t *cib, void (*dnotify)(gpointer user_data)); IPC_Channel *(*channel)(cib_t* cib); int (*inputfd)(cib_t* cib); int (*noop)(cib_t *cib, int call_options); int (*ping)( cib_t *cib, xmlNode **output_data, int call_options); int (*query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options); int (*query_from)( cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options); int (*is_master) (cib_t *cib); int (*set_master)(cib_t *cib, int call_options); int (*set_slave) (cib_t *cib, int call_options); int (*set_slave_all)(cib_t *cib, int call_options); int (*sync)(cib_t *cib, const char *section, int call_options); int (*sync_from)( cib_t *cib, const char *host, const char *section, int call_options); int (*bump_epoch)(cib_t *cib, int call_options); int (*create)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*modify)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*update)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*replace)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*delete)(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*delete_absolute)( cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int (*erase)( cib_t *cib, xmlNode **output_data, int call_options); int (*quit)(cib_t *cib, int call_options); gboolean (*msgready)(cib_t* cib); int (*rcvmsg)(cib_t* cib, int blocking); gboolean (*dispatch)(IPC_Channel *channel, gpointer user_data); int (*register_callback)( cib_t* cib, const char *callback, int enabled); } cib_api_operations_t; struct cib_s { enum cib_state state; enum cib_conn_type type; + enum cib_variant variant; int call_id; int call_timeout; void *variant_opaque; GList *notify_list; void (*op_callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output); cib_api_operations_t *cmds; }; -typedef struct cib_notify_client_s -{ - const char *event; - const char *obj_id; /* implement one day */ - const char *obj_type; /* implement one day */ - void (*callback)( - const char *event, xmlNode *msg); - -} cib_notify_client_t; - -struct timer_rec_s -{ - int call_id; - int timeout; - guint ref; -}; - -typedef struct cib_callback_client_s -{ - void (*callback)(xmlNode*, int, int, xmlNode*, void*); - void *user_data; - gboolean only_success; - struct timer_rec_s *timer; - -} cib_callback_client_t; - /* Core functions */ extern cib_t *cib_new(void); +extern cib_t *cib_new_variant(enum cib_variant variant); + extern void cib_delete(cib_t *cib); -extern gboolean startCib(const char *filename); -extern xmlNode *get_cib_copy(cib_t *cib); -extern xmlNode *cib_get_generation(cib_t *cib); -extern int cib_compare_generation(xmlNode *left, xmlNode *right); +extern int num_cib_op_callbacks(void); +extern void remove_cib_op_callback(int call_id, gboolean all_callbacks); extern gboolean add_cib_op_callback_timeout( int call_id, int timeout, gboolean only_success, void *user_data, void (*callback)(xmlNode*, int, int, xmlNode*,void*)); -extern gboolean add_cib_op_callback( - int call_id, gboolean only_success, void *user_data, - void (*callback)(xmlNode*, int, int, xmlNode*,void*)); -extern void remove_cib_op_callback(int call_id, gboolean all_callbacks); -extern int num_cib_op_callbacks(void); - -/* Utility functions */ -extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root); -extern xmlNode *create_cib_fragment_adv( - xmlNode *update, const char *section, const char *source); -extern char *cib_pluralSection(const char *a_section); -extern const char *get_crm_option( - xmlNode *cib, const char *name, gboolean do_warn); - -/* Error Interpretation*/ -extern const char *cib_error2string(enum cib_errors); -extern const char *cib_op2string(enum cib_update_op); - -extern xmlNode *createEmptyCib(void); -extern gboolean verifyCibXml(xmlNode *cib); -extern int cib_section2enum(const char *a_section); - -#define create_cib_fragment(update,cib_section) create_cib_fragment_adv(update, cib_section, __FUNCTION__) - -extern gboolean cib_config_changed(xmlNode *old_cib, xmlNode *new_cib, xmlNode **result); - -extern xmlNode *diff_cib_object( - xmlNode *old, xmlNode *new,gboolean suppress); - -extern gboolean apply_cib_diff( - xmlNode *old, xmlNode *diff, xmlNode **new); - -extern void log_cib_diff(int log_level, xmlNode *diff, const char *function); - -extern gboolean cib_diff_version_details( - xmlNode *diff, int *admin_epoch, int *epoch, int *updates, - int *_admin_epoch, int *_epoch, int *_updates); - -extern gboolean cib_version_details( - xmlNode *cib, int *admin_epoch, int *epoch, int *updates); - -extern enum cib_errors update_attr( - cib_t *the_cib, int call_options, - const char *section, const char *node_uuid, const char *set_name, - const char *attr_id, const char *attr_name, const char *attr_value, gboolean to_console); - -extern enum cib_errors find_attr_details( - xmlNode *xml_search, const char *node_uuid, - const char *set_name, const char *attr_id, const char *attr_name, - xmlNode **xml_obj, gboolean to_console); - -extern enum cib_errors read_attr( - cib_t *the_cib, - const char *section, const char *node_uuid, const char *set_name, - const char *attr_id, const char *attr_name, char **attr_value, gboolean to_console); - -extern enum cib_errors delete_attr( - cib_t *the_cib, int options, - const char *section, const char *node_uuid, const char *set_name, - const char *attr_id, const char *attr_name, const char *attr_value, gboolean to_console); - -extern enum cib_errors query_node_uuid( - cib_t *the_cib, const char *uname, char **uuid); - -extern enum cib_errors query_node_uname( - cib_t *the_cib, const char *uuid, char **uname); - -extern enum cib_errors query_standby(cib_t *the_cib, const char *uuid, - char **scope, char **standby_value); - -extern enum cib_errors set_standby( - cib_t *the_cib, - const char *uuid, const char *scope, const char *standby_value); -enum cib_errors delete_standby( - cib_t *the_cib, - const char *uuid, const char *scope, const char *standby_value); +#define add_cib_op_callback(id, flag, data, fn) add_cib_op_callback_timeout(id, 0, flag, data, fn) -extern const char *feature_set(xmlNode *xml_obj); +#include +#include #endif diff --git a/include/crm/cib_ops.h b/include/crm/cib_ops.h new file mode 100644 index 0000000000..0d3bc7a47f --- /dev/null +++ b/include/crm/cib_ops.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2004 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CIB_OPS__H +#define CIB_OPS__H + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +enum cib_errors +cib_process_query( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors +cib_process_erase( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors +cib_process_bump( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors +cib_process_replace( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors +cib_process_modify( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors +cib_process_delete( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors +cib_process_diff( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); + +enum cib_errors cib_update_counter(xmlNode *xml_obj, const char *field, gboolean reset); +xmlNode *diff_cib_object(xmlNode *old_cib, xmlNode *new_cib, gboolean suppress); +gboolean apply_cib_diff(xmlNode *old, xmlNode *diff, xmlNode **new); +gboolean cib_config_changed(xmlNode *old_cib, xmlNode *new_cib, xmlNode **result); + +#endif diff --git a/include/crm/cib_util.h b/include/crm/cib_util.h new file mode 100644 index 0000000000..05334208d2 --- /dev/null +++ b/include/crm/cib_util.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2004 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CIB_UTIL__H +#define CIB_UTIL__H + +/* Utility functions */ +extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root); +extern xmlNode *create_cib_fragment_adv( + xmlNode *update, const char *section, const char *source); +extern char *cib_pluralSection(const char *a_section); +extern const char *get_crm_option( + xmlNode *cib, const char *name, gboolean do_warn); + +/* Error Interpretation*/ +extern const char *cib_error2string(enum cib_errors); + +extern xmlNode *createEmptyCib(void); +extern gboolean verifyCibXml(xmlNode *cib); +extern int cib_section2enum(const char *a_section); + +#define create_cib_fragment(update,cib_section) create_cib_fragment_adv(update, cib_section, __FUNCTION__) + +extern gboolean cib_config_changed(xmlNode *old_cib, xmlNode *new_cib, xmlNode **result); + +extern xmlNode *diff_cib_object( + xmlNode *old, xmlNode *new,gboolean suppress); + +extern gboolean apply_cib_diff( + xmlNode *old, xmlNode *diff, xmlNode **new); + +extern void log_cib_diff(int log_level, xmlNode *diff, const char *function); + +extern gboolean cib_diff_version_details( + xmlNode *diff, int *admin_epoch, int *epoch, int *updates, + int *_admin_epoch, int *_epoch, int *_updates); + +extern gboolean cib_version_details( + xmlNode *cib, int *admin_epoch, int *epoch, int *updates); + +extern enum cib_errors update_attr( + cib_t *the_cib, int call_options, + const char *section, const char *node_uuid, const char *set_name, + const char *attr_id, const char *attr_name, const char *attr_value, gboolean to_console); + +extern enum cib_errors find_attr_details( + xmlNode *xml_search, const char *node_uuid, + const char *set_name, const char *attr_id, const char *attr_name, + xmlNode **xml_obj, gboolean to_console); + +extern enum cib_errors read_attr( + cib_t *the_cib, + const char *section, const char *node_uuid, const char *set_name, + const char *attr_id, const char *attr_name, char **attr_value, gboolean to_console); + +extern enum cib_errors delete_attr( + cib_t *the_cib, int options, + const char *section, const char *node_uuid, const char *set_name, + const char *attr_id, const char *attr_name, const char *attr_value, gboolean to_console); + +extern enum cib_errors query_node_uuid( + cib_t *the_cib, const char *uname, char **uuid); + +extern enum cib_errors query_node_uname( + cib_t *the_cib, const char *uuid, char **uname); + +extern enum cib_errors query_standby(cib_t *the_cib, const char *uuid, + char **scope, char **standby_value); + +extern enum cib_errors set_standby( + cib_t *the_cib, + const char *uuid, const char *scope, const char *standby_value); + +enum cib_errors delete_standby( + cib_t *the_cib, + const char *uuid, const char *scope, const char *standby_value); + +extern const char *feature_set(xmlNode *xml_obj); + +extern gboolean startCib(const char *filename); +extern xmlNode *get_cib_copy(cib_t *cib); +extern xmlNode *cib_get_generation(cib_t *cib); +extern int cib_compare_generation(xmlNode *left, xmlNode *right); + +#endif diff --git a/lib/crm/cib/Makefile.am b/lib/crm/cib/Makefile.am index 539dc0e7c7..db19a2758b 100644 --- a/lib/crm/cib/Makefile.am +++ b/lib/crm/cib/Makefile.am @@ -1,39 +1,39 @@ # # 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 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl ## libraries lib_LTLIBRARIES = libcib.la ## SOURCES noinst_HEADERS = cib_private.h -libcib_la_SOURCES = cib_client.c cib_native.c cib_attrs.c \ - cib_version.c +libcib_la_SOURCES = cib_ops.c cib_utils.c cib_client.c cib_native.c cib_attrs.c \ + cib_version.c cib_file.c libcib_la_LDFLAGS = -version-info 1:1:0 libcib_la_CFLAGS = -I$(top_builddir) clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/lib/crm/cib/cib_client.c b/lib/crm/cib/cib_client.c index 756f82a5a1..3f2d4faab9 100644 --- a/lib/crm/cib/cib_client.c +++ b/lib/crm/cib/cib_client.c @@ -1,1654 +1,515 @@ /* - * Copyright (c) 2004 International Business Machines + * Copyright (c) 2004 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -/* short term hack to reduce callback messages */ -typedef struct cib_native_opaque_s -{ - IPC_Channel *command_channel; - IPC_Channel *callback_channel; - GCHSource *callback_source; - -} cib_native_opaque_t; - GHashTable *cib_op_callback_table = NULL; -gboolean verify_cib_cmds(cib_t *cib); - int cib_client_set_op_callback( cib_t *cib, void (*callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)); -int cib_client_noop(cib_t *cib, int call_options); -int cib_client_ping(cib_t *cib, xmlNode **output_data, int call_options); - -int cib_client_query(cib_t *cib, const char *section, - xmlNode **output_data, int call_options); -int cib_client_query_from(cib_t *cib, const char *host, const char *section, - xmlNode **output_data, int call_options); - -int cib_client_sync(cib_t *cib, const char *section, int call_options); -int cib_client_sync_from( - cib_t *cib, const char *host, const char *section, int call_options); - -int cib_client_is_master(cib_t *cib); -int cib_client_set_slave(cib_t *cib, int call_options); -int cib_client_set_slave_all(cib_t *cib, int call_options); -int cib_client_set_master(cib_t *cib, int call_options); - -int cib_client_bump_epoch(cib_t *cib, int call_options); -int cib_client_create(cib_t *cib, const char *section, xmlNode *data, - xmlNode **output_data, int call_options); -int cib_client_modify(cib_t *cib, const char *section, xmlNode *data, - xmlNode **output_data, int call_options); -int cib_client_update(cib_t *cib, const char *section, xmlNode *data, - xmlNode **output_data, int call_options); -int cib_client_replace(cib_t *cib, const char *section, xmlNode *data, - xmlNode **output_data, int call_options); -int cib_client_delete(cib_t *cib, const char *section, xmlNode *data, - xmlNode **output_data, int call_options); -int cib_client_delete_absolute( - cib_t *cib, const char *section, xmlNode *data, - xmlNode **output_data, int call_options); -int cib_client_erase( - cib_t *cib, xmlNode **output_data, int call_options); -int cib_client_quit(cib_t *cib, int call_options); int cib_client_add_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); int cib_client_del_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)); gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b); extern cib_t *cib_native_new(cib_t *cib); -extern void cib_native_delete(cib_t *cib); +extern cib_t *cib_file_new(cib_t *cib, const char *filename); -static enum cib_variant configured_variant = cib_native; -static void cib_destroy_op_callback(gpointer data) -{ - cib_callback_client_t *blob = data; - if(blob->timer && blob->timer->ref > 0) { - g_source_remove(blob->timer->ref); +#define op_common(cib) \ + if(cib == NULL) { \ + return cib_missing; \ + } else if(cib->cmds->variant_op == NULL) { \ + return cib_variant; \ } - crm_free(blob); -} - -/* define of the api functions*/ -cib_t* -cib_new(void) -{ - cib_t* new_cib = NULL; - - if(configured_variant != cib_native) { - crm_err("Only the native CIB type is currently implemented"); - return NULL; - } - - if(cib_op_callback_table != NULL) { - g_hash_table_destroy(cib_op_callback_table); - cib_op_callback_table = NULL; - } - if(cib_op_callback_table == NULL) { - cib_op_callback_table = g_hash_table_new_full( - g_direct_hash, g_direct_equal, - NULL, cib_destroy_op_callback); - } - - crm_malloc0(new_cib, sizeof(cib_t)); - - new_cib->call_id = 1; - - new_cib->type = cib_none; - new_cib->state = cib_disconnected; - - new_cib->op_callback = NULL; - new_cib->variant_opaque = NULL; - new_cib->notify_list = NULL; - - /* the rest will get filled in by the variant constructor */ - crm_malloc0(new_cib->cmds, sizeof(cib_api_operations_t)); - - new_cib->cmds->set_op_callback = cib_client_set_op_callback; - new_cib->cmds->add_notify_callback = cib_client_add_notify_callback; - new_cib->cmds->del_notify_callback = cib_client_del_notify_callback; - - new_cib->cmds->noop = cib_client_noop; - new_cib->cmds->ping = cib_client_ping; - new_cib->cmds->query = cib_client_query; - new_cib->cmds->sync = cib_client_sync; - - new_cib->cmds->query_from = cib_client_query_from; - new_cib->cmds->sync_from = cib_client_sync_from; - - new_cib->cmds->is_master = cib_client_is_master; - new_cib->cmds->set_master = cib_client_set_master; - new_cib->cmds->set_slave = cib_client_set_slave; - new_cib->cmds->set_slave_all = cib_client_set_slave_all; - - new_cib->cmds->bump_epoch = cib_client_bump_epoch; - - new_cib->cmds->create = cib_client_create; - new_cib->cmds->modify = cib_client_modify; - new_cib->cmds->update = cib_client_update; - new_cib->cmds->replace = cib_client_replace; - new_cib->cmds->delete = cib_client_delete; - new_cib->cmds->erase = cib_client_erase; - new_cib->cmds->quit = cib_client_quit; - - new_cib->cmds->delete_absolute = cib_client_delete_absolute; - - cib_native_new(new_cib); - if(verify_cib_cmds(new_cib) == FALSE) { - cib_delete(new_cib); - return NULL; - } - - return new_cib; -} - -void -cib_delete(cib_t *cib) -{ - GList *list = cib->notify_list; - while(list != NULL) { - cib_notify_client_t *client = g_list_nth_data(list, 0); - list = g_list_remove(list, client); - crm_free(client); - } - - cib_native_delete(cib); - g_hash_table_destroy(cib_op_callback_table); - crm_free(cib->cmds); - crm_free(cib); -} - -int -cib_client_set_op_callback( - cib_t *cib, void (*callback)(const xmlNode *msg, int call_id, - int rc, xmlNode *output)) -{ - if(callback == NULL) { - crm_info("Un-Setting operation callback"); - - } else { - crm_debug_3("Setting operation callback"); - } - cib->op_callback = callback; - return cib_ok; -} - -int cib_client_noop(cib_t *cib, int call_options) +static int cib_client_noop(cib_t *cib, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CRM_OP_NOOP, NULL, NULL, NULL, NULL, call_options); } -int cib_client_ping(cib_t *cib, xmlNode **output_data, int call_options) +static int cib_client_ping(cib_t *cib, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CRM_OP_PING, NULL,NULL,NULL, output_data, call_options); } -int cib_client_query(cib_t *cib, const char *section, +static int cib_client_query(cib_t *cib, const char *section, xmlNode **output_data, int call_options) { return cib->cmds->query_from( cib, NULL, section, output_data, call_options); } -int cib_client_query_from(cib_t *cib, const char *host, const char *section, +static int cib_client_query_from(cib_t *cib, const char *host, const char *section, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_QUERY, host, section, NULL, output_data, call_options); } - -int cib_client_is_master(cib_t *cib) +static int cib_client_is_master(cib_t *cib) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } + op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_ISMASTER, NULL, NULL,NULL,NULL, cib_scope_local|cib_sync_call); } -int cib_client_set_slave(cib_t *cib, int call_options) +static int cib_client_set_slave(cib_t *cib, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_SLAVE, NULL,NULL,NULL,NULL, call_options); } -int cib_client_set_slave_all(cib_t *cib, int call_options) +static int cib_client_set_slave_all(cib_t *cib, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_SLAVEALL, NULL,NULL,NULL,NULL, call_options); } -int cib_client_set_master(cib_t *cib, int call_options) +static int cib_client_set_master(cib_t *cib, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) crm_debug_3("Adding cib_scope_local to options"); return cib->cmds->variant_op( cib, CIB_OP_MASTER, NULL,NULL,NULL,NULL, call_options|cib_scope_local); } - - -int cib_client_bump_epoch(cib_t *cib, int call_options) +static int cib_client_bump_epoch(cib_t *cib, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_BUMP, NULL, NULL, NULL, NULL, call_options); } -int cib_client_sync(cib_t *cib, const char *section, int call_options) +static int cib_client_sync(cib_t *cib, const char *section, int call_options) { return cib->cmds->sync_from(cib, NULL, section, call_options); } -int cib_client_sync_from( +static int cib_client_sync_from( cib_t *cib, const char *host, const char *section, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CIB_OP_SYNC, host, section, NULL, NULL, call_options); } -int cib_client_create(cib_t *cib, const char *section, xmlNode *data, +static int cib_client_create(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_CREATE, NULL, section, data, output_data, call_options); } - -int cib_client_modify(cib_t *cib, const char *section, xmlNode *data, +static int cib_client_modify(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_MODIFY, NULL, section, data, output_data, call_options); } -int cib_client_update(cib_t *cib, const char *section, xmlNode *data, +static int cib_client_update(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_UPDATE, NULL, section, data, output_data, call_options); } - -int cib_client_replace(cib_t *cib, const char *section, xmlNode *data, +static int cib_client_replace(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } else if(data == NULL) { - return cib_missing_data; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_REPLACE, NULL, section, data, output_data, call_options); } -int cib_client_delete(cib_t *cib, const char *section, xmlNode *data, +static int cib_client_delete(cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_DELETE, NULL, section, data, output_data, call_options); } -int cib_client_delete_absolute( +static int cib_client_delete_absolute( cib_t *cib, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_DELETE_ALT, NULL, section, data, output_data, call_options); } -int cib_client_erase( +static int cib_client_erase( cib_t *cib, xmlNode **output_data, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op(cib, CIB_OP_ERASE, NULL, NULL, NULL, output_data, call_options); } - -int cib_client_quit(cib_t *cib, int call_options) +static int cib_client_quit(cib_t *cib, int call_options) { - if(cib == NULL) { - return cib_missing; - } else if(cib->state == cib_disconnected) { - return cib_not_connected; - } else if(cib->cmds->variant_op == NULL) { - return cib_variant; - } - + op_common(cib) return cib->cmds->variant_op( cib, CRM_OP_QUIT, NULL, NULL, NULL, NULL, call_options); } + +static void cib_destroy_op_callback(gpointer data) +{ + cib_callback_client_t *blob = data; + if(blob->timer && blob->timer->ref > 0) { + g_source_remove(blob->timer->ref); + } + crm_free(blob); +} + +cib_t* +cib_new(void) +{ + if(getenv("CIB_file")) { + return cib_new_variant(cib_file); + } + + return cib_new_variant(cib_native); +} + +/* this is backwards... + cib_*_new should call this not the other way around + */ + +cib_t* +cib_new_variant(enum cib_variant variant) +{ + cib_t* new_cib = NULL; + crm_malloc0(new_cib, sizeof(cib_t)); + + if(cib_op_callback_table != NULL) { + g_hash_table_destroy(cib_op_callback_table); + cib_op_callback_table = NULL; + } + if(cib_op_callback_table == NULL) { + cib_op_callback_table = g_hash_table_new_full( + g_direct_hash, g_direct_equal, + NULL, cib_destroy_op_callback); + } + + new_cib->call_id = 1; + + new_cib->type = cib_none; + new_cib->state = cib_disconnected; + new_cib->variant = variant; + + new_cib->op_callback = NULL; + new_cib->variant_opaque = NULL; + new_cib->notify_list = NULL; + + /* the rest will get filled in by the variant constructor */ + crm_malloc0(new_cib->cmds, sizeof(cib_api_operations_t)); + + new_cib->cmds->set_op_callback = cib_client_set_op_callback; + new_cib->cmds->add_notify_callback = cib_client_add_notify_callback; + new_cib->cmds->del_notify_callback = cib_client_del_notify_callback; + + new_cib->cmds->noop = cib_client_noop; + new_cib->cmds->ping = cib_client_ping; + new_cib->cmds->query = cib_client_query; + new_cib->cmds->sync = cib_client_sync; + + new_cib->cmds->query_from = cib_client_query_from; + new_cib->cmds->sync_from = cib_client_sync_from; + + new_cib->cmds->is_master = cib_client_is_master; + new_cib->cmds->set_master = cib_client_set_master; + new_cib->cmds->set_slave = cib_client_set_slave; + new_cib->cmds->set_slave_all = cib_client_set_slave_all; + + new_cib->cmds->bump_epoch = cib_client_bump_epoch; + + new_cib->cmds->create = cib_client_create; + new_cib->cmds->modify = cib_client_modify; + new_cib->cmds->update = cib_client_update; + new_cib->cmds->replace = cib_client_replace; + new_cib->cmds->delete = cib_client_delete; + new_cib->cmds->erase = cib_client_erase; + new_cib->cmds->quit = cib_client_quit; + + new_cib->cmds->delete_absolute = cib_client_delete_absolute; + + switch(variant) { + case cib_native: + cib_native_new(new_cib); + break; + + case cib_file: + cib_file_new(new_cib, NULL); + break; + default: + crm_err("Only the native CIB type is currently implemented"); + crm_free(new_cib); + return NULL; + } + + return new_cib; +} + +void +cib_delete(cib_t *cib) +{ + GList *list = cib->notify_list; + while(list != NULL) { + cib_notify_client_t *client = g_list_nth_data(list, 0); + list = g_list_remove(list, client); + crm_free(client); + } + + g_hash_table_destroy(cib_op_callback_table); + cib->cmds->free(cib); + cib = NULL; +} + + +int +cib_client_set_op_callback( + cib_t *cib, void (*callback)(const xmlNode *msg, int call_id, + int rc, xmlNode *output)) +{ + if(callback == NULL) { + crm_info("Un-Setting operation callback"); + + } else { + crm_debug_3("Setting operation callback"); + } + cib->op_callback = callback; + return cib_ok; +} + int cib_client_add_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)) { GList *list_item = NULL; cib_notify_client_t *new_client = NULL; + + if(cib->variant != cib_native) { + crm_err("Not supported"); + return cib_NOTSUPPORTED; + } crm_debug_2("Adding callback for %s events (%d)", event, g_list_length(cib->notify_list)); crm_malloc0(new_client, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; list_item = g_list_find_custom( cib->notify_list, new_client, ciblib_GCompareFunc); if(list_item != NULL) { crm_warn("Callback already present"); crm_free(new_client); } else { cib->notify_list = g_list_append( cib->notify_list, new_client); cib->cmds->register_callback(cib, event, 1); crm_debug_3("Callback added (%d)", g_list_length(cib->notify_list)); } return cib_ok; } int cib_client_del_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, xmlNode *msg)) { GList *list_item = NULL; cib_notify_client_t *new_client = NULL; + if(cib->variant != cib_native) { + crm_err("Not supported"); + return cib_NOTSUPPORTED; + } + crm_debug("Removing callback for %s events", event); crm_malloc0(new_client, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; list_item = g_list_find_custom( cib->notify_list, new_client, ciblib_GCompareFunc); cib->cmds->register_callback(cib, event, 0); if(list_item != NULL) { cib_notify_client_t *list_client = list_item->data; cib->notify_list = g_list_remove(cib->notify_list, list_client); crm_free(list_client); crm_debug_3("Removed callback"); } else { crm_debug_3("Callback not present"); } crm_free(new_client); return cib_ok; } gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b) { const cib_notify_client_t *a_client = a; const cib_notify_client_t *b_client = b; if(a_client->callback == b_client->callback && safe_str_neq(a_client->event, b_client->event)) { return 0; } else if(((long)a_client->callback) < ((long)b_client->callback)) { return -1; } return 1; } static gboolean cib_async_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; int call_id = timer->call_id; cib_callback_client_t *blob = NULL; crm_err("Async call %d timed out after %ds", call_id, timer->timeout); /* Send an async reply with rc=cib_remote_timeout */ blob = g_hash_table_lookup(cib_op_callback_table, GINT_TO_POINTER(call_id)); if(blob != NULL && blob->callback != NULL) { crm_debug_3("Callback found for call %d", call_id); blob->callback(NULL, call_id, cib_remote_timeout, NULL, blob->user_data); } remove_cib_op_callback(timer->call_id, FALSE); /* Always return TRUE, never remove the handler * We do that in remove_cib_op_callback() */ return TRUE; } -gboolean -add_cib_op_callback( - int call_id, gboolean only_success, void *user_data, - void (*callback)(xmlNode*, int, int, xmlNode*,void*)) -{ - return add_cib_op_callback_timeout(call_id, 0, only_success, user_data, callback); -} - gboolean add_cib_op_callback_timeout( int call_id, int timeout, gboolean only_success, void *user_data, void (*callback)(xmlNode*, int, int, xmlNode*,void*)) { cib_callback_client_t *blob = NULL; if(call_id < 0) { crm_warn("CIB call failed: %s", cib_error2string(call_id)); if(only_success == FALSE) { callback(NULL, call_id, call_id, NULL, user_data); } return FALSE; } crm_malloc0(blob, sizeof(cib_callback_client_t)); blob->only_success = only_success; blob->user_data = user_data; blob->callback = callback; if(timeout > 0) { struct timer_rec_s *async_timer = NULL; crm_malloc0(async_timer, sizeof(struct timer_rec_s)); blob->timer = async_timer; async_timer->call_id = call_id; async_timer->timeout = timeout*1000; async_timer->ref = Gmain_timeout_add( async_timer->timeout, cib_async_timeout_handler, async_timer); } g_hash_table_insert(cib_op_callback_table, GINT_TO_POINTER(call_id), blob); return TRUE; } void remove_cib_op_callback(int call_id, gboolean all_callbacks) { if(all_callbacks) { if(cib_op_callback_table != NULL) { g_hash_table_destroy(cib_op_callback_table); } cib_op_callback_table = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, cib_destroy_op_callback); } else { g_hash_table_remove( cib_op_callback_table, GINT_TO_POINTER(call_id)); } } int num_cib_op_callbacks(void) { if(cib_op_callback_table == NULL) { return 0; } return g_hash_table_size(cib_op_callback_table); } - - -char * -cib_pluralSection(const char *a_section) -{ - char *a_section_parent = NULL; - if (a_section == NULL) { - a_section_parent = crm_strdup("all"); - - } else if(strcasecmp(a_section, XML_TAG_CIB) == 0) { - a_section_parent = crm_strdup("all"); - - } else if(strcasecmp(a_section, XML_CIB_TAG_NODE) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_NODES); - - } else if(strcasecmp(a_section, XML_CIB_TAG_STATE) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_STATUS); - - } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINT) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); - - } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_LOCATION) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); - - } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_DEPEND) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); - - } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_ORDER) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); - - } else if(strcasecmp(a_section, "resource") == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); - - } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCE) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); - - } else if(strcasecmp(a_section, XML_CIB_TAG_GROUP) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); - - } else if(strcasecmp(a_section, XML_CIB_TAG_INCARNATION) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); - - } else if(strcasecmp(a_section, XML_CIB_TAG_NVPAIR) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); - - } else if(strcasecmp(a_section, XML_TAG_ATTR_SETS) == 0) { - a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); - - } else { - crm_err("Unknown section %s", a_section); - a_section_parent = crm_strdup("all"); - } - - crm_debug_2("Plural of %s is %s", crm_str(a_section), a_section_parent); - - return a_section_parent; -} - -const char * -cib_error2string(enum cib_errors return_code) -{ - const char *error_msg = NULL; - switch(return_code) { - case cib_bad_permissions: - error_msg = "bad permissions for the on-disk configuration. shutdown heartbeat and repair."; - break; - case cib_bad_digest: - error_msg = "the on-disk configuration was manually altered. shutdown heartbeat and repair."; - break; - case cib_bad_config: - error_msg = "the on-disk configuration is not valid"; - break; - case cib_msg_field_add: - error_msg = "failed adding field to cib message"; - break; - case cib_id_check: - error_msg = "missing id or id-collision detected"; - break; - case cib_operation: - error_msg = "invalid operation"; - break; - case cib_create_msg: - error_msg = "couldnt create cib message"; - break; - case cib_client_gone: - error_msg = "client left before we could send reply"; - break; - case cib_not_connected: - error_msg = "not connected"; - break; - case cib_not_authorized: - error_msg = "not authorized"; - break; - case cib_send_failed: - error_msg = "send failed"; - break; - case cib_reply_failed: - error_msg = "reply failed"; - break; - case cib_return_code: - error_msg = "no return code"; - break; - case cib_output_ptr: - error_msg = "nowhere to store output"; - break; - case cib_output_data: - error_msg = "corrupt output data"; - break; - case cib_connection: - error_msg = "connection failed"; - break; - case cib_callback_register: - error_msg = "couldnt register callback channel"; - break; - case cib_authentication: - error_msg = ""; - break; - case cib_registration_msg: - error_msg = "invalid registration msg"; - break; - case cib_callback_token: - error_msg = "callback token not found"; - break; - case cib_missing: - error_msg = "cib object missing"; - break; - case cib_variant: - error_msg = "unknown/corrupt cib variant"; - break; - case CIBRES_MISSING_ID: - error_msg = "The id field is missing"; - break; - case CIBRES_MISSING_TYPE: - error_msg = "The type field is missing"; - break; - case CIBRES_MISSING_FIELD: - error_msg = "A required field is missing"; - break; - case CIBRES_OBJTYPE_MISMATCH: - error_msg = "CIBRES_OBJTYPE_MISMATCH"; - break; - case cib_EXISTS: - error_msg = "The object already exists"; - break; - case cib_NOTEXISTS: - error_msg = "The object/attribute does not exist"; - break; - case CIBRES_CORRUPT: - error_msg = "The CIB is corrupt"; - break; - case cib_NOOBJECT: - error_msg = "The update was empty"; - break; - case cib_NOPARENT: - error_msg = "The parent object does not exist"; - break; - case cib_NODECOPY: - error_msg = "Failed while copying update"; - break; - case CIBRES_OTHER: - error_msg = "CIBRES_OTHER"; - break; - case cib_ok: - error_msg = "ok"; - break; - case cib_unknown: - error_msg = "Unknown error"; - break; - case cib_STALE: - error_msg = "Discarded old update"; - break; - case cib_ACTIVATION: - error_msg = "Activation Failed"; - break; - case cib_NOSECTION: - error_msg = "Required section was missing"; - break; - case cib_NOTSUPPORTED: - error_msg = "Supplied information is not supported"; - break; - case cib_not_master: - error_msg = "Local service is not the master instance"; - break; - case cib_client_corrupt: - error_msg = "Service client not valid"; - break; - case cib_remote_timeout: - error_msg = "Remote node did not respond"; - break; - case cib_master_timeout: - error_msg = "No master service is currently active"; - break; - case cib_revision_unsupported: - error_msg = "The required CIB revision number is not supported"; - break; - case cib_revision_unknown: - error_msg = "The CIB revision number could not be determined"; - break; - case cib_missing_data: - error_msg = "Required data for this CIB API call not found"; - break; - case cib_no_quorum: - error_msg = "Write requires quorum"; - break; - case cib_diff_failed: - error_msg = "Application of an update diff failed"; - break; - case cib_diff_resync: - error_msg = "Application of an update diff failed, requesting a full refresh"; - break; - case cib_bad_section: - error_msg = "Invalid CIB section specified"; - break; - case cib_old_data: - error_msg = "Update was older than existing configuration"; - break; - case cib_dtd_validation: - error_msg = "Update does not conform to the DTD in "DTD_DIRECTORY"/crm.dtd"; - break; - case cib_invalid_argument: - error_msg = "Invalid argument"; - break; - } - - if(error_msg == NULL) { - crm_err("Unknown CIB Error Code: %d", return_code); - error_msg = ""; - } - - return error_msg; -} - -const char * -cib_op2string(enum cib_update_op operation) -{ - const char *operation_msg = NULL; - switch(operation) { - case 0: - operation_msg = "none"; - break; - case 1: - operation_msg = "add"; - break; - case 2: - operation_msg = "modify"; - break; - case 3: - operation_msg = "delete"; - break; - case CIB_UPDATE_OP_MAX: - operation_msg = "invalid operation"; - break; - - } - - if(operation_msg == NULL) { - crm_err("Unknown CIB operation %d", operation); - operation_msg = ""; - } - - return operation_msg; -} - -int -cib_section2enum(const char *a_section) -{ - if(a_section == NULL || strcasecmp(a_section, "all") == 0) { - return cib_section_all; - - } else if(strcasecmp(a_section, XML_CIB_TAG_NODES) == 0) { - return cib_section_nodes; - - } else if(strcasecmp(a_section, XML_CIB_TAG_STATUS) == 0) { - return cib_section_status; - - } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) { - return cib_section_constraints; - - } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCES) == 0) { - return cib_section_resources; - - } else if(strcasecmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) { - return cib_section_crmconfig; - - } - crm_err("Unknown CIB section: %s", a_section); - return cib_section_none; -} - - -int -cib_compare_generation(xmlNode *left, xmlNode *right) -{ - int lpc = 0; - const char *attributes[] = { - XML_ATTR_GENERATION_ADMIN, - XML_ATTR_GENERATION, - XML_ATTR_NUMUPDATES, - XML_ATTR_NUMPEERS - }; - - crm_log_xml_debug_3(left, "left"); - crm_log_xml_debug_3(right, "right"); - - for(lpc = 0; lpc < DIMOF(attributes); lpc++) { - int int_elem_l = -1; - int int_elem_r = -1; - const char *elem_r = NULL; - const char *elem_l = crm_element_value(left, attributes[lpc]); - - if(right != NULL) { - elem_r = crm_element_value(right, attributes[lpc]); - } - - if(elem_l != NULL) { int_elem_l = crm_parse_int(elem_l, NULL); } - if(elem_r != NULL) { int_elem_r = crm_parse_int(elem_r, NULL); } - - if(int_elem_l < int_elem_r) { - crm_debug_2("%s (%s < %s)", attributes[lpc], - crm_str(elem_l), crm_str(elem_r)); - return -1; - - } else if(int_elem_l > int_elem_r) { - crm_debug_2("%s (%s > %s)", attributes[lpc], - crm_str(elem_l), crm_str(elem_r)); - return 1; - } - } - - return 0; -} - -xmlNode* -get_cib_copy(cib_t *cib) -{ - xmlNode *xml_cib; -#if CRM_DEPRECATED_SINCE_2_0_4 - xmlNode *xml_cib_copy; -#endif - int options = cib_scope_local|cib_sync_call; - if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) { - crm_err("Couldnt retrieve the CIB"); - return NULL; - } else if(xml_cib == NULL) { - crm_err("The CIB result was empty"); - return NULL; - } - - if(safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) { - return xml_cib; - -#if CRM_DEPRECATED_SINCE_2_0_4 - } else { - xml_cib_copy = copy_xml( - find_xml_node(xml_cib, XML_TAG_CIB, TRUE)); - free_xml(xml_cib); - return xml_cib_copy; -#endif - } - free_xml(xml_cib); - return NULL; -} - -xmlNode* -cib_get_generation(cib_t *cib) -{ - xmlNode *the_cib = get_cib_copy(cib); - xmlNode *generation = create_xml_node( - NULL, XML_CIB_TAG_GENERATION_TUPPLE); - - if(the_cib != NULL) { - copy_in_properties(generation, the_cib); - free_xml(the_cib); - } - - return generation; -} - -gboolean -apply_cib_diff(xmlNode *old, xmlNode *diff, xmlNode **new) -{ - gboolean result = TRUE; - const char *value = NULL; - - int this_updates = 0; - int this_epoch = 0; - int this_admin_epoch = 0; - - 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; - - CRM_CHECK(diff != NULL, return FALSE); - CRM_CHECK(old != NULL, return FALSE); - - value = crm_element_value(old, XML_ATTR_GENERATION_ADMIN); - this_admin_epoch = crm_parse_int(value, "0"); - crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION_ADMIN, - this_admin_epoch, value); - - value = crm_element_value(old, XML_ATTR_GENERATION); - this_epoch = crm_parse_int(value, "0"); - crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION, this_epoch, value); - - value = crm_element_value(old, XML_ATTR_NUMUPDATES); - this_updates = crm_parse_int(value, "0"); - crm_debug_3("%s=%d (%s)", XML_ATTR_NUMUPDATES, this_updates, value); - - cib_diff_version_details( - diff, - &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, - &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); - - value = NULL; - if(result && diff_del_admin_epoch != this_admin_epoch) { - value = XML_ATTR_GENERATION_ADMIN; - result = FALSE; - crm_debug_3("%s=%d", value, diff_del_admin_epoch); - - } else if(result && diff_del_epoch != this_epoch) { - value = XML_ATTR_GENERATION; - result = FALSE; - crm_debug_3("%s=%d", value, diff_del_epoch); - - } else if(result && diff_del_updates != this_updates) { - value = XML_ATTR_NUMUPDATES; - result = FALSE; - crm_debug_3("%s=%d", value, diff_del_updates); - } - - if(result) { - xmlNode *tmp = NULL; - xmlNode *diff_copy = copy_xml(diff); - - tmp = find_xml_node(diff_copy, "diff-removed", TRUE); - if(tmp != NULL) { - xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); - xml_remove_prop(tmp, XML_ATTR_GENERATION); - xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); - } - - tmp = find_xml_node(diff_copy, "diff-added", TRUE); - if(tmp != NULL) { - xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); - xml_remove_prop(tmp, XML_ATTR_GENERATION); - xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); - } - - result = apply_xml_diff(old, diff_copy, new); - free_xml(diff_copy); - - } else { - crm_err("target and diff %s values didnt match", value); - } - - - return result; -} - -gboolean xml_has_child(xmlNode *data, const char *name); - -gboolean -xml_has_child(xmlNode *data, const char *name) -{ - xml_child_iter_filter(data, child, name, - return TRUE; - ); - return FALSE; -} - -gboolean -cib_config_changed(xmlNode *old_cib, xmlNode *new_cib, xmlNode **result) -{ - gboolean config_changes = FALSE; - const char *tag = NULL; - xmlNode *diff = NULL; - xmlNode *dest = NULL; - - if(result) { - *result = NULL; - } - - diff = diff_xml_object(old_cib, new_cib, FALSE); - if(diff == NULL) { - return FALSE; - } - - tag = "diff-removed"; - dest = find_xml_node(diff, tag, FALSE); - if(dest) { - dest = find_xml_node(dest, "cib", FALSE); - - } - - if(dest) { - if(xml_has_child(dest, "status")) { - xmlNode *status = first_named_child(dest, "status"); - free_xml(status); - } - if(xml_has_children(dest)) { - config_changes = TRUE; - } - } - - tag = "diff-added"; - dest = find_xml_node(diff, tag, FALSE); - if(dest) { - dest = find_xml_node(dest, "cib", FALSE); - } - - if(dest) { - if(xml_has_child(dest, "status")) { - xmlNode *status = first_named_child(dest, "status"); - free_xml(status); - } - - xml_prop_iter(dest, name, value, config_changes = TRUE); - - if(xml_has_children(dest)) { - config_changes = TRUE; - } - } - - - if(result) { - *result = diff; - } else { - free_xml(diff); - } - - return config_changes; -} - -xmlNode * -diff_cib_object(xmlNode *old_cib, xmlNode *new_cib, gboolean suppress) -{ - xmlNode *dest = NULL; - xmlNode *src = NULL; - const char *name = NULL; - const char *value = NULL; - - xmlNode *diff = diff_xml_object(old_cib, new_cib, suppress); - - /* add complete version information */ - src = old_cib; - dest = find_xml_node(diff, "diff-removed", FALSE); - if(src != NULL && dest != NULL) { - name = XML_ATTR_GENERATION_ADMIN; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); - - name = XML_ATTR_GENERATION; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); - - name = XML_ATTR_NUMUPDATES; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); - } - - src = new_cib; - dest = find_xml_node(diff, "diff-added", FALSE); - if(src != NULL && dest != NULL) { - name = XML_ATTR_GENERATION_ADMIN; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); - - name = XML_ATTR_GENERATION; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); - - name = XML_ATTR_NUMUPDATES; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); - } - return diff; -} - -void -log_cib_diff(int log_level, xmlNode *diff, const char *function) -{ - int add_updates = 0; - int add_epoch = 0; - int add_admin_epoch = 0; - - int del_updates = 0; - int del_epoch = 0; - int del_admin_epoch = 0; - - if(diff == NULL) { - return; - } - - cib_diff_version_details( - diff, &add_admin_epoch, &add_epoch, &add_updates, - &del_admin_epoch, &del_epoch, &del_updates); - - if(add_updates != del_updates) { - do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function, - del_admin_epoch, del_epoch, del_updates); - do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function, - add_admin_epoch, add_epoch, add_updates); - } else if(diff != NULL) { - do_crm_log(log_level, - "%s: Local-only Change: %d.%d.%d", function, - add_admin_epoch, add_epoch, add_updates); - } - - log_xml_diff(log_level, diff, function); -} - -gboolean -cib_version_details( - xmlNode *cib, int *admin_epoch, int *epoch, int *updates) -{ - const char *value = NULL; - if(cib == NULL) { - *admin_epoch = -1; - *epoch = -1; - *updates = -1; - return FALSE; - - } else { - value = crm_element_value(cib, XML_ATTR_GENERATION_ADMIN); - *admin_epoch = crm_parse_int(value, "-1"); - - value = crm_element_value(cib, XML_ATTR_GENERATION); - *epoch = crm_parse_int(value, "-1"); - - value = crm_element_value(cib, XML_ATTR_NUMUPDATES); - *updates = crm_parse_int(value, "-1"); - } - return TRUE; -} - -gboolean -cib_diff_version_details( - xmlNode *diff, int *admin_epoch, int *epoch, int *updates, - int *_admin_epoch, int *_epoch, int *_updates) -{ - xmlNode *tmp = NULL; - - tmp = find_xml_node(diff, "diff-added", FALSE); - cib_version_details(tmp, admin_epoch, epoch, updates); - - tmp = find_xml_node(diff, "diff-removed", FALSE); - cib_version_details(tmp, _admin_epoch, _epoch, _updates); - return TRUE; -} - -/* - * The caller should never free the return value - */ -xmlNode* -get_object_root(const char *object_type, xmlNode *the_root) -{ - const char *node_stack[2]; - xmlNode *tmp_node = NULL; - - if(the_root == NULL) { - crm_err("CIB root object was NULL"); - return NULL; - } - - node_stack[0] = XML_CIB_TAG_CONFIGURATION; - node_stack[1] = object_type; - - if(object_type == NULL - || strlen(object_type) == 0 - || safe_str_eq(XML_CIB_TAG_SECTION_ALL, object_type) - || safe_str_eq(XML_TAG_CIB, object_type)) { - /* get the whole cib */ - return the_root; - - } else if(strcasecmp(object_type, XML_CIB_TAG_STATUS) == 0) { - /* these live in a different place */ - tmp_node = find_xml_node(the_root, XML_CIB_TAG_STATUS, FALSE); - - node_stack[0] = object_type; - node_stack[1] = NULL; - - } else { - /* tmp_node = first_named_child(the_root, XML_CIB_TAG_CONFIGURATION); */ - tmp_node = find_xml_node_nested(the_root, node_stack, 2); - } - - if (tmp_node == NULL) { - crm_debug_2("Section [%s [%s]] not present in %s", - node_stack[0], - node_stack[1]?node_stack[1]:"", - crm_element_name(the_root)); - } - return tmp_node; -} - -const char * -get_crm_option(xmlNode *cib, const char *name, gboolean do_warn) -{ - const char * value = NULL; - xmlNode * a_default = NULL; - xmlNode * config = get_object_root(XML_CIB_TAG_CRMCONFIG, cib); - - if(config != NULL) { - a_default = find_entity(config, XML_CIB_TAG_NVPAIR, name); - } - - if(a_default == NULL) { - if(do_warn) { - crm_warn("Option %s not set", name); - } - return NULL; - } - - value = crm_element_value(a_default, XML_NVPAIR_ATTR_VALUE); - if(safe_str_eq(value, "")) { - value = NULL; - } - - return value; -} - -xmlNode* -create_cib_fragment_adv( - xmlNode *update, const char *update_section, const char *source) -{ - xmlNode *cib = NULL; - gboolean whole_cib = FALSE; - xmlNode *object_root = NULL; - const char *update_name = NULL; - char *local_section = NULL; - -/* crm_debug("Creating a blank fragment: %s", update_section); */ - - if(update == NULL && update_section == NULL) { - crm_debug_3("Creating a blank fragment"); - update = createEmptyCib(); - crm_xml_add(cib, XML_ATTR_ORIGIN, source); - return update; - - } else if(update == NULL) { - crm_err("No update to create a fragment for"); - return NULL; - - } else if(update_section == NULL) { - local_section = cib_pluralSection(update_name); - update_section = local_section; - } - - if(safe_str_eq(crm_element_name(update), XML_TAG_CIB)) { - whole_cib = TRUE; - } - - if(whole_cib == FALSE) { - cib = createEmptyCib(); - crm_xml_add(cib, XML_ATTR_ORIGIN, source); - object_root = get_object_root(update_section, cib); - add_node_copy(object_root, update); - - } else { - cib = copy_xml(update); - crm_xml_add(cib, XML_ATTR_ORIGIN, source); - } - - crm_free(local_section); - crm_debug_3("Verifying created fragment"); - if(verifyCibXml(cib) == FALSE) { - crm_err("Fragment creation failed"); - crm_log_xml_err(cib, "[src]"); - free_xml(cib); - cib = NULL; - } - - return cib; -} - -/* - * It is the callers responsibility to free both the new CIB (output) - * and the new CIB (input) - */ -xmlNode* -createEmptyCib(void) -{ - xmlNode *cib_root = NULL, *config = NULL, *status = NULL; - - cib_root = create_xml_node(NULL, XML_TAG_CIB); - - config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); - status = create_xml_node(cib_root, XML_CIB_TAG_STATUS); - -/* crm_xml_add(cib_root, "version", "1"); */ - crm_xml_add(cib_root, "generated", XML_BOOLEAN_TRUE); - - create_xml_node(config, XML_CIB_TAG_CRMCONFIG); - create_xml_node(config, XML_CIB_TAG_NODES); - create_xml_node(config, XML_CIB_TAG_RESOURCES); - create_xml_node(config, XML_CIB_TAG_CONSTRAINTS); - - if (verifyCibXml(cib_root)) { - return cib_root; - } - - free_xml(cib_root); - crm_crit("The generated CIB did not pass integrity testing!!" - " All hope is lost."); - return NULL; -} - - -gboolean -verifyCibXml(xmlNode *cib) -{ - int lpc = 0; - gboolean is_valid = TRUE; - xmlNode *tmp_node = NULL; - - const char *sections[] = { - XML_CIB_TAG_NODES, - XML_CIB_TAG_RESOURCES, - XML_CIB_TAG_CONSTRAINTS, - XML_CIB_TAG_STATUS, - XML_CIB_TAG_CRMCONFIG - }; - - if (cib == NULL) { - crm_warn("CIB was empty."); - return FALSE; - } - - /* basic tests... are the standard section all there */ - for(lpc = 0; lpc < DIMOF(sections); lpc++) { - tmp_node = get_object_root(sections[lpc], cib); - if (tmp_node == NULL) { - crm_warn("Section %s is not present in the CIB", - sections[lpc]); - is_valid = FALSE; - } - } - - /* more integrity tests */ - - return is_valid; -} - - -gboolean verify_cib_cmds(cib_t *cib) -{ - gboolean valid = TRUE; - if(cib->cmds->variant_op == NULL) { - crm_err("Operation variant_op not set"); - valid = FALSE; - } - if(cib->cmds->signon == NULL) { - crm_err("Operation signon not set"); - valid = FALSE; - } - if(cib->cmds->signoff == NULL) { - crm_err("Operation signoff not set"); - valid = FALSE; - } - if(cib->cmds->free == NULL) { - crm_err("Operation free not set"); - valid = FALSE; - } - if(cib->cmds->set_op_callback == NULL) { - crm_err("Operation set_op_callback not set"); - valid = FALSE; - } - if(cib->cmds->add_notify_callback == NULL) { - crm_err("Operation add_notify_callback not set"); - valid = FALSE; - } - if(cib->cmds->del_notify_callback == NULL) { - crm_err("Operation del_notify_callback not set"); - valid = FALSE; - } - if(cib->cmds->set_connection_dnotify == NULL) { - crm_err("Operation set_connection_dnotify not set"); - valid = FALSE; - } - if(cib->cmds->channel == NULL) { - crm_err("Operation channel not set"); - valid = FALSE; - } - if(cib->cmds->inputfd == NULL) { - crm_err("Operation inputfd not set"); - valid = FALSE; - } - if(cib->cmds->noop == NULL) { - crm_err("Operation noop not set"); - valid = FALSE; - } - if(cib->cmds->ping == NULL) { - crm_err("Operation ping not set"); - valid = FALSE; - } - if(cib->cmds->query == NULL) { - crm_err("Operation query not set"); - valid = FALSE; - } - if(cib->cmds->query_from == NULL) { - crm_err("Operation query_from not set"); - valid = FALSE; - } - if(cib->cmds->is_master == NULL) { - crm_err("Operation is_master not set"); - valid = FALSE; - } - if(cib->cmds->set_master == NULL) { - crm_err("Operation set_master not set"); - valid = FALSE; - } - if(cib->cmds->set_slave == NULL) { - crm_err("Operation set_slave not set"); - valid = FALSE; - } - if(cib->cmds->set_slave_all == NULL) { - crm_err("Operation set_slave_all not set"); - valid = FALSE; - } - if(cib->cmds->sync == NULL) { - crm_err("Operation sync not set"); - valid = FALSE; - } if(cib->cmds->sync_from == NULL) { - crm_err("Operation sync_from not set"); - valid = FALSE; - } - if(cib->cmds->bump_epoch == NULL) { - crm_err("Operation bump_epoch not set"); - valid = FALSE; - } - if(cib->cmds->create == NULL) { - crm_err("Operation create not set"); - valid = FALSE; - } - if(cib->cmds->modify == NULL) { - crm_err("Operation modify not set"); - valid = FALSE; - } - if(cib->cmds->replace == NULL) { - crm_err("Operation replace not set"); - valid = FALSE; - } - if(cib->cmds->delete == NULL) { - crm_err("Operation delete not set"); - valid = FALSE; - } - if(cib->cmds->erase == NULL) { - crm_err("Operation erase not set"); - valid = FALSE; - } - if(cib->cmds->quit == NULL) { - crm_err("Operation quit not set"); - valid = FALSE; - } - - if(cib->cmds->msgready == NULL) { - crm_err("Operation msgready not set"); - valid = FALSE; - } - if(cib->cmds->rcvmsg == NULL) { - crm_err("Operation rcvmsg not set"); - valid = FALSE; - } - if(cib->cmds->dispatch == NULL) { - crm_err("Operation dispatch not set"); - valid = FALSE; - } - - return valid; -} diff --git a/lib/crm/cib/cib_file.c b/lib/crm/cib/cib_file.c new file mode 100644 index 0000000000..2940d79fd5 --- /dev/null +++ b/lib/crm/cib/cib_file.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2004 International Business Machines + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct cib_file_opaque_s +{ + int flags; + char *filename; + +} cib_file_opaque_t; + +int cib_file_perform_op( + cib_t *cib, const char *op, const char *host, const char *section, + xmlNode *data, xmlNode **output_data, int call_options); + +int cib_file_signon(cib_t* cib, const char *name, enum cib_conn_type type); +int cib_file_signoff(cib_t* cib); +int cib_file_free(cib_t* cib); + +static gboolean cib_file_msgready(cib_t* cib) { return FALSE; } +static IPC_Channel *cib_file_channel(cib_t* cib) { return NULL; } +static int cib_file_inputfd(cib_t* cib) { return cib_NOTSUPPORTED; } +static int cib_file_rcvmsg(cib_t* cib, int blocking) { return cib_NOTSUPPORTED; } +static gboolean cib_file_dispatch(IPC_Channel *channel, gpointer user_data) { return FALSE; } + +static int cib_file_set_connection_dnotify( + cib_t *cib, void (*dnotify)(gpointer user_data)) +{ + return cib_NOTSUPPORTED; +} + + +static int cib_file_register_callback(cib_t* cib, const char *callback, int enabled) +{ + return cib_NOTSUPPORTED; +} + +cib_t *cib_file_new (cib_t *cib, const char *cib_location); + +cib_t* +cib_file_new (cib_t *cib, const char *cib_location) +{ + cib_file_opaque_t *private = NULL; + crm_malloc0(private, sizeof(cib_file_opaque_t)); + + cib->variant_opaque = private; + + if(cib_location == NULL) { + cib_location = getenv("CIB_file"); + } + private->filename = crm_strdup(cib_location); + + /* assign variant specific ops*/ + cib->cmds->variant_op = cib_file_perform_op; + cib->cmds->signon = cib_file_signon; + cib->cmds->signoff = cib_file_signoff; + cib->cmds->free = cib_file_free; + cib->cmds->channel = cib_file_channel; + cib->cmds->inputfd = cib_file_inputfd; + cib->cmds->msgready = cib_file_msgready; + cib->cmds->rcvmsg = cib_file_rcvmsg; + cib->cmds->dispatch = cib_file_dispatch; + + cib->cmds->register_callback = cib_file_register_callback; + cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify; + + return cib; +} +static xmlNode *in_mem_cib = NULL; +static int load_file_cib(const char *filename) +{ + int rc = cib_ok; + struct stat buf; + xmlNode *root = NULL; + FILE *cib_file = NULL; + gboolean dtd_ok = TRUE; + const char *ignore_dtd = NULL; + xmlNode *status = NULL; + + + rc = stat(filename, &buf); + if (rc == 0) { + cib_file = fopen(filename, "r"); + if(cib_file == NULL) { + cl_perror("Could not open config file %s for reading", filename); + return cib_not_authorized; + + } else { + root = file2xml(cib_file, FALSE); + fclose(cib_file); + } + } + + rc = 0; + if(root == NULL) { + crm_warn("Continuing with an empty configuration."); + root = createEmptyCib(); + } + + status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE); + if(status == NULL) { + create_xml_node(root, XML_CIB_TAG_STATUS); + } + + ignore_dtd = crm_element_value(root, "ignore_dtd"); + dtd_ok = validate_with_dtd(root, TRUE, DTD_DIRECTORY"/crm.dtd"); + if(dtd_ok == FALSE) { + crm_err("CIB does not validate against "DTD_DIRECTORY"/crm.dtd"); + if(ignore_dtd != NULL && crm_is_true(ignore_dtd) == FALSE) { + rc = cib_dtd_validation; + goto bail; + } + } + + if(do_id_check(root, NULL, TRUE, FALSE)) { + crm_err("%s does not contain a vaild configuration:" + " ID check failed", + filename); + rc = cib_id_check; + goto bail; + } + + in_mem_cib = root; + return rc; + + bail: + free_xml(root); + root = NULL; + return rc; +} + +int +cib_file_signon(cib_t* cib, const char *name, enum cib_conn_type type) +{ + int rc = cib_ok; + cib_file_opaque_t *private = cib->variant_opaque; + + if(private->filename == FALSE) { + rc = cib_missing; + } else { + rc = load_file_cib(private->filename); + } + + if(rc == cib_ok) { + fprintf(stderr, "%s: Opened connection to local file '%s'\n", name, private->filename); + cib->state = cib_connected_command; + cib->type = cib_command; + + } else { + fprintf(stderr, "%s: Connection to local file '%s' failed: %s\n", + name, private->filename, cib_error2string(rc)); + } + + return rc; +} + +int +cib_file_signoff(cib_t* cib) +{ + int rc = cib_ok; + cib_file_opaque_t *private = cib->variant_opaque; + + crm_debug("Signing out of the CIB Service"); + + rc = write_xml_file(in_mem_cib, private->filename, FALSE); + if(rc > 0) { + crm_info("Wrote CIB to %s", private->filename); + } else { + crm_err("Could not write CIB to %s", private->filename); + } + crm_free(in_mem_cib); + + cib->state = cib_disconnected; + cib->type = cib_none; + + return rc; +} + +int +cib_file_free (cib_t* cib) +{ + int rc = cib_ok; + + crm_warn("Freeing CIB"); + if(cib->state != cib_disconnected) { + rc = cib_file_signoff(cib); + if(rc == cib_ok) { + cib_file_opaque_t *private = cib->variant_opaque; + crm_free(private->filename); + crm_free(cib->cmds); + crm_free(private); + crm_free(cib); + } + } + + return rc; +} + +struct cib_func_entry +{ + const char *op; + gboolean read_only; + cib_op_t fn; +}; + +static struct cib_func_entry cib_file_ops[] = { + {CIB_OP_QUERY, TRUE, cib_process_query}, + {CIB_OP_MODIFY, FALSE, cib_process_modify}, + /* {CIB_OP_UPDATE, FALSE, cib_process_change}, */ + {CIB_OP_APPLY_DIFF, FALSE, cib_process_diff}, + {CIB_OP_BUMP, FALSE, cib_process_bump}, + {CIB_OP_REPLACE, FALSE, cib_process_replace}, + /* {CIB_OP_CREATE, FALSE, cib_process_change}, */ + {CIB_OP_DELETE, FALSE, cib_process_delete}, + {CIB_OP_ERASE, FALSE, cib_process_erase}, +}; + + +int +cib_file_perform_op( + cib_t *cib, const char *op, const char *host, const char *section, + xmlNode *data, xmlNode **output_data, int call_options) +{ + int rc = cib_ok; + gboolean query = FALSE; + gboolean changed = FALSE; + xmlNode *output = NULL; + xmlNode *result_cib = NULL; + cib_op_t *fn = NULL; + int lpc = 0; + static int max_msg_types = DIMOF(cib_file_ops); + + crm_info("%s on %s", op, section); + + if(cib->state == cib_disconnected) { + return cib_not_connected; + } + + if(output_data != NULL) { + *output_data = NULL; + } + + if(op == NULL) { + crm_err("No operation specified"); + return cib_operation; + } + + for (lpc = 0; lpc < max_msg_types; lpc++) { + if (safe_str_eq(op, cib_file_ops[lpc].op)) { + fn = &(cib_file_ops[lpc].fn); + query = cib_file_ops[lpc].read_only; + break; + } + } + + if(fn == NULL) { + return cib_NOTSUPPORTED; + } + + cib->call_id++; + rc = cib_perform_op(op, call_options, fn, query, + section, NULL, data, TRUE, &changed, in_mem_cib, &result_cib, &output); + + if(rc != cib_ok) { + crm_err("Call failed: %s", cib_error2string(rc)); + crm_log_xml(LOG_WARNING, "failed", output); + free_xml(result_cib); + + } else if(query == FALSE) { + free_xml(in_mem_cib); + in_mem_cib = result_cib; + } + + if(output_data && output) { + *output_data = copy_xml(output); + } + + if(cib->op_callback != NULL) { + cib->op_callback(NULL, cib->call_id, rc, output); + } + + return rc; +} + + diff --git a/lib/crm/cib/cib_native.c b/lib/crm/cib/cib_native.c index 43e3a43268..bb587aedf3 100644 --- a/lib/crm/cib/cib_native.c +++ b/lib/crm/cib/cib_native.c @@ -1,885 +1,881 @@ /* * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct cib_native_opaque_s { IPC_Channel *command_channel; IPC_Channel *callback_channel; GCHSource *callback_source; } cib_native_opaque_t; int cib_native_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int cib_native_signon(cib_t* cib, const char *name, enum cib_conn_type type); int cib_native_signoff(cib_t* cib); int cib_native_free(cib_t* cib); IPC_Channel *cib_native_channel(cib_t* cib); int cib_native_inputfd(cib_t* cib); gboolean cib_native_msgready(cib_t* cib); int cib_native_rcvmsg(cib_t* cib, int blocking); gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data); cib_t *cib_native_new (cib_t *cib); -void cib_native_delete(cib_t *cib); int cib_native_set_connection_dnotify( cib_t *cib, void (*dnotify)(gpointer user_data)); void cib_native_notify(gpointer data, gpointer user_data); void cib_native_callback(cib_t *cib, xmlNode *msg); int cib_native_register_callback(cib_t* cib, const char *callback, int enabled); cib_t* cib_native_new (cib_t *cib) { cib_native_opaque_t *native = NULL; - crm_malloc0(cib->variant_opaque, sizeof(cib_native_opaque_t)); - - native = cib->variant_opaque; + crm_malloc0(native, sizeof(cib_native_opaque_t)); + native->command_channel = NULL; native->callback_channel = NULL; + cib->variant_opaque = native; + /* assign variant specific ops*/ cib->cmds->variant_op = cib_native_perform_op; cib->cmds->signon = cib_native_signon; cib->cmds->signoff = cib_native_signoff; cib->cmds->free = cib_native_free; cib->cmds->channel = cib_native_channel; cib->cmds->inputfd = cib_native_inputfd; cib->cmds->msgready = cib_native_msgready; cib->cmds->rcvmsg = cib_native_rcvmsg; cib->cmds->dispatch = cib_native_dispatch; cib->cmds->register_callback = cib_native_register_callback; cib->cmds->set_connection_dnotify = cib_native_set_connection_dnotify; - - return cib; -} -void -cib_native_delete(cib_t *cib) -{ - crm_free(cib->variant_opaque); + return cib; } int cib_native_signon(cib_t* cib, const char *name, enum cib_conn_type type) { int rc = cib_ok; char *uuid_ticket = NULL; xmlNode *reg_msg = NULL; cib_native_opaque_t *native = cib->variant_opaque; crm_debug_4("Connecting command channel"); if(type == cib_command) { cib->state = cib_connected_command; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_rw); } else if(type == cib_query) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_ro); } else if(type == cib_query_synchronous) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_ro_synchronous); } else if(type == cib_command_synchronous) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_rw_synchronous); } else { return cib_not_connected; } if(native->command_channel == NULL) { crm_debug("Connection to command channel failed"); rc = cib_connection; } else if(native->command_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to command channel failed"); rc = cib_authentication; } if(type == cib_query_synchronous || type == cib_command_synchronous) { return rc; } if(rc == cib_ok) { crm_debug_4("Connecting callback channel"); native->callback_source = init_client_ipc_comms( cib_channel_callback, cib_native_dispatch, cib, &(native->callback_channel)); if(native->callback_channel == NULL) { crm_debug("Connection to callback channel failed"); rc = cib_connection; } else if(native->callback_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to callback channel failed"); rc = cib_authentication; } else if(native->callback_source == NULL) { crm_err("Callback source not recorded"); rc = cib_connection; } else { native->callback_channel->send_queue->max_qlen = 500; } } if(rc == cib_ok) { crm_debug_4("Waiting for msg on command channel"); reg_msg = xmlfromIPC(native->command_channel, 0); if(native->command_channel->ops->get_chan_status( native->command_channel) != IPC_CONNECT) { crm_err("No reply message - disconnected - %d", rc); rc = cib_not_connected; } else if(rc != IPC_OK) { crm_err("No reply message - failed - %d", rc); rc = cib_reply_failed; } else if(reg_msg == NULL) { crm_err("No reply message - empty - %d", rc); rc = cib_reply_failed; } } if(rc == cib_ok) { const char *msg_type = NULL; msg_type = crm_element_value(reg_msg, F_CIB_OPERATION); if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) { crm_err("Invalid registration message: %s", msg_type); rc = cib_registration_msg; } else { const char *tmp_ticket = NULL; crm_debug_4("Retrieving callback channel ticket"); tmp_ticket = crm_element_value( reg_msg, F_CIB_CALLBACK_TOKEN); if(tmp_ticket == NULL) { rc = cib_callback_token; } else { uuid_ticket = crm_strdup(tmp_ticket); } } } if(reg_msg != NULL) { free_xml(reg_msg); reg_msg = NULL; } if(rc == cib_ok) { crm_debug_4("Registering callback channel with ticket %s", crm_str(uuid_ticket)); reg_msg = create_xml_node(NULL, __FUNCTION__); crm_xml_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); crm_xml_add(reg_msg, F_CIB_CALLBACK_TOKEN, uuid_ticket); crm_xml_add(reg_msg, F_CIB_CLIENTNAME, name); if(send_ipc_message( native->callback_channel, reg_msg) == FALSE) { rc = cib_callback_register; } free_xml(reg_msg); crm_free(uuid_ticket); } if(rc == cib_ok) { /* In theory IPC_INTR could trip us up here */ crm_debug_4("wait for the callback channel setup to complete"); reg_msg = xmlfromIPC(native->callback_channel, 0); if(native->callback_channel->ops->get_chan_status( native->callback_channel) != IPC_CONNECT) { crm_err("No reply message - disconnected - %d", rc); rc = cib_not_connected; } else if(reg_msg == NULL) { crm_err("No reply message - empty - %d", rc); rc = cib_reply_failed; } free_xml(reg_msg); } if(rc == cib_ok) { crm_debug("Connection to CIB successful"); return cib_ok; } crm_debug("Connection to CIB failed: %s", cib_error2string(rc)); cib_native_signoff(cib); return rc; } int cib_native_signoff(cib_t* cib) { cib_native_opaque_t *native = cib->variant_opaque; crm_debug("Signing out of the CIB Service"); /* close channels */ if (native->command_channel != NULL) { native->command_channel->ops->destroy( native->command_channel); native->command_channel = NULL; } if (native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } if (native->callback_channel != NULL) { #ifdef BUG native->callback_channel->ops->destroy( native->callback_channel); #endif native->callback_channel = NULL; } cib->state = cib_disconnected; cib->type = cib_none; return cib_ok; } int cib_native_free (cib_t* cib) { int rc = cib_ok; crm_warn("Freeing CIB"); if(cib->state != cib_disconnected) { rc = cib_native_signoff(cib); if(rc == cib_ok) { + crm_free(cib->variant_opaque); + crm_free(cib->cmds); crm_free(cib); } } return rc; } IPC_Channel * cib_native_channel(cib_t* cib) { cib_native_opaque_t *native = NULL; if(cib == NULL) { crm_err("Missing cib object"); return NULL; } native = cib->variant_opaque; if(native != NULL) { return native->callback_channel; } crm_err("couldnt find variant specific data in %p", cib); return NULL; } int cib_native_inputfd(cib_t* cib) { IPC_Channel *ch = cib_native_channel(cib); return ch->ops->get_recv_select_fd(ch); } static xmlNode * cib_create_op( int call_id, const char *op, const char *host, const char *section, xmlNode *data, int call_options) { int rc = HA_OK; xmlNode *op_msg = create_xml_node(NULL, "cib-op"); CRM_CHECK(op_msg != NULL, return NULL); crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command"); crm_xml_add(op_msg, F_TYPE, T_CIB); crm_xml_add(op_msg, F_CIB_OPERATION, op); crm_xml_add(op_msg, F_CIB_HOST, host); crm_xml_add(op_msg, F_CIB_SECTION, section); crm_xml_add_int(op_msg, F_CIB_CALLID, call_id); crm_debug_4("Sending call options: %.8lx, %d", (long)call_options, call_options); crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options); if(data != NULL) { #if 0 const char *tag = crm_element_name(data); xmlNode *cib = data; if(safe_str_neq(tag, XML_TAG_CIB)) { cib = find_xml_node(data, XML_TAG_CIB, FALSE); if(cib != NULL) { tag = XML_TAG_CIB; } } if(safe_str_eq(tag, XML_TAG_CIB)) { const char *version = feature_set(cib); crm_xml_add(cib, XML_ATTR_CIB_REVISION, version); } else { crm_info("Skipping feature check for %s tag", tag); } #endif add_message_xml(op_msg, F_CIB_CALLDATA, data); } if (rc != HA_OK) { crm_err("Failed to create CIB operation message"); crm_log_xml(LOG_ERR, "op", op_msg); free_xml(op_msg); return NULL; } if(call_options & cib_inhibit_bcast) { CRM_CHECK((call_options & cib_scope_local), return NULL); } return op_msg; } static gboolean timer_expired = FALSE; static struct timer_rec_s *sync_timer = NULL; static gboolean cib_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; timer_expired = TRUE; crm_err("Call %d timed out after %ds", timer->call_id, timer->timeout); /* Always return TRUE, never remove the handler * We do that after the while-loop in cib_native_perform_op() */ return TRUE; } int cib_native_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { int rc = HA_OK; xmlNode *op_msg = NULL; xmlNode *op_reply = NULL; cib_native_opaque_t *native = cib->variant_opaque; if(sync_timer == NULL) { crm_malloc0(sync_timer, sizeof(struct timer_rec_s)); } if(cib->state == cib_disconnected) { return cib_not_connected; } if(output_data != NULL) { *output_data = NULL; } if(op == NULL) { crm_err("No operation specified"); return cib_operation; } cib->call_id++; /* prevent call_id from being negative (or zero) and conflicting * with the cib_errors enum * use 2 because we use it as (cib->call_id - 1) below */ if(cib->call_id < 1) { cib->call_id = 1; } op_msg = cib_create_op( cib->call_id, op, host, section, data, call_options); if(op_msg == NULL) { return cib_create_msg; } crm_debug_3("Sending %s message to CIB service", op); if(send_ipc_message(native->command_channel, op_msg) == FALSE) { crm_err("Sending message to CIB service FAILED"); free_xml(op_msg); return cib_send_failed; } else { crm_debug_3("Message sent"); } free_xml(op_msg); if((call_options & cib_discard_reply)) { crm_debug_3("Discarding reply"); return cib_ok; } else if(!(call_options & cib_sync_call)) { crm_debug_3("Async call, returning"); CRM_CHECK(cib->call_id != 0, return cib_reply_failed); return cib->call_id; } rc = IPC_OK; crm_debug_3("Waiting for a syncronous reply"); if(cib->call_timeout > 0) { /* We need this, even with msgfromIPC_timeout(), because we might * get other/older replies that don't match the active request */ timer_expired = FALSE; sync_timer->call_id = cib->call_id; sync_timer->timeout = cib->call_timeout*1000; sync_timer->ref = Gmain_timeout_add( sync_timer->timeout, cib_timeout_handler, sync_timer); } while(timer_expired == FALSE && IPC_ISRCONN(native->command_channel)) { int reply_id = -1; int msg_id = cib->call_id; op_reply = xmlfromIPC(native->command_channel, cib->call_timeout); if(op_reply == NULL) { break; } crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); CRM_CHECK(reply_id > 0, free_xml(op_reply); if(sync_timer->ref > 0) { g_source_remove(sync_timer->ref); sync_timer->ref = 0; } return cib_reply_failed); if(reply_id == msg_id) { break; } else if(reply_id < msg_id) { crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml( LOG_MSG, "Old reply", op_reply); } else if((reply_id - 10000) > msg_id) { /* wrap-around case */ crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml( LOG_MSG, "Old reply", op_reply); } else { crm_err("Received a __future__ reply:" " %d (wanted %d)", reply_id, msg_id); } free_xml(op_reply); op_reply = NULL; } if(sync_timer->ref > 0) { g_source_remove(sync_timer->ref); sync_timer->ref = 0; } if(timer_expired) { return cib_remote_timeout; } if(op_reply == NULL) { if(IPC_ISRCONN(native->command_channel) == FALSE) { crm_err("No reply message - disconnected - %d", native->command_channel->ch_status); cib->state = cib_disconnected; return cib_not_connected; } crm_err("No reply message - empty - %d", rc); return cib_reply_failed; } if(IPC_ISRCONN(native->command_channel) == FALSE) { crm_err("CIB disconnected: %d", native->command_channel->ch_status); cib->state = cib_disconnected; } crm_debug_3("Syncronous reply recieved"); rc = cib_ok; /* Start processing the reply... */ if(crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { rc = cib_return_code; } if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } if(rc == cib_ok || rc == cib_not_master || rc == cib_master_timeout) { crm_log_xml(LOG_MSG, "passed", op_reply); } else { /* } else if(rc == cib_remote_timeout) { */ crm_err("Call failed: %s", cib_error2string(rc)); crm_log_xml(LOG_WARNING, "failed", op_reply); } if(output_data == NULL) { /* do nothing more */ } else if(!(call_options & cib_discard_reply)) { - xmlNode *tmp = *output_data = get_message_xml(op_reply, F_CIB_CALLDATA); + xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); if(tmp == NULL) { crm_debug_3("No output in reply to \"%s\" command %d", op, cib->call_id - 1); } else { *output_data = copy_xml(tmp); } } free_xml(op_reply); return rc; } gboolean cib_native_msgready(cib_t* cib) { cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; if(native->command_channel != NULL) { /* drain the channel */ IPC_Channel *cmd_ch = native->command_channel; xmlNode *cmd_msg = NULL; while(cmd_ch->ch_status != IPC_DISCONNECT && cmd_ch->ops->is_message_pending(cmd_ch)) { /* this will happen when the CIB exited from beneath us */ cmd_msg = xmlfromIPC(cmd_ch, 0); free_xml(cmd_msg); } } else { crm_err("No command channel"); } if(native->callback_channel == NULL) { crm_err("No callback channel"); return FALSE; } else if(native->callback_channel->ch_status == IPC_DISCONNECT) { crm_info("Lost connection to the CIB service [%d].", native->callback_channel->farside_pid); return FALSE; } else if(native->callback_channel->ops->is_message_pending( native->callback_channel)) { crm_debug_4("Message pending on command channel [%d]", native->callback_channel->farside_pid); return TRUE; } crm_debug_3("No message pending"); return FALSE; } int cib_native_rcvmsg(cib_t* cib, int blocking) { const char *type = NULL; xmlNode* msg = NULL; cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; /* if it is not blocking mode and no message in the channel, return */ if (blocking == 0 && cib_native_msgready(cib) == FALSE) { crm_debug_3("No message ready and non-blocking..."); return 0; } else if (cib_native_msgready(cib) == FALSE) { crm_debug("Waiting for message from CIB service..."); if(native->callback_channel == NULL) { return 0; } else if(native->callback_channel->ch_status != IPC_CONNECT) { return 0; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT){ return 0; } native->callback_channel->ops->waitin(native->callback_channel); } /* IPC_INTR is not a factor here */ msg = xmlfromIPC(native->callback_channel, 0); if (msg == NULL) { crm_warn("Received a NULL msg from CIB service."); return 0; } /* do callbacks */ type = crm_element_value(msg, F_TYPE); crm_debug_4("Activating %s callbacks...", type); if(safe_str_eq(type, T_CIB)) { cib_native_callback(cib, msg); } else if(safe_str_eq(type, T_CIB_NOTIFY)) { g_list_foreach(cib->notify_list, cib_native_notify, msg); } else { crm_err("Unknown message type: %s", type); } free_xml(msg); return 1; } void cib_native_callback(cib_t *cib, xmlNode *msg) { int rc = 0; int call_id = 0; xmlNode *output = NULL; cib_callback_client_t *blob = NULL; cib_callback_client_t local_blob; /* gcc4 had a point... make sure (at least) local_blob.callback * is initialized before use */ local_blob.callback = NULL; local_blob.user_data = NULL; local_blob.only_success = FALSE; crm_element_value_int(msg, F_CIB_CALLID, &call_id); blob = g_hash_table_lookup( cib_op_callback_table, GINT_TO_POINTER(call_id)); if(blob != NULL) { crm_debug_3("Callback found for call %d", call_id); /* local_blob.callback = blob->callback; */ /* local_blob.user_data = blob->user_data; */ /* local_blob.only_success = blob->only_success; */ local_blob = *blob; blob = NULL; remove_cib_op_callback(call_id, FALSE); } else { crm_debug_3("No callback found for call %d", call_id); local_blob.callback = NULL; } crm_element_value_int(msg, F_CIB_RC, &rc); if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } output = get_message_xml(msg, F_CIB_CALLDATA); if(local_blob.callback != NULL && (rc == cib_ok || local_blob.only_success == FALSE)) { local_blob.callback( msg, call_id, rc, output, local_blob.user_data); } else if(cib->op_callback == NULL && rc != cib_ok) { crm_warn("CIB command failed: %s", cib_error2string(rc)); crm_log_xml(LOG_DEBUG, "Failed CIB Update", msg); } if(cib->op_callback == NULL) { crm_debug_3("No OP callback set, ignoring reply"); } else { cib->op_callback(msg, call_id, rc, output); } crm_debug_4("OP callback activated."); } void cib_native_notify(gpointer data, gpointer user_data) { xmlNode *msg = user_data; cib_notify_client_t *entry = data; const char *event = NULL; if(msg == NULL) { crm_warn("Skipping callback - NULL message"); return; } event = crm_element_value(msg, F_SUBTYPE); if(entry == NULL) { crm_warn("Skipping callback - NULL callback client"); return; } else if(entry->callback == NULL) { crm_warn("Skipping callback - NULL callback"); return; } else if(safe_str_neq(entry->event, event)) { crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event); return; } crm_debug_4("Invoking callback for %p/%s event...", entry, event); entry->callback(event, msg); crm_debug_4("Callback invoked..."); } gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; cib_t *cib = user_data; cib_native_opaque_t *native = NULL; crm_debug_3("Received callback"); if(user_data == NULL){ crm_err("user_data field must contain the CIB struct"); return FALSE; } native = cib->variant_opaque; while(cib_native_msgready(cib)) { lpc++; /* invoke the callbacks but dont block */ if(cib_native_rcvmsg(cib, 0) < 1) { break; } } crm_debug_3("%d CIB messages dispatched", lpc); if(native->callback_channel && native->callback_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/callback].", channel->farside_pid); if(native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } return FALSE; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/command].", channel->farside_pid); return FALSE; } return TRUE; } int cib_native_set_connection_dnotify( cib_t *cib, void (*dnotify)(gpointer user_data)) { cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; if(dnotify == NULL) { crm_warn("Setting dnotify back to default value"); set_IPC_Channel_dnotify(native->callback_source, default_ipc_connection_destroy); } else { crm_debug_3("Setting dnotify"); set_IPC_Channel_dnotify(native->callback_source, dnotify); } return cib_ok; } int cib_native_register_callback(cib_t* cib, const char *callback, int enabled) { xmlNode *notify_msg = create_xml_node(NULL, "cib-callback"); cib_native_opaque_t *native = cib->variant_opaque; /* short term hack - should make this generic somehow */ crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY); crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback); crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled); send_ipc_message(native->callback_channel, notify_msg); free_xml(notify_msg); return cib_ok; } diff --git a/lib/crm/cib/cib_ops.c b/lib/crm/cib/cib_ops.c new file mode 100644 index 0000000000..2d4d7c76dd --- /dev/null +++ b/lib/crm/cib/cib_ops.c @@ -0,0 +1,620 @@ +/* + * 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 + +enum cib_errors +cib_process_query( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *obj_root = NULL; + enum cib_errors result = cib_ok; + + crm_debug_2("Processing \"%s\" event for section=%s", + op, crm_str(section)); + + CRM_CHECK(*answer == NULL, free_xml(*answer)); + *answer = NULL; + + if (safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { + section = NULL; + } + + obj_root = get_object_root(section, existing_cib); + + if(obj_root == NULL) { + result = cib_NOTEXISTS; + + } else { + *answer = obj_root; + } + + if(result == cib_ok && *answer == NULL) { + crm_err("Error creating query response"); + result = cib_output_data; + } + + return result; +} + +enum cib_errors +cib_process_erase( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + enum cib_errors result = cib_ok; + + crm_debug_2("Processing \"%s\" event", op); + *answer = NULL; + free_xml(*result_cib); + *result_cib = createEmptyCib(); + + copy_in_properties(*result_cib, existing_cib); + cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); + + return result; +} + +enum cib_errors +cib_process_bump( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + enum cib_errors result = cib_ok; + + crm_debug_2("Processing \"%s\" event for epoch=%s", + op, crm_str(crm_element_value(existing_cib, XML_ATTR_GENERATION))); + + *answer = NULL; + cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); + + return result; +} + + +enum cib_errors +cib_update_counter(xmlNode *xml_obj, const char *field, gboolean reset) +{ + char *new_value = NULL; + char *old_value = NULL; + int int_value = -1; + + if(reset == FALSE && crm_element_value(xml_obj, field) != NULL) { + old_value = crm_element_value_copy(xml_obj, field); + } + if(old_value != NULL) { + crm_malloc0(new_value, 128); + int_value = atoi(old_value); + sprintf(new_value, "%d", ++int_value); + } else { + new_value = crm_strdup("1"); + } + + crm_debug_4("%s %d(%s)->%s", + field, int_value, crm_str(old_value), crm_str(new_value)); + crm_xml_add(xml_obj, field, new_value); + + crm_free(new_value); + crm_free(old_value); + + return cib_ok; +} + +enum cib_errors +cib_process_replace( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + const char *tag = NULL; + gboolean verbose = FALSE; + enum cib_errors result = cib_ok; + + crm_debug_2("Processing \"%s\" event for section=%s", + op, crm_str(section)); + *answer = NULL; + + if (input == NULL) { + return cib_NOOBJECT; + } + + tag = crm_element_name(input); + + if (options & cib_verbose) { + verbose = TRUE; + } + if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { + section = NULL; + + } else if(safe_str_eq(tag, section)) { + section = NULL; + } + + if(safe_str_eq(tag, XML_TAG_CIB)) { + int updates = 0; + int epoch = 0; + int admin_epoch = 0; + + int replace_updates = 0; + int replace_epoch = 0; + int replace_admin_epoch = 0; + const char *reason = NULL; + + cib_version_details( + existing_cib, &admin_epoch, &epoch, &updates); + cib_version_details(input, &replace_admin_epoch, + &replace_epoch, &replace_updates); + + if(replace_admin_epoch < admin_epoch) { + reason = XML_ATTR_GENERATION_ADMIN; + + } else if(replace_admin_epoch > admin_epoch) { + /* no more checks */ + + } else if(replace_epoch < epoch) { + reason = XML_ATTR_GENERATION; + + } else if(replace_epoch > epoch) { + /* no more checks */ + + } else if(replace_updates < updates) { + reason = XML_ATTR_NUMUPDATES; + } + + if(reason != NULL) { + crm_warn("Replacement %d.%d.%d not applied to %d.%d.%d:" + " current %s is greater than the replacement", + replace_admin_epoch, replace_epoch, + replace_updates, admin_epoch, epoch, updates, + reason); + result = cib_old_data; + } + + /* sync_in_progress = 0; */ + crm_err("Set sync_in_progress=0"); + + free_xml(*result_cib); + *result_cib = copy_xml(input); + + } else { + xmlNode *obj_root = NULL; + gboolean ok = TRUE; + obj_root = get_object_root(section, *result_cib); + ok = replace_xml_child(NULL, obj_root, input, FALSE); + if(ok == FALSE) { + crm_debug_2("No matching object to replace"); + result = cib_NOTEXISTS; + } + } + + return result; +} + +enum cib_errors +cib_process_delete( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *obj_root = NULL; + crm_debug_2("Processing \"%s\" event", op); + + if(input == NULL) { + crm_err("Cannot perform modification with no data"); + return cib_NOOBJECT; + } + + obj_root = get_object_root(section, *result_cib); + + crm_validate_data(input); + crm_validate_data(*result_cib); + + if(replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) { + crm_debug_2("No matching object to delete"); + } + + return cib_ok; +} + +enum cib_errors +cib_process_modify( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + xmlNode *obj_root = NULL; + crm_debug_2("Processing \"%s\" event", op); + + if(input == NULL) { + crm_err("Cannot perform modification with no data"); + return cib_NOOBJECT; + } + + obj_root = get_object_root(section, *result_cib); + + crm_validate_data(input); + crm_validate_data(*result_cib); + + if(update_xml_child(obj_root, input) == FALSE) { + return cib_NOTEXISTS; + } + + return cib_ok; +} + +enum cib_errors +cib_process_diff( + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) +{ + unsigned int log_level = LOG_DEBUG; + const char *value = NULL; + const char *reason = NULL; + gboolean apply_diff = TRUE; + enum cib_errors result = cib_ok; + + int this_updates = 0; + int this_epoch = 0; + int this_admin_epoch = 0; + + 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; + + crm_debug_2("Processing \"%s\" event", op); + + cib_diff_version_details( + input, + &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, + &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + + + value = crm_element_value(existing_cib, XML_ATTR_GENERATION); + this_epoch = atoi(value?value:"0"); + + value = crm_element_value(existing_cib, XML_ATTR_NUMUPDATES); + this_updates = atoi(value?value:"0"); + + value = crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN); + this_admin_epoch = atoi(value?value:"0"); + + if(diff_del_admin_epoch == diff_add_admin_epoch + && diff_del_epoch == diff_add_epoch + && diff_del_updates == diff_add_updates) { + if(diff_add_admin_epoch == -1 && diff_add_epoch == -1 && diff_add_updates == -1) { + diff_add_epoch = this_epoch; + diff_add_updates = this_updates + 1; + diff_add_admin_epoch = this_admin_epoch; + diff_del_epoch = this_epoch; + diff_del_updates = this_updates; + diff_del_admin_epoch = this_admin_epoch; + } else { + apply_diff = FALSE; + log_level = LOG_ERR; + reason = "+ and - versions in the diff did not change"; + log_cib_diff(LOG_ERR, input, __FUNCTION__); + } + } + + if(apply_diff && diff_del_admin_epoch > this_admin_epoch) { + result = cib_diff_resync; + apply_diff = FALSE; + log_level = LOG_INFO; + reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is less than required"; + + } else if(apply_diff && diff_del_admin_epoch < this_admin_epoch) { + apply_diff = FALSE; + log_level = LOG_WARNING; + reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is greater than required"; + } + + if(apply_diff && diff_del_epoch > this_epoch) { + result = cib_diff_resync; + apply_diff = FALSE; + log_level = LOG_INFO; + reason = "current \""XML_ATTR_GENERATION"\" is less than required"; + + } else if(apply_diff && diff_del_epoch < this_epoch) { + apply_diff = FALSE; + log_level = LOG_WARNING; + reason = "current \""XML_ATTR_GENERATION"\" is greater than required"; + } + + if(apply_diff && diff_del_updates > this_updates) { + result = cib_diff_resync; + apply_diff = FALSE; + log_level = LOG_INFO; + reason = "current \""XML_ATTR_NUMUPDATES"\" is less than required"; + + } else if(apply_diff && diff_del_updates < this_updates) { + apply_diff = FALSE; + log_level = LOG_WARNING; + reason = "current \""XML_ATTR_NUMUPDATES"\" is greater than required"; + } + + if(apply_diff) { + free_xml(*result_cib); + *result_cib = NULL; + if(apply_xml_diff(existing_cib, input, result_cib) == FALSE) { + log_level = LOG_NOTICE; + reason = "Failed application of an update diff"; + + if(options & cib_force_diff) { + result = cib_diff_resync; + } + + } else if((options & cib_force_diff) && !validate_with_dtd( + *result_cib, FALSE, DTD_DIRECTORY"/crm.dtd")) { + + log_level = LOG_NOTICE; + result = cib_diff_resync; + reason = "Failed DTD validation of a global update."; + } + } + + if(reason != NULL) { + do_crm_log( + log_level, + "Diff %d.%d.%d -> %d.%d.%d not applied to %d.%d.%d: %s", + diff_del_admin_epoch,diff_del_epoch,diff_del_updates, + diff_add_admin_epoch,diff_add_epoch,diff_add_updates, + this_admin_epoch,this_epoch,this_updates, reason); + + if(result == cib_ok) { + result = cib_diff_failed; + } + + } else if(apply_diff) { + crm_debug_2("Diff %d.%d.%d -> %d.%d.%d was applied", + diff_del_admin_epoch,diff_del_epoch,diff_del_updates, + diff_add_admin_epoch,diff_add_epoch,diff_add_updates); + } + return result; +} + +gboolean +apply_cib_diff(xmlNode *old, xmlNode *diff, xmlNode **new) +{ + gboolean result = TRUE; + const char *value = NULL; + + int this_updates = 0; + int this_epoch = 0; + int this_admin_epoch = 0; + + 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; + + CRM_CHECK(diff != NULL, return FALSE); + CRM_CHECK(old != NULL, return FALSE); + + value = crm_element_value(old, XML_ATTR_GENERATION_ADMIN); + this_admin_epoch = crm_parse_int(value, "0"); + crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION_ADMIN, + this_admin_epoch, value); + + value = crm_element_value(old, XML_ATTR_GENERATION); + this_epoch = crm_parse_int(value, "0"); + crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION, this_epoch, value); + + value = crm_element_value(old, XML_ATTR_NUMUPDATES); + this_updates = crm_parse_int(value, "0"); + crm_debug_3("%s=%d (%s)", XML_ATTR_NUMUPDATES, this_updates, value); + + cib_diff_version_details( + diff, + &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, + &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + + value = NULL; + if(result && diff_del_admin_epoch != this_admin_epoch) { + value = XML_ATTR_GENERATION_ADMIN; + result = FALSE; + crm_debug_3("%s=%d", value, diff_del_admin_epoch); + + } else if(result && diff_del_epoch != this_epoch) { + value = XML_ATTR_GENERATION; + result = FALSE; + crm_debug_3("%s=%d", value, diff_del_epoch); + + } else if(result && diff_del_updates != this_updates) { + value = XML_ATTR_NUMUPDATES; + result = FALSE; + crm_debug_3("%s=%d", value, diff_del_updates); + } + + if(result) { + xmlNode *tmp = NULL; + xmlNode *diff_copy = copy_xml(diff); + + tmp = find_xml_node(diff_copy, "diff-removed", TRUE); + if(tmp != NULL) { + xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); + xml_remove_prop(tmp, XML_ATTR_GENERATION); + xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); + } + + tmp = find_xml_node(diff_copy, "diff-added", TRUE); + if(tmp != NULL) { + xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); + xml_remove_prop(tmp, XML_ATTR_GENERATION); + xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); + } + + result = apply_xml_diff(old, diff_copy, new); + free_xml(diff_copy); + + } else { + crm_err("target and diff %s values didnt match", value); + } + + + return result; +} + +gboolean +cib_config_changed(xmlNode *old_cib, xmlNode *new_cib, xmlNode **result) +{ + gboolean config_changes = FALSE; + const char *tag = NULL; + xmlNode *diff = NULL; + xmlNode *dest = NULL; + + if(result) { + *result = NULL; + } + + diff = diff_xml_object(old_cib, new_cib, FALSE); + if(diff == NULL) { + return FALSE; + } + + tag = "diff-removed"; + dest = find_xml_node(diff, tag, FALSE); + if(dest) { + dest = find_xml_node(dest, "cib", FALSE); + } + + if(dest) { + if(first_named_child(dest, "status")) { + xmlNode *status = first_named_child(dest, "status"); + free_xml(status); + } + if(xml_has_children(dest)) { + config_changes = TRUE; + } + } + + tag = "diff-added"; + dest = find_xml_node(diff, tag, FALSE); + if(dest) { + dest = find_xml_node(dest, "cib", FALSE); + } + + if(dest) { + if(first_named_child(dest, "status")) { + xmlNode *status = first_named_child(dest, "status"); + free_xml(status); + } + + xml_prop_iter(dest, name, value, config_changes = TRUE); + + if(xml_has_children(dest)) { + config_changes = TRUE; + } + } + + + if(result) { + *result = diff; + } else { + free_xml(diff); + } + + return config_changes; +} + +xmlNode * +diff_cib_object(xmlNode *old_cib, xmlNode *new_cib, gboolean suppress) +{ + xmlNode *dest = NULL; + xmlNode *src = NULL; + const char *name = NULL; + const char *value = NULL; + + xmlNode *diff = diff_xml_object(old_cib, new_cib, suppress); + + /* add complete version information */ + src = old_cib; + dest = find_xml_node(diff, "diff-removed", FALSE); + if(src != NULL && dest != NULL) { + name = XML_ATTR_GENERATION_ADMIN; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + + name = XML_ATTR_GENERATION; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + + name = XML_ATTR_NUMUPDATES; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + } + + src = new_cib; + dest = find_xml_node(diff, "diff-added", FALSE); + if(src != NULL && dest != NULL) { + name = XML_ATTR_GENERATION_ADMIN; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + + name = XML_ATTR_GENERATION; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + + name = XML_ATTR_NUMUPDATES; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + } + return diff; +} diff --git a/lib/crm/cib/cib_private.h b/lib/crm/cib/cib_private.h index 5a3c0270ce..94692ec636 100644 --- a/lib/crm/cib/cib_private.h +++ b/lib/crm/cib/cib_private.h @@ -1,29 +1,59 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB_PRIVATE__H #define CIB_PRIVATE__H #include extern GHashTable *cib_op_callback_table; +typedef struct cib_notify_client_s +{ + const char *event; + const char *obj_id; /* implement one day */ + const char *obj_type; /* implement one day */ + void (*callback)(const char *event, xmlNode *msg); + +} cib_notify_client_t; -#endif +typedef struct cib_callback_client_s +{ + void (*callback)(xmlNode*, int, int, xmlNode*, void*); + void *user_data; + gboolean only_success; + struct timer_rec_s *timer; + +} cib_callback_client_t; + +struct timer_rec_s +{ + int call_id; + int timeout; + guint ref; +}; +typedef enum cib_errors (*cib_op_t)(const char *, int, const char *, xmlNode *, + xmlNode*, xmlNode*, xmlNode**, xmlNode**); +enum cib_errors +cib_perform_op(const char *op, int call_options, cib_op_t *fn, gboolean is_query, + const char *section, xmlNode *req, xmlNode *input, + gboolean manage_counters, gboolean *config_changed, + xmlNode *current_cib, xmlNode **result_cib, xmlNode **output); +#endif diff --git a/lib/crm/cib/cib_utils.c b/lib/crm/cib/cib_utils.c new file mode 100644 index 0000000000..b18b3f5dbe --- /dev/null +++ b/lib/crm/cib/cib_utils.c @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2004 International Business Machines + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +char * +cib_pluralSection(const char *a_section) +{ + char *a_section_parent = NULL; + if (a_section == NULL) { + a_section_parent = crm_strdup("all"); + + } else if(strcasecmp(a_section, XML_TAG_CIB) == 0) { + a_section_parent = crm_strdup("all"); + + } else if(strcasecmp(a_section, XML_CIB_TAG_NODE) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_NODES); + + } else if(strcasecmp(a_section, XML_CIB_TAG_STATE) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_STATUS); + + } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINT) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); + + } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_LOCATION) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); + + } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_DEPEND) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); + + } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_ORDER) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); + + } else if(strcasecmp(a_section, "resource") == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); + + } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCE) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); + + } else if(strcasecmp(a_section, XML_CIB_TAG_GROUP) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); + + } else if(strcasecmp(a_section, XML_CIB_TAG_INCARNATION) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); + + } else if(strcasecmp(a_section, XML_CIB_TAG_NVPAIR) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); + + } else if(strcasecmp(a_section, XML_TAG_ATTR_SETS) == 0) { + a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); + + } else { + crm_err("Unknown section %s", a_section); + a_section_parent = crm_strdup("all"); + } + + crm_debug_2("Plural of %s is %s", crm_str(a_section), a_section_parent); + + return a_section_parent; +} + +const char * +cib_error2string(enum cib_errors return_code) +{ + const char *error_msg = NULL; + switch(return_code) { + case cib_bad_permissions: + error_msg = "bad permissions for the on-disk configuration. shutdown heartbeat and repair."; + break; + case cib_bad_digest: + error_msg = "the on-disk configuration was manually altered. shutdown heartbeat and repair."; + break; + case cib_bad_config: + error_msg = "the on-disk configuration is not valid"; + break; + case cib_msg_field_add: + error_msg = "failed adding field to cib message"; + break; + case cib_id_check: + error_msg = "missing id or id-collision detected"; + break; + case cib_operation: + error_msg = "invalid operation"; + break; + case cib_create_msg: + error_msg = "couldnt create cib message"; + break; + case cib_client_gone: + error_msg = "client left before we could send reply"; + break; + case cib_not_connected: + error_msg = "not connected"; + break; + case cib_not_authorized: + error_msg = "not authorized"; + break; + case cib_send_failed: + error_msg = "send failed"; + break; + case cib_reply_failed: + error_msg = "reply failed"; + break; + case cib_return_code: + error_msg = "no return code"; + break; + case cib_output_ptr: + error_msg = "nowhere to store output"; + break; + case cib_output_data: + error_msg = "corrupt output data"; + break; + case cib_connection: + error_msg = "connection failed"; + break; + case cib_callback_register: + error_msg = "couldnt register callback channel"; + break; + case cib_authentication: + error_msg = ""; + break; + case cib_registration_msg: + error_msg = "invalid registration msg"; + break; + case cib_callback_token: + error_msg = "callback token not found"; + break; + case cib_missing: + error_msg = "cib object missing"; + break; + case cib_variant: + error_msg = "unknown/corrupt cib variant"; + break; + case CIBRES_MISSING_ID: + error_msg = "The id field is missing"; + break; + case CIBRES_MISSING_TYPE: + error_msg = "The type field is missing"; + break; + case CIBRES_MISSING_FIELD: + error_msg = "A required field is missing"; + break; + case CIBRES_OBJTYPE_MISMATCH: + error_msg = "CIBRES_OBJTYPE_MISMATCH"; + break; + case cib_EXISTS: + error_msg = "The object already exists"; + break; + case cib_NOTEXISTS: + error_msg = "The object/attribute does not exist"; + break; + case CIBRES_CORRUPT: + error_msg = "The CIB is corrupt"; + break; + case cib_NOOBJECT: + error_msg = "The update was empty"; + break; + case cib_NOPARENT: + error_msg = "The parent object does not exist"; + break; + case cib_NODECOPY: + error_msg = "Failed while copying update"; + break; + case CIBRES_OTHER: + error_msg = "CIBRES_OTHER"; + break; + case cib_ok: + error_msg = "ok"; + break; + case cib_unknown: + error_msg = "Unknown error"; + break; + case cib_STALE: + error_msg = "Discarded old update"; + break; + case cib_ACTIVATION: + error_msg = "Activation Failed"; + break; + case cib_NOSECTION: + error_msg = "Required section was missing"; + break; + case cib_NOTSUPPORTED: + error_msg = "The action/feature is not supported"; + break; + case cib_not_master: + error_msg = "Local service is not the master instance"; + break; + case cib_client_corrupt: + error_msg = "Service client not valid"; + break; + case cib_remote_timeout: + error_msg = "Remote node did not respond"; + break; + case cib_master_timeout: + error_msg = "No master service is currently active"; + break; + case cib_revision_unsupported: + error_msg = "The required CIB revision number is not supported"; + break; + case cib_revision_unknown: + error_msg = "The CIB revision number could not be determined"; + break; + case cib_missing_data: + error_msg = "Required data for this CIB API call not found"; + break; + case cib_no_quorum: + error_msg = "Write requires quorum"; + break; + case cib_diff_failed: + error_msg = "Application of an update diff failed"; + break; + case cib_diff_resync: + error_msg = "Application of an update diff failed, requesting a full refresh"; + break; + case cib_bad_section: + error_msg = "Invalid CIB section specified"; + break; + case cib_old_data: + error_msg = "Update was older than existing configuration"; + break; + case cib_dtd_validation: + error_msg = "Update does not conform to the DTD in "DTD_DIRECTORY"/crm.dtd"; + break; + case cib_invalid_argument: + error_msg = "Invalid argument"; + break; + } + + if(error_msg == NULL) { + crm_err("Unknown CIB Error Code: %d", return_code); + error_msg = ""; + } + + return error_msg; +} + +int +cib_section2enum(const char *a_section) +{ + if(a_section == NULL || strcasecmp(a_section, "all") == 0) { + return cib_section_all; + + } else if(strcasecmp(a_section, XML_CIB_TAG_NODES) == 0) { + return cib_section_nodes; + + } else if(strcasecmp(a_section, XML_CIB_TAG_STATUS) == 0) { + return cib_section_status; + + } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) { + return cib_section_constraints; + + } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCES) == 0) { + return cib_section_resources; + + } else if(strcasecmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) { + return cib_section_crmconfig; + + } + crm_err("Unknown CIB section: %s", a_section); + return cib_section_none; +} + + +int +cib_compare_generation(xmlNode *left, xmlNode *right) +{ + int lpc = 0; + const char *attributes[] = { + XML_ATTR_GENERATION_ADMIN, + XML_ATTR_GENERATION, + XML_ATTR_NUMUPDATES, + XML_ATTR_NUMPEERS + }; + + crm_log_xml_debug_3(left, "left"); + crm_log_xml_debug_3(right, "right"); + + for(lpc = 0; lpc < DIMOF(attributes); lpc++) { + int int_elem_l = -1; + int int_elem_r = -1; + const char *elem_r = NULL; + const char *elem_l = crm_element_value(left, attributes[lpc]); + + if(right != NULL) { + elem_r = crm_element_value(right, attributes[lpc]); + } + + if(elem_l != NULL) { int_elem_l = crm_parse_int(elem_l, NULL); } + if(elem_r != NULL) { int_elem_r = crm_parse_int(elem_r, NULL); } + + if(int_elem_l < int_elem_r) { + crm_debug_2("%s (%s < %s)", attributes[lpc], + crm_str(elem_l), crm_str(elem_r)); + return -1; + + } else if(int_elem_l > int_elem_r) { + crm_debug_2("%s (%s > %s)", attributes[lpc], + crm_str(elem_l), crm_str(elem_r)); + return 1; + } + } + + return 0; +} + +xmlNode* +get_cib_copy(cib_t *cib) +{ + xmlNode *xml_cib; +#if CRM_DEPRECATED_SINCE_2_0_4 + xmlNode *xml_cib_copy; +#endif + int options = cib_scope_local|cib_sync_call; + if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) { + crm_err("Couldnt retrieve the CIB"); + return NULL; + } else if(xml_cib == NULL) { + crm_err("The CIB result was empty"); + return NULL; + } + + if(safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) { + return xml_cib; + +#if CRM_DEPRECATED_SINCE_2_0_4 + } else { + xml_cib_copy = copy_xml( + find_xml_node(xml_cib, XML_TAG_CIB, TRUE)); + free_xml(xml_cib); + return xml_cib_copy; +#endif + } + free_xml(xml_cib); + return NULL; +} + +xmlNode* +cib_get_generation(cib_t *cib) +{ + xmlNode *the_cib = get_cib_copy(cib); + xmlNode *generation = create_xml_node( + NULL, XML_CIB_TAG_GENERATION_TUPPLE); + + if(the_cib != NULL) { + copy_in_properties(generation, the_cib); + free_xml(the_cib); + } + + return generation; +} + + +void +log_cib_diff(int log_level, xmlNode *diff, const char *function) +{ + int add_updates = 0; + int add_epoch = 0; + int add_admin_epoch = 0; + + int del_updates = 0; + int del_epoch = 0; + int del_admin_epoch = 0; + + if(diff == NULL) { + return; + } + + cib_diff_version_details( + diff, &add_admin_epoch, &add_epoch, &add_updates, + &del_admin_epoch, &del_epoch, &del_updates); + + if(add_updates != del_updates) { + do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function, + del_admin_epoch, del_epoch, del_updates); + do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function, + add_admin_epoch, add_epoch, add_updates); + } else if(diff != NULL) { + do_crm_log(log_level, + "%s: Local-only Change: %d.%d.%d", function, + add_admin_epoch, add_epoch, add_updates); + } + + log_xml_diff(log_level, diff, function); +} + +gboolean +cib_version_details( + xmlNode *cib, int *admin_epoch, int *epoch, int *updates) +{ + const char *value = NULL; + if(cib == NULL) { + *admin_epoch = -1; + *epoch = -1; + *updates = -1; + return FALSE; + + } else { + value = crm_element_value(cib, XML_ATTR_GENERATION_ADMIN); + *admin_epoch = crm_parse_int(value, "-1"); + + value = crm_element_value(cib, XML_ATTR_GENERATION); + *epoch = crm_parse_int(value, "-1"); + + value = crm_element_value(cib, XML_ATTR_NUMUPDATES); + *updates = crm_parse_int(value, "-1"); + } + return TRUE; +} + +gboolean +cib_diff_version_details( + xmlNode *diff, int *admin_epoch, int *epoch, int *updates, + int *_admin_epoch, int *_epoch, int *_updates) +{ + xmlNode *tmp = NULL; + + tmp = find_xml_node(diff, "diff-added", FALSE); + cib_version_details(tmp, admin_epoch, epoch, updates); + + tmp = find_xml_node(diff, "diff-removed", FALSE); + cib_version_details(tmp, _admin_epoch, _epoch, _updates); + return TRUE; +} + +/* + * The caller should never free the return value + */ +xmlNode* +get_object_root(const char *object_type, xmlNode *the_root) +{ + const char *node_stack[2]; + xmlNode *tmp_node = NULL; + + if(the_root == NULL) { + crm_err("CIB root object was NULL"); + return NULL; + } + + node_stack[0] = XML_CIB_TAG_CONFIGURATION; + node_stack[1] = object_type; + + if(object_type == NULL + || strlen(object_type) == 0 + || safe_str_eq(XML_CIB_TAG_SECTION_ALL, object_type) + || safe_str_eq(XML_TAG_CIB, object_type)) { + /* get the whole cib */ + return the_root; + + } else if(strcasecmp(object_type, XML_CIB_TAG_STATUS) == 0) { + /* these live in a different place */ + tmp_node = find_xml_node(the_root, XML_CIB_TAG_STATUS, FALSE); + + node_stack[0] = object_type; + node_stack[1] = NULL; + + } else { + /* tmp_node = first_named_child(the_root, XML_CIB_TAG_CONFIGURATION); */ + tmp_node = find_xml_node_nested(the_root, node_stack, 2); + } + + if (tmp_node == NULL) { + crm_debug_2("Section [%s [%s]] not present in %s", + node_stack[0], + node_stack[1]?node_stack[1]:"", + crm_element_name(the_root)); + } + return tmp_node; +} + +const char * +get_crm_option(xmlNode *cib, const char *name, gboolean do_warn) +{ + const char * value = NULL; + xmlNode * a_default = NULL; + xmlNode * config = get_object_root(XML_CIB_TAG_CRMCONFIG, cib); + + if(config != NULL) { + a_default = find_entity(config, XML_CIB_TAG_NVPAIR, name); + } + + if(a_default == NULL) { + if(do_warn) { + crm_warn("Option %s not set", name); + } + return NULL; + } + + value = crm_element_value(a_default, XML_NVPAIR_ATTR_VALUE); + if(safe_str_eq(value, "")) { + value = NULL; + } + + return value; +} + +xmlNode* +create_cib_fragment_adv( + xmlNode *update, const char *update_section, const char *source) +{ + xmlNode *cib = NULL; + gboolean whole_cib = FALSE; + xmlNode *object_root = NULL; + const char *update_name = NULL; + char *local_section = NULL; + +/* crm_debug("Creating a blank fragment: %s", update_section); */ + + if(update == NULL && update_section == NULL) { + crm_debug_3("Creating a blank fragment"); + update = createEmptyCib(); + crm_xml_add(cib, XML_ATTR_ORIGIN, source); + return update; + + } else if(update == NULL) { + crm_err("No update to create a fragment for"); + return NULL; + + } else if(update_section == NULL) { + local_section = cib_pluralSection(update_name); + update_section = local_section; + } + + if(safe_str_eq(crm_element_name(update), XML_TAG_CIB)) { + whole_cib = TRUE; + } + + if(whole_cib == FALSE) { + cib = createEmptyCib(); + crm_xml_add(cib, XML_ATTR_ORIGIN, source); + object_root = get_object_root(update_section, cib); + add_node_copy(object_root, update); + + } else { + cib = copy_xml(update); + crm_xml_add(cib, XML_ATTR_ORIGIN, source); + } + + crm_free(local_section); + crm_debug_3("Verifying created fragment"); + return cib; +} + +/* + * It is the callers responsibility to free both the new CIB (output) + * and the new CIB (input) + */ +xmlNode* +createEmptyCib(void) +{ + xmlNode *cib_root = NULL, *config = NULL, *status = NULL; + + cib_root = create_xml_node(NULL, XML_TAG_CIB); + + config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); + status = create_xml_node(cib_root, XML_CIB_TAG_STATUS); + +/* crm_xml_add(cib_root, "version", "1"); */ + crm_xml_add(cib_root, "generated", XML_BOOLEAN_TRUE); + crm_xml_add(cib_root, XML_ATTR_GENERATION, "0"); + crm_xml_add(cib_root, XML_ATTR_GENERATION_ADMIN, "0"); + crm_xml_add(cib_root, XML_ATTR_NUMUPDATES, "0"); + + create_xml_node(config, XML_CIB_TAG_CRMCONFIG); + create_xml_node(config, XML_CIB_TAG_NODES); + create_xml_node(config, XML_CIB_TAG_RESOURCES); + create_xml_node(config, XML_CIB_TAG_CONSTRAINTS); + + return cib_root; +} + + +enum cib_errors +cib_perform_op(const char *op, int call_options, cib_op_t *fn, gboolean is_query, + const char *section, xmlNode *req, xmlNode *input, + gboolean manage_counters, gboolean *config_changed, + xmlNode *current_cib, xmlNode **result_cib, xmlNode **output) +{ + int rc = cib_ok; + xmlNode *scratch = NULL; + + CRM_CHECK(output != NULL && result_cib != NULL && config_changed != NULL, + return cib_output_data); + + *output = NULL; + *result_cib = NULL; + *config_changed = FALSE; + + if(fn == NULL) { + return cib_operation; + } + + if(rc != cib_ok) { + return rc; + } + + if(is_query) { + rc = (*fn)(op, call_options, section, req, input, current_cib, result_cib, output); + return rc; + } + + scratch = copy_xml(current_cib); + rc = (*fn)(op, call_options, section, req, input, current_cib, &scratch, output); + + crm_log_xml_debug(current_cib, "old"); + crm_log_xml_debug(scratch, "new"); + crm_log_xml_debug(*output, "output"); + + CRM_CHECK(current_cib != scratch, return cib_unknown); + + if(rc == cib_ok) { + + CRM_CHECK(scratch != NULL, return cib_unknown); + + if(do_id_check(scratch, NULL, TRUE, FALSE)) { + rc = cib_id_check; + if(call_options & cib_force_diff) { + crm_err("Global update introduces id collision!"); + } + } + + if(rc == cib_ok) { + gboolean dtd_ok; + const char *ignore_dtd; + + fix_plus_plus_recursive(scratch); + crm_log_xml_debug(scratch, "newer"); + *config_changed = cib_config_changed(current_cib, scratch, NULL); + + crm_log_xml_debug(scratch, "newest"); + + if(manage_counters && *config_changed) { + crm_debug("Config changed"); + cib_update_counter(scratch, XML_ATTR_NUMUPDATES, TRUE); + cib_update_counter(scratch, XML_ATTR_GENERATION, FALSE); + } else if(manage_counters) { + crm_debug("Status changed"); + cib_update_counter(scratch, XML_ATTR_NUMUPDATES, FALSE); + } + + ignore_dtd = crm_element_value(scratch, "ignore_dtd"); + dtd_ok = validate_with_dtd(scratch, TRUE, DTD_DIRECTORY"/crm.dtd"); + + if( +#if CRM_DEPRECATED_SINCE_2_0_4 + ignore_dtd != NULL && +#endif + crm_is_true(ignore_dtd) == FALSE && dtd_ok == FALSE) { + crm_err("Updated CIB does not validate against "DTD_DIRECTORY"/crm.dtd..."); + rc = cib_dtd_validation; + } + } + } + + *result_cib = scratch; + return rc; +}