diff --git a/cib/Makefile.am b/cib/Makefile.am index a7d753e6e0..790614f35c 100644 --- a/cib/Makefile.am +++ b/cib/Makefile.am @@ -1,60 +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 + 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_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 5d1eabd05f..33125f4e53 100644 --- a/cib/callbacks.c +++ b/cib/callbacks.c @@ -1,1903 +1,1815 @@ /* * 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( crm_data_t *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( crm_data_t *cib_update, crm_data_t *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); -HA_Message *cib_msg_copy(HA_Message *msg, gboolean with_data); void send_cib_replace(const HA_Message *sync_request, const char *host); void cib_process_request( HA_Message *request, gboolean privileged, gboolean force_synchronous, gboolean from_peer, cib_client_t *cib_client); HA_Message *cib_construct_reply(HA_Message *request, HA_Message *output, int rc); -gboolean syncd_once = FALSE; - extern GHashTable *client_list; int next_client_id = 0; -gboolean cib_is_master = FALSE; 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; static HA_Message * cib_prepare_common(HA_Message *root, const char *section) { HA_Message *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(HA_Message *request, HA_Message **data, const char **section) { *data = NULL; *section = cl_get_string(request, F_CIB_SECTION); if(verify_section(*section) == FALSE) { return cib_bad_section; } return cib_ok; } static enum cib_errors cib_prepare_data(HA_Message *request, HA_Message **data, const char **section) { HA_Message *input_fragment = get_message_xml(request, F_CIB_CALLDATA); *section = cl_get_string(request, F_CIB_SECTION); *data = cib_prepare_common(input_fragment, *section); free_xml(input_fragment); if(verify_section(*section) == FALSE) { return cib_bad_section; } return cib_ok; } static enum cib_errors cib_prepare_sync(HA_Message *request, HA_Message **data, const char **section) { *section = cl_get_string(request, F_CIB_SECTION); *data = request; if(verify_section(*section) == FALSE) { return cib_bad_section; } return cib_ok; } static enum cib_errors cib_prepare_diff(HA_Message *request, HA_Message **data, const char **section) { HA_Message *input_fragment = NULL; const char *update = cl_get_string(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_message(LOG_WARNING, request)); *data = cib_prepare_common(input_fragment, NULL); free_xml(input_fragment); return cib_ok; } static enum cib_errors cib_cleanup_query(const char *op, HA_Message **data, HA_Message **output) { CRM_DEV_ASSERT(*data == NULL); return cib_ok; } static enum cib_errors cib_cleanup_data(const char *op, HA_Message **data, HA_Message **output) { free_xml(*output); free_xml(*data); return cib_ok; } static enum cib_errors cib_cleanup_output(const char *op, HA_Message **data, HA_Message **output) { free_xml(*output); return cib_ok; } static enum cib_errors cib_cleanup_none(const char *op, HA_Message **data, HA_Message **output) { CRM_DEV_ASSERT(*data == NULL); CRM_DEV_ASSERT(*output == NULL); return cib_ok; } static enum cib_errors cib_cleanup_sync(const char *op, HA_Message **data, HA_Message **output) { /* data is non-NULL but doesnt need to be free'd */ 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)(HA_Message *, crm_data_t**, const char **); enum cib_errors (*cleanup)(crm_data_t**, crm_data_t**); enum cib_errors (*fn)( const char *, int, const char *, crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**); } cib_operation_t; */ /* technically bump does modify the cib... * but we want to split the "bump" from the "sync" */ cib_operation_t cib_server_ops[] = { {NULL, FALSE, FALSE, FALSE, 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_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}, }; int send_via_callback_channel(HA_Message *msg, const char *token); enum cib_errors cib_process_command( HA_Message *request, HA_Message **reply, crm_data_t **cib_diff, gboolean privileged); gboolean cib_common_callback(IPC_Channel *channel, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); -enum cib_errors cib_get_operation_id(const HA_Message * msg, int *operation); - gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client); 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; HA_Message *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 = ha_msg_new(3); ha_msg_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); ha_msg_add(reg_msg, F_CIB_CLIENTID, new_client->id); ha_msg_add( reg_msg, F_CIB_CALLBACK_TOKEN, new_client->callback_id); send_ipc_message(channel, reg_msg); crm_msg_del(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; HA_Message *op_request = NULL; HA_Message *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)) { crm_msg_del(op_request); if(channel->ops->is_message_pending(channel) == 0) { break; } op_request = msgfromIPC_noauth(channel); if(op_request == NULL) { break; } type = cl_get_string(op_request, F_CIB_OPERATION); if(safe_str_eq(type, T_CIB_NOTIFY) ) { /* Update the notify filters for this client */ int on_off = 0; ha_msg_value_int( op_request, F_CIB_NOTIFY_ACTIVATE, &on_off); type = cl_get_string(op_request, F_CIB_NOTIFY_TYPE); crm_info("Setting %s callbacks for %s: %s", type, cib_client->name, on_off?"on":"off"); if(safe_str_eq(type, T_CIB_POST_NOTIFY)) { cib_client->post_notify = on_off; } else if(safe_str_eq(type, T_CIB_PRE_NOTIFY)) { cib_client->pre_notify = on_off; } else if(safe_str_eq(type, T_CIB_UPDATE_CONFIRM)) { cib_client->confirmations = on_off; } else if(safe_str_eq(type, T_CIB_DIFF_NOTIFY)) { cib_client->diffs = on_off; } 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 = cl_get_string(op_request, F_CIB_CALLBACK_TOKEN); client_name = cl_get_string(op_request, F_CIB_CLIENTNAME); CRM_DEV_ASSERT(uuid_ticket != NULL); if(crm_assert_failed) { register_failed = crm_assert_failed; } CRM_DEV_ASSERT(client_name != NULL); if(crm_assert_failed) { register_failed = crm_assert_failed; } if(register_failed == FALSE) { hash_client = g_hash_table_lookup(client_list, uuid_ticket); if(hash_client != NULL) { crm_err("Duplicate registration request..." " disconnecting"); register_failed = TRUE; } } if(register_failed) { crm_err("Registration request failed... disconnecting"); crm_msg_del(op_request); return FALSE; } 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 = ha_msg_new(2); ha_msg_add(registered, F_CIB_OPERATION, CRM_OP_REGISTER); ha_msg_add(registered, F_CIB_CLIENTID, cib_client->id); send_ipc_message(channel, registered); crm_msg_del(registered); if(channel->ch_status == IPC_CONNECT) { break; } } crm_msg_del(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(HA_Message *op_request, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); void cib_common_callback_worker(HA_Message *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 = cl_get_string(op_request, F_CIB_OPERATION); - rc = cib_get_operation_id(op_request, &call_type); + 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; HA_Message *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 = msgfromIPC_noauth(channel); if (op_request == NULL) { perror("Receive failure:"); break; } lpc++; crm_assert_failed = FALSE; crm_log_message_adv(LOG_MSG, "Client[inbound]", op_request); ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id); ha_msg_add(op_request, F_CIB_CLIENTNAME, cib_client->name); cib_common_callback_worker( op_request, cib_client, force_synchronous, privileged); crm_msg_del(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, HA_Message *msg); static void do_local_notify(HA_Message *notify_src, const char *client_id, gboolean sync_reply, gboolean from_peer) { /* send callback to originating child */ cib_client_t *client_obj = NULL; HA_Message *client_reply = NULL; enum cib_errors local_rc = cib_ok; crm_debug_2("Performing notification"); 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)); } ha_msg_del(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_server_ops[call_type].modifies_cib && !(call_options & cib_inhibit_bcast)) { /* we need to send an update anyway */ *needs_reply = TRUE; } else { *needs_reply = FALSE; } if(host == NULL && (call_options & cib_scope_local)) { crm_debug_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, HA_Message *request, gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward) { const char *op = cl_get_string(request, F_CIB_OPERATION); const char *originator = cl_get_string(request, F_ORIG); const char *host = cl_get_string(request, F_CIB_HOST); const char *reply_to = cl_get_string(request, F_CIB_ISREPLY); const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE); const char *delegated = cl_get_string(request, F_CIB_DELEGATED); 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(cl_get_string(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_message_adv(LOG_ERR, "Peer[inbound]", request); } return FALSE; } static void forward_request(HA_Message *request, cib_client_t *cib_client, int call_options) { HA_Message *forward_msg = NULL; const char *op = cl_get_string(request, F_CIB_OPERATION); const char *host = cl_get_string(request, F_CIB_HOST); forward_msg = cib_msg_copy(request, TRUE); ha_msg_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; } crm_msg_del(forward_msg); } static void send_peer_reply( HA_Message *msg, crm_data_t *result_diff, const char *originator, gboolean broadcast) { HA_Message *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); ha_msg_add(reply_copy, F_CIB_ISREPLY, originator); ha_msg_add(reply_copy, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); ha_msg_mod(reply_copy, F_CIB_OPERATION, CIB_OP_APPLY_DIFF); digest = calculate_xml_digest(the_cib, FALSE, TRUE); ha_msg_mod(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_message(LOG_DEBUG_3, 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"); ha_msg_add(reply_copy, F_CIB_ISREPLY, originator); send_cluster_message(originator, crm_msg_cib, reply_copy, FALSE); } crm_msg_del(reply_copy); } void cib_process_request( HA_Message *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; crm_data_t *result_diff = NULL; enum cib_errors rc = cib_ok; HA_Message *op_reply = NULL; const char *op = cl_get_string(request, F_CIB_OPERATION); const char *originator = cl_get_string(request, F_ORIG); const char *host = cl_get_string(request, F_CIB_HOST); const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE); crm_debug_4("%s Processing msg %s", cib_our_uname, cl_get_string(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; } ha_msg_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(request, &call_type); + rc = cib_get_operation_id(op, &call_type); if(cib_server_ops[call_type].modifies_cib) { 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_server_ops[call_type].modifies_cib; 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)", cl_get_string(request, F_CIB_OPERATION), originator, cl_get_string(request, F_CIB_CLIENTID), cl_get_string(request, F_CIB_CALLID), update); rc = cib_process_command( request, &op_reply, &result_diff, 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_message_adv(LOG_DEBUG, "CIB[output]", op_reply); crm_log_message_adv(LOG_INFO, "Input message", request); } if(op_reply == NULL && (needs_reply || local_notify)) { crm_err("Unexpected NULL reply to message"); crm_log_message(LOG_ERR, request); needs_reply = FALSE; local_notify = FALSE; } } crm_debug_3("processing response cases"); if(local_notify) { const char *client_id = cl_get_string(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_server_ops[call_type].modifies_cib == 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); } crm_msg_del(op_reply); free_xml(result_diff); return; } HA_Message * cib_construct_reply(HA_Message *request, HA_Message *output, int rc) { int lpc = 0; HA_Message *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 = ha_msg_new(8); ha_msg_add(reply, F_TYPE, T_CIB); for(lpc = 0; lpc < DIMOF(names); lpc++) { name = names[lpc]; value = cl_get_string(request, name); ha_msg_add(reply, name, value); } ha_msg_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(HA_Message *request, HA_Message **reply, crm_data_t **cib_diff, gboolean privileged) { - crm_data_t *output = NULL; - crm_data_t *input = NULL; - - crm_data_t *current_cib = NULL; - crm_data_t *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; - crm_data_t *filtered = NULL; - - const char *op = NULL; - const char *section = NULL; - gboolean config_changed = FALSE; - gboolean global_update = crm_is_true( - cl_get_string(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 = cl_get_string(request, F_CIB_OPERATION); - ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options); - rc = cib_get_operation_id(request, &call_type); - - if(rc == cib_ok && - cib_server_ops[call_type].needs_privileges - && privileged == FALSE) { - /* abort */ - rc = cib_not_authorized; - } + gboolean send_r_notify = FALSE; + crm_data_t *output = NULL; + crm_data_t *input = NULL; + + crm_data_t *current_cib = NULL; + crm_data_t *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; + crm_data_t *filtered = NULL; + + const char *op = NULL; + const char *section = NULL; + gboolean config_changed = FALSE; + gboolean global_update = crm_is_true(cl_get_string(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 = cl_get_string(request, F_CIB_OPERATION); + ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options); + rc = cib_get_operation_id(op, &call_type); + + if(rc == cib_ok && + cib_server_ops[call_type].needs_privileges + && privileged == FALSE) { + /* abort */ + rc = cib_not_authorized; + } - if(rc == cib_ok - && stand_alone == FALSE - && global_update == FALSE - && cib_server_ops[call_type].needs_quorum - && can_write(call_options) == FALSE) { - rc = cib_no_quorum; - } + if(rc == cib_ok + && stand_alone == FALSE + && global_update == FALSE + && cib_server_ops[call_type].needs_quorum + && can_write(call_options) == FALSE) { + rc = cib_no_quorum; + } - /* prevent NUMUPDATES from being incrimented - apply the change as-is */ - if(global_update) { - call_options |= cib_inhibit_bcast; - call_options |= cib_force_diff; - } + /* 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_server_ops[call_type].prepare(request, &input, §ion); - if(rc == cib_ok) { - rc = rc2; - } + rc2 = cib_server_ops[call_type].prepare(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; + if(rc != cib_ok) { + crm_debug_2("Call setup failed: %s", cib_error2string(rc)); + goto done; - } else if(cib_server_ops[call_type].modifies_cib == FALSE) { - rc = cib_server_ops[call_type].fn( - op, call_options, section, input, - current_cib, &result_cib, &output); + } else if(cib_server_ops[call_type].modifies_cib == FALSE) { + rc = cib_perform_op(op, call_options, section, input, FALSE, &config_changed, + current_cib, &result_cib, &output); - CRM_CHECK(result_cib == NULL, free_xml(result_cib)); - goto done; - } + CRM_CHECK(result_cib == NULL, free_xml(result_cib)); + goto done; + } - /* Handle a valid write action */ + /* 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) { - result_cib = copy_xml(current_cib); - - rc = cib_server_ops[call_type].fn( - op, call_options, section, input, - current_cib, &result_cib, &output); + if((call_options & cib_inhibit_notify) == 0) { + cib_pre_notify(call_options, op, + get_object_root(section, current_cib), input); + } - fix_plus_plus_recursive(result_cib); - } + 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_message(LOG_ERR, request)); + crm_debug_2("Skipping update: global replace"); + manage_counters = FALSE; - if(rc == cib_ok) { - - CRM_DEV_ASSERT(result_cib != NULL); - CRM_DEV_ASSERT(current_cib != result_cib); + } else if(cib_server_ops[call_type].fn == cib_process_change + && (call_options & cib_inhibit_bcast)) { + /* skip */ + crm_debug_2("Skipping update: inhibit broadcast"); + manage_counters = FALSE; + } - update_counters(__FILE__, __FUNCTION__, result_cib); - config_changed = cib_config_changed(current_cib, result_cib, &filtered); + rc = cib_perform_op( + op, call_options, section, 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); - if(global_update) { - /* skip */ - CRM_CHECK(call_type == 4 || call_type == 11, - crm_err("Call type: %d", call_type); - crm_log_message(LOG_ERR, request)); - crm_debug_2("Skipping update: global replace"); - - } else if(cib_server_ops[call_type].fn == cib_process_change - && (call_options & cib_inhibit_bcast)) { - /* skip */ - crm_debug_2("Skipping update: inhibit broadcast"); - - } else { - cib_update_counter(result_cib, XML_ATTR_NUMUPDATES, FALSE); - - if(config_changed) { - cib_update_counter(result_cib, XML_ATTR_NUMUPDATES, TRUE); - cib_update_counter(result_cib, XML_ATTR_GENERATION, FALSE); - } - } - - if(do_id_check(result_cib, NULL, TRUE, FALSE)) { - rc = cib_id_check; - if(call_options & cib_force_diff) { - crm_err("Global update introduces id collision!"); - } - - } else { - *cib_diff = diff_cib_object(current_cib, result_cib, FALSE); - } - } - + } else { + rc = activateCibXml(result_cib, config_changed); if(rc != cib_ok) { - free_xml(result_cib); - - } else { - rc = activateCibXml(result_cib, config_changed); - if(rc != cib_ok) { - crm_warn("Activation failed"); - } + crm_warn("Activation failed"); } + } - if((call_options & cib_inhibit_notify) == 0) { - const char *call_id = cl_get_string(request, F_CIB_CALLID); - const char *client = cl_get_string(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((call_options & cib_inhibit_notify) == 0) { + const char *call_id = cl_get_string(request, F_CIB_CALLID); + const char *client = cl_get_string(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; - 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(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(config_changed) { - log_level = LOG_DEBUG_2; - } else { - log_level = LOG_DEBUG_3; - } + } 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); + 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); - } + if((call_options & cib_discard_reply) == 0) { + *reply = cib_construct_reply(request, output, rc); + } - if(call_type >= 0) { - cib_server_ops[call_type].cleanup(op, &input, &output); - } - if(per_action_cib) { - uninitializeCib(); - } - return rc; + if(call_type >= 0) { + cib_server_ops[call_type].cleanup(op, &input, &output); + } + if(per_action_cib) { + uninitializeCib(); + } + return rc; } int send_via_callback_channel(HA_Message *msg, const char *token) { cib_client_t *hash_client = NULL; GList *list_item = NULL; enum cib_errors rc = cib_ok; crm_debug_3("Delivering msg %p to client %s", msg, token); if(token == NULL) { crm_err("No client id token, cant send message"); if(rc == cib_ok) { rc = cib_missing; } } else { /* A client that left before we could reply is not really * _our_ error. Warn instead. */ hash_client = g_hash_table_lookup(client_list, token); if(hash_client == NULL) { crm_warn("Cannot find client for token %s", token); rc = cib_client_gone; } else if(hash_client->channel == NULL) { crm_err("Cannot find channel for client %s", token); rc = cib_client_corrupt; } else if(hash_client->channel->ops->get_chan_status( hash_client->channel) == IPC_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 */ HA_Message *orig_msg = list_item->data; crm_debug_3("Removing msg from delegated list"); hash_client->delegated_calls = g_list_remove( hash_client->delegated_calls, orig_msg); CRM_DEV_ASSERT(orig_msg != msg); crm_msg_del(orig_msg); } if(rc == cib_ok) { crm_debug_3("Delivering reply to client %s", token); if(send_ipc_message(hash_client->channel, msg) == FALSE) { crm_warn("Delivery of reply to client %s/%s failed", hash_client->name, token); rc = cib_reply_failed; } } return rc; } gint cib_GCompareFunc(gconstpointer a, gconstpointer b) { const HA_Message *a_msg = a; const HA_Message *b_msg = b; int msg_a_id = 0; int msg_b_id = 0; ha_msg_value_int(a_msg, F_CIB_CALLID, &msg_a_id); ha_msg_value_int(b_msg, F_CIB_CALLID, &msg_b_id); if(msg_a_id == msg_b_id) { return 0; } else if(msg_a_id < msg_b_id) { return -1; } return 1; } gboolean cib_msg_timeout(gpointer data) { crm_debug_4("Checking if any clients have timed out messages"); /* g_hash_table_foreach(client_list, cib_GHFunc, NULL); */ return TRUE; } void cib_GHFunc(gpointer key, gpointer value, gpointer user_data) { int timeout = 0; /* 1 iteration == 10 seconds */ HA_Message *msg = NULL; HA_Message *reply = NULL; const char *host_to = NULL; cib_client_t *client = value; GListPtr list = client->delegated_calls; while(list != NULL) { msg = list->data; ha_msg_value_int(msg, F_CIB_TIMEOUT, &timeout); if(timeout <= 0) { list = list->next; continue; } else { int seen = 0; ha_msg_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; ha_msg_mod_int(msg, F_CIB_SEENCOUNT, seen); list = list->next; continue; } } cib_num_timeouts++; host_to = cl_get_string(msg, F_CIB_HOST); crm_warn("Sending operation timeout msg to client %s", client->id); reply = ha_msg_new(4); ha_msg_add(reply, F_TYPE, T_CIB); ha_msg_add(reply, F_CIB_OPERATION, cl_get_string(msg, F_CIB_OPERATION)); ha_msg_add(reply, F_CIB_CALLID, cl_get_string(msg, F_CIB_CALLID)); if(host_to == NULL) { ha_msg_add_int(reply, F_CIB_RC, cib_master_timeout); } else { ha_msg_add_int(reply, F_CIB_RC, cib_remote_timeout); } send_ipc_message(client->channel, reply); list = list->next; client->delegated_calls = g_list_remove( client->delegated_calls, msg); crm_msg_del(msg); crm_msg_del(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(HA_Message * msg, void* private_data) { int call_type = 0; int call_options = 0; const char *originator = cl_get_string(msg, F_ORIG); const char *seq = cl_get_string(msg, F_SEQ); const char *op = cl_get_string(msg, F_CIB_OPERATION); crm_node_t *node = NULL; crm_log_message_adv(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(msg, &call_type) != cib_ok) { + 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); ha_msg_value_int(msg, F_CIB_CALLOPTS, &call_options); crm_debug_4("Retrieved call options: %d", call_options); if(cl_get_string(msg, F_CIB_CLIENTNAME) == NULL) { ha_msg_add(msg, F_CIB_CLIENTNAME, originator); } cib_process_request(msg, FALSE, TRUE, TRUE, NULL); return; } -HA_Message * -cib_msg_copy(HA_Message *msg, gboolean with_data) -{ - int lpc = 0; - const char *field = NULL; - const char *value = NULL; - HA_Message *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 - }; - - HA_Message *copy = ha_msg_new(10); - CRM_ASSERT(copy != NULL); - - for(lpc = 0; lpc < DIMOF(field_list); lpc++) { - field = field_list[lpc]; - value = cl_get_string(msg, field); - if(value != NULL) { - ha_msg_add(copy, field, value); - } - } - for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) { - field = data_list[lpc]; - value_struct = get_message_xml(msg, field); - if(value_struct != NULL) { - add_message_xml(copy, field, value_struct); - } - free_xml(value_struct); - } - - return copy; -} - - -enum cib_errors -cib_get_operation_id(const HA_Message * msg, int *operation) -{ - int lpc = 0; - int max_msg_types = DIMOF(cib_server_ops); - const char *op = cl_get_string(msg, F_CIB_OPERATION); - - for (lpc = 0; lpc < max_msg_types; lpc++) { - if (safe_str_eq(op, cib_server_ops[lpc].operation)) { - *operation = lpc; - return cib_ok; - } - } - crm_err("Operation %s is not valid", op); - *operation = -1; - return cib_operation; -} - void cib_client_status_callback(const char * node, const char * client, const char * status, void * private) { 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; HA_Message *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 = ha_msg_new(3); ha_msg_add(leaving, F_TYPE, "cib"); ha_msg_add(leaving, F_CIB_OPERATION, "cib_shutdown_req"); send_cluster_message(NULL, crm_msg_cib, leaving, TRUE); crm_msg_del(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/callbacks.h b/cib/callbacks.h index ac2c824870..bde1e8b3bb 100644 --- a/cib/callbacks.h +++ b/cib/callbacks.h @@ -1,100 +1,98 @@ /* * 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 #if SUPPORT_HEARTBEAT # include # include #endif #include #include #include extern gboolean cib_is_master; extern GHashTable *client_list; extern GHashTable *peer_hash; typedef struct cib_client_s { char *id; char *name; char *callback_id; const char *channel_name; IPC_Channel *channel; GCHSource *source; unsigned long num_calls; int pre_notify; int post_notify; int confirmations; int replace; int diffs; GList *delegated_calls; } cib_client_t; typedef struct cib_operation_s { const char* operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_quorum; enum cib_errors (*prepare)(HA_Message *, crm_data_t**, const char **); enum cib_errors (*cleanup)(const char *, crm_data_t**, crm_data_t**); enum cib_errors (*fn)( const char *, int, const char *, crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**); } cib_operation_t; -extern cib_operation_t cib_server_ops[]; - extern gboolean cib_client_connect_null( IPC_Channel *channel, gpointer user_data); extern gboolean cib_client_connect_rw_ro( IPC_Channel *channel, gpointer user_data); extern gboolean cib_client_connect_rw_synch( IPC_Channel *channel, gpointer user_data); extern gboolean cib_client_connect_ro_synch( IPC_Channel *channel, gpointer user_data); extern gboolean cib_null_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_rw_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_ro_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_rw_synchronous_callback( IPC_Channel *channel, gpointer user_data); extern gboolean cib_ro_synchronous_callback( IPC_Channel *channel, gpointer user_data); extern void cib_peer_callback(HA_Message * msg, void* private_data); extern void cib_client_status_callback(const char * node, const char * client, const char * status, void * private); #if SUPPORT_HEARTBEAT extern gboolean cib_ccm_dispatch(int fd, gpointer user_data); extern void cib_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data); #endif diff --git a/cib/cibpipe.c b/cib/cibpipe.c new file mode 100644 index 0000000000..1797a1ac40 --- /dev/null +++ b/cib/cibpipe.c @@ -0,0 +1,333 @@ +/* + * 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); + +#define OPTARGS "V?o:QDUCEX:t:MBfRx:P5S" + +int +main(int argc, char ** argv) +{ + int flag; + int rc = 0; + int argerr = 0; + + 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; + + crm_data_t *input = NULL; + crm_data_t *output = NULL; + crm_data_t *result_cib = NULL; + crm_data_t *current_cib = 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, 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 new file mode 100644 index 0000000000..b9ea7773b6 --- /dev/null +++ b/cib/common.c @@ -0,0 +1,396 @@ +/* + * 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; +extern gboolean stand_alone; +extern enum cib_errors cib_status; +extern gboolean can_write(int flags); +extern enum cib_errors cib_perform_command( + HA_Message *request, HA_Message **reply, crm_data_t **cib_diff, gboolean privileged); + + +static HA_Message * +cib_prepare_common(HA_Message *root, const char *section) +{ + HA_Message *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(HA_Message *request, HA_Message **data, const char **section) +{ + *data = NULL; + *section = cl_get_string(request, F_CIB_SECTION); + if(verify_section(*section) == FALSE) { + return cib_bad_section; + } + return cib_ok; +} + +static enum cib_errors +cib_prepare_data(HA_Message *request, HA_Message **data, const char **section) +{ + HA_Message *input_fragment = get_message_xml(request, F_CIB_CALLDATA); + *section = cl_get_string(request, F_CIB_SECTION); + *data = cib_prepare_common(input_fragment, *section); + free_xml(input_fragment); + if(verify_section(*section) == FALSE) { + return cib_bad_section; + } + return cib_ok; +} + +static enum cib_errors +cib_prepare_sync(HA_Message *request, HA_Message **data, const char **section) +{ + *section = cl_get_string(request, F_CIB_SECTION); + *data = request; + if(verify_section(*section) == FALSE) { + return cib_bad_section; + } + return cib_ok; +} + +static enum cib_errors +cib_prepare_diff(HA_Message *request, HA_Message **data, const char **section) +{ + HA_Message *input_fragment = NULL; + const char *update = cl_get_string(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_message(LOG_WARNING, request)); + *data = cib_prepare_common(input_fragment, NULL); + free_xml(input_fragment); + return cib_ok; +} + +static enum cib_errors +cib_cleanup_query(const char *op, HA_Message **data, HA_Message **output) +{ + CRM_DEV_ASSERT(*data == NULL); + return cib_ok; +} + +static enum cib_errors +cib_cleanup_data(const char *op, HA_Message **data, HA_Message **output) +{ + free_xml(*output); + free_xml(*data); + return cib_ok; +} + +static enum cib_errors +cib_cleanup_output(const char *op, HA_Message **data, HA_Message **output) +{ + free_xml(*output); + return cib_ok; +} + +static enum cib_errors +cib_cleanup_none(const char *op, HA_Message **data, HA_Message **output) +{ + CRM_DEV_ASSERT(*data == NULL); + CRM_DEV_ASSERT(*output == NULL); + return cib_ok; +} + +static enum cib_errors +cib_cleanup_sync(const char *op, HA_Message **data, HA_Message **output) +{ + /* data is non-NULL but doesnt need to be free'd */ + 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)(HA_Message *, crm_data_t**, const char **); + enum cib_errors (*cleanup)(crm_data_t**, crm_data_t**); + enum cib_errors (*fn)( + const char *, int, const char *, + crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**); + } cib_operation_t; +*/ +/* technically bump does modify the cib... + * but we want to split the "bump" from the "sync" + */ +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_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; +} + +HA_Message * +cib_msg_copy(HA_Message *msg, gboolean with_data) +{ + int lpc = 0; + const char *field = NULL; + const char *value = NULL; + HA_Message *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 + }; + + HA_Message *copy = ha_msg_new(10); + CRM_ASSERT(copy != NULL); + + for(lpc = 0; lpc < DIMOF(field_list); lpc++) { + field = field_list[lpc]; + value = cl_get_string(msg, field); + if(value != NULL) { + ha_msg_add(copy, field, value); + } + } + for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) { + field = data_list[lpc]; + value_struct = get_message_xml(msg, field); + if(value_struct != NULL) { + add_message_xml(copy, field, value_struct); + } + free_xml(value_struct); + } + + return copy; +} + +enum cib_errors +cib_perform_op( + const char *op, int call_options, const char *section, crm_data_t *input, + gboolean manage_counters, gboolean *config_changed, + crm_data_t *current_cib, crm_data_t **result_cib, crm_data_t **output) +{ + int rc = cib_ok; + int call_type = 0; + crm_data_t *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) { + return cib_server_ops[call_type].fn( + op, call_options, section, input, current_cib, result_cib, output); + } + + scratch = copy_xml(current_cib); + rc = cib_server_ops[call_type].fn( + op, call_options, section, input, current_cib, &scratch, 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; +} diff --git a/cib/common.h b/cib/common.h new file mode 100644 index 0000000000..cd8e5acd7f --- /dev/null +++ b/cib/common.h @@ -0,0 +1,30 @@ +/* + * 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 + */ + +extern HA_Message *cib_msg_copy(HA_Message *msg, gboolean with_data); +extern HA_Message *cib_construct_reply(HA_Message *request, HA_Message *output, int rc); +extern enum cib_errors revision_check(crm_data_t *cib_update, crm_data_t *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, crm_data_t *input, + gboolean manage_counters, gboolean *config_changed, + crm_data_t *current_cib, crm_data_t **result_cib, crm_data_t **output); + +extern enum cib_errors cib_update_counter( + crm_data_t *xml_obj, const char *field, gboolean reset); diff --git a/cib/io.c b/cib/io.c index f9f79e0184..f8374d7c23 100644 --- a/cib/io.c +++ b/cib/io.c @@ -1,836 +1,840 @@ /* * 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; -crm_data_t *the_cib = NULL; crm_data_t *node_search = NULL; crm_data_t *resource_search = NULL; crm_data_t *constraint_search = NULL; crm_data_t *status_search = NULL; extern gboolean cib_writes_enabled; extern GTRIGSource *cib_writer; extern enum cib_errors cib_status; int set_connected_peers(crm_data_t *xml_obj); void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data); int write_cib_contents(gpointer p); extern void cib_cleanup(void); static gboolean validate_cib_digest(crm_data_t *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(crm_data_t *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, crm_data_t **on_disk_cib) { int s_res = -1; struct stat buf; FILE *cib_file = NULL; gboolean passed = TRUE; crm_data_t *root = NULL; 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 crm_data_t* retrieveCib(const char *filename, const char *sigfile, gboolean archive_invalid) { struct stat buf; FILE *cib_file = NULL; crm_data_t *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 manually 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; } crm_data_t* 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"); crm_data_t *root = NULL; crm_data_t *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) { 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 */ crm_data_t* get_the_CIB(void) { return the_cib; } gboolean uninitializeCib(void) { crm_data_t *tmp_cib = the_cib; if(tmp_cib == NULL) { crm_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(crm_data_t *new_cib) { gboolean is_valid = TRUE; crm_data_t *tmp_node = NULL; if(new_cib == NULL) { return FALSE; } xml_validate(new_cib); tmp_node = get_object_root(XML_CIB_TAG_NODES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, new_cib); if (tmp_node == NULL) { is_valid = FALSE; } tmp_node = get_object_root(XML_CIB_TAG_STATUS, new_cib); if (is_valid && tmp_node == NULL) { create_xml_node(new_cib, XML_CIB_TAG_STATUS); } if(is_valid == FALSE) { crm_warn("CIB Verification failed"); return FALSE; } update_counters(__FILE__, __PRETTY_FUNCTION__, new_cib); the_cib = new_cib; initialized = TRUE; return TRUE; } 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); } } crm_free(backup_file); return rc; } /* * This method will free the old CIB pointer on success and the new one * on failure. */ int activateCibXml(crm_data_t *new_cib, gboolean to_disk) { int error_code = cib_ok; crm_data_t *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) { 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_2("Triggering CIB write"); + 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; crm_data_t *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(crm_data_t *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, crm_data_t *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 f59edd07e7..f4a3d98d41 100644 --- a/cib/messages.c +++ b/cib/messages.c @@ -1,931 +1,932 @@ /* * 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 - #define MAX_DIFF_RETRY 5 +#ifdef CIBPIPE +gboolean cib_is_master = TRUE; +#else +gboolean cib_is_master = FALSE; +#endif + +gboolean syncd_once = FALSE; +crm_data_t *the_cib = NULL; extern const char *cib_our_uname; -extern gboolean syncd_once; enum cib_errors revision_check(crm_data_t *cib_update, crm_data_t *cib_copy, int flags); int get_revision(crm_data_t *xml_obj, int cur_revision); enum cib_errors updateList( crm_data_t *local_cib, crm_data_t *update_command, crm_data_t *failed, int operation, const char *section); gboolean check_generation(crm_data_t *newCib, crm_data_t *oldCib); gboolean update_results( crm_data_t *failed, crm_data_t *target, int operation, int return_code); enum cib_errors cib_update_counter( crm_data_t *xml_obj, const char *field, gboolean reset); enum cib_errors sync_our_cib(HA_Message *request, gboolean all); extern HA_Message *cib_msg_copy(const HA_Message *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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { +#ifdef CIBPIPE + return cib_invalid_argument; +#else enum cib_errors result = cib_ok; const char *host = cl_get_string(input, F_ORIG); *answer = NULL; if(cl_get_string(input, 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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { crm_data_t *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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { - crm_data_t *local_diff = NULL; 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); - local_diff = diff_cib_object(existing_cib, *result_cib, FALSE); - cib_replace_notify(*result_cib, result, local_diff); - free_xml(local_diff); - return result; } enum cib_errors cib_process_bump( const char *op, int options, const char *section, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { - return sync_our_cib(input, TRUE); +#ifdef CIBPIPE + return cib_invalid_argument; +#else + return sync_our_cib(input, TRUE); +#endif } enum cib_errors cib_process_sync_one( const char *op, int options, const char *section, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { - return sync_our_cib(input, FALSE); +#ifdef CIBPIPE + return cib_invalid_argument; +#else + return sync_our_cib(input, FALSE); +#endif } enum cib_errors cib_update_counter(crm_data_t *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( const char *op, int options, const char *section, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **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); 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(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."; } } } 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) { HA_Message *sync_me = ha_msg_new(3); free_xml(*result_cib); *result_cib = NULL; result = cib_diff_resync; crm_info("Requesting re-sync from peer: %s", reason); sync_in_progress++; ha_msg_add(sync_me, F_TYPE, "cib"); ha_msg_add(sync_me, F_CIB_OPERATION, CIB_OP_SYNC_ONE); ha_msg_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; } ha_msg_del(sync_me); } else if(do_resync) { crm_warn("Not resyncing in master mode"); } - - +#endif return result; } enum cib_errors cib_process_replace( const char *op, int options, const char *section, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { const char *tag = NULL; - gboolean send_notify = FALSE; 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); - send_notify = TRUE; } else { crm_data_t *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; - - } else if(safe_str_eq(section, XML_CIB_TAG_NODES)) { - send_notify = TRUE; - - } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { - send_notify = TRUE; - - } else if(safe_str_eq(tag, XML_CIB_TAG_STATUS)) { - send_notify = TRUE; - - } else if(safe_str_eq(tag, XML_CIB_TAG_NODES)) { - send_notify = TRUE; } } - - if(send_notify) { - crm_data_t *local_diff = NULL; - local_diff = diff_cib_object(existing_cib, *result_cib, FALSE); - cib_replace_notify(*result_cib, result, local_diff); - free_xml(local_diff); - } return result; } enum cib_errors cib_process_delete( const char *op, int options, const char *section, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { crm_data_t *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, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { crm_data_t *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_change( const char *op, int options, const char *section, crm_data_t *input, crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer) { gboolean verbose = FALSE; crm_data_t *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; crm_data_t *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(crm_data_t *local_cib, crm_data_t *xml_section, crm_data_t *failed, int operation, const char *section) { int rc = cib_ok; crm_data_t *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(crm_data_t *newCib, crm_data_t *oldCib) { if(cib_compare_generation(newCib, oldCib) >= 0) { return TRUE; } crm_warn("Generation from update is older than the existing one"); return FALSE; } gboolean update_results( crm_data_t *failed, crm_data_t *target, int operation, int return_code) { gboolean was_error = FALSE; const char *error_msg = NULL; const char *operation_msg = NULL; crm_data_t *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(crm_data_t *cib_update, crm_data_t *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(HA_Message *request, gboolean all) { enum cib_errors result = cib_ok; const char *host = cl_get_string(request, F_ORIG); const char *op = cl_get_string(request, F_CIB_OPERATION); HA_Message *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_message(LOG_ERR, 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) { ha_msg_add(replace_request, F_CIB_ISREPLY, host); } ha_msg_mod(replace_request, F_CIB_OPERATION, CIB_OP_REPLACE); ha_msg_add(replace_request, "original_"F_CIB_OPERATION, op); ha_msg_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; } ha_msg_del(replace_request); return result; } +#endif