diff --git a/cib/callbacks.c b/cib/callbacks.c index 9111bf03bd..81baa2576b 100644 --- a/cib/callbacks.c +++ b/cib/callbacks.c @@ -1,1437 +1,1440 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" extern GMainLoop* mainloop; extern gboolean cib_shutdown_flag; extern gboolean stand_alone; extern const char* cib_root; #if SUPPORT_HEARTBEAT extern ll_cluster_t *hb_conn; #endif extern void cib_ha_connection_destroy(gpointer user_data); extern enum cib_errors cib_update_counter( xmlNode *xml_obj, const char *field, gboolean reset); extern void GHFunc_count_peers( gpointer key, gpointer value, gpointer user_data); extern enum cib_errors revision_check( xmlNode *cib_update, xmlNode *cib_copy, int flags); void initiate_exit(void); void terminate_cib(const char *caller); gint cib_GCompareFunc(gconstpointer a, gconstpointer b); void cib_GHFunc(gpointer key, gpointer value, gpointer user_data); gboolean can_write(int flags); void send_cib_replace(const xmlNode *sync_request, const char *host); void cib_process_request( xmlNode *request, gboolean privileged, gboolean force_synchronous, gboolean from_peer, cib_client_t *cib_client); void cib_common_callback_worker(xmlNode *op_request, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); extern GHashTable *client_list; int next_client_id = 0; extern const char *cib_our_uname; extern unsigned long cib_num_ops, cib_num_local, cib_num_updates, cib_num_fail; extern unsigned long cib_bad_connects, cib_num_timeouts; extern longclock_t cib_call_time; extern enum cib_errors cib_status; int send_via_callback_channel(xmlNode *msg, const char *token); enum cib_errors cib_process_command( xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged); gboolean cib_common_callback(IPC_Channel *channel, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client); int num_clients = 0; static void cib_ipc_connection_destroy(gpointer user_data) { cib_client_t *cib_client = user_data; /* cib_process_disconnect */ if(cib_client == NULL) { crm_debug_4("Destroying %p", user_data); return; } if(cib_client->source != NULL) { crm_debug_4("Deleting %s (%p) from mainloop", cib_client->name, cib_client->source); G_main_del_IPC_Channel(cib_client->source); cib_client->source = NULL; } crm_debug_3("Destroying %s (%p)", cib_client->name, user_data); num_clients--; crm_debug_2("Num unfree'd clients: %d", num_clients); crm_free(cib_client->name); crm_free(cib_client->callback_id); crm_free(cib_client->id); crm_free(cib_client); crm_debug_4("Freed the cib client"); return; } gboolean cib_client_connect(IPC_Channel *channel, gpointer user_data) { cl_uuid_t client_id; xmlNode *reg_msg = NULL; cib_client_t *new_client = NULL; char uuid_str[UU_UNPARSE_SIZEOF]; const char *channel_name = user_data; gboolean (*callback)(IPC_Channel *channel, gpointer user_data); crm_debug_3("Connecting channel"); if (channel == NULL) { crm_err("Channel was NULL"); cib_bad_connects++; return FALSE; } else if (channel->ch_status != IPC_CONNECT) { crm_err("Channel was disconnected"); cib_bad_connects++; return FALSE; } else if(channel_name == NULL) { crm_err("user_data must contain channel name"); cib_bad_connects++; return FALSE; } else if(cib_shutdown_flag) { crm_info("Ignoring new client [%d] during shutdown", channel->farside_pid); return FALSE; } callback = cib_ro_callback; if(safe_str_eq(channel_name, cib_channel_rw)) { callback = cib_rw_callback; } 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); 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); 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); /* make sure we can find ourselves later for sync calls * redirected to the master instance */ g_hash_table_insert(client_list, new_client->id, new_client); reg_msg = create_xml_node(NULL, "callback"); crm_xml_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); crm_xml_add(reg_msg, F_CIB_CLIENTID, new_client->id); send_ipc_message(channel, reg_msg); free_xml(reg_msg); 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_callback(IPC_Channel *channel, gpointer user_data) { gboolean result = FALSE; result = cib_common_callback(channel, user_data, FALSE, FALSE); return result; } void cib_common_callback_worker(xmlNode *op_request, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged) { int rc = cib_ok; int call_type = 0; const char *op = NULL; longclock_t call_stop = 0; longclock_t call_start = 0; call_start = time_longclock(); cib_client->num_calls++; op = crm_element_value(op_request, F_CIB_OPERATION); if(safe_str_eq(op, CRM_OP_REGISTER) ) { goto done; } else if(safe_str_eq(op, T_CIB_NOTIFY) ) { /* Update the notify filters for this client */ int on_off = 0; const char *type = crm_element_value(op_request, F_CIB_NOTIFY_TYPE);; crm_element_value_int(op_request, F_CIB_NOTIFY_ACTIVATE, &on_off); 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; } goto done; } 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); } done: 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; const char *value = NULL; xmlNode *op_request = NULL; gboolean keep_channel = TRUE; if(cib_client == NULL) { crm_err("Receieved call from unknown source. Discarding."); return FALSE; } crm_debug_2("Callback for %s on %s channel", cib_client->id, cib_client->channel_name); while(IPC_ISRCONN(channel)) { if(channel->ops->is_message_pending(channel) == 0) { break; } op_request = xmlfromIPC(channel, 0); if (op_request == NULL) { break; } lpc++; crm_assert_failed = FALSE; crm_log_xml(LOG_MSG, "Client[inbound]", op_request); if(cib_client->name == NULL) { value = crm_element_value(op_request, F_CIB_CLIENTNAME); if(value == NULL) { cib_client->name = crm_itoa(channel->farside_pid); } else { cib_client->name = crm_strdup(value); } if(safe_str_eq(cib_client->name, CRM_SYSTEM_TENGINE)) { /* Cheap hack... * The TE is _always_ interested in these * Enable now to avoid timing issues */ cib_client->diffs = TRUE; } } CRM_CHECK(cib_client->id != NULL, crm_err("Invalid client: %p", cib_client)); crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id); crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name); if(cib_client->callback_id == NULL) { value = crm_element_value(op_request, F_CIB_CALLBACK_TOKEN); if(value != NULL) { cib_client->callback_id = crm_strdup(value); crm_debug_2("Callback channel for %s is %s", cib_client->id, cib_client->callback_id); } else { cib_client->callback_id = crm_strdup(cib_client->id); } } cib_common_callback_worker( op_request, cib_client, force_synchronous, privileged); free_xml(op_request); if(channel->ch_status == IPC_CONNECT) { break; } } crm_debug_2("Processed %d messages", lpc); if(channel->ch_status != IPC_CONNECT) { crm_debug_2("Client disconnected"); keep_channel = cib_process_disconnect(channel, cib_client); } return keep_channel; } extern void cib_send_remote_msg(void *session, xmlNode *msg); static void do_local_notify(xmlNode *notify_src, const char *client_id, gboolean sync_reply, gboolean from_peer) { /* send callback to originating child */ cib_client_t *client_obj = NULL; xmlNode *client_reply = NULL; enum cib_errors local_rc = cib_ok; crm_debug_2("Performing notification"); client_reply = cib_msg_copy(notify_src, TRUE); if(client_id != NULL) { client_obj = g_hash_table_lookup( client_list, client_id); } else { crm_debug_2("No client to sent the response to." " F_CIB_CLIENTID not set."); } crm_debug_3("Sending callback to request originator"); if(client_obj == NULL) { local_rc = cib_reply_failed; - } else if (crm_str_eq(client_obj->channel_name, "remote", FALSE)) { - crm_debug("Send message over TLS connection"); - cib_send_remote_msg(client_obj->channel, client_reply); - } else { const char *client_id = client_obj->callback_id; crm_debug_2("Sending %ssync response to %s %s", sync_reply?"":"an a-", client_obj->name, from_peer?"(originator of delegated request)":""); if(sync_reply) { client_id = client_obj->id; } local_rc = send_via_callback_channel(client_reply, client_id); } if(local_rc != cib_ok && client_obj != NULL) { crm_warn("%sSync reply to %s failed: %s", sync_reply?"":"A-", client_obj?client_obj->name:"", cib_error2string(local_rc)); } free_xml(client_reply); } static void parse_local_options( cib_client_t *cib_client, int call_type, int call_options, const char *host, const char *op, gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward) { if(cib_op_modifies(call_type) && !(call_options & cib_inhibit_bcast)) { /* we need to send an update anyway */ *needs_reply = TRUE; } else { *needs_reply = FALSE; } if(host == NULL && (call_options & cib_scope_local)) { crm_debug_2("Processing locally scoped %s op from %s", op, cib_client->name); *local_notify = TRUE; } else if(host == NULL && cib_is_master) { crm_debug_2("Processing master %s op locally from %s", op, cib_client->name); *local_notify = TRUE; } else if(safe_str_eq(host, cib_our_uname)) { crm_debug_2("Processing locally addressed %s op from %s", op, cib_client->name); *local_notify = TRUE; } else if(stand_alone) { *needs_forward = FALSE; *local_notify = TRUE; *process = TRUE; } else { crm_debug_2("%s op from %s needs to be forwarded to %s", op, cib_client->name, host?host:"the master instance"); *needs_forward = TRUE; *process = FALSE; } } static gboolean parse_peer_options( int call_type, xmlNode *request, gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward) { const char *op = crm_element_value(request, F_CIB_OPERATION); const char *originator = crm_element_value(request, F_ORIG); const char *host = crm_element_value(request, F_CIB_HOST); const char *reply_to = crm_element_value(request, F_CIB_ISREPLY); const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); const char *delegated = crm_element_value(request, F_CIB_DELEGATED); if(safe_str_eq(op, "cib_shutdown_req")) { if(reply_to != NULL) { crm_debug("Processing %s from %s", op, host); *needs_reply = FALSE; } else { crm_debug("Processing %s reply from %s", op, host); } return TRUE; } else if(crm_is_true(update) && safe_str_eq(reply_to, cib_our_uname)) { crm_debug_2("Processing global/peer update from %s" " that originated from us", originator); *needs_reply = FALSE; if(crm_element_value(request, F_CIB_CLIENTID) != NULL) { *local_notify = TRUE; } return TRUE; } else if(crm_is_true(update)) { crm_debug_2("Processing global/peer update from %s", originator); *needs_reply = FALSE; return TRUE; } else if(host != NULL && safe_str_eq(host, cib_our_uname)) { crm_debug_2("Processing request sent to us from %s", originator); return TRUE; } else if(delegated != NULL && cib_is_master == TRUE) { crm_debug_2("Processing request sent to master instance from %s", originator); return TRUE; } else if(reply_to != NULL && safe_str_eq(reply_to, cib_our_uname)) { crm_debug_2("Forward reply sent from %s to local clients", originator); *process = FALSE; *needs_reply = FALSE; *local_notify = TRUE; return TRUE; } else if(delegated != NULL) { crm_debug_2("Ignoring msg for master instance"); } else if(host != NULL) { /* this is for a specific instance and we're not it */ crm_debug_2("Ignoring msg for instance on %s", crm_str(host)); } else if(reply_to == NULL && cib_is_master == FALSE) { /* this is for the master instance and we're not it */ crm_debug_2("Ignoring reply to %s", crm_str(reply_to)); } else { crm_err("Nothing for us to do?"); crm_log_xml(LOG_ERR, "Peer[inbound]", request); } return FALSE; } static void forward_request(xmlNode *request, cib_client_t *cib_client, int call_options) { xmlNode *forward_msg = NULL; const char *op = crm_element_value(request, F_CIB_OPERATION); const char *host = crm_element_value(request, F_CIB_HOST); forward_msg = cib_msg_copy(request, TRUE); crm_xml_add(forward_msg, F_CIB_DELEGATED, cib_our_uname); if(host != NULL) { crm_debug_2("Forwarding %s op to %s", op, host); send_cluster_message(host, crm_msg_cib, forward_msg, FALSE); } else { crm_debug_2("Forwarding %s op to master instance", op); send_cluster_message(NULL, crm_msg_cib, forward_msg, FALSE); } if(call_options & cib_discard_reply) { crm_debug_2("Client not interested in reply"); } else if(call_options & cib_sync_call) { /* keep track of the request so we can time it * out if required */ crm_debug_2("Registering delegated call from %s", cib_client->id); cib_client->delegated_calls = g_list_append( cib_client->delegated_calls, forward_msg); forward_msg = NULL; } free_xml(forward_msg); } static void send_peer_reply( xmlNode *msg, xmlNode *result_diff, const char *originator, gboolean broadcast) { xmlNode *reply_copy = NULL; CRM_ASSERT(msg != NULL); reply_copy = cib_msg_copy(msg, TRUE); if(broadcast) { /* this (successful) call modified the CIB _and_ the * change needs to be broadcast... * send via HA to other nodes */ int diff_add_updates = 0; int diff_add_epoch = 0; int diff_add_admin_epoch = 0; int diff_del_updates = 0; int diff_del_epoch = 0; int diff_del_admin_epoch = 0; char *digest = NULL; cib_diff_version_details( result_diff, &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); crm_debug("Sending update diff %d.%d.%d -> %d.%d.%d", diff_del_admin_epoch,diff_del_epoch,diff_del_updates, diff_add_admin_epoch,diff_add_epoch,diff_add_updates); crm_xml_add(reply_copy, F_CIB_ISREPLY, originator); crm_xml_add(reply_copy, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); crm_xml_add(reply_copy, F_CIB_OPERATION, CIB_OP_APPLY_DIFF); digest = calculate_xml_digest(the_cib, FALSE, TRUE); crm_xml_add(result_diff, XML_ATTR_DIGEST, digest); /* crm_log_xml_debug(the_cib, digest); */ crm_free(digest); add_message_xml(reply_copy, F_CIB_UPDATE_DIFF, result_diff); crm_log_xml(LOG_DEBUG_3, "copy", reply_copy); send_cluster_message(NULL, crm_msg_cib, reply_copy, TRUE); } else if(originator != NULL) { /* send reply via HA to originating node */ crm_debug_2("Sending request result to originator only"); crm_xml_add(reply_copy, F_CIB_ISREPLY, originator); send_cluster_message(originator, crm_msg_cib, reply_copy, FALSE); } free_xml(reply_copy); } void cib_process_request( xmlNode *request, gboolean force_synchronous, gboolean privileged, gboolean from_peer, cib_client_t *cib_client) { int call_type = 0; int call_options = 0; gboolean process = TRUE; gboolean needs_reply = TRUE; gboolean local_notify = FALSE; gboolean needs_forward = FALSE; xmlNode *result_diff = NULL; enum cib_errors rc = cib_ok; xmlNode *op_reply = NULL; const char *op = crm_element_value(request, F_CIB_OPERATION); const char *originator = crm_element_value(request, F_ORIG); const char *host = crm_element_value(request, F_CIB_HOST); const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE); crm_debug_4("%s Processing msg %s", cib_our_uname, crm_element_value(request, F_SEQ)); cib_num_ops++; if(cib_num_ops == 0) { cib_num_fail = 0; cib_num_local = 0; cib_num_updates = 0; crm_info("Stats wrapped around"); } if(host != NULL && strlen(host) == 0) { host = NULL; } crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); crm_debug_4("Retrieved call options: %d", call_options); if(force_synchronous) { call_options |= cib_sync_call; } crm_debug_2("Processing %s message (%s) for %s...", from_peer?"peer":"local", from_peer?originator:cib_our_uname, host?host:"master"); rc = cib_get_operation_id(op, &call_type); if(cib_op_modifies(call_type)) { cib_num_updates++; } if(rc != cib_ok) { /* TODO: construct error reply */ crm_err("Pre-processing of command failed: %s", cib_error2string(rc)); } else if(from_peer == FALSE) { parse_local_options(cib_client, call_type, call_options, host, op, &local_notify, &needs_reply, &process, &needs_forward); } else if(parse_peer_options(call_type, request, &local_notify, &needs_reply, &process, &needs_forward) == FALSE) { return; } crm_debug_3("Finished determining processing actions"); if(call_options & cib_discard_reply) { needs_reply = cib_op_modifies(call_type); local_notify = FALSE; } if(needs_forward) { forward_request(request, cib_client, call_options); return; } if(cib_status != cib_ok) { rc = cib_status; crm_err("Operation ignored, cluster configuration is invalid." " Please repair and restart: %s", cib_error2string(cib_status)); op_reply = cib_construct_reply(request, the_cib, cib_status); } else if(process) { cib_num_local++; crm_debug_2("Performing local processing:" " op=%s origin=%s/%s,%s (update=%s)", crm_element_value(request, F_CIB_OPERATION), originator, crm_element_value(request, F_CIB_CLIENTID), crm_element_value(request, F_CIB_CALLID), update); rc = cib_process_command( request, &op_reply, &result_diff, privileged); crm_debug_2("Processing complete"); if(rc == cib_diff_resync || rc == cib_diff_failed || rc == cib_old_data) { crm_warn("%s operation failed: %s", crm_str(op), cib_error2string(rc)); } else if(rc != cib_ok) { cib_num_fail++; crm_err("%s operation failed: %s", crm_str(op), cib_error2string(rc)); crm_log_xml(LOG_DEBUG, "CIB[output]", op_reply); crm_log_xml(LOG_INFO, "Input message", request); } if(op_reply == NULL && (needs_reply || local_notify)) { crm_err("Unexpected NULL reply to message"); crm_log_xml(LOG_ERR, "null reply", request); needs_reply = FALSE; local_notify = FALSE; } } crm_debug_3("processing response cases"); if(local_notify) { const char *client_id = crm_element_value(request, F_CIB_CLIENTID); if(process == FALSE) { do_local_notify(request, client_id, call_options & cib_sync_call, from_peer); } else { do_local_notify(op_reply, client_id, call_options & cib_sync_call, from_peer); } } /* from now on we are the server */ if(needs_reply == FALSE || stand_alone) { /* nothing more to do... * this was a non-originating slave update */ crm_debug_2("Completed slave update"); } else if(rc == cib_ok && result_diff != NULL && !(call_options & cib_inhibit_bcast)) { send_peer_reply(request, result_diff, originator, TRUE); } else if(call_options & cib_discard_reply) { crm_debug_4("Caller isn't interested in reply"); } else if (from_peer) { crm_debug_2("Directing reply to %s", originator); if(call_options & cib_inhibit_bcast) { crm_debug_3("Request not broadcast: inhibited"); } if(cib_op_modifies(call_type) == FALSE || result_diff == NULL) { crm_debug_3("Request not broadcast: R/O call"); } if(rc != cib_ok) { crm_debug_3("Request not broadcast: call failed: %s", cib_error2string(rc)); } send_peer_reply(op_reply, result_diff, originator, FALSE); } free_xml(op_reply); free_xml(result_diff); return; } xmlNode * cib_construct_reply(xmlNode *request, xmlNode *output, int rc) { int lpc = 0; xmlNode *reply = NULL; const char *name = NULL; const char *value = NULL; const char *names[] = { F_CIB_OPERATION, F_CIB_CALLID, F_CIB_CLIENTID, F_CIB_CALLOPTS }; crm_debug_4("Creating a basic reply"); reply = create_xml_node(NULL, "cib-reply"); crm_xml_add(reply, F_TYPE, T_CIB); for(lpc = 0; lpc < DIMOF(names); lpc++) { name = names[lpc]; value = crm_element_value(request, name); crm_xml_add(reply, name, value); } crm_xml_add_int(reply, F_CIB_RC, rc); if(output != NULL) { crm_debug_4("Attaching reply output"); add_message_xml(reply, F_CIB_CALLDATA, output); } return reply; } enum cib_errors cib_process_command(xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged) { gboolean send_r_notify = FALSE; xmlNode *output = NULL; xmlNode *input = NULL; xmlNode *current_cib = NULL; xmlNode *result_cib = NULL; int call_type = 0; int call_options = 0; enum cib_errors rc = cib_ok; enum cib_errors rc2 = cib_ok; int log_level = LOG_DEBUG_3; xmlNode *filtered = NULL; const char *op = NULL; const char *section = NULL; gboolean config_changed = FALSE; gboolean global_update = crm_is_true(crm_element_value(request, F_CIB_GLOBAL_UPDATE)); CRM_ASSERT(cib_status == cib_ok); *reply = NULL; *cib_diff = NULL; if(per_action_cib) { CRM_CHECK(the_cib == NULL, free_xml(the_cib)); the_cib = readCibXmlFile(cib_root, "cib.xml", FALSE); CRM_CHECK(the_cib != NULL, return cib_NOOBJECT); } current_cib = the_cib; /* Start processing the request... */ op = crm_element_value(request, F_CIB_OPERATION); crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); rc = cib_get_operation_id(op, &call_type); if(rc == cib_ok) { rc = cib_op_can_run(call_type, call_options, privileged, global_update); } /* prevent NUMUPDATES from being incrimented - apply the change as-is */ if(global_update) { call_options |= cib_inhibit_bcast; call_options |= cib_force_diff; } rc2 = cib_op_prepare(call_type, request, &input, §ion); if(rc == cib_ok) { rc = rc2; } if(rc != cib_ok) { crm_debug_2("Call setup failed: %s", cib_error2string(rc)); goto done; } else if(cib_op_modifies(call_type) == FALSE) { rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE, section, request, input, FALSE, &config_changed, current_cib, &result_cib, &output); CRM_CHECK(result_cib == NULL, free_xml(result_cib)); goto done; } /* Handle a valid write action */ if((call_options & cib_inhibit_notify) == 0) { cib_pre_notify(call_options, op, get_object_root(section, current_cib), input); } if(rc == cib_ok) { gboolean manage_counters = TRUE; if(global_update) { /* skip */ CRM_CHECK(call_type == 4 || call_type == 11, crm_err("Call type: %d", call_type); crm_log_xml(LOG_ERR, "bad op", request)); crm_debug_2("Skipping update: global replace"); manage_counters = FALSE; } else if(call_options & cib_inhibit_bcast) { /* skip */ crm_debug_2("Skipping update: inhibit broadcast"); manage_counters = FALSE; } rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE, section, request, input, manage_counters, &config_changed, current_cib, &result_cib, &output); *cib_diff = diff_cib_object(current_cib, result_cib, FALSE); } if(rc != cib_ok) { free_xml(result_cib); } else { rc = activateCibXml(result_cib, config_changed); if(rc != cib_ok) { crm_warn("Activation failed"); } } if((call_options & cib_inhibit_notify) == 0) { const char *call_id = crm_element_value(request, F_CIB_CALLID); const char *client = crm_element_value(request, F_CIB_CLIENTNAME); cib_post_notify(call_options, op, input, rc, the_cib); cib_diff_notify(call_options, client, call_id, op, input, rc, *cib_diff); } if(rc == cib_ok && safe_str_eq(CIB_OP_ERASE, op)) { send_r_notify = TRUE; } else if(rc == cib_ok && safe_str_eq(CIB_OP_REPLACE, op)) { if(section == NULL) { send_r_notify = TRUE; } else if(safe_str_eq(section, XML_TAG_CIB)) { send_r_notify = TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_NODES)) { send_r_notify = TRUE; } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { send_r_notify = TRUE; } } if(send_r_notify) { cib_replace_notify(the_cib, rc, *cib_diff); } if(rc == cib_dtd_validation && global_update) { log_level = LOG_WARNING; crm_log_xml_info(input, "cib:global_update"); } else if(rc != cib_ok) { log_level = LOG_DEBUG_4; } else if(cib_is_master && config_changed) { log_level = LOG_INFO; } else if(cib_is_master) { log_level = LOG_DEBUG; log_xml_diff(LOG_DEBUG_2, filtered, "cib:diff:filtered"); } else if(config_changed) { log_level = LOG_DEBUG_2; } else { log_level = LOG_DEBUG_3; } log_xml_diff(log_level, *cib_diff, "cib:diff"); free_xml(filtered); done: if((call_options & cib_discard_reply) == 0) { *reply = cib_construct_reply(request, output, rc); /* crm_log_xml_info(*reply, "cib:reply"); */ } if(call_type >= 0) { cib_op_cleanup(call_type, op, &input, &output); } if(per_action_cib) { uninitializeCib(); } return rc; } int send_via_callback_channel(xmlNode *msg, const char *token) { cib_client_t *hash_client = NULL; GList *list_item = NULL; enum cib_errors rc = cib_ok; crm_debug_3("Delivering msg %p to client %s", msg, token); if(token == NULL) { crm_err("No client id token, cant send message"); if(rc == cib_ok) { rc = cib_missing; } } else { /* A client that left before we could reply is not really * _our_ error. Warn instead. */ hash_client = g_hash_table_lookup(client_list, token); if(hash_client == NULL) { crm_warn("Cannot find client for token %s", token); rc = cib_client_gone; + } else if (crm_str_eq(hash_client->channel_name, "remote", FALSE)) { + /* just hope it's alive */ + } else if(hash_client->channel == NULL) { crm_err("Cannot find channel for client %s", token); rc = cib_client_corrupt; } else if(hash_client->channel->ops->get_chan_status( hash_client->channel) == IPC_DISCONNECT) { crm_warn("Client %s has disconnected", token); rc = cib_client_gone; cib_num_timeouts++; } } /* this is a more important error so overwriting rc is warrented */ if(msg == NULL) { crm_err("No message to send"); rc = cib_reply_failed; } if(rc == cib_ok) { list_item = g_list_find_custom( hash_client->delegated_calls, msg, cib_GCompareFunc); } if(list_item != NULL) { /* remove it - no need to time it out */ xmlNode *orig_msg = list_item->data; crm_debug_3("Removing msg from delegated list"); hash_client->delegated_calls = g_list_remove( hash_client->delegated_calls, orig_msg); CRM_DEV_ASSERT(orig_msg != msg); free_xml(orig_msg); } if(rc == cib_ok) { - crm_debug_3("Delivering reply to client %s", token); - if(send_ipc_message(hash_client->channel, msg) == FALSE) { - crm_warn("Delivery of reply to client %s/%s failed", - hash_client->name, token); - rc = cib_reply_failed; - } + crm_debug_3("Delivering reply to client %s (%s)", + token, hash_client->channel_name); + if (crm_str_eq(hash_client->channel_name, "remote", FALSE)) { + cib_send_remote_msg(hash_client->channel, msg); + + } else if(send_ipc_message(hash_client->channel, msg) == FALSE) { + crm_warn("Delivery of reply to client %s/%s failed", + hash_client->name, token); + rc = cib_reply_failed; + } } return rc; } gint cib_GCompareFunc(gconstpointer a, gconstpointer b) { const xmlNode *a_msg = a; const xmlNode *b_msg = b; int msg_a_id = 0; int msg_b_id = 0; const char *value = NULL; value = crm_element_value_const(a_msg, F_CIB_CALLID); msg_a_id = crm_parse_int(value, NULL); value = crm_element_value_const(b_msg, F_CIB_CALLID); msg_b_id = crm_parse_int(value, NULL); if(msg_a_id == msg_b_id) { return 0; } else if(msg_a_id < msg_b_id) { return -1; } return 1; } void cib_GHFunc(gpointer key, gpointer value, gpointer user_data) { int timeout = 0; /* 1 iteration == 10 seconds */ xmlNode *msg = NULL; xmlNode *reply = NULL; const char *host_to = NULL; cib_client_t *client = value; GListPtr list = client->delegated_calls; while(list != NULL) { msg = list->data; crm_element_value_int(msg, F_CIB_TIMEOUT, &timeout); if(timeout <= 0) { list = list->next; continue; } else { int seen = 0; crm_element_value_int(msg, F_CIB_SEENCOUNT, &seen); crm_debug_4("Timeout %d, seen %d", timeout, seen); if(seen < timeout) { crm_debug_4("Updating seen count for msg from client %s", client->id); seen += 10; crm_xml_add_int(msg, F_CIB_SEENCOUNT, seen); list = list->next; continue; } } cib_num_timeouts++; host_to = crm_element_value(msg, F_CIB_HOST); crm_warn("Sending operation timeout msg to client %s", client->id); reply = create_xml_node(NULL, "cib-reply"); crm_xml_add(reply, F_TYPE, T_CIB); crm_xml_add(reply, F_CIB_OPERATION, crm_element_value(msg, F_CIB_OPERATION)); crm_xml_add(reply, F_CIB_CALLID, crm_element_value(msg, F_CIB_CALLID)); if(host_to == NULL) { crm_xml_add_int(reply, F_CIB_RC, cib_master_timeout); } else { crm_xml_add_int(reply, F_CIB_RC, cib_remote_timeout); } send_ipc_message(client->channel, reply); list = list->next; client->delegated_calls = g_list_remove( client->delegated_calls, msg); free_xml(msg); free_xml(reply); } } gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client) { if (channel == NULL) { CRM_DEV_ASSERT(cib_client == NULL); } else if (cib_client == NULL) { crm_err("No client"); } else { CRM_DEV_ASSERT(channel->ch_status != IPC_CONNECT); crm_debug_2("Cleaning up after client disconnect: %s/%s/%s", crm_str(cib_client->name), cib_client->channel_name, cib_client->id); if(cib_client->id != NULL) { if(!g_hash_table_remove(client_list, cib_client->id)) { crm_err("Client %s not found in the hashtable", cib_client->name); } } } if(cib_shutdown_flag && g_hash_table_size(client_list) == 0) { crm_info("All clients disconnected..."); initiate_exit(); } return FALSE; } void cib_ha_peer_callback(HA_Message * msg, void* private_data) { xmlNode *xml = convert_ha_message(NULL, msg, __FUNCTION__); cib_peer_callback(xml, private_data); } void cib_peer_callback(xmlNode * msg, void* private_data) { int call_type = 0; int call_options = 0; const char *originator = crm_element_value(msg, F_ORIG); const char *seq = crm_element_value(msg, F_SEQ); const char *op = crm_element_value(msg, F_CIB_OPERATION); crm_node_t *node = NULL; crm_log_xml(LOG_MSG, "Peer[inbound]", msg); crm_debug_2("Peer %s message (%s) from %s", op, seq, originator); if(originator == NULL || safe_str_eq(originator, cib_our_uname)) { crm_debug_2("Discarding %s message %s from ourselves", op, seq); return; } if(crm_peer_cache == NULL) { crm_info("Discarding %s message (%s) from %s:" " membership not established", op, seq, originator); return; } node = g_hash_table_lookup(crm_peer_cache, originator); if(node == NULL || crm_is_member_active(node) == FALSE) { crm_warn("Discarding %s message (%s) from %s:" " not in our membership", op, seq, originator); return; } if(cib_get_operation_id(op, &call_type) != cib_ok) { crm_debug("Discarding %s message (%s) from %s:" " Invalid operation", op, seq, originator); return; } crm_debug_2("Processing %s msg (%s) from %s",op, seq, originator); crm_element_value_int(msg, F_CIB_CALLOPTS, &call_options); crm_debug_4("Retrieved call options: %d", call_options); if(crm_element_value(msg, F_CIB_CLIENTNAME) == NULL) { crm_xml_add(msg, F_CIB_CLIENTNAME, originator); } cib_process_request(msg, FALSE, TRUE, TRUE, NULL); return; } void cib_client_status_callback(const char * node, const char * client, const char * status, void * private) { crm_node_t *member = NULL; if(safe_str_eq(client, CRM_SYSTEM_CIB)) { crm_info("Status update: Client %s/%s now has status [%s]", node, client, status); if(safe_str_eq(status, JOINSTATUS)){ status = ONLINESTATUS; } else if(safe_str_eq(status, LEAVESTATUS)){ status = OFFLINESTATUS; } member = g_hash_table_lookup(crm_peer_cache, node); if(member == NULL) { /* Make sure it gets created */ const char *uuid = get_uuid(node); member = crm_update_peer(0, 0, -1, 0, uuid, node, NULL, NULL); } crm_update_peer_proc(node, crm_proc_cib, status); set_connected_peers(the_cib); } return; } #if SUPPORT_HEARTBEAT extern oc_ev_t *cib_ev_token; gboolean cib_ccm_dispatch(int fd, gpointer user_data) { int rc = 0; oc_ev_t *ccm_token = (oc_ev_t*)user_data; crm_debug_2("received callback"); rc = oc_ev_handle_event(ccm_token); if(0 == rc) { return TRUE; } crm_err("CCM connection appears to have failed: rc=%d.", rc); /* eventually it might be nice to recover and reconnect... but until then... */ crm_err("Exiting to recover from CCM connection failure"); exit(2); return FALSE; } int current_instance = 0; void cib_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data) { gboolean update_id = FALSE; const oc_ev_membership_t *membership = data; CRM_ASSERT(membership != NULL); crm_info("Processing CCM event=%s (id=%d)", ccm_event_name(event), membership->m_instance); if(current_instance > membership->m_instance) { crm_err("Membership instance ID went backwards! %d->%d", current_instance, membership->m_instance); CRM_ASSERT(current_instance <= membership->m_instance); } switch(event) { case OC_EV_MS_NEW_MEMBERSHIP: case OC_EV_MS_INVALID: update_id = TRUE; break; case OC_EV_MS_PRIMARY_RESTORED: update_id = TRUE; break; case OC_EV_MS_NOT_PRIMARY: crm_debug_2("Ignoring transitional CCM event: %s", ccm_event_name(event)); break; case OC_EV_MS_EVICTED: crm_err("Evicted from CCM: %s", ccm_event_name(event)); break; default: crm_err("Unknown CCM event: %d", event); } if(update_id) { unsigned int lpc = 0; CRM_CHECK(membership != NULL, return); current_instance = membership->m_instance; for(lpc=0; lpc < membership->m_n_out; lpc++) { crm_update_ccm_node( membership, lpc+membership->m_out_idx, CRM_NODE_LOST); } for(lpc=0; lpc < membership->m_n_member; lpc++) { crm_update_ccm_node( membership, lpc+membership->m_memb_idx,CRM_NODE_ACTIVE); } } oc_ev_callback_done(cookie); set_connected_peers(the_cib); return; } #endif gboolean can_write(int flags) { return TRUE; } static gboolean cib_force_exit(gpointer data) { crm_notice("Forcing exit!"); terminate_cib(__FUNCTION__); return FALSE; } void initiate_exit(void) { int active = 0; xmlNode *leaving = NULL; active = crm_active_peers(crm_proc_cib); if(active < 2) { terminate_cib(__FUNCTION__); return; } crm_info("Sending disconnect notification to %d peers...", active); leaving = create_xml_node(NULL, "exit-notification"); crm_xml_add(leaving, F_TYPE, "cib"); crm_xml_add(leaving, F_CIB_OPERATION, "cib_shutdown_req"); send_cluster_message(NULL, crm_msg_cib, leaving, TRUE); free_xml(leaving); Gmain_timeout_add(crm_get_msec("5s"), cib_force_exit, NULL); } void terminate_cib(const char *caller) { #if SUPPORT_AIS if(is_openais_cluster()) { cib_ha_connection_destroy(NULL); return; } #endif #if SUPPORT_HEARTBEAT if(hb_conn != NULL) { crm_info("%s: Disconnecting heartbeat", caller); hb_conn->llc_ops->signoff(hb_conn, FALSE); } else { crm_err("%s: No heartbeat connection", caller); } #endif uninitializeCib(); crm_info("Exiting..."); if (mainloop != NULL && g_main_is_running(mainloop)) { g_main_quit(mainloop); } else { exit(LSB_EXIT_OK); } } diff --git a/cib/remote.c b/cib/remote.c index bc65c10870..525575eba6 100644 --- a/cib/remote.c +++ b/cib/remote.c @@ -1,430 +1,433 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "callbacks.h" /* #undef HAVE_PAM_PAM_APPL_H */ /* #undef HAVE_GNUTLS_GNUTLS_H */ #ifdef HAVE_GNUTLS_GNUTLS_H # undef KEYFILE # include #endif #include #include #if HAVE_SECURITY_PAM_APPL_H # include # define HAVE_PAM 1 #else # if HAVE_PAM_PAM_APPL_H # include # define HAVE_PAM 1 # endif #endif int init_remote_listener(int port); #ifdef HAVE_GNUTLS_GNUTLS_H # define DH_BITS 1024 gnutls_dh_params dh_params; extern gnutls_anon_server_credentials anon_cred_s; static void debug_log(int level, const char *str) { fputs (str, stderr); } extern gnutls_session *create_tls_session(int csock, int type); #endif extern int num_clients; int authenticate_user(const char* user, const char* passwd); gboolean cib_remote_listen(int ssock, gpointer data); gboolean cib_remote_msg(int csock, gpointer data); -extern void cib_process_request( - xmlNode *request, gboolean privileged, gboolean force_synchronous, - gboolean from_peer, cib_client_t *cib_client); +extern void cib_common_callback_worker( + xmlNode *op_request, cib_client_t *cib_client, gboolean force_synchronous, gboolean privileged); + #define ERROR_SUFFIX " Shutting down remote listener" int init_remote_listener(int port) { int ssock; struct sockaddr_in saddr; int optval; if(port <= 0) { /* dont start it */ return 0; } #ifdef HAVE_GNUTLS_GNUTLS_H crm_notice("Starting a tls listener on port %d.", port); gnutls_global_init(); /* gnutls_global_set_log_level (10); */ gnutls_global_set_log_function (debug_log); gnutls_dh_params_init(&dh_params); gnutls_dh_params_generate2(dh_params, DH_BITS); gnutls_anon_allocate_server_credentials (&anon_cred_s); gnutls_anon_set_server_dh_params (anon_cred_s, dh_params); #else crm_warn("Starting a _plain_text_ listener on port %d.", port); #endif #ifndef HAVE_PAM crm_warn("PAM is _not_ enabled!"); #endif /* create server socket */ ssock = socket(AF_INET, SOCK_STREAM, 0); if (ssock == -1) { cl_perror("Can not create server socket."ERROR_SUFFIX); return -1; } /* reuse address */ optval = 1; setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); /* bind server socket*/ memset(&saddr, '\0', sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(port); if (bind(ssock, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { cl_perror("Can not bind server socket."ERROR_SUFFIX); return -2; } if (listen(ssock, 10) == -1) { cl_perror("Can not start listen."ERROR_SUFFIX); return -3; } G_main_add_fd(G_PRIORITY_HIGH, ssock, FALSE, cib_remote_listen, NULL, default_ipc_connection_destroy); return 0; } static int check_group_membership(const char* usr, const char* grp) { int index = 0; struct group *group = NULL; CRM_CHECK(usr != NULL, return FALSE); CRM_CHECK(grp != NULL, return FALSE); group = getgrnam(grp); if (group == NULL) { crm_err("No group named '%s' exists!", grp); return FALSE; } while (TRUE) { char* member = group->gr_mem[index++]; if(member == NULL) { break; } else if (crm_str_eq(usr, member, TRUE)) { return TRUE; } }; return FALSE; } -#define WELCOME "" - gboolean cib_remote_listen(int ssock, gpointer data) { int lpc = 0; int csock; unsigned laddr; struct sockaddr_in addr; #ifdef HAVE_GNUTLS_GNUTLS_H gnutls_session *session = NULL; #endif cib_client_t *new_client = NULL; xmlNode *login = NULL; const char *user = NULL; const char *pass = NULL; const char *tmp = NULL; cl_uuid_t client_id; char uuid_str[UU_UNPARSE_SIZEOF]; crm_debug("New connection"); /* accept the connection */ laddr = sizeof(addr); csock = accept(ssock, (struct sockaddr*)&addr, &laddr); if (csock == -1) { crm_err("accept socket failed"); return TRUE; } #ifdef HAVE_GNUTLS_GNUTLS_H /* create gnutls session for the server socket */ session = create_tls_session(csock, GNUTLS_SERVER); if (session == NULL) { crm_err("TLS session creation failed"); close(csock); return TRUE; } #endif do { crm_debug_2("Iter: %d", lpc++); #ifdef HAVE_GNUTLS_GNUTLS_H login = cib_recv_remote_msg(session); #else login = cib_recv_remote_msg(GINT_TO_POINTER(csock)); #endif sleep(1); } while(login == NULL && lpc < 10); crm_log_xml_info(login, "Login: "); if(login == NULL) { goto bail; } tmp = crm_element_name(login); if(safe_str_neq(tmp, "cib_command")) { crm_err("Wrong tag: %s", tmp); goto bail; } tmp = crm_element_value(login, "op"); if(safe_str_neq(tmp, "authenticate")) { crm_err("Wrong operation: %s", tmp); goto bail; } user = crm_element_value(login, "user"); pass = crm_element_value(login, "password"); if(check_group_membership(user, HA_APIGROUP) == FALSE) { crm_err("User is not a member of the required group"); goto bail; } else if (authenticate_user(user, pass) == FALSE) { crm_err("PAM auth failed"); goto bail; } + /* send ACK */ crm_malloc0(new_client, sizeof(cib_client_t)); num_clients++; new_client->channel_name = "remote"; - + new_client->name = crm_element_value_copy(login, "name"); + 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); new_client->callback_id = NULL; #ifdef HAVE_GNUTLS_GNUTLS_H new_client->channel = (void*)session; - gnutls_record_send (*session, WELCOME, sizeof (WELCOME)); #else new_client->channel = GINT_TO_POINTER(csock); - write(csock, WELCOME, sizeof (WELCOME)); #endif new_client->source = (void*)G_main_add_fd( G_PRIORITY_HIGH, csock, FALSE, cib_remote_msg, new_client, default_ipc_connection_destroy); g_hash_table_insert(client_list, new_client->id, new_client); + + free_xml(login); + login = create_xml_node(NULL, "cib_result"); + crm_xml_add(login, F_CIB_OPERATION, CRM_OP_REGISTER); + crm_xml_add(login, F_CIB_CLIENTID, new_client->id); + cib_send_remote_msg(new_client->channel, login); + free_xml(login); + return TRUE; bail: #ifdef HAVE_GNUTLS_GNUTLS_H gnutls_bye(*session, GNUTLS_SHUT_RDWR); gnutls_deinit(*session); gnutls_free(session); #endif close(csock); + free_xml(login); return TRUE; } gboolean cib_remote_msg(int csock, gpointer data) { - cl_uuid_t call_id; - char call_uuid[UU_UNPARSE_SIZEOF]; const char *value = NULL; xmlNode *command = NULL; cib_client_t *client = data; command = cib_recv_remote_msg(client->channel); if(command == NULL) { crm_info("Could not parse command"); return FALSE; } - crm_log_xml(LOG_MSG+1, "Command: ", command); + crm_log_xml(LOG_MSG+1, "Raw command: ", command); value = crm_element_name(command); if(safe_str_neq(value, "cib_command")) { goto bail; } - cl_uuid_generate(&call_id); - cl_uuid_unparse(&call_id, call_uuid); + if(client->name == NULL) { + value = crm_element_value(command, F_CLIENTNAME); + if(value == NULL) { + client->name = crm_strdup(client->id); + } else { + client->name = crm_strdup(value); + } + } - crm_xml_add(command, F_TYPE, T_CIB); - crm_xml_add(command, F_CIB_CLIENTID, client->id); - crm_xml_add(command, F_CIB_CLIENTNAME, client->name); - crm_xml_add(command, F_CIB_CALLID, call_uuid); - if(crm_element_value(command, F_CIB_CALLOPTS) == NULL) { - crm_xml_add_int(command, F_CIB_CALLOPTS, 0); + if(client->callback_id == NULL) { + value = crm_element_value(command, F_CIB_CALLBACK_TOKEN); + if(value != NULL) { + client->callback_id = crm_strdup(value); + crm_debug_2("Callback channel for %s is %s", + client->id, client->callback_id); + + } else { + client->callback_id = crm_strdup(client->id); + } } - - crm_log_xml(LOG_MSG, "Fixed Command: ", command); + /* unset dangerous options */ xml_remove_prop(command, F_ORIG); xml_remove_prop(command, F_CIB_HOST); xml_remove_prop(command, F_CIB_GLOBAL_UPDATE); + + crm_xml_add(command, F_TYPE, T_CIB); + crm_xml_add(command, F_CIB_CLIENTID, client->id); + crm_xml_add(command, F_CIB_CLIENTNAME, client->name); - value = crm_element_value(command, F_CIB_OPERATION); - if(safe_str_eq(value, T_CIB_NOTIFY) ) { - /* Update the notify filters for this client */ - int on_off = 0; - const char *on_off_s = crm_element_value(command, F_CIB_NOTIFY_ACTIVATE); - value = crm_element_value(command, F_CIB_NOTIFY_TYPE); - on_off = crm_parse_int(on_off_s, "0"); - - crm_info("Setting %s callbacks for %s: %s", - value, client->name, on_off?"on":"off"); - - if(safe_str_eq(value, T_CIB_POST_NOTIFY)) { - client->post_notify = on_off; - - } else if(safe_str_eq(value, T_CIB_PRE_NOTIFY)) { - client->pre_notify = on_off; - - } else if(safe_str_eq(value, T_CIB_UPDATE_CONFIRM)) { - client->confirmations = on_off; - - } else if(safe_str_eq(value, T_CIB_DIFF_NOTIFY)) { - client->diffs = on_off; - - } else if(safe_str_eq(value, T_CIB_REPLACE_NOTIFY)) { - client->replace = on_off; - - } - goto bail; + if(crm_element_value(command, F_CIB_CALLID) == NULL) { + cl_uuid_t call_id; + char call_uuid[UU_UNPARSE_SIZEOF]; + + /* fix the command */ + cl_uuid_generate(&call_id); + cl_uuid_unparse(&call_id, call_uuid); + crm_xml_add(command, F_CIB_CALLID, call_uuid); + } + + if(crm_element_value(command, F_CIB_CALLOPTS) == NULL) { + crm_xml_add_int(command, F_CIB_CALLOPTS, 0); } - cib_process_request(command, TRUE, TRUE, FALSE, client); + crm_log_xml(LOG_MSG, "Remote command: ", command); + cib_common_callback_worker(command, client, FALSE, TRUE); bail: free_xml(command); return TRUE; } #ifdef HAVE_PAM /* * Useful Examples: * http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html * http://developer.apple.com/samplecode/CryptNoMore/index.html */ static int construct_pam_passwd(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { int i; char* passwd = (char*)data; struct pam_response *reply = NULL; crm_malloc0(reply, n * sizeof(*reply)); CRM_ASSERT(reply != NULL); /* Construct a PAM password message */ for (i = 0; i < n; ++i) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: reply[i].resp = passwd; break; case PAM_ERROR_MSG: crm_err("PAM error: %s", msg[i]->msg); break; case PAM_TEXT_INFO: crm_info("PAM info: %s", msg[i]->msg); break; default: crm_err("Unhandled message type: %d", msg[i]->msg_style); goto bail; break; } } *resp = reply; return PAM_SUCCESS; bail: crm_free(reply); return PAM_CONV_ERR; } #endif int authenticate_user(const char* user, const char* passwd) { #ifndef HAVE_PAM gboolean pass = TRUE; #else gboolean pass = FALSE; int rc = 0; struct pam_handle *handle = NULL; struct pam_conv passwd_data; passwd_data.conv = construct_pam_passwd; passwd_data.appdata_ptr = strdup(passwd); rc = pam_start ("cib", user, &passwd_data, &handle); if (rc != PAM_SUCCESS) { goto bail; } rc = pam_authenticate (handle, 0); if(rc != PAM_SUCCESS) { crm_err("pam_authenticate: %s (%d)", pam_strerror(handle, rc), rc); goto bail; } rc = pam_acct_mgmt(handle, 0); /* permitted access? */ if(rc != PAM_SUCCESS) { crm_err("pam_acct: %s (%d)", pam_strerror(handle, rc), rc); goto bail; } pass = TRUE; bail: rc = pam_end (handle, rc); #endif return pass; } diff --git a/lib/crm/cib/cib_file.c b/lib/crm/cib/cib_file.c index a1d49df63d..339044117d 100644 --- a/lib/crm/cib/cib_file.c +++ b/lib/crm/cib/cib_file.c @@ -1,320 +1,312 @@ /* * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct cib_file_opaque_s { int flags; char *filename; } cib_file_opaque_t; int cib_file_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int cib_file_signon(cib_t* cib, const char *name, enum cib_conn_type type); int cib_file_signoff(cib_t* cib); int cib_file_free(cib_t* cib); -static gboolean cib_file_msgready(cib_t* cib) { return FALSE; } -static IPC_Channel *cib_file_channel(cib_t* cib) { return NULL; } static int cib_file_inputfd(cib_t* cib) { return cib_NOTSUPPORTED; } -static int cib_file_rcvmsg(cib_t* cib, int blocking) { return cib_NOTSUPPORTED; } -static gboolean cib_file_dispatch(IPC_Channel *channel, gpointer user_data) { return FALSE; } static int cib_file_set_connection_dnotify( cib_t *cib, void (*dnotify)(gpointer user_data)) { return cib_NOTSUPPORTED; } static int cib_file_register_callback(cib_t* cib, const char *callback, int enabled) { return cib_NOTSUPPORTED; } cib_t* cib_file_new (const char *cib_location) { cib_file_opaque_t *private = NULL; cib_t *cib = cib_new_variant(); crm_malloc0(private, sizeof(cib_file_opaque_t)); cib->variant = cib_file; cib->variant_opaque = private; if(cib_location == NULL) { cib_location = getenv("CIB_file"); } private->filename = crm_strdup(cib_location); /* assign variant specific ops*/ cib->cmds->variant_op = cib_file_perform_op; cib->cmds->signon = cib_file_signon; cib->cmds->signoff = cib_file_signoff; cib->cmds->free = cib_file_free; - cib->cmds->channel = cib_file_channel; cib->cmds->inputfd = cib_file_inputfd; - cib->cmds->msgready = cib_file_msgready; - cib->cmds->rcvmsg = cib_file_rcvmsg; - cib->cmds->dispatch = cib_file_dispatch; cib->cmds->register_callback = cib_file_register_callback; cib->cmds->set_connection_dnotify = cib_file_set_connection_dnotify; return cib; } static xmlNode *in_mem_cib = NULL; static int load_file_cib(const char *filename) { int rc = cib_ok; struct stat buf; xmlNode *root = NULL; FILE *cib_file = NULL; gboolean dtd_ok = TRUE; const char *ignore_dtd = NULL; xmlNode *status = NULL; rc = stat(filename, &buf); if (rc == 0) { cib_file = fopen(filename, "r"); if(cib_file == NULL) { cl_perror("Could not open config file %s for reading", filename); return cib_not_authorized; } else { root = file2xml(cib_file, FALSE); fclose(cib_file); } } rc = 0; if(root == NULL) { crm_warn("Continuing with an empty configuration."); root = createEmptyCib(); } status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE); if(status == NULL) { create_xml_node(root, XML_CIB_TAG_STATUS); } ignore_dtd = crm_element_value(root, "ignore_dtd"); dtd_ok = validate_with_dtd(root, TRUE, DTD_DIRECTORY"/crm.dtd"); if(dtd_ok == FALSE) { crm_err("CIB does not validate against "DTD_DIRECTORY"/crm.dtd"); if(ignore_dtd != NULL && crm_is_true(ignore_dtd) == FALSE) { rc = cib_dtd_validation; goto bail; } } if(do_id_check(root, NULL, TRUE, FALSE)) { crm_err("%s does not contain a vaild configuration:" " ID check failed", filename); rc = cib_id_check; goto bail; } in_mem_cib = root; return rc; bail: free_xml(root); root = NULL; return rc; } int cib_file_signon(cib_t* cib, const char *name, enum cib_conn_type type) { int rc = cib_ok; cib_file_opaque_t *private = cib->variant_opaque; if(private->filename == FALSE) { rc = cib_missing; } else { rc = load_file_cib(private->filename); } if(rc == cib_ok) { fprintf(stderr, "%s: Opened connection to local file '%s'\n", name, private->filename); cib->state = cib_connected_command; cib->type = cib_command; } else { fprintf(stderr, "%s: Connection to local file '%s' failed: %s\n", name, private->filename, cib_error2string(rc)); } return rc; } int cib_file_signoff(cib_t* cib) { int rc = cib_ok; cib_file_opaque_t *private = cib->variant_opaque; crm_debug("Signing out of the CIB Service"); rc = write_xml_file(in_mem_cib, private->filename, FALSE); if(rc > 0) { crm_info("Wrote CIB to %s", private->filename); } else { crm_err("Could not write CIB to %s", private->filename); } crm_free(in_mem_cib); cib->state = cib_disconnected; cib->type = cib_none; return rc; } int cib_file_free (cib_t* cib) { int rc = cib_ok; crm_warn("Freeing CIB"); if(cib->state != cib_disconnected) { rc = cib_file_signoff(cib); if(rc == cib_ok) { cib_file_opaque_t *private = cib->variant_opaque; crm_free(private->filename); crm_free(cib->cmds); crm_free(private); crm_free(cib); } } return rc; } struct cib_func_entry { const char *op; gboolean read_only; cib_op_t fn; }; static struct cib_func_entry cib_file_ops[] = { {CIB_OP_QUERY, TRUE, cib_process_query}, {CIB_OP_MODIFY, FALSE, cib_process_modify}, /* {CIB_OP_UPDATE, FALSE, cib_process_change}, */ {CIB_OP_APPLY_DIFF, FALSE, cib_process_diff}, {CIB_OP_BUMP, FALSE, cib_process_bump}, {CIB_OP_REPLACE, FALSE, cib_process_replace}, /* {CIB_OP_CREATE, FALSE, cib_process_change}, */ {CIB_OP_DELETE, FALSE, cib_process_delete}, {CIB_OP_ERASE, FALSE, cib_process_erase}, }; int cib_file_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { int rc = cib_ok; gboolean query = FALSE; gboolean changed = FALSE; xmlNode *output = NULL; xmlNode *result_cib = NULL; cib_op_t *fn = NULL; int lpc = 0; static int max_msg_types = DIMOF(cib_file_ops); crm_info("%s on %s", op, section); if(cib->state == cib_disconnected) { return cib_not_connected; } if(output_data != NULL) { *output_data = NULL; } if(op == NULL) { crm_err("No operation specified"); return cib_operation; } for (lpc = 0; lpc < max_msg_types; lpc++) { if (safe_str_eq(op, cib_file_ops[lpc].op)) { fn = &(cib_file_ops[lpc].fn); query = cib_file_ops[lpc].read_only; break; } } if(fn == NULL) { return cib_NOTSUPPORTED; } cib->call_id++; rc = cib_perform_op(op, call_options, fn, query, section, NULL, data, TRUE, &changed, in_mem_cib, &result_cib, &output); if(rc != cib_ok) { crm_err("Call failed: %s", cib_error2string(rc)); crm_log_xml(LOG_WARNING, "failed", output); free_xml(result_cib); } else if(query == FALSE) { free_xml(in_mem_cib); in_mem_cib = result_cib; } if(output_data && output) { *output_data = copy_xml(output); } if(cib->op_callback != NULL) { cib->op_callback(NULL, cib->call_id, rc, output); } return rc; } diff --git a/lib/crm/cib/cib_native.c b/lib/crm/cib/cib_native.c index 139e6577aa..2ef707620b 100644 --- a/lib/crm/cib/cib_native.c +++ b/lib/crm/cib/cib_native.c @@ -1,768 +1,661 @@ /* * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct cib_native_opaque_s { IPC_Channel *command_channel; IPC_Channel *callback_channel; GCHSource *callback_source; char *token; } cib_native_opaque_t; int cib_native_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int cib_native_free(cib_t* cib); int cib_native_signoff(cib_t* cib); int cib_native_signon(cib_t* cib, const char *name, enum cib_conn_type type); IPC_Channel *cib_native_channel(cib_t* cib); gboolean cib_native_msgready(cib_t* cib); gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data); int cib_native_inputfd(cib_t* cib); int cib_native_rcvmsg(cib_t* cib, int blocking); int cib_native_set_connection_dnotify(cib_t *cib, void (*dnotify)(gpointer user_data)); -void cib_native_callback(cib_t *cib, xmlNode *msg); -void cib_native_notify(gpointer data, gpointer user_data); -int cib_native_register_callback(cib_t* cib, const char *callback, int enabled); - cib_t* cib_native_new (void) { cib_native_opaque_t *native = NULL; cib_t *cib = cib_new_variant(); crm_malloc0(native, sizeof(cib_native_opaque_t)); cib->variant = cib_native; cib->variant_opaque = native; native->command_channel = NULL; native->callback_channel = NULL; /* assign variant specific ops*/ cib->cmds->variant_op = cib_native_perform_op; cib->cmds->signon = cib_native_signon; cib->cmds->signoff = cib_native_signoff; cib->cmds->free = cib_native_free; - cib->cmds->channel = cib_native_channel; cib->cmds->inputfd = cib_native_inputfd; - cib->cmds->msgready = cib_native_msgready; - cib->cmds->rcvmsg = cib_native_rcvmsg; - cib->cmds->dispatch = cib_native_dispatch; cib->cmds->register_callback = cib_native_register_callback; cib->cmds->set_connection_dnotify = cib_native_set_connection_dnotify; return cib; } int cib_native_signon(cib_t* cib, const char *name, enum cib_conn_type type) { int rc = cib_ok; xmlNode *hello = NULL; char *uuid_ticket = NULL; cib_native_opaque_t *native = cib->variant_opaque; crm_debug_4("Connecting command channel"); if(type == cib_command) { cib->state = cib_connected_command; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_rw); } else if(type == cib_query) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_ro); } else if(type == cib_query_synchronous) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_ro_synchronous); } else if(type == cib_command_synchronous) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_rw_synchronous); } else { return cib_not_connected; } if(native->command_channel == NULL) { crm_debug("Connection to command channel failed"); rc = cib_connection; } else if(native->command_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to command channel failed"); rc = cib_authentication; } if(rc == cib_ok) { if(rc == cib_ok) { rc = get_channel_token(native->command_channel, &uuid_ticket); native->token = uuid_ticket; } } if(type == cib_query_synchronous || type == cib_command_synchronous) { goto do_register; } if(rc == cib_ok) { crm_debug_4("Connecting callback channel"); native->callback_source = init_client_ipc_comms( cib_channel_callback, cib_native_dispatch, cib, &(native->callback_channel)); if(native->callback_channel == NULL) { crm_debug("Connection to callback channel failed"); rc = cib_connection; } else if(native->callback_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to callback channel failed"); rc = cib_authentication; } else if(native->callback_source == NULL) { crm_err("Callback source not recorded"); rc = cib_connection; } else { native->callback_channel->send_queue->max_qlen = 500; } } if(rc == cib_ok) { rc = get_channel_token(native->callback_channel, &uuid_ticket); if(rc == cib_ok) { crm_free(native->token); native->token = uuid_ticket; } } do_register: hello = cib_create_op(0, native->token, CRM_OP_REGISTER, NULL, NULL, NULL, 0); crm_xml_add(hello, F_CIB_CLIENTNAME, name); if(send_ipc_message(native->command_channel, hello) == FALSE) { rc = cib_callback_register; } free_xml(hello); if(rc == cib_ok) { crm_debug("Connection to CIB successful"); return cib_ok; } crm_debug("Connection to CIB failed: %s", cib_error2string(rc)); cib_native_signoff(cib); return rc; } int cib_native_signoff(cib_t* cib) { cib_native_opaque_t *native = cib->variant_opaque; crm_debug("Signing out of the CIB Service"); /* close channels */ if (native->command_channel != NULL) { native->command_channel->ops->destroy( native->command_channel); native->command_channel = NULL; } if (native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } if (native->callback_channel != NULL) { #ifdef BUG native->callback_channel->ops->destroy( native->callback_channel); #endif native->callback_channel = NULL; } cib->state = cib_disconnected; cib->type = cib_none; return cib_ok; } int cib_native_free (cib_t* cib) { int rc = cib_ok; crm_warn("Freeing CIB"); if(cib->state != cib_disconnected) { rc = cib_native_signoff(cib); if(rc == cib_ok) { crm_free(cib->variant_opaque); crm_free(cib->cmds); crm_free(cib); } } return rc; } IPC_Channel * cib_native_channel(cib_t* cib) { cib_native_opaque_t *native = NULL; if(cib == NULL) { crm_err("Missing cib object"); return NULL; } native = cib->variant_opaque; if(native != NULL) { return native->callback_channel; } crm_err("couldnt find variant specific data in %p", cib); return NULL; } int cib_native_inputfd(cib_t* cib) { IPC_Channel *ch = cib_native_channel(cib); return ch->ops->get_recv_select_fd(ch); } static gboolean timer_expired = FALSE; static struct timer_rec_s *sync_timer = NULL; static gboolean cib_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; timer_expired = TRUE; crm_err("Call %d timed out after %ds", timer->call_id, timer->timeout); /* Always return TRUE, never remove the handler * We do that after the while-loop in cib_native_perform_op() */ return TRUE; } int cib_native_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { int rc = HA_OK; xmlNode *op_msg = NULL; xmlNode *op_reply = NULL; cib_native_opaque_t *native = cib->variant_opaque; if(sync_timer == NULL) { crm_malloc0(sync_timer, sizeof(struct timer_rec_s)); } if(cib->state == cib_disconnected) { return cib_not_connected; } if(output_data != NULL) { *output_data = NULL; } if(op == NULL) { crm_err("No operation specified"); return cib_operation; } cib->call_id++; /* prevent call_id from being negative (or zero) and conflicting * with the cib_errors enum * use 2 because we use it as (cib->call_id - 1) below */ if(cib->call_id < 1) { cib->call_id = 1; } op_msg = cib_create_op( cib->call_id, native->token, op, host, section, data, call_options); if(op_msg == NULL) { return cib_create_msg; } crm_debug_3("Sending %s message to CIB service", op); if(send_ipc_message(native->command_channel, op_msg) == FALSE) { crm_err("Sending message to CIB service FAILED"); free_xml(op_msg); return cib_send_failed; } else { crm_debug_3("Message sent"); } free_xml(op_msg); if((call_options & cib_discard_reply)) { crm_debug_3("Discarding reply"); return cib_ok; } else if(!(call_options & cib_sync_call)) { crm_debug_3("Async call, returning"); CRM_CHECK(cib->call_id != 0, return cib_reply_failed); return cib->call_id; } rc = IPC_OK; crm_debug_3("Waiting for a syncronous reply"); if(cib->call_timeout > 0) { /* We need this, even with msgfromIPC_timeout(), because we might * get other/older replies that don't match the active request */ timer_expired = FALSE; sync_timer->call_id = cib->call_id; sync_timer->timeout = cib->call_timeout*1000; sync_timer->ref = Gmain_timeout_add( sync_timer->timeout, cib_timeout_handler, sync_timer); } while(timer_expired == FALSE && IPC_ISRCONN(native->command_channel)) { int reply_id = -1; int msg_id = cib->call_id; op_reply = xmlfromIPC(native->command_channel, cib->call_timeout); if(op_reply == NULL) { break; } crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); CRM_CHECK(reply_id > 0, free_xml(op_reply); if(sync_timer->ref > 0) { g_source_remove(sync_timer->ref); sync_timer->ref = 0; } return cib_reply_failed); if(reply_id == msg_id) { break; } else if(reply_id < msg_id) { crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml( LOG_MSG, "Old reply", op_reply); } else if((reply_id - 10000) > msg_id) { /* wrap-around case */ crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml( LOG_MSG, "Old reply", op_reply); } else { crm_err("Received a __future__ reply:" " %d (wanted %d)", reply_id, msg_id); } free_xml(op_reply); op_reply = NULL; } if(sync_timer->ref > 0) { g_source_remove(sync_timer->ref); sync_timer->ref = 0; } if(timer_expired) { return cib_remote_timeout; } if(IPC_ISRCONN(native->command_channel) == FALSE) { crm_err("CIB disconnected: %d", native->command_channel->ch_status); cib->state = cib_disconnected; } if(op_reply == NULL) { crm_err("No reply message - empty - %d", rc); return cib_reply_failed; } crm_debug_3("Syncronous reply recieved"); rc = cib_ok; /* Start processing the reply... */ if(crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { rc = cib_return_code; } if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } if(rc == cib_ok || rc == cib_not_master || rc == cib_master_timeout) { crm_log_xml(LOG_MSG, "passed", op_reply); } else { /* } else if(rc == cib_remote_timeout) { */ crm_err("Call failed: %s", cib_error2string(rc)); crm_log_xml(LOG_WARNING, "failed", op_reply); } if(output_data == NULL) { /* do nothing more */ } else if(!(call_options & cib_discard_reply)) { xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); if(tmp == NULL) { crm_debug_3("No output in reply to \"%s\" command %d", op, cib->call_id - 1); } else { *output_data = copy_xml(tmp); } } free_xml(op_reply); return rc; } gboolean cib_native_msgready(cib_t* cib) { cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; if(native->command_channel != NULL) { /* drain the channel */ IPC_Channel *cmd_ch = native->command_channel; xmlNode *cmd_msg = NULL; while(cmd_ch->ch_status != IPC_DISCONNECT && cmd_ch->ops->is_message_pending(cmd_ch)) { /* this will happen when the CIB exited from beneath us */ cmd_msg = xmlfromIPC(cmd_ch, 0); free_xml(cmd_msg); } } else { crm_err("No command channel"); } if(native->callback_channel == NULL) { crm_err("No callback channel"); return FALSE; } else if(native->callback_channel->ch_status == IPC_DISCONNECT) { crm_info("Lost connection to the CIB service [%d].", native->callback_channel->farside_pid); return FALSE; } else if(native->callback_channel->ops->is_message_pending( native->callback_channel)) { crm_debug_4("Message pending on command channel [%d]", native->callback_channel->farside_pid); return TRUE; } crm_debug_3("No message pending"); return FALSE; } int cib_native_rcvmsg(cib_t* cib, int blocking) { const char *type = NULL; xmlNode* msg = NULL; cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; /* if it is not blocking mode and no message in the channel, return */ if (blocking == 0 && cib_native_msgready(cib) == FALSE) { crm_debug_3("No message ready and non-blocking..."); return 0; } else if (cib_native_msgready(cib) == FALSE) { crm_debug("Waiting for message from CIB service..."); if(native->callback_channel == NULL) { return 0; } else if(native->callback_channel->ch_status != IPC_CONNECT) { return 0; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT){ return 0; } native->callback_channel->ops->waitin(native->callback_channel); } /* IPC_INTR is not a factor here */ msg = xmlfromIPC(native->callback_channel, 0); if (msg == NULL) { crm_warn("Received a NULL msg from CIB service."); return 0; } /* do callbacks */ type = crm_element_value(msg, F_TYPE); crm_debug_4("Activating %s callbacks...", type); if(safe_str_eq(type, T_CIB)) { cib_native_callback(cib, msg); } else if(safe_str_eq(type, T_CIB_NOTIFY)) { g_list_foreach(cib->notify_list, cib_native_notify, msg); } else { crm_err("Unknown message type: %s", type); } free_xml(msg); return 1; } -void -cib_native_callback(cib_t *cib, xmlNode *msg) -{ - int rc = 0; - int call_id = 0; - xmlNode *output = NULL; - - cib_callback_client_t *blob = NULL; - - cib_callback_client_t local_blob; - - /* gcc4 had a point... make sure (at least) local_blob.callback - * is initialized before use - */ - local_blob.callback = NULL; - local_blob.user_data = NULL; - local_blob.only_success = FALSE; - - crm_element_value_int(msg, F_CIB_CALLID, &call_id); - blob = g_hash_table_lookup( - cib_op_callback_table, GINT_TO_POINTER(call_id)); - - if(blob != NULL) { - crm_debug_3("Callback found for call %d", call_id); -/* local_blob.callback = blob->callback; */ -/* local_blob.user_data = blob->user_data; */ -/* local_blob.only_success = blob->only_success; */ - local_blob = *blob; - blob = NULL; - - remove_cib_op_callback(call_id, FALSE); - - } else { - crm_debug_3("No callback found for call %d", call_id); - local_blob.callback = NULL; - } - - crm_element_value_int(msg, F_CIB_RC, &rc); - if(rc == cib_diff_resync) { - /* This is an internal value that clients do not and should not care about */ - rc = cib_ok; - } - - output = get_message_xml(msg, F_CIB_CALLDATA); - - if(local_blob.callback != NULL - && (rc == cib_ok || local_blob.only_success == FALSE)) { - local_blob.callback( - msg, call_id, rc, output, local_blob.user_data); - - } else if(cib->op_callback == NULL && rc != cib_ok) { - crm_warn("CIB command failed: %s", cib_error2string(rc)); - crm_log_xml(LOG_DEBUG, "Failed CIB Update", msg); - } - - if(cib->op_callback == NULL) { - crm_debug_3("No OP callback set, ignoring reply"); - } else { - cib->op_callback(msg, call_id, rc, output); - } - crm_debug_4("OP callback activated."); -} - - -void -cib_native_notify(gpointer data, gpointer user_data) -{ - xmlNode *msg = user_data; - cib_notify_client_t *entry = data; - const char *event = NULL; - - if(msg == NULL) { - crm_warn("Skipping callback - NULL message"); - return; - } - - event = crm_element_value(msg, F_SUBTYPE); - - if(entry == NULL) { - crm_warn("Skipping callback - NULL callback client"); - return; - - } else if(entry->callback == NULL) { - crm_warn("Skipping callback - NULL callback"); - return; - - } else if(safe_str_neq(entry->event, event)) { - crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s", - entry, entry->event, event); - return; - } - - crm_debug_4("Invoking callback for %p/%s event...", entry, event); - entry->callback(event, msg); - crm_debug_4("Callback invoked..."); -} - gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; cib_t *cib = user_data; cib_native_opaque_t *native = NULL; crm_debug_3("Received callback"); if(user_data == NULL){ crm_err("user_data field must contain the CIB struct"); return FALSE; } native = cib->variant_opaque; while(cib_native_msgready(cib)) { lpc++; /* invoke the callbacks but dont block */ if(cib_native_rcvmsg(cib, 0) < 1) { break; } } crm_debug_3("%d CIB messages dispatched", lpc); if(native->callback_channel && native->callback_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/callback].", channel->farside_pid); if(native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } return FALSE; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/command].", channel->farside_pid); return FALSE; } return TRUE; } int cib_native_set_connection_dnotify( cib_t *cib, void (*dnotify)(gpointer user_data)) { cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; if(dnotify == NULL) { crm_warn("Setting dnotify back to default value"); set_IPC_Channel_dnotify(native->callback_source, default_ipc_connection_destroy); } else { crm_debug_3("Setting dnotify"); set_IPC_Channel_dnotify(native->callback_source, dnotify); } return cib_ok; } - int cib_native_register_callback(cib_t* cib, const char *callback, int enabled) { xmlNode *notify_msg = create_xml_node(NULL, "cib-callback"); cib_native_opaque_t *native = cib->variant_opaque; - /* short term hack - should make this generic somehow */ crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY); crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback); crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled); send_ipc_message(native->callback_channel, notify_msg); free_xml(notify_msg); return cib_ok; } diff --git a/lib/crm/cib/cib_private.h b/lib/crm/cib/cib_private.h index ffc2241dfd..f2925b34e8 100644 --- a/lib/crm/cib/cib_private.h +++ b/lib/crm/cib/cib_private.h @@ -1,66 +1,69 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB_PRIVATE__H #define CIB_PRIVATE__H #include extern GHashTable *cib_op_callback_table; typedef struct cib_notify_client_s { const char *event; const char *obj_id; /* implement one day */ const char *obj_type; /* implement one day */ void (*callback)(const char *event, xmlNode *msg); } cib_notify_client_t; typedef struct cib_callback_client_s { void (*callback)(xmlNode*, int, int, xmlNode*, void*); void *user_data; gboolean only_success; struct timer_rec_s *timer; } cib_callback_client_t; struct timer_rec_s { int call_id; int timeout; guint ref; }; typedef enum cib_errors (*cib_op_t)(const char *, int, const char *, xmlNode *, xmlNode*, xmlNode*, xmlNode**, xmlNode**); extern cib_t *cib_new_variant(void); enum cib_errors cib_perform_op(const char *op, int call_options, cib_op_t *fn, gboolean is_query, const char *section, xmlNode *req, xmlNode *input, gboolean manage_counters, gboolean *config_changed, xmlNode *current_cib, xmlNode **result_cib, xmlNode **output); extern xmlNode *cib_create_op( int call_id, const char *token, const char *op, const char *host, const char *section, xmlNode *data, int call_options); extern int get_channel_token(IPC_Channel *ch, char **token); +void cib_native_callback(cib_t *cib, xmlNode *msg); +void cib_native_notify(gpointer data, gpointer user_data); +int cib_native_register_callback(cib_t* cib, const char *callback, int enabled); #endif diff --git a/lib/crm/cib/cib_remote.c b/lib/crm/cib/cib_remote.c index 40c358bd1e..966cd96182 100644 --- a/lib/crm/cib/cib_remote.c +++ b/lib/crm/cib/cib_remote.c @@ -1,534 +1,612 @@ /* * Copyright (c) 2008 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GNUTLS_GNUTLS_H # undef KEYFILE # include #endif #include #include #define DH_BITS 1024 extern gnutls_anon_client_credentials anon_cred_c; const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 }; +struct remote_connection_s +{ + int socket; + gboolean encrypted; + gnutls_session *session; + GFDSource *source; + char *token; +}; typedef struct cib_remote_opaque_s { int flags; int socket; int port; char *server; char *user; char *passwd; - gnutls_session* session; - char *token; + struct remote_connection_s command; + struct remote_connection_s callback; } cib_remote_opaque_t; +gboolean cib_remote_dispatch(int fd, gpointer user_data); int cib_remote_signon(cib_t* cib, const char *name, enum cib_conn_type type); int cib_remote_signoff(cib_t* cib); int cib_remote_free(cib_t* cib); int cib_remote_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); -static gboolean cib_remote_msgready(cib_t* cib) { return FALSE; } -static IPC_Channel *cib_remote_channel(cib_t* cib) { return NULL; } -static int cib_remote_inputfd(cib_t* cib) { return cib_NOTSUPPORTED; } -static int cib_remote_rcvmsg(cib_t* cib, int blocking) { return cib_NOTSUPPORTED; } -static gboolean cib_remote_dispatch(IPC_Channel *channel, gpointer user_data) { return FALSE; } + +static int cib_remote_inputfd(cib_t* cib) { + cib_remote_opaque_t *private = cib->variant_opaque; + return private->callback.socket; +} static int cib_remote_set_connection_dnotify( cib_t *cib, void (*dnotify)(gpointer user_data)) { return cib_NOTSUPPORTED; } - -static int cib_remote_register_callback(cib_t* cib, const char *callback, int enabled) +static int +cib_remote_register_callback(cib_t* cib, const char *callback, int enabled) { - return cib_NOTSUPPORTED; + xmlNode *notify_msg = create_xml_node(NULL, "cib-callback"); + cib_remote_opaque_t *private = cib->variant_opaque; + + crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY); + crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback); + crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled); + cib_send_remote_msg(private->callback.session, notify_msg); + free_xml(notify_msg); + return cib_ok; } cib_t* cib_remote_new (const char *server, const char *user, const char *passwd, int port) { cib_remote_opaque_t *private = NULL; cib_t *cib = cib_new_variant(); crm_malloc0(private, sizeof(cib_remote_opaque_t)); cib->variant = cib_remote; cib->variant_opaque = private; if(server) { private->server = crm_strdup(server); } if(user) { private->user = crm_strdup(user); } if(passwd) { private->passwd = crm_strdup(passwd); } private->port = port; /* assign variant specific ops*/ cib->cmds->variant_op = cib_remote_perform_op; cib->cmds->signon = cib_remote_signon; cib->cmds->signoff = cib_remote_signoff; cib->cmds->free = cib_remote_free; - cib->cmds->channel = cib_remote_channel; cib->cmds->inputfd = cib_remote_inputfd; - cib->cmds->msgready = cib_remote_msgready; - cib->cmds->rcvmsg = cib_remote_rcvmsg; - cib->cmds->dispatch = cib_remote_dispatch; cib->cmds->register_callback = cib_remote_register_callback; cib->cmds->set_connection_dnotify = cib_remote_set_connection_dnotify; return cib; } static int cib_tls_close(cib_t *cib) { cib_remote_opaque_t *private = cib->variant_opaque; close(private->socket); gnutls_anon_free_client_credentials (anon_cred_c); gnutls_global_deinit(); return 0; } extern gnutls_session *create_tls_session(int csock, int type); +static void +cib_remote_connection_destroy(gpointer user_data) +{ + crm_err("Connection destroyed"); + return; +} + +gboolean cib_remote_dispatch(int fd, gpointer user_data) +{ + cib_t *cib = user_data; + cib_remote_opaque_t *private = cib->variant_opaque; + if(fd == private->callback.socket) { + xmlNode *msg = NULL; + const char *type = NULL; + + crm_info("Message on callback channel"); + msg = cib_recv_remote_msg(private->callback.session); + + type = crm_element_value(msg, F_TYPE); + crm_debug_4("Activating %s callbacks...", type); + + if(safe_str_eq(type, T_CIB)) { + cib_native_callback(cib, msg); + + } else if(safe_str_eq(type, T_CIB_NOTIFY)) { + g_list_foreach(cib->notify_list, cib_native_notify, msg); + + } else { + crm_err("Unknown message type: %s", type); + } + + if(msg != NULL) { + free_xml(msg); + return TRUE; + } + + } else if(fd == private->command.socket) { + crm_err("Message on command channel"); + + } else { + crm_err("Unknown fd"); + } + + return FALSE; +} static int -cib_tls_signon(cib_t *cib) +cib_tls_signon(cib_t *cib, struct remote_connection_s *connection) { int sock; cib_remote_opaque_t *private = cib->variant_opaque; struct sockaddr_in addr; int rc = 0; char *server = private->server; int ret_ga; struct addrinfo *res; struct addrinfo hints; + xmlNode *answer = NULL; + xmlNode *login = NULL; + + connection->encrypted = TRUE; + connection->socket = 0; + connection->session = NULL; + /* create socket */ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1 ) { cl_perror("Socket creation failed"); return -1; } /* getaddrinfo */ bzero(&hints, sizeof(struct addrinfo)); hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_RAW; if(hints.ai_family == AF_INET6) { hints.ai_protocol = IPPROTO_ICMPV6; } else { hints.ai_protocol = IPPROTO_ICMP; } crm_debug("Looking up %s", server); ret_ga = getaddrinfo(server, NULL, &hints, &res); if (ret_ga) { crm_err("getaddrinfo: %s", gai_strerror(ret_ga)); return -1; } if (res->ai_canonname) { server = res->ai_canonname; } crm_debug("Got address %s for %s", server, private->server); if (!res->ai_addr) { fprintf(stderr, "getaddrinfo failed"); exit(1); } #if 1 memcpy(&addr, res->ai_addr, res->ai_addrlen); #else /* connect to server*/ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(server); #endif addr.sin_port = htons(private->port); if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { cl_perror("Connection to %s:%d failed", server, private->port); close(sock); return -1; } /* initialize GnuTls lib*/ gnutls_global_init(); gnutls_anon_allocate_client_credentials(&anon_cred_c); /* bind the socket to GnuTls lib */ - private->session = create_tls_session(sock, GNUTLS_CLIENT); - if (private->session == NULL) { + connection->session = create_tls_session(sock, GNUTLS_CLIENT); + if (connection->session == NULL) { cl_perror("Session creation for %s:%d failed", server, private->port); close(sock); cib_tls_close(cib); return -1; } /* login to server */ - crm_err("Do login..."); - { - xmlNode *answer = NULL; - xmlNode *login = create_xml_node(NULL, "cib_command"); - crm_xml_add(login, "op", "authenticate"); - crm_xml_add(login, "user", private->user); - crm_xml_add(login, "password", private->passwd); - crm_xml_add(login, "hidden", "password"); - - cib_send_remote_msg(private->session, login); - answer = cib_recv_remote_msg(private->session); + login = create_xml_node(NULL, "cib_command"); + crm_xml_add(login, "op", "authenticate"); + crm_xml_add(login, "user", private->user); + crm_xml_add(login, "password", private->passwd); + crm_xml_add(login, "hidden", "password"); + + cib_send_remote_msg(connection->session, login); + free_xml(login); + + answer = cib_recv_remote_msg(connection->session); + crm_log_xml_err(answer, "Reply"); + if(answer == NULL) { + rc = cib_authentication; + + } else { + /* grab the token */ + const char *msg_type = crm_element_value(answer, F_CIB_OPERATION); + const char *tmp_ticket = crm_element_value(answer, F_CIB_CLIENTID); - crm_log_xml_err(answer, "Reply"); - if(answer == NULL) { - rc = 1; + if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) { + crm_err("Invalid registration message: %s", msg_type); + rc = cib_registration_msg; + + } else if(tmp_ticket == NULL) { + rc = cib_callback_token; + } else { - /* create a second connection for notifications? */ - -/* GFDSource* */ -/* G_main_add_fd(int priority, int fd, gboolean can_recurse */ -/* , gboolean (*dispatch)(int fd, gpointer user_data) */ -/* , gpointer userdata */ -/* , GDestroyNotify notify) */ - /* ais_source = G_main_add_fd( */ - /* G_PRIORITY_HIGH, sock, FALSE, ais_dispatch, dispatch, destroy); */ - private->socket = sock; - } - - free_xml(login); + connection->token = crm_strdup(tmp_ticket); + } } if (rc != 0) { cib_tls_close(cib); } + + connection->socket = sock; + connection->source = G_main_add_fd( + G_PRIORITY_HIGH, connection->socket, FALSE, + cib_remote_dispatch, cib, cib_remote_connection_destroy); + return rc; } int cib_remote_signon(cib_t* cib, const char *name, enum cib_conn_type type) { int rc = cib_ok; cib_remote_opaque_t *private = cib->variant_opaque; if(private->passwd == NULL) { struct termios settings; int rc; rc = tcgetattr (0, &settings); settings.c_lflag &= ~ECHO; rc = tcsetattr (0, TCSANOW, &settings); - fprintf(stdout, "Password: "); crm_malloc0(private->passwd, 1024); scanf("%s", private->passwd); fprintf(stdout, "\n"); /* fprintf(stderr, "entered: '%s'\n", buffer); */ settings.c_lflag |= ECHO; rc = tcsetattr (0, TCSANOW, &settings); } if(private->server == NULL || private->user == NULL) { rc = cib_missing; - } else { - rc = cib_tls_signon(cib); } + if(rc == cib_ok) { + rc = cib_tls_signon(cib, &(private->command)); + } + + if(rc == cib_ok) { + rc = cib_tls_signon(cib, &(private->callback)); + } + + if(rc == cib_ok) { + xmlNode *hello = cib_create_op(0, private->callback.token, CRM_OP_REGISTER, NULL, NULL, NULL, 0); + crm_xml_add(hello, F_CIB_CLIENTNAME, name); + cib_send_remote_msg(private->command.session, hello); + free_xml(hello); + } if(rc == cib_ok) { fprintf(stderr, "%s: Opened connection to %s:%d\n", name, private->server, private->port); cib->state = cib_connected_command; cib->type = cib_command; } else { fprintf(stderr, "%s: Connection to %s:%d failed: %s\n", name, private->server, private->port, cib_error2string(rc)); } return rc; } int cib_remote_signoff(cib_t* cib) { int rc = cib_ok; /* cib_remote_opaque_t *private = cib->variant_opaque; */ crm_debug("Signing out of the CIB Service"); cib_tls_close(cib); cib->state = cib_disconnected; cib->type = cib_none; return rc; } int cib_remote_free (cib_t* cib) { int rc = cib_ok; crm_warn("Freeing CIB"); if(cib->state != cib_disconnected) { rc = cib_remote_signoff(cib); if(rc == cib_ok) { cib_remote_opaque_t *private = cib->variant_opaque; crm_free(private->server); crm_free(private->user); crm_free(private->passwd); crm_free(cib->cmds); crm_free(private); crm_free(cib); } } return rc; } static gboolean timer_expired = FALSE; static struct timer_rec_s *sync_timer = NULL; static gboolean cib_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; timer_expired = TRUE; crm_err("Call %d timed out after %ds", timer->call_id, timer->timeout); /* Always return TRUE, never remove the handler * We do that after the while-loop in cib_native_perform_op() */ return TRUE; } int cib_remote_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { int rc = HA_OK; xmlNode *op_msg = NULL; xmlNode *op_reply = NULL; cib_remote_opaque_t *private = cib->variant_opaque; if(sync_timer == NULL) { crm_malloc0(sync_timer, sizeof(struct timer_rec_s)); } if(cib->state == cib_disconnected) { return cib_not_connected; } if(output_data != NULL) { *output_data = NULL; } if(op == NULL) { crm_err("No operation specified"); return cib_operation; } cib->call_id++; /* prevent call_id from being negative (or zero) and conflicting * with the cib_errors enum * use 2 because we use it as (cib->call_id - 1) below */ if(cib->call_id < 1) { cib->call_id = 1; } op_msg = cib_create_op( - cib->call_id, private->token, op, host, section, data, call_options); + cib->call_id, private->callback.token, op, host, section, data, call_options); if(op_msg == NULL) { return cib_create_msg; } crm_debug_3("Sending %s message to CIB service", op); - cib_send_remote_msg(private->session, op_msg); + cib_send_remote_msg(private->command.session, op_msg); free_xml(op_msg); if((call_options & cib_discard_reply)) { crm_debug_3("Discarding reply"); return cib_ok; } else if(!(call_options & cib_sync_call)) { - crm_err("Async calls are not yet supported"); - /* return cib->call_id; */ + return cib->call_id; } rc = IPC_OK; crm_debug_3("Waiting for a syncronous reply"); if(cib->call_timeout > 0) { /* We need this, even with msgfromIPC_timeout(), because we might * get other/older replies that don't match the active request */ timer_expired = FALSE; sync_timer->call_id = cib->call_id; sync_timer->timeout = cib->call_timeout*1000; sync_timer->ref = Gmain_timeout_add( sync_timer->timeout, cib_timeout_handler, sync_timer); } while(timer_expired == FALSE) { + int reply_id = -1; + int msg_id = cib->call_id; - op_reply = cib_recv_remote_msg(private->session); + op_reply = cib_recv_remote_msg(private->command.session); if(op_reply == NULL) { break; } -#if 1 - break; -#else - int reply_id = -1; - int msg_id = cib->call_id; crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); CRM_CHECK(reply_id > 0, free_xml(op_reply); if(sync_timer->ref > 0) { g_source_remove(sync_timer->ref); sync_timer->ref = 0; } return cib_reply_failed); if(reply_id == msg_id) { break; } else if(reply_id < msg_id) { crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml( LOG_MSG, "Old reply", op_reply); } else if((reply_id - 10000) > msg_id) { /* wrap-around case */ crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml( LOG_MSG, "Old reply", op_reply); } else { crm_err("Received a __future__ reply:" " %d (wanted %d)", reply_id, msg_id); } -#endif + free_xml(op_reply); op_reply = NULL; } if(sync_timer->ref > 0) { g_source_remove(sync_timer->ref); sync_timer->ref = 0; } if(timer_expired) { return cib_remote_timeout; } /* if(IPC_ISRCONN(native->command_channel) == FALSE) { */ /* crm_err("CIB disconnected: %d", */ /* native->command_channel->ch_status); */ /* cib->state = cib_disconnected; */ /* } */ if(op_reply == NULL) { crm_err("No reply message - empty - %d", rc); return cib_reply_failed; } crm_debug_3("Syncronous reply recieved"); rc = cib_ok; /* Start processing the reply... */ if(crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { rc = cib_return_code; } if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } if(rc == cib_ok || rc == cib_not_master || rc == cib_master_timeout) { crm_log_xml(LOG_DEBUG, "passed", op_reply); } else { /* } else if(rc == cib_remote_timeout) { */ crm_err("Call failed: %s", cib_error2string(rc)); crm_log_xml(LOG_WARNING, "failed", op_reply); } if(output_data == NULL) { /* do nothing more */ } else if(!(call_options & cib_discard_reply)) { xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); if(tmp == NULL) { crm_debug_3("No output in reply to \"%s\" command %d", op, cib->call_id - 1); } else { *output_data = copy_xml(tmp); } } free_xml(op_reply); return rc; } diff --git a/lib/crm/cib/cib_utils.c b/lib/crm/cib/cib_utils.c index 671245ba5a..3de643fa20 100644 --- a/lib/crm/cib/cib_utils.c +++ b/lib/crm/cib/cib_utils.c @@ -1,783 +1,875 @@ /* * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include char * cib_pluralSection(const char *a_section) { char *a_section_parent = NULL; if (a_section == NULL) { a_section_parent = crm_strdup("all"); } else if(strcasecmp(a_section, XML_TAG_CIB) == 0) { a_section_parent = crm_strdup("all"); } else if(strcasecmp(a_section, XML_CIB_TAG_NODE) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_NODES); } else if(strcasecmp(a_section, XML_CIB_TAG_STATE) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_STATUS); } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINT) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_LOCATION) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_DEPEND) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_ORDER) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcasecmp(a_section, "resource") == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCE) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); } else if(strcasecmp(a_section, XML_CIB_TAG_GROUP) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); } else if(strcasecmp(a_section, XML_CIB_TAG_INCARNATION) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); } else if(strcasecmp(a_section, XML_CIB_TAG_NVPAIR) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); } else if(strcasecmp(a_section, XML_TAG_ATTR_SETS) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); } else { crm_err("Unknown section %s", a_section); a_section_parent = crm_strdup("all"); } crm_debug_2("Plural of %s is %s", crm_str(a_section), a_section_parent); return a_section_parent; } const char * cib_error2string(enum cib_errors return_code) { const char *error_msg = NULL; switch(return_code) { case cib_bad_permissions: error_msg = "bad permissions for the on-disk configuration. shutdown heartbeat and repair."; break; case cib_bad_digest: error_msg = "the on-disk configuration was manually altered. shutdown heartbeat and repair."; break; case cib_bad_config: error_msg = "the on-disk configuration is not valid"; break; case cib_msg_field_add: error_msg = "failed adding field to cib message"; break; case cib_id_check: error_msg = "missing id or id-collision detected"; break; case cib_operation: error_msg = "invalid operation"; break; case cib_create_msg: error_msg = "couldnt create cib message"; break; case cib_client_gone: error_msg = "client left before we could send reply"; break; case cib_not_connected: error_msg = "not connected"; break; case cib_not_authorized: error_msg = "not authorized"; break; case cib_send_failed: error_msg = "send failed"; break; case cib_reply_failed: error_msg = "reply failed"; break; case cib_return_code: error_msg = "no return code"; break; case cib_output_ptr: error_msg = "nowhere to store output"; break; case cib_output_data: error_msg = "corrupt output data"; break; case cib_connection: error_msg = "connection failed"; break; case cib_callback_register: error_msg = "couldnt register callback channel"; break; case cib_authentication: error_msg = ""; break; case cib_registration_msg: error_msg = "invalid registration msg"; break; case cib_callback_token: error_msg = "callback token not found"; break; case cib_missing: error_msg = "cib object missing"; break; case cib_variant: error_msg = "unknown/corrupt cib variant"; break; case CIBRES_MISSING_ID: error_msg = "The id field is missing"; break; case CIBRES_MISSING_TYPE: error_msg = "The type field is missing"; break; case CIBRES_MISSING_FIELD: error_msg = "A required field is missing"; break; case CIBRES_OBJTYPE_MISMATCH: error_msg = "CIBRES_OBJTYPE_MISMATCH"; break; case cib_EXISTS: error_msg = "The object already exists"; break; case cib_NOTEXISTS: error_msg = "The object/attribute does not exist"; break; case CIBRES_CORRUPT: error_msg = "The CIB is corrupt"; break; case cib_NOOBJECT: error_msg = "The update was empty"; break; case cib_NOPARENT: error_msg = "The parent object does not exist"; break; case cib_NODECOPY: error_msg = "Failed while copying update"; break; case CIBRES_OTHER: error_msg = "CIBRES_OTHER"; break; case cib_ok: error_msg = "ok"; break; case cib_unknown: error_msg = "Unknown error"; break; case cib_STALE: error_msg = "Discarded old update"; break; case cib_ACTIVATION: error_msg = "Activation Failed"; break; case cib_NOSECTION: error_msg = "Required section was missing"; break; case cib_NOTSUPPORTED: error_msg = "The action/feature is not supported"; break; case cib_not_master: error_msg = "Local service is not the master instance"; break; case cib_client_corrupt: error_msg = "Service client not valid"; break; case cib_remote_timeout: error_msg = "Remote node did not respond"; break; case cib_master_timeout: error_msg = "No master service is currently active"; break; case cib_revision_unsupported: error_msg = "The required CIB revision number is not supported"; break; case cib_revision_unknown: error_msg = "The CIB revision number could not be determined"; break; case cib_missing_data: error_msg = "Required data for this CIB API call not found"; break; case cib_no_quorum: error_msg = "Write requires quorum"; break; case cib_diff_failed: error_msg = "Application of an update diff failed"; break; case cib_diff_resync: error_msg = "Application of an update diff failed, requesting a full refresh"; break; case cib_bad_section: error_msg = "Invalid CIB section specified"; break; case cib_old_data: error_msg = "Update was older than existing configuration"; break; case cib_dtd_validation: error_msg = "Update does not conform to the DTD in "DTD_DIRECTORY"/crm.dtd"; break; case cib_invalid_argument: error_msg = "Invalid argument"; break; } if(error_msg == NULL) { crm_err("Unknown CIB Error Code: %d", return_code); error_msg = ""; } return error_msg; } int cib_section2enum(const char *a_section) { if(a_section == NULL || strcasecmp(a_section, "all") == 0) { return cib_section_all; } else if(strcasecmp(a_section, XML_CIB_TAG_NODES) == 0) { return cib_section_nodes; } else if(strcasecmp(a_section, XML_CIB_TAG_STATUS) == 0) { return cib_section_status; } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) { return cib_section_constraints; } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCES) == 0) { return cib_section_resources; } else if(strcasecmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) { return cib_section_crmconfig; } crm_err("Unknown CIB section: %s", a_section); return cib_section_none; } int cib_compare_generation(xmlNode *left, xmlNode *right) { int lpc = 0; const char *attributes[] = { XML_ATTR_GENERATION_ADMIN, XML_ATTR_GENERATION, XML_ATTR_NUMUPDATES, XML_ATTR_NUMPEERS }; crm_log_xml_debug_3(left, "left"); crm_log_xml_debug_3(right, "right"); for(lpc = 0; lpc < DIMOF(attributes); lpc++) { int int_elem_l = -1; int int_elem_r = -1; const char *elem_r = NULL; const char *elem_l = crm_element_value(left, attributes[lpc]); if(right != NULL) { elem_r = crm_element_value(right, attributes[lpc]); } if(elem_l != NULL) { int_elem_l = crm_parse_int(elem_l, NULL); } if(elem_r != NULL) { int_elem_r = crm_parse_int(elem_r, NULL); } if(int_elem_l < int_elem_r) { crm_debug_2("%s (%s < %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r)); return -1; } else if(int_elem_l > int_elem_r) { crm_debug_2("%s (%s > %s)", attributes[lpc], crm_str(elem_l), crm_str(elem_r)); return 1; } } return 0; } xmlNode* get_cib_copy(cib_t *cib) { xmlNode *xml_cib; #if CRM_DEPRECATED_SINCE_2_0_4 xmlNode *xml_cib_copy; #endif int options = cib_scope_local|cib_sync_call; if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) { crm_err("Couldnt retrieve the CIB"); return NULL; } else if(xml_cib == NULL) { crm_err("The CIB result was empty"); return NULL; } if(safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) { return xml_cib; #if CRM_DEPRECATED_SINCE_2_0_4 } else { xml_cib_copy = copy_xml( find_xml_node(xml_cib, XML_TAG_CIB, TRUE)); free_xml(xml_cib); return xml_cib_copy; #endif } free_xml(xml_cib); return NULL; } xmlNode* cib_get_generation(cib_t *cib) { xmlNode *the_cib = get_cib_copy(cib); xmlNode *generation = create_xml_node( NULL, XML_CIB_TAG_GENERATION_TUPPLE); if(the_cib != NULL) { copy_in_properties(generation, the_cib); free_xml(the_cib); } return generation; } void log_cib_diff(int log_level, xmlNode *diff, const char *function) { int add_updates = 0; int add_epoch = 0; int add_admin_epoch = 0; int del_updates = 0; int del_epoch = 0; int del_admin_epoch = 0; if(diff == NULL) { return; } cib_diff_version_details( diff, &add_admin_epoch, &add_epoch, &add_updates, &del_admin_epoch, &del_epoch, &del_updates); if(add_updates != del_updates) { do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function, del_admin_epoch, del_epoch, del_updates); do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function, add_admin_epoch, add_epoch, add_updates); } else if(diff != NULL) { do_crm_log(log_level, "%s: Local-only Change: %d.%d.%d", function, add_admin_epoch, add_epoch, add_updates); } log_xml_diff(log_level, diff, function); } gboolean cib_version_details( xmlNode *cib, int *admin_epoch, int *epoch, int *updates) { const char *value = NULL; if(cib == NULL) { *admin_epoch = -1; *epoch = -1; *updates = -1; return FALSE; } else { value = crm_element_value(cib, XML_ATTR_GENERATION_ADMIN); *admin_epoch = crm_parse_int(value, "-1"); value = crm_element_value(cib, XML_ATTR_GENERATION); *epoch = crm_parse_int(value, "-1"); value = crm_element_value(cib, XML_ATTR_NUMUPDATES); *updates = crm_parse_int(value, "-1"); } return TRUE; } gboolean cib_diff_version_details( xmlNode *diff, int *admin_epoch, int *epoch, int *updates, int *_admin_epoch, int *_epoch, int *_updates) { xmlNode *tmp = NULL; tmp = find_xml_node(diff, "diff-added", FALSE); cib_version_details(tmp, admin_epoch, epoch, updates); tmp = find_xml_node(diff, "diff-removed", FALSE); cib_version_details(tmp, _admin_epoch, _epoch, _updates); return TRUE; } /* * The caller should never free the return value */ xmlNode* get_object_root(const char *object_type, xmlNode *the_root) { const char *node_stack[2]; xmlNode *tmp_node = NULL; if(the_root == NULL) { crm_err("CIB root object was NULL"); return NULL; } node_stack[0] = XML_CIB_TAG_CONFIGURATION; node_stack[1] = object_type; if(object_type == NULL || strlen(object_type) == 0 || safe_str_eq(XML_CIB_TAG_SECTION_ALL, object_type) || safe_str_eq(XML_TAG_CIB, object_type)) { /* get the whole cib */ return the_root; } else if(strcasecmp(object_type, XML_CIB_TAG_STATUS) == 0) { /* these live in a different place */ tmp_node = find_xml_node(the_root, XML_CIB_TAG_STATUS, FALSE); node_stack[0] = object_type; node_stack[1] = NULL; } else { /* tmp_node = first_named_child(the_root, XML_CIB_TAG_CONFIGURATION); */ tmp_node = find_xml_node_nested(the_root, node_stack, 2); } if (tmp_node == NULL) { crm_debug_2("Section [%s [%s]] not present in %s", node_stack[0], node_stack[1]?node_stack[1]:"", crm_element_name(the_root)); } return tmp_node; } const char * get_crm_option(xmlNode *cib, const char *name, gboolean do_warn) { const char * value = NULL; xmlNode * a_default = NULL; xmlNode * config = get_object_root(XML_CIB_TAG_CRMCONFIG, cib); if(config != NULL) { a_default = find_entity(config, XML_CIB_TAG_NVPAIR, name); } if(a_default == NULL) { if(do_warn) { crm_warn("Option %s not set", name); } return NULL; } value = crm_element_value(a_default, XML_NVPAIR_ATTR_VALUE); if(safe_str_eq(value, "")) { value = NULL; } return value; } xmlNode* create_cib_fragment_adv( xmlNode *update, const char *update_section, const char *source) { xmlNode *cib = NULL; gboolean whole_cib = FALSE; xmlNode *object_root = NULL; const char *update_name = NULL; char *local_section = NULL; /* crm_debug("Creating a blank fragment: %s", update_section); */ if(update == NULL && update_section == NULL) { crm_debug_3("Creating a blank fragment"); update = createEmptyCib(); crm_xml_add(cib, XML_ATTR_ORIGIN, source); return update; } else if(update == NULL) { crm_err("No update to create a fragment for"); return NULL; } else if(update_section == NULL) { local_section = cib_pluralSection(update_name); update_section = local_section; } if(safe_str_eq(crm_element_name(update), XML_TAG_CIB)) { whole_cib = TRUE; } if(whole_cib == FALSE) { cib = createEmptyCib(); crm_xml_add(cib, XML_ATTR_ORIGIN, source); object_root = get_object_root(update_section, cib); add_node_copy(object_root, update); } else { cib = copy_xml(update); crm_xml_add(cib, XML_ATTR_ORIGIN, source); } crm_free(local_section); crm_debug_3("Verifying created fragment"); return cib; } /* * It is the callers responsibility to free both the new CIB (output) * and the new CIB (input) */ xmlNode* createEmptyCib(void) { xmlNode *cib_root = NULL, *config = NULL, *status = NULL; cib_root = create_xml_node(NULL, XML_TAG_CIB); config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); status = create_xml_node(cib_root, XML_CIB_TAG_STATUS); /* crm_xml_add(cib_root, "version", "1"); */ crm_xml_add(cib_root, "generated", XML_BOOLEAN_TRUE); crm_xml_add(cib_root, XML_ATTR_GENERATION, "0"); crm_xml_add(cib_root, XML_ATTR_GENERATION_ADMIN, "0"); crm_xml_add(cib_root, XML_ATTR_NUMUPDATES, "0"); create_xml_node(config, XML_CIB_TAG_CRMCONFIG); create_xml_node(config, XML_CIB_TAG_NODES); create_xml_node(config, XML_CIB_TAG_RESOURCES); create_xml_node(config, XML_CIB_TAG_CONSTRAINTS); return cib_root; } enum cib_errors cib_perform_op(const char *op, int call_options, cib_op_t *fn, gboolean is_query, const char *section, xmlNode *req, xmlNode *input, gboolean manage_counters, gboolean *config_changed, xmlNode *current_cib, xmlNode **result_cib, xmlNode **output) { int rc = cib_ok; xmlNode *scratch = NULL; CRM_CHECK(output != NULL && result_cib != NULL && config_changed != NULL, return cib_output_data); *output = NULL; *result_cib = NULL; *config_changed = FALSE; if(fn == NULL) { return cib_operation; } if(rc != cib_ok) { return rc; } if(is_query) { rc = (*fn)(op, call_options, section, req, input, current_cib, result_cib, output); return rc; } scratch = copy_xml(current_cib); rc = (*fn)(op, call_options, section, req, input, current_cib, &scratch, output); /* crm_log_xml_debug(current_cib, "old"); crm_log_xml_debug(scratch, "new"); crm_log_xml_debug(*output, "output"); */ CRM_CHECK(current_cib != scratch, return cib_unknown); if(rc == cib_ok) { CRM_CHECK(scratch != NULL, return cib_unknown); if(do_id_check(scratch, NULL, TRUE, FALSE)) { rc = cib_id_check; if(call_options & cib_force_diff) { crm_err("Global update introduces id collision!"); } } if(rc == cib_ok) { gboolean dtd_ok; const char *ignore_dtd; fix_plus_plus_recursive(scratch); /* crm_log_xml_debug(scratch, "newer"); */ *config_changed = cib_config_changed(current_cib, scratch, NULL); /* crm_log_xml_debug(scratch, "newest"); */ if(manage_counters && *config_changed) { 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); } ignore_dtd = crm_element_value(scratch, "ignore_dtd"); dtd_ok = validate_with_dtd(scratch, TRUE, DTD_DIRECTORY"/crm.dtd"); if( #if CRM_DEPRECATED_SINCE_2_0_4 ignore_dtd != NULL && #endif crm_is_true(ignore_dtd) == FALSE && dtd_ok == FALSE) { crm_err("Updated CIB does not validate against "DTD_DIRECTORY"/crm.dtd..."); rc = cib_dtd_validation; } } } *result_cib = scratch; return rc; } int get_channel_token(IPC_Channel *ch, char **token) { int rc = cib_ok; xmlNode *reg_msg = NULL; const char *msg_type = NULL; const char *tmp_ticket = NULL; CRM_CHECK(ch != NULL, return cib_missing); CRM_CHECK(token != NULL, return cib_output_ptr); crm_debug_4("Waiting for msg on command channel"); reg_msg = xmlfromIPC(ch, 0); if(ch->ops->get_chan_status(ch) != IPC_CONNECT) { crm_err("No reply message - disconnected"); free_xml(reg_msg); return cib_not_connected; } else if(reg_msg == NULL) { crm_err("No reply message - empty"); return cib_reply_failed; } msg_type = crm_element_value(reg_msg, F_CIB_OPERATION); tmp_ticket = crm_element_value(reg_msg, F_CIB_CLIENTID); if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) { crm_err("Invalid registration message: %s", msg_type); rc = cib_registration_msg; } else if(tmp_ticket == NULL) { rc = cib_callback_token; } else { *token = crm_strdup(tmp_ticket); } free_xml(reg_msg); return cib_ok; } xmlNode * cib_create_op( int call_id, const char *token, const char *op, const char *host, const char *section, xmlNode *data, int call_options) { int rc = HA_OK; xmlNode *op_msg = create_xml_node(NULL, "cib_command"); CRM_CHECK(op_msg != NULL, return NULL); CRM_CHECK(token != NULL, return NULL); crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command"); crm_xml_add(op_msg, F_TYPE, T_CIB); crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token); crm_xml_add(op_msg, F_CIB_OPERATION, op); crm_xml_add(op_msg, F_CIB_HOST, host); crm_xml_add(op_msg, F_CIB_SECTION, section); crm_xml_add_int(op_msg, F_CIB_CALLID, call_id); crm_debug_4("Sending call options: %.8lx, %d", (long)call_options, call_options); crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options); if(data != NULL) { #if 0 const char *tag = crm_element_name(data); xmlNode *cib = data; if(safe_str_neq(tag, XML_TAG_CIB)) { cib = find_xml_node(data, XML_TAG_CIB, FALSE); if(cib != NULL) { tag = XML_TAG_CIB; } } if(safe_str_eq(tag, XML_TAG_CIB)) { const char *version = feature_set(cib); crm_xml_add(cib, XML_ATTR_CIB_REVISION, version); } else { crm_info("Skipping feature check for %s tag", tag); } #endif add_message_xml(op_msg, F_CIB_CALLDATA, data); } if (rc != HA_OK) { crm_err("Failed to create CIB operation message"); crm_log_xml(LOG_ERR, "op", op_msg); free_xml(op_msg); return NULL; } if(call_options & cib_inhibit_bcast) { CRM_CHECK((call_options & cib_scope_local), return NULL); } return op_msg; } + +void +cib_native_callback(cib_t *cib, xmlNode *msg) +{ + int rc = 0; + int call_id = 0; + xmlNode *output = NULL; + + cib_callback_client_t *blob = NULL; + + cib_callback_client_t local_blob; + + local_blob.callback = NULL; + local_blob.user_data = NULL; + local_blob.only_success = FALSE; + + crm_element_value_int(msg, F_CIB_CALLID, &call_id); + blob = g_hash_table_lookup( + cib_op_callback_table, GINT_TO_POINTER(call_id)); + + if(blob != NULL) { + crm_debug_3("Callback found for call %d", call_id); + local_blob = *blob; + blob = NULL; + + remove_cib_op_callback(call_id, FALSE); + + } else { + crm_debug_3("No callback found for call %d", call_id); + local_blob.callback = NULL; + } + + crm_element_value_int(msg, F_CIB_RC, &rc); + if(rc == cib_diff_resync) { + /* This is an internal value that clients do not and should not care about */ + rc = cib_ok; + } + + output = get_message_xml(msg, F_CIB_CALLDATA); + + if(local_blob.callback != NULL + && (rc == cib_ok || local_blob.only_success == FALSE)) { + local_blob.callback( + msg, call_id, rc, output, local_blob.user_data); + + } else if(cib->op_callback == NULL && rc != cib_ok) { + crm_warn("CIB command failed: %s", cib_error2string(rc)); + crm_log_xml(LOG_DEBUG, "Failed CIB Update", msg); + } + + if(cib->op_callback == NULL) { + crm_debug_3("No OP callback set, ignoring reply"); + } else { + cib->op_callback(msg, call_id, rc, output); + } + crm_debug_4("OP callback activated."); +} + + +void +cib_native_notify(gpointer data, gpointer user_data) +{ + xmlNode *msg = user_data; + cib_notify_client_t *entry = data; + const char *event = NULL; + + if(msg == NULL) { + crm_warn("Skipping callback - NULL message"); + return; + } + + event = crm_element_value(msg, F_SUBTYPE); + + if(entry == NULL) { + crm_warn("Skipping callback - NULL callback client"); + return; + + } else if(entry->callback == NULL) { + crm_warn("Skipping callback - NULL callback"); + return; + + } else if(safe_str_neq(entry->event, event)) { + crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s", + entry, entry->event, event); + return; + } + + crm_debug_4("Invoking callback for %p/%s event...", entry, event); + entry->callback(event, msg); + crm_debug_4("Callback invoked..."); +} +