Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/cib/callbacks.c b/cib/callbacks.c
index 40671cdc15..5bf092799e 100644
--- a/cib/callbacks.c
+++ b/cib/callbacks.c
@@ -1,1430 +1,1427 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <clplumbing/uids.h>
#include <clplumbing/cl_uuid.h>
#include <clplumbing/cl_malloc.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/cluster.h>
#include <crm/common/ctrl.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <cibio.h>
#include <callbacks.h>
#include <cibmessages.h>
#include <cibprimatives.h>
#include <notify.h>
#include <heartbeat.h>
#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);
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);
}
}
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 {
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:"<unknown>", 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, &section);
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 (%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/cibio.h b/cib/cibio.h
index eefd50928d..40132ab645 100644
--- a/cib/cibio.h
+++ b/cib/cibio.h
@@ -1,61 +1,56 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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_IO__H
#define CIB_IO__H
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <clplumbing/ipc.h>
#include <clplumbing/cl_log.h>
#include <crm/common/xml.h>
extern gboolean initialized;
extern gboolean per_action_cib;
extern xmlNode *the_cib;
extern xmlNode *node_search;
extern xmlNode *resource_search;
extern xmlNode *constraint_search;
extern xmlNode *status_search;
extern xmlNode *get_the_CIB(void);
extern int initializeCib(xmlNode *cib);
extern gboolean uninitializeCib(void);
extern xmlNode *createEmptyCib(void);
extern gboolean verifyCibXml(xmlNode *cib);
extern xmlNode *readCibXml(char *buffer);
extern xmlNode *readCibXmlFile(
const char *dir, const char *file, gboolean discard_status);
extern int activateCibBuffer(char *buffer, const char *filename);
extern int activateCibXml(xmlNode *doc, gboolean to_disk);
-extern gboolean update_quorum(xmlNode *xml_obj);
-extern gboolean set_connected_peers(xmlNode *xml_obj);
-extern gboolean update_counters(
- const char *file, const char *fn, xmlNode *xml_obj);
-
/* extern xmlNode *server_get_cib_copy(void); */
#endif
diff --git a/cib/common.c b/cib/common.c
index 24b6a3f9d2..b581c2e645 100644
--- a/cib/common.c
+++ b/cib/common.c
@@ -1,379 +1,374 @@
/*
* Copyright (C) 2008 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <clplumbing/uids.h>
#include <clplumbing/cl_uuid.h>
#include <clplumbing/cl_malloc.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/cluster.h>
#include <crm/common/ctrl.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <cibio.h>
#include <callbacks.h>
#include <cibmessages.h>
#include <cibprimatives.h>
#include "common.h"
extern gboolean cib_is_master;
extern const char* cib_root;
gboolean stand_alone = FALSE;
extern enum cib_errors cib_status;
extern gboolean can_write(int flags);
extern enum cib_errors cib_perform_command(
xmlNode *request, xmlNode **reply, xmlNode **cib_diff, gboolean privileged);
static xmlNode *
cib_prepare_common(xmlNode *root, const char *section)
{
xmlNode *data = NULL;
/* extract the CIB from the fragment */
if(root == NULL) {
return NULL;
} else if(safe_str_eq(crm_element_name(root), XML_TAG_FRAGMENT)
|| safe_str_eq(crm_element_name(root), F_CIB_CALLDATA)) {
data = find_xml_node(root, XML_TAG_CIB, TRUE);
if(data != NULL) {
crm_debug_3("Extracted CIB from %s", TYPE(root));
} else {
crm_log_xml_debug_4(root, "No CIB");
}
} else {
data = root;
}
/* grab the section specified for the command */
if(section != NULL
&& data != NULL
&& safe_str_eq(crm_element_name(data), XML_TAG_CIB)){
- int rc = revision_check(data, the_cib, 0/* call_options */);
- if(rc == cib_ok) {
- data = get_object_root(section, data);
- if(data != NULL) {
- crm_debug_3("Extracted %s from CIB", section);
- } else {
- crm_log_xml_debug_4(root, "No Section");
- }
+ data = get_object_root(section, data);
+ if(data != NULL) {
+ crm_debug_3("Extracted %s from CIB", section);
} else {
- crm_debug_2("Revision check failed");
+ crm_log_xml_debug_4(root, "No Section");
}
}
crm_log_xml_debug_4(root, "cib:input");
return data;
}
static gboolean
verify_section(const char *section)
{
if(section == NULL) {
return TRUE;
} else if(safe_str_eq(section, XML_TAG_CIB)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_NODES)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_RESOURCES)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_CONSTRAINTS)) {
return TRUE;
}
return FALSE;
}
static enum cib_errors
cib_prepare_none(xmlNode *request, xmlNode **data, const char **section)
{
*data = NULL;
*section = crm_element_value(request, F_CIB_SECTION);
if(verify_section(*section) == FALSE) {
return cib_bad_section;
}
return cib_ok;
}
static enum cib_errors
cib_prepare_data(xmlNode *request, xmlNode **data, const char **section)
{
xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA);
*section = crm_element_value(request, F_CIB_SECTION);
*data = cib_prepare_common(input_fragment, *section);
/* crm_log_xml_debug(*data, "data"); */
if(verify_section(*section) == FALSE) {
return cib_bad_section;
}
return cib_ok;
}
static enum cib_errors
cib_prepare_sync(xmlNode *request, xmlNode **data, const char **section)
{
*section = crm_element_value(request, F_CIB_SECTION);
*data = NULL;
if(verify_section(*section) == FALSE) {
return cib_bad_section;
}
return cib_ok;
}
static enum cib_errors
cib_prepare_diff(xmlNode *request, xmlNode **data, const char **section)
{
xmlNode *input_fragment = NULL;
const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
*data = NULL;
*section = NULL;
if(crm_is_true(update)) {
input_fragment = get_message_xml(request,F_CIB_UPDATE_DIFF);
} else {
input_fragment = get_message_xml(request, F_CIB_CALLDATA);
}
CRM_CHECK(input_fragment != NULL,crm_log_xml(LOG_WARNING, "no input", request));
*data = cib_prepare_common(input_fragment, NULL);
return cib_ok;
}
static enum cib_errors
cib_cleanup_query(const char *op, xmlNode **data, xmlNode **output)
{
CRM_DEV_ASSERT(*data == NULL);
return cib_ok;
}
static enum cib_errors
cib_cleanup_data(const char *op, xmlNode **data, xmlNode **output)
{
free_xml(*output);
*data = NULL;
return cib_ok;
}
static enum cib_errors
cib_cleanup_output(const char *op, xmlNode **data, xmlNode **output)
{
free_xml(*output);
return cib_ok;
}
static enum cib_errors
cib_cleanup_none(const char *op, xmlNode **data, xmlNode **output)
{
CRM_DEV_ASSERT(*data == NULL);
CRM_DEV_ASSERT(*output == NULL);
return cib_ok;
}
static enum cib_errors
cib_cleanup_sync(const char *op, xmlNode **data, xmlNode **output)
{
/* data is non-NULL but doesnt need to be free'd */
CRM_DEV_ASSERT(*data == NULL);
CRM_DEV_ASSERT(*output == NULL);
return cib_ok;
}
/*
typedef struct cib_operation_s
{
const char* operation;
gboolean modifies_cib;
gboolean needs_privileges;
gboolean needs_quorum;
enum cib_errors (*prepare)(xmlNode *, xmlNode**, const char **);
enum cib_errors (*cleanup)(xmlNode**, xmlNode**);
enum cib_errors (*fn)(
const char *, int, const char *,
xmlNode*, xmlNode*, xmlNode**, xmlNode**);
} cib_operation_t;
*/
/* technically bump does modify the cib...
* but we want to split the "bump" from the "sync"
*/
static cib_operation_t cib_server_ops[] = {
{NULL, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
{CIB_OP_QUERY, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_query, cib_process_query},
{CIB_OP_MODIFY, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_modify},
{CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_server_process_diff},
{CIB_OP_SLAVE, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_SLAVEALL, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_SYNC_ONE, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync_one},
{CIB_OP_MASTER, TRUE, TRUE, FALSE, cib_prepare_data, cib_cleanup_data, cib_process_readwrite},
{CIB_OP_ISMASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_BUMP, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_bump},
{CIB_OP_REPLACE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_replace_svr},
{CIB_OP_CREATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_create},
{CIB_OP_DELETE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete},
{CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete_absolute},
{CIB_OP_SYNC, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync},
{CRM_OP_QUIT, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_quit},
{CRM_OP_PING, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_output, cib_process_ping},
{CIB_OP_ERASE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_erase},
{CRM_OP_NOOP, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
{"cib_shutdown_req",FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_shutdown_req},
};
enum cib_errors
cib_get_operation_id(const char *op, int *operation)
{
int lpc = 0;
int max_msg_types = DIMOF(cib_server_ops);
for (lpc = 0; lpc < max_msg_types; lpc++) {
if (safe_str_eq(op, cib_server_ops[lpc].operation)) {
*operation = lpc;
return cib_ok;
}
}
crm_err("Operation %s is not valid", op);
*operation = -1;
return cib_operation;
}
xmlNode *
cib_msg_copy(xmlNode *msg, gboolean with_data)
{
int lpc = 0;
const char *field = NULL;
const char *value = NULL;
xmlNode *value_struct = NULL;
static const char *field_list[] = {
F_XML_TAGNAME ,
F_TYPE ,
F_CIB_CLIENTID ,
F_CIB_CALLOPTS ,
F_CIB_CALLID ,
F_CIB_OPERATION ,
F_CIB_ISREPLY ,
F_CIB_SECTION ,
F_CIB_HOST ,
F_CIB_RC ,
F_CIB_DELEGATED ,
F_CIB_OBJID ,
F_CIB_OBJTYPE ,
F_CIB_EXISTING ,
F_CIB_SEENCOUNT ,
F_CIB_TIMEOUT ,
F_CIB_CALLBACK_TOKEN ,
F_CIB_GLOBAL_UPDATE ,
F_CIB_CLIENTNAME ,
F_CIB_NOTIFY_TYPE ,
F_CIB_NOTIFY_ACTIVATE
};
static const char *data_list[] = {
F_CIB_CALLDATA ,
F_CIB_UPDATE ,
F_CIB_UPDATE_RESULT
};
xmlNode *copy = create_xml_node(NULL, "copy");
CRM_ASSERT(copy != NULL);
for(lpc = 0; lpc < DIMOF(field_list); lpc++) {
field = field_list[lpc];
value = crm_element_value(msg, field);
if(value != NULL) {
crm_xml_add(copy, field, value);
}
}
for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) {
field = data_list[lpc];
value_struct = get_message_xml(msg, field);
if(value_struct != NULL) {
add_message_xml(copy, field, value_struct);
}
}
return copy;
}
cib_op_t *cib_op_func(int call_type)
{
return &(cib_server_ops[call_type].fn);
}
gboolean cib_op_modifies(int call_type)
{
return cib_server_ops[call_type].modifies_cib;
}
int cib_op_can_run(
int call_type, int call_options, gboolean privileged, gboolean global_update)
{
int rc = cib_ok;
if(rc == cib_ok &&
cib_server_ops[call_type].needs_privileges
&& privileged == FALSE) {
/* abort */
return cib_not_authorized;
}
if(rc == cib_ok
&& stand_alone == FALSE
&& global_update == FALSE
&& (call_options & cib_quorum_override) == 0
&& cib_server_ops[call_type].needs_quorum) {
return cib_no_quorum;
}
return cib_ok;
}
int cib_op_prepare(
int call_type, xmlNode *request, xmlNode **input, const char **section)
{
return cib_server_ops[call_type].prepare(request, input, section);
}
int cib_op_cleanup(
int call_type, const char *op, xmlNode **input, xmlNode **output)
{
return cib_server_ops[call_type].cleanup(op, input, output);
}
diff --git a/cib/common.h b/cib/common.h
index c2663803ef..b3bed67e41 100644
--- a/cib/common.h
+++ b/cib/common.h
@@ -1,44 +1,40 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../lib/crm/cib/cib_private.h"
extern xmlNode *cib_msg_copy(xmlNode *msg, gboolean with_data);
extern xmlNode *cib_construct_reply(xmlNode *request, xmlNode *output, int rc);
-extern enum cib_errors revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags);
extern enum cib_errors cib_get_operation_id(const char *op, int *operation);
extern enum cib_errors cib_perform_op(
const char *op, int call_options, cib_op_t *fn, gboolean is_query,
const char *section, xmlNode *req, xmlNode *input,
gboolean manage_counters, gboolean *config_changed,
xmlNode *current_cib, xmlNode **result_cib, xmlNode **output);
-extern enum cib_errors cib_update_counter(
- xmlNode *xml_obj, const char *field, gboolean reset);
-
extern cib_op_t *cib_op_func(int call_type);
extern gboolean cib_op_modifies(int call_type);
extern int cib_op_prepare(
int call_type, xmlNode *request, xmlNode **input, const char **section);
extern int cib_op_cleanup(
int call_type, const char *op, xmlNode **input, xmlNode **output);
extern int cib_op_can_run(
int call_type, int call_options, gboolean privileged, gboolean global_update);
diff --git a/cib/io.c b/cib/io.c
index 5a9b722535..cd68db1ba1 100644
--- a/cib/io.c
+++ b/cib/io.c
@@ -1,809 +1,755 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <heartbeat.h>
#include <crm/crm.h>
#include <cibio.h>
#include <crm/cib.h>
#include <crm/common/util.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/common/cluster.h>
#include <clplumbing/cl_misc.h>
#include <clplumbing/lsb_exitcodes.h>
#include <cibprimatives.h>
int archive_file(const char *oldname, const char *newname, const char *ext, gboolean preserve);
const char * local_resource_path[] =
{
XML_CIB_TAG_STATUS,
};
const char * resource_path[] =
{
XML_CIB_TAG_RESOURCES,
};
const char * node_path[] =
{
XML_CIB_TAG_NODES,
};
const char * constraint_path[] =
{
XML_CIB_TAG_CONSTRAINTS,
};
gboolean initialized = FALSE;
xmlNode *node_search = NULL;
xmlNode *resource_search = NULL;
xmlNode *constraint_search = NULL;
xmlNode *status_search = NULL;
extern gboolean cib_writes_enabled;
extern GTRIGSource *cib_writer;
extern enum cib_errors cib_status;
int set_connected_peers(xmlNode *xml_obj);
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data);
int write_cib_contents(gpointer p);
extern void cib_cleanup(void);
static gboolean
validate_cib_digest(xmlNode *local_cib, const char *sigfile)
{
int s_res = -1;
struct stat buf;
char *digest = NULL;
char *expected = NULL;
gboolean passed = FALSE;
FILE *expected_strm = NULL;
int start = 0, length = 0, read_len = 0;
CRM_ASSERT(sigfile != NULL);
s_res = stat(sigfile, &buf);
if (s_res != 0) {
crm_warn("No on-disk digest present");
return TRUE;
}
if(local_cib != NULL) {
digest = calculate_xml_digest(local_cib, FALSE, FALSE);
}
expected_strm = fopen(sigfile, "r");
if(expected_strm == NULL) {
cl_perror("Could not open signature file %s for reading", sigfile);
goto bail;
}
start = ftell(expected_strm);
fseek(expected_strm, 0L, SEEK_END);
length = ftell(expected_strm);
fseek(expected_strm, 0L, start);
CRM_ASSERT(start == ftell(expected_strm));
crm_debug_3("Reading %d bytes from file", length);
crm_malloc0(expected, (length+1));
read_len = fread(expected, 1, length, expected_strm);
CRM_ASSERT(read_len == length);
fclose(expected_strm);
bail:
if(expected == NULL) {
crm_err("On-disk digest is empty");
} else if(safe_str_eq(expected, digest)) {
crm_debug_2("Digest comparision passed: %s", digest);
passed = TRUE;
} else {
crm_err("Digest comparision failed: expected %s (%s), calculated %s",
expected, sigfile, digest);
}
crm_free(digest);
crm_free(expected);
return passed;
}
static int
write_cib_digest(xmlNode *local_cib, char *digest)
{
int rc = 0;
char *local_digest = NULL;
FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w");
if(digest_strm == NULL) {
cl_perror("Cannot open signature file "CIB_FILENAME ".sig for writing");
return -1;
}
if(digest == NULL) {
local_digest = calculate_xml_digest(local_cib, FALSE, FALSE);
CRM_ASSERT(digest != NULL);
digest = local_digest;
}
rc = fprintf(digest_strm, "%s", digest);
if(rc < 0) {
cl_perror("Cannot write to signature file "CIB_FILENAME ".sig");
}
CRM_ASSERT(digest_strm != NULL);
if(fflush(digest_strm) != 0) {
cl_perror("fflush for %s failed:", digest);
rc = -1;
}
if(fsync(fileno(digest_strm)) < 0) {
cl_perror("fsync for %s failed:", digest);
rc = -1;
}
fclose(digest_strm);
crm_free(local_digest);
return rc;
}
static gboolean
validate_on_disk_cib(const char *filename, xmlNode **on_disk_cib)
{
int s_res = -1;
struct stat buf;
FILE *cib_file = NULL;
gboolean passed = TRUE;
xmlNode *root = NULL;
CRM_ASSERT(filename != NULL);
s_res = stat(filename, &buf);
if (s_res == 0) {
char *sigfile = NULL;
size_t fnsize;
cib_file = fopen(filename, "r");
if(cib_file == NULL) {
cl_perror("Couldn't open config file %s for reading", filename);
return FALSE;
}
crm_debug_2("Reading cluster configuration from: %s", filename);
root = file2xml(cib_file, FALSE);
fclose(cib_file);
fnsize = strlen(filename) + 5;
crm_malloc0(sigfile, fnsize);
snprintf(sigfile, fnsize, "%s.sig", filename);
if(validate_cib_digest(root, sigfile) == FALSE) {
passed = FALSE;
}
crm_free(sigfile);
}
if(on_disk_cib != NULL) {
*on_disk_cib = root;
} else {
free_xml(root);
}
return passed;
}
static int
cib_unlink(const char *file)
{
int rc = unlink(file);
if (rc < 0) {
cl_perror("Could not unlink %s - Disabling disk writes and continuing", file);
cib_writes_enabled = FALSE;
}
return rc;
}
/*
* It is the callers responsibility to free the output of this function
*/
static xmlNode*
retrieveCib(const char *filename, const char *sigfile, gboolean archive_invalid)
{
struct stat buf;
FILE *cib_file = NULL;
xmlNode *root = NULL;
crm_info("Reading cluster configuration from: %s (digest: %s)",
filename, sigfile);
if(stat(filename, &buf) != 0) {
crm_warn("Cluster configuration not found: %s", filename);
return NULL;
}
cib_file = fopen(filename, "r");
if(cib_file == NULL) {
cl_perror("Could not open config file %s for reading", filename);
} else {
root = file2xml(cib_file, FALSE);
fclose(cib_file);
}
if(root == NULL) {
crm_err("%s exists but does NOT contain valid XML. ", filename);
crm_warn("Continuing but %s will NOT used.", filename);
} else if(validate_cib_digest(root, sigfile) == FALSE) {
crm_err("Checksum of %s failed! Configuration contents ignored!", filename);
crm_err("Usually this is caused by manual changes, "
"please refer to http://linux-ha.org/v2/faq/cib_changes_detected");
crm_warn("Continuing but %s will NOT used.", filename);
free_xml(root);
root = NULL;
if(archive_invalid) {
int rc = 0;
char *suffix = crm_itoa(getpid());
/* Archive the original files so the contents are not lost */
crm_err("Archiving corrupt or unusable configuration to %s.%s", filename, suffix);
rc = archive_file(filename, NULL, suffix, TRUE);
if(rc < 0) {
crm_err("Archival of %s failed - Disabling disk writes and continuing", filename);
cib_writes_enabled = FALSE;
}
rc = archive_file(sigfile, NULL, suffix, TRUE);
if(rc < 0) {
crm_err("Archival of %s failed - Disabling disk writes and continuing", sigfile);
cib_writes_enabled = FALSE;
}
/* Unlink the original files so they dont get in the way later */
cib_unlink(filename);
cib_unlink(sigfile);
crm_free(suffix);
}
}
return root;
}
xmlNode*
readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
{
gboolean dtd_ok = TRUE;
char *filename = NULL, *sigfile = NULL;
const char *name = NULL;
const char *value = NULL;
const char *ignore_dtd = NULL;
const char *use_valgrind = getenv("HA_VALGRIND_ENABLED");
xmlNode *root = NULL;
xmlNode *status = NULL;
if(!crm_is_writable(dir, file, HA_CCMUSER, NULL, FALSE)) {
cib_status = cib_bad_permissions;
return NULL;
}
filename = crm_concat(dir, file, '/');
sigfile = crm_concat(filename, "sig", '.');
cib_status = cib_ok;
root = retrieveCib(filename, sigfile, TRUE);
if(root == NULL) {
char *tmp = NULL;
/* Try the backups */
tmp = filename;
filename = crm_concat(tmp, "last", '.');
crm_free(tmp);
tmp = sigfile;
sigfile = crm_concat(tmp, "last", '.');
crm_free(tmp);
crm_warn("Primary configuration corrupt or unusable, trying backup...");
root = retrieveCib(filename, sigfile, FALSE);
}
if(root == NULL) {
root = createEmptyCib();
crm_warn("Continuing with an empty configuration.");
} else {
crm_xml_add(root, "generated", XML_BOOLEAN_FALSE);
}
if(cib_writes_enabled && crm_is_true(use_valgrind)) {
cib_writes_enabled = FALSE;
crm_err("HA_VALGRIND_ENABLED: %s",
getenv("HA_VALGRIND_ENABLED"));
crm_err("*********************************************************");
crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
crm_err("*********************************************************");
}
status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE);
if(discard_status && status != NULL) {
/* strip out the status section if there is one */
free_xml_from_parent(root, status);
status = NULL;
}
if(status == NULL) {
create_xml_node(root, XML_CIB_TAG_STATUS);
}
/* Do this before DTD validation happens */
/* fill in some defaults */
name = XML_ATTR_GENERATION_ADMIN;
value = crm_element_value(root, name);
if(value == NULL) {
crm_warn("No value for %s was specified in the configuration.",
name);
crm_warn("The reccomended course of action is to shutdown,"
" run crm_verify and fix any errors it reports.");
crm_warn("We will default to zero and continue but may get"
" confused about which configuration to use if"
" multiple nodes are powered up at the same time.");
crm_xml_add_int(root, name, 0);
}
name = XML_ATTR_GENERATION;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
name = XML_ATTR_NUMUPDATES;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
/* unset these and require the DC/CCM to update as needed */
- update_counters(__FILE__, __PRETTY_FUNCTION__, root);
xml_remove_prop(root, XML_ATTR_DC_UUID);
if(discard_status) {
crm_log_xml_info(root, "[on-disk]");
}
-
- ignore_dtd = crm_element_value(root, "ignore_dtd");
+
+ crm_err("Fix this");
+ ignore_dtd = crm_element_value(root, XML_ATTR_VALIDATION);
dtd_ok = validate_xml(root, NULL, TRUE);
if(dtd_ok == FALSE) {
- crm_err("CIB does not validate");
- if(ignore_dtd != NULL && crm_is_true(ignore_dtd) == FALSE) {
- cib_status = cib_dtd_validation;
- }
+ crm_err("CIB does not validate with %s", ignore_dtd);
+ cib_status = cib_dtd_validation;
} else if(ignore_dtd == NULL) {
crm_notice("Enabling DTD validation on"
" the existing (sane) configuration");
crm_xml_add(root, "ignore_dtd", XML_BOOLEAN_FALSE);
}
-
- if(do_id_check(root, NULL, TRUE, FALSE)) {
- crm_err("%s does not contain a vaild configuration:"
- " ID check failed",
- filename);
- cib_status = cib_id_check;
- }
crm_free(filename);
crm_free(sigfile);
return root;
}
/*
* The caller should never free the return value
*/
xmlNode*
get_the_CIB(void)
{
return the_cib;
}
gboolean
uninitializeCib(void)
{
xmlNode *tmp_cib = the_cib;
if(tmp_cib == NULL) {
crm_debug("The CIB has already been deallocated.");
return FALSE;
}
initialized = FALSE;
the_cib = NULL;
node_search = NULL;
resource_search = NULL;
constraint_search = NULL;
status_search = NULL;
crm_debug("Deallocating the CIB.");
free_xml(tmp_cib);
crm_debug("The CIB has been deallocated.");
return TRUE;
}
/*
* This method will not free the old CIB pointer or the new one.
* We rely on the caller to have saved a pointer to the old CIB
* and to free the old/bad one depending on what is appropriate.
*/
gboolean
initializeCib(xmlNode *new_cib)
{
if(new_cib == NULL) {
return FALSE;
}
- update_counters(__FILE__, __PRETTY_FUNCTION__, new_cib);
-
the_cib = new_cib;
initialized = TRUE;
return TRUE;
}
static void
sync_file(const char *file)
{
FILE *syncme = fopen(file, "a");
if(syncme == NULL) {
cl_perror("Cannot open file %s for syncing", file);
return;
}
if(fsync(fileno(syncme)) < 0) {
cl_perror("fsync for %s failed:", file);
}
fclose(syncme);
}
int
archive_file(const char *oldname, const char *newname, const char *ext, gboolean preserve)
{
/* move 'oldname' to 'newname' by creating a hard link to it
* and then removing the original hard link
*/
int rc = 0;
int res = 0;
struct stat tmp;
int s_res = 0;
char *backup_file = NULL;
static const char *back_ext = "bak";
/* calculate the backup name if required */
if(newname != NULL) {
backup_file = crm_strdup(newname);
} else {
int max_name_len = 1024;
crm_malloc0(backup_file, max_name_len);
if (ext == NULL) {
ext = back_ext;
}
snprintf(backup_file, max_name_len - 1, "%s.%s", oldname, ext);
}
if(backup_file == NULL || strlen(backup_file) == 0) {
crm_err("%s backup filename was %s",
newname == NULL?"calculated":"supplied",
backup_file == NULL?"null":"empty");
rc = -4;
}
s_res = stat(backup_file, &tmp);
/* move the old backup */
if (rc == 0 && s_res >= 0) {
if(preserve == FALSE) {
res = unlink(backup_file);
if (res < 0) {
cl_perror("Could not unlink %s", backup_file);
rc = -1;
}
} else {
crm_info("Archive file %s exists... backing it up first", backup_file);
res = archive_file(backup_file, NULL, NULL, preserve);
if (res < 0) {
return res;
}
}
}
s_res = stat(oldname, &tmp);
/* copy */
if (rc == 0 && s_res >= 0) {
res = link(oldname, backup_file);
if (res < 0) {
cl_perror("Could not create backup %s from %s",
backup_file, oldname);
rc = -2;
} else if(preserve) {
crm_info("%s archived as %s", oldname, backup_file);
} else {
crm_debug("%s archived as %s", oldname, backup_file);
}
sync_file(backup_file);
}
crm_free(backup_file);
return rc;
}
/*
* This method will free the old CIB pointer on success and the new one
* on failure.
*/
int
activateCibXml(xmlNode *new_cib, gboolean to_disk)
{
int error_code = cib_ok;
xmlNode *saved_cib = the_cib;
crm_debug_2("Activating new CIB");
-
crm_log_xml_debug_4(new_cib, "Attempting to activate CIB");
- update_counters(__FILE__, __FUNCTION__, new_cib);
CRM_ASSERT(new_cib != saved_cib);
if(saved_cib != NULL) {
crm_validate_data(saved_cib);
}
if(initializeCib(new_cib) == FALSE) {
error_code = cib_ACTIVATION;
crm_err("Ignoring invalid or NULL CIB");
}
if(error_code != cib_ok) {
if(saved_cib != NULL) {
crm_warn("Reverting to last known CIB");
if (initializeCib(saved_cib) == FALSE) {
/* oh we are so dead */
crm_crit("Couldn't re-initialize the old CIB!");
cl_flush_logs();
exit(1);
}
} else {
crm_crit("Could not write out new CIB and no saved"
" version to revert to");
}
} else if(per_action_cib && cib_writes_enabled && cib_status == cib_ok) {
crm_err("Per-action CIB");
write_cib_contents(the_cib);
} else if(cib_writes_enabled && cib_status == cib_ok && to_disk) {
crm_debug("Triggering CIB write");
G_main_set_trigger(cib_writer);
} else {
crm_debug_3("disk: %d, writes: %d", to_disk, cib_writes_enabled);
}
if(the_cib != saved_cib && the_cib != new_cib) {
CRM_DEV_ASSERT(error_code != cib_ok);
CRM_DEV_ASSERT(the_cib == NULL);
}
if(the_cib != new_cib) {
free_xml(new_cib);
CRM_DEV_ASSERT(error_code != cib_ok);
}
if(the_cib != saved_cib) {
free_xml(saved_cib);
}
return error_code;
}
int
write_cib_contents(gpointer p)
{
int rc = 0;
gboolean need_archive = FALSE;
struct stat buf;
char *digest = NULL;
int exit_rc = LSB_EXIT_OK;
xmlNode *cib_status_root = NULL;
/* we can scribble on "the_cib" here and not affect the parent */
const char *epoch = crm_element_value(the_cib, XML_ATTR_GENERATION);
const char *updates = crm_element_value(the_cib, XML_ATTR_NUMUPDATES);
const char *admin_epoch = crm_element_value(
the_cib, XML_ATTR_GENERATION_ADMIN);
need_archive = (stat(CIB_FILENAME, &buf) == 0);
if (need_archive) {
crm_debug("Archiving current version");
/* check the admin didnt modify it underneath us */
if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) {
crm_err("%s was manually modified while Heartbeat was active!",
CIB_FILENAME);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
/* These calls leak, but we're in a separate process that will exit
* when the function does... so it's of no consequence
*/
CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL);
rc = archive_file(CIB_FILENAME, NULL, "last", FALSE);
if(rc != 0) {
crm_err("Could not make backup of the existing CIB: %d", rc);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
rc = archive_file(CIB_FILENAME".sig", NULL, "last", FALSE);
if(rc != 0) {
crm_warn("Could not make backup of the existing CIB digest: %d",
rc);
}
CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL);
CRM_ASSERT(retrieveCib(CIB_FILENAME".last", CIB_FILENAME".sig.last", FALSE) != NULL);
crm_debug("Verified CIB archive");
}
/* Given that we discard the status section on startup
* there is no point writing it out in the first place
* since users just get confused by it
*
* Although, it does help me once in a while
*
* So delete the status section before we write it out
*/
if(p == NULL) {
cib_status_root = find_xml_node(the_cib, XML_CIB_TAG_STATUS, TRUE);
CRM_DEV_ASSERT(cib_status_root != NULL);
if(cib_status_root != NULL) {
free_xml_from_parent(the_cib, cib_status_root);
}
}
rc = write_xml_file(the_cib, CIB_FILENAME, FALSE);
crm_debug("Wrote CIB to disk");
if(rc <= 0) {
crm_err("Changes couldn't be written to disk");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
digest = calculate_xml_digest(the_cib, FALSE, FALSE);
crm_info("Wrote version %s.%s.%s of the CIB to disk (digest: %s)",
admin_epoch?admin_epoch:"0",
epoch?epoch:"0", updates?updates:"0", digest);
rc = write_cib_digest(the_cib, digest);
crm_debug("Wrote digest to disk");
if(rc <= 0) {
crm_err("Digest couldn't be written to disk");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
CRM_ASSERT(retrieveCib(CIB_FILENAME, CIB_FILENAME".sig", FALSE) != NULL);
if(need_archive) {
CRM_ASSERT(retrieveCib(CIB_FILENAME".last", CIB_FILENAME".sig.last", FALSE) != NULL);
}
crm_debug("Wrote and verified CIB");
cleanup:
crm_free(digest);
if(p == NULL) {
/* fork-and-write mode */
exit(exit_rc);
}
/* stand-alone mode */
return exit_rc;
}
-gboolean
-set_connected_peers(xmlNode *xml_obj)
-{
- guint active = 0;
- int current = 0;
- char *peers_s = NULL;
- const char *current_s = NULL;
- if(xml_obj == NULL) {
- return FALSE;
- }
-
- current_s = crm_element_value(xml_obj, XML_ATTR_NUMPEERS);
- current = crm_parse_int(current_s, "0");
- active = crm_active_peers(crm_proc_cib);
-
- if(current != active) {
- crm_malloc0(peers_s, 32);
- snprintf(peers_s, 32, "%u", active);
- crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s);
- crm_debug("We now have %s (%u) active peers", peers_s, active);
- crm_free(peers_s);
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-update_counters(const char *file, const char *fn, xmlNode *xml_obj)
-{
- gboolean did_update = FALSE;
-
- did_update = did_update || set_connected_peers(xml_obj);
-
- if(did_update) {
- do_crm_log(LOG_DEBUG, "Counters updated by %s", fn);
- }
- return did_update;
-}
-
-
-
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data)
{
int *active = user_data;
if(safe_str_eq(value, ONLINESTATUS)) {
(*active)++;
} else if(safe_str_eq(value, JOINSTATUS)) {
(*active)++;
}
}
diff --git a/cib/main.c b/cib/main.c
index 792967177e..08bb6d09a8 100644
--- a/cib/main.c
+++ b/cib/main.c
@@ -1,633 +1,632 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <heartbeat.h>
#include <clplumbing/cl_misc.h>
#include <clplumbing/uids.h>
#include <clplumbing/coredumps.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/cluster.h>
#include <crm/common/ctrl.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <cibio.h>
#include <callbacks.h>
#if HAVE_LIBXML2
# include <libxml/parser.h>
#endif
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#if HAVE_BZLIB_H
# include <bzlib.h>
#endif
extern int init_remote_listener(int port);
extern gboolean stand_alone;
gboolean cib_shutdown_flag = FALSE;
gboolean per_action_cib = FALSE;
enum cib_errors cib_status = cib_ok;
#if SUPPORT_HEARTBEAT
oc_ev_t *cib_ev_token;
ll_cluster_t *hb_conn = NULL;
extern void oc_ev_special(const oc_ev_t *, oc_ev_class_t , int );
gboolean cib_register_ha(ll_cluster_t *hb_cluster, const char *client_name);
#endif
GMainLoop* mainloop = NULL;
const char* cib_root = WORKING_DIR;
char *cib_our_uname = NULL;
gboolean preserve_status = FALSE;
gboolean cib_writes_enabled = TRUE;
void usage(const char* cmd, int exit_status);
int cib_init(void);
gboolean cib_shutdown(int nsig, gpointer unused);
void cib_ha_connection_destroy(gpointer user_data);
gboolean startCib(const char *filename);
extern int write_cib_contents(gpointer p);
GTRIGSource *cib_writer = NULL;
GHashTable *client_list = NULL;
char *channel1 = NULL;
char *channel2 = NULL;
char *channel3 = NULL;
char *channel4 = NULL;
char *channel5 = NULL;
#define OPTARGS "aswr:V?"
void cib_cleanup(void);
static void
cib_diskwrite_complete(gpointer userdata, int status, int signo, int exitcode)
{
if(exitcode != LSB_EXIT_OK || signo != 0 || status != 0) {
crm_err("Disk write failed: status=%d, signo=%d, exitcode=%d",
status, signo, exitcode);
if(cib_writes_enabled) {
crm_err("Disabling disk writes after write failure");
cib_writes_enabled = FALSE;
}
} else {
crm_debug_2("Disk write passed");
}
}
int
main(int argc, char ** argv)
{
int flag;
int rc = 0;
int argerr = 0;
#ifdef HAVE_GETOPT_H
int option_index = 0;
static struct option long_options[] = {
{"per-action-cib", 0, 0, 'a'},
{"stand-alone", 0, 0, 's'},
{"disk-writes", 0, 0, 'w'},
{"cib-root", 1, 0, 'r'},
{"verbose", 0, 0, 'V'},
{"help", 0, 0, '?'},
{0, 0, 0, 0}
};
#endif
crm_log_init(CRM_SYSTEM_CIB, LOG_INFO, TRUE, TRUE, 0, NULL);
G_main_add_SignalHandler(
G_PRIORITY_HIGH, SIGTERM, cib_shutdown, NULL, NULL);
cib_writer = G_main_add_tempproc_trigger(
G_PRIORITY_LOW, write_cib_contents, "write_cib_contents",
NULL, NULL, NULL, cib_diskwrite_complete);
EnableProcLogging();
set_sigchld_proctrack(G_PRIORITY_HIGH,DEFAULT_MAXDISPATCHTIME);
crm_peer_init();
client_list = g_hash_table_new(g_str_hash, g_str_equal);
while (1) {
#ifdef HAVE_GETOPT_H
flag = getopt_long(argc, argv, OPTARGS,
long_options, &option_index);
#else
flag = getopt(argc, argv, OPTARGS);
#endif
if (flag == -1)
break;
switch(flag) {
case 'V':
alter_debug(DEBUG_INC);
break;
case 's':
stand_alone = TRUE;
preserve_status = TRUE;
cib_writes_enabled = FALSE;
cl_log_enable_stderr(1);
break;
case '?': /* Help message */
usage(crm_system_name, LSB_EXIT_OK);
break;
case 'f':
per_action_cib = TRUE;
break;
case 'w':
cib_writes_enabled = TRUE;
break;
case 'r':
cib_root = optarg;
break;
default:
++argerr;
break;
}
}
crm_info("Retrieval of a per-action CIB: %s",
per_action_cib?"enabled":"disabled");
if (optind > argc) {
++argerr;
}
if (argerr) {
usage(crm_system_name,LSB_EXIT_GENERIC);
}
/* read local config file */
rc = cib_init();
CRM_CHECK(g_hash_table_size(client_list) == 0,
crm_warn("Not all clients gone at exit"));
cib_cleanup();
#if SUPPORT_HEARTBEAT
if(hb_conn) {
hb_conn->llc_ops->delete(hb_conn);
}
#endif
crm_info("Done");
return rc;
}
void
cib_cleanup(void)
{
crm_peer_destroy();
g_hash_table_destroy(client_list);
crm_free(cib_our_uname);
#if HAVE_LIBXML2
xmlCleanupParser();
#endif
crm_free(channel1);
crm_free(channel2);
crm_free(channel3);
crm_free(channel4);
crm_free(channel5);
}
unsigned long cib_num_ops = 0;
const char *cib_stat_interval = "10min";
unsigned long cib_num_local = 0, cib_num_updates = 0, cib_num_fail = 0;
unsigned long cib_bad_connects = 0, cib_num_timeouts = 0;
longclock_t cib_call_time = 0;
gboolean cib_stats(gpointer data);
gboolean
cib_stats(gpointer data)
{
int local_log_level = LOG_DEBUG;
static unsigned long last_stat = 0;
unsigned int cib_calls_ms = 0;
static unsigned long cib_stat_interval_ms = 0;
if(cib_stat_interval_ms == 0) {
cib_stat_interval_ms = crm_get_msec(cib_stat_interval);
}
cib_calls_ms = longclockto_ms(cib_call_time);
if((cib_num_ops - last_stat) > 0) {
unsigned long calls_diff = cib_num_ops - last_stat;
double stat_1 = (1000*cib_calls_ms)/calls_diff;
local_log_level = LOG_INFO;
do_crm_log(local_log_level,
"Processed %lu operations"
" (%.2fus average, %lu%% utilization) in the last %s",
calls_diff, stat_1,
(100*cib_calls_ms)/cib_stat_interval_ms,
cib_stat_interval);
}
do_crm_log(local_log_level+1,
"\tDetail: %lu operations (%ums total)"
" (%lu local, %lu updates, %lu failures,"
" %lu timeouts, %lu bad connects)",
cib_num_ops, cib_calls_ms, cib_num_local, cib_num_updates,
cib_num_fail, cib_bad_connects, cib_num_timeouts);
last_stat = cib_num_ops;
cib_call_time = 0;
return TRUE;
}
#if SUPPORT_HEARTBEAT
gboolean ccm_connect(void);
static void ccm_connection_destroy(gpointer user_data)
{
crm_err("CCM connection failed... blocking while we reconnect");
CRM_ASSERT(ccm_connect());
return;
}
gboolean ccm_connect(void)
{
gboolean did_fail = TRUE;
int num_ccm_fails = 0;
int max_ccm_fails = 30;
int ret;
int cib_ev_fd;
while(did_fail) {
did_fail = FALSE;
crm_info("Registering with CCM...");
ret = oc_ev_register(&cib_ev_token);
if (ret != 0) {
did_fail = TRUE;
}
if(did_fail == FALSE) {
crm_debug_3("Setting up CCM callbacks");
ret = oc_ev_set_callback(
cib_ev_token, OC_EV_MEMB_CLASS,
cib_ccm_msg_callback, NULL);
if (ret != 0) {
crm_warn("CCM callback not set");
did_fail = TRUE;
}
}
if(did_fail == FALSE) {
oc_ev_special(cib_ev_token, OC_EV_MEMB_CLASS, 0);
crm_debug_3("Activating CCM token");
ret = oc_ev_activate(cib_ev_token, &cib_ev_fd);
if (ret != 0){
crm_warn("CCM Activation failed");
did_fail = TRUE;
}
}
if(did_fail) {
num_ccm_fails++;
oc_ev_unregister(cib_ev_token);
if(num_ccm_fails < max_ccm_fails){
crm_warn("CCM Connection failed %d times (%d max)",
num_ccm_fails, max_ccm_fails);
sleep(3);
} else {
crm_err("CCM Activation failed %d (max) times",
num_ccm_fails);
return FALSE;
}
}
}
crm_debug("CCM Activation passed... all set to go!");
G_main_add_fd(G_PRIORITY_HIGH, cib_ev_fd, FALSE,
cib_ccm_dispatch, cib_ev_token,
ccm_connection_destroy);
return TRUE;
}
#endif
#if SUPPORT_AIS
static gboolean cib_ais_dispatch(AIS_Message *wrapper, char *data, int sender)
{
xmlNode *xml = NULL;
crm_debug_2("Message received: '%.80s'", data);
switch(wrapper->header.id) {
case crm_class_members:
case crm_class_notify:
- update_counters(__FILE__, __PRETTY_FUNCTION__, the_cib);
break;
default:
xml = string2xml(data);
if(xml == NULL) {
goto bail;
}
crm_xml_add(xml, F_ORIG, wrapper->sender.uname);
crm_xml_add_int(xml, F_SEQ, wrapper->id);
cib_peer_callback(xml, NULL);
break;
}
free_xml(xml);
return TRUE;
bail:
crm_err("Invalid XML: '%.120s'", data);
return TRUE;
}
static void
cib_ais_destroy(gpointer user_data)
{
crm_err("AIS connection terminated");
ais_fd_sync = -1;
exit(1);
}
#endif
int
cib_init(void)
{
gboolean was_error = FALSE;
if(startCib("cib.xml") == FALSE){
crm_crit("Cannot start CIB... terminating");
exit(1);
}
if(stand_alone == FALSE) {
void *dispatch = cib_ha_peer_callback;
void *destroy = cib_ha_connection_destroy;
if(is_openais_cluster()) {
#if SUPPORT_AIS
destroy = cib_ais_destroy;
dispatch = cib_ais_dispatch;
#endif
}
if(crm_cluster_connect(&cib_our_uname, NULL, dispatch, destroy,
#if SUPPORT_HEARTBEAT
&hb_conn
#else
NULL
#endif
) == FALSE){
crm_crit("Cannot sign in to the cluster... terminating");
exit(100);
}
#if 0
if(is_openais_cluster()) {
crm_info("Requesting the list of configured nodes");
send_ais_text(
crm_class_members, __FUNCTION__, TRUE, NULL, crm_msg_ais);
}
#endif
#if SUPPORT_HEARTBEAT
if(is_heartbeat_cluster()) {
if(was_error == FALSE) {
if (HA_OK != hb_conn->llc_ops->set_cstatus_callback(
hb_conn, cib_client_status_callback, hb_conn)) {
crm_err("Cannot set cstatus callback: %s",
hb_conn->llc_ops->errmsg(hb_conn));
was_error = TRUE;
}
}
if(was_error == FALSE) {
was_error = (ccm_connect() == FALSE);
}
if(was_error == FALSE) {
/* Async get client status information in the cluster */
crm_info("Requesting the list of configured nodes");
hb_conn->llc_ops->client_status(
hb_conn, NULL, CRM_SYSTEM_CIB, -1);
}
}
#endif
} else {
cib_our_uname = crm_strdup("localhost");
}
channel1 = crm_strdup(cib_channel_callback);
was_error = init_server_ipc_comms(
channel1, cib_client_connect,
default_ipc_connection_destroy);
channel2 = crm_strdup(cib_channel_ro);
was_error = was_error || init_server_ipc_comms(
channel2, cib_client_connect,
default_ipc_connection_destroy);
channel3 = crm_strdup(cib_channel_rw);
was_error = was_error || init_server_ipc_comms(
channel3, cib_client_connect,
default_ipc_connection_destroy);
if(stand_alone) {
if(was_error) {
crm_err("Couldnt start");
return 1;
}
cib_is_master = TRUE;
/* Create the mainloop and run it... */
mainloop = g_main_new(FALSE);
crm_info("Starting %s mainloop", crm_system_name);
g_main_run(mainloop);
return_to_orig_privs();
return 0;
}
if(was_error == FALSE) {
/* Create the mainloop and run it... */
mainloop = g_main_new(FALSE);
crm_info("Starting %s mainloop", crm_system_name);
Gmain_timeout_add(
crm_get_msec(cib_stat_interval), cib_stats, NULL);
g_main_run(mainloop);
return_to_orig_privs();
} else {
crm_err("Couldnt start all communication channels, exiting.");
}
return 0;
}
void
usage(const char* cmd, int exit_status)
{
FILE* stream;
stream = exit_status ? stderr : stdout;
fprintf(stream, "usage: %s [-%s]\n", cmd, OPTARGS);
fprintf(stream, "\t--%s (-%c)\t\tTurn on debug info."
" Additional instances increase verbosity\n", "verbose", 'V');
fprintf(stream, "\t--%s (-%c)\t\tThis help message\n", "help", '?');
fprintf(stream, "\t--%s (-%c)\tAdvanced use only\n", "per-action-cib", 'a');
fprintf(stream, "\t--%s (-%c)\tAdvanced use only\n", "stand-alone", 's');
fprintf(stream, "\t--%s (-%c)\tAdvanced use only\n", "disk-writes", 'w');
fprintf(stream, "\t--%s (-%c)\t\tAdvanced use only\n", "cib-root", 'r');
fflush(stream);
exit(exit_status);
}
void
cib_ha_connection_destroy(gpointer user_data)
{
if(cib_shutdown_flag) {
crm_info("Heartbeat disconnection complete... exiting");
} else {
crm_err("Heartbeat connection lost! Exiting.");
}
uninitializeCib();
crm_info("Exiting...");
if (mainloop != NULL && g_main_is_running(mainloop)) {
g_main_quit(mainloop);
} else {
exit(LSB_EXIT_OK);
}
}
static void
disconnect_cib_client(gpointer key, gpointer value, gpointer user_data)
{
cib_client_t *a_client = value;
crm_debug_2("Processing client %s/%s... send=%d, recv=%d",
crm_str(a_client->name), crm_str(a_client->channel_name),
(int)a_client->channel->send_queue->current_qlen,
(int)a_client->channel->recv_queue->current_qlen);
if(a_client->channel->ch_status == IPC_CONNECT) {
a_client->channel->ops->resume_io(a_client->channel);
if(a_client->channel->send_queue->current_qlen != 0
|| a_client->channel->recv_queue->current_qlen != 0) {
crm_info("Flushed messages to/from %s/%s... send=%d, recv=%d",
crm_str(a_client->name),
crm_str(a_client->channel_name),
(int)a_client->channel->send_queue->current_qlen,
(int)a_client->channel->recv_queue->current_qlen);
}
}
if(a_client->channel->ch_status == IPC_CONNECT) {
crm_warn("Disconnecting %s/%s...",
crm_str(a_client->name),
crm_str(a_client->channel_name));
a_client->channel->ops->disconnect(a_client->channel);
}
}
extern gboolean cib_process_disconnect(
IPC_Channel *channel, cib_client_t *cib_client);
gboolean
cib_shutdown(int nsig, gpointer unused)
{
if(cib_shutdown_flag == FALSE) {
cib_shutdown_flag = TRUE;
crm_debug("Disconnecting %d clients",
g_hash_table_size(client_list));
g_hash_table_foreach(client_list, disconnect_cib_client, NULL);
crm_info("Disconnected %d clients",
g_hash_table_size(client_list));
cib_process_disconnect(NULL, NULL);
} else {
crm_info("Waiting for %d clients to disconnect...",
g_hash_table_size(client_list));
}
return TRUE;
}
gboolean
startCib(const char *filename)
{
gboolean active = FALSE;
xmlNode *cib = readCibXmlFile(cib_root, filename, !preserve_status);
CRM_ASSERT(cib != NULL);
if(activateCibXml(cib, TRUE) == 0) {
int port = 0;
const char *port_s = crm_element_value(cib, "remote_access_port");
active = TRUE;
if(port_s) {
port = crm_parse_int(port_s, NULL);
init_remote_listener(port);
}
crm_info("CIB Initialization completed successfully");
if(per_action_cib) {
uninitializeCib();
}
}
return active;
}
diff --git a/cib/messages.c b/cib/messages.c
index 939c117de1..63f3db5a1b 100644
--- a/cib/messages.c
+++ b/cib/messages.c
@@ -1,551 +1,501 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <heartbeat.h>
#include <clplumbing/cl_log.h>
#include <time.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/msg.h>
#include <crm/common/xml.h>
#include <crm/common/cluster.h>
#include <cibio.h>
#include <cibmessages.h>
#include <cibprimatives.h>
#include <callbacks.h>
#define MAX_DIFF_RETRY 5
#ifdef CIBPIPE
gboolean cib_is_master = TRUE;
#else
gboolean cib_is_master = FALSE;
#endif
xmlNode *the_cib = NULL;
gboolean syncd_once = FALSE;
extern const char *cib_our_uname;
enum cib_errors revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags);
int get_revision(xmlNode *xml_obj, int cur_revision);
enum cib_errors updateList(
xmlNode *local_cib, xmlNode *update_command, xmlNode *failed,
int operation, const char *section);
gboolean check_generation(xmlNode *newCib, xmlNode *oldCib);
gboolean update_results(
xmlNode *failed, xmlNode *target, const char *operation, int return_code);
enum cib_errors cib_update_counter(
xmlNode *xml_obj, const char *field, gboolean reset);
enum cib_errors sync_our_cib(xmlNode *request, gboolean all);
extern xmlNode *cib_msg_copy(const xmlNode *msg, gboolean with_data);
extern gboolean cib_shutdown_flag;
extern void terminate_cib(const char *caller);
enum cib_errors
cib_process_shutdown_req(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
#ifdef CIBPIPE
return cib_invalid_argument;
#else
enum cib_errors result = cib_ok;
const char *host = crm_element_value(req, F_ORIG);
*answer = NULL;
if(crm_element_value(req, F_CIB_ISREPLY) == NULL) {
crm_info("Shutdown REQ from %s", host);
return cib_ok;
} else if(cib_shutdown_flag) {
crm_info("Shutdown ACK from %s", host);
terminate_cib(__FUNCTION__);
return cib_ok;
} else {
crm_err("Shutdown ACK from %s - not shutting down",host);
result = cib_unknown;
}
return result;
#endif
}
enum cib_errors
cib_process_default(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
enum cib_errors result = cib_ok;
crm_debug_2("Processing \"%s\" event", op);
*answer = NULL;
if(op == NULL) {
result = cib_operation;
crm_err("No operation specified");
} else if(strcasecmp(CRM_OP_NOOP, op) == 0) {
;
} else {
result = cib_NOTSUPPORTED;
crm_err("Action [%s] is not supported by the CIB", op);
}
return result;
}
enum cib_errors
cib_process_quit(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
enum cib_errors result = cib_ok;
crm_debug_2("Processing \"%s\" event", op);
crm_warn("The CRMd has asked us to exit... complying");
exit(0);
return result;
}
enum cib_errors
cib_process_readwrite(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
#ifdef CIBPIPE
return cib_invalid_argument;
#else
enum cib_errors result = cib_ok;
crm_debug_2("Processing \"%s\" event", op);
if(safe_str_eq(op, CIB_OP_ISMASTER)) {
if(cib_is_master == TRUE) {
result = cib_ok;
} else {
result = cib_not_master;
}
return result;
}
if(safe_str_eq(op, CIB_OP_MASTER)) {
if(cib_is_master == FALSE) {
crm_info("We are now in R/W mode");
cib_is_master = TRUE;
syncd_once = TRUE;
update_validation(result_cib, TRUE, FALSE);
} else {
crm_debug("We are still in R/W mode");
}
} else if(cib_is_master) {
crm_info("We are now in R/O mode");
cib_is_master = FALSE;
}
return result;
#endif
}
enum cib_errors
cib_process_ping(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
#ifdef CIBPIPE
return cib_invalid_argument;
#else
enum cib_errors result = cib_ok;
crm_debug_2("Processing \"%s\" event", op);
*answer = createPingAnswerFragment(CRM_SYSTEM_CIB, "ok");
return result;
#endif
}
enum cib_errors
cib_process_sync(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
#ifdef CIBPIPE
return cib_invalid_argument;
#else
return sync_our_cib(req, TRUE);
#endif
}
enum cib_errors
cib_process_sync_one(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
#ifdef CIBPIPE
return cib_invalid_argument;
#else
return sync_our_cib(req, FALSE);
#endif
}
int sync_in_progress = 0;
enum cib_errors
cib_server_process_diff(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
int rc = cib_ok;
if(cib_is_master) {
/* the master is never waiting for a resync */
sync_in_progress = 0;
}
if(sync_in_progress > MAX_DIFF_RETRY) {
/* request another full-sync,
* the last request may have been lost
*/
sync_in_progress = 0;
}
if(sync_in_progress) {
int diff_add_updates = 0;
int diff_add_epoch = 0;
int diff_add_admin_epoch = 0;
int diff_del_updates = 0;
int diff_del_epoch = 0;
int diff_del_admin_epoch = 0;
cib_diff_version_details(
input,
&diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
&diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
sync_in_progress++;
crm_warn("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)",
diff_del_admin_epoch,diff_del_epoch,diff_del_updates,
diff_add_admin_epoch,diff_add_epoch,diff_add_updates);
return cib_diff_resync;
}
rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer);
if(rc == cib_diff_resync && cib_is_master == FALSE) {
xmlNode *sync_me = create_xml_node(NULL, "sync-me");
free_xml(*result_cib);
*result_cib = NULL;
crm_info("Requesting re-sync from peer");
sync_in_progress++;
crm_xml_add(sync_me, F_TYPE, "cib");
crm_xml_add(sync_me, F_CIB_OPERATION, CIB_OP_SYNC_ONE);
crm_xml_add(sync_me, F_CIB_DELEGATED, cib_our_uname);
if(send_cluster_message(NULL, crm_msg_cib, sync_me, FALSE) == FALSE) {
rc = cib_not_connected;
}
free_xml(sync_me);
} else if(rc == cib_diff_resync) {
rc = cib_diff_failed;
if(options & cib_force_diff) {
crm_warn("Not requesting full refresh in slave mode.");
}
}
return rc;
}
enum cib_errors
cib_process_replace_svr(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
const char *tag = crm_element_name(input);
enum cib_errors rc = cib_process_replace(
op, options, section, req, input, existing_cib, result_cib, answer);
if(rc == cib_ok && safe_str_eq(tag, XML_TAG_CIB)) {
sync_in_progress = 0;
}
return rc;
}
enum cib_errors
cib_process_create(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
xmlNode *failed = NULL;
enum cib_errors result = cib_ok;
xmlNode *update_section = NULL;
crm_debug_2("Processing \"%s\" event for section=%s", op, crm_str(section));
if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) {
section = NULL;
} else if(safe_str_eq(XML_TAG_CIB, section)) {
section = NULL;
} else if(safe_str_eq(crm_element_name(input), XML_TAG_CIB)) {
section = NULL;
}
CRM_CHECK(strcasecmp(CIB_OP_CREATE, op) == 0, return cib_operation);
if(input == NULL) {
crm_err("Cannot perform modification with no data");
return cib_NOOBJECT;
}
if(section == NULL) {
return cib_process_modify(op, options, section, req, input, existing_cib, result_cib, answer);
}
failed = create_xml_node(NULL, XML_TAG_FAILED);
update_section = get_object_root(section, *result_cib);
if(safe_str_eq(crm_element_name(input), section)) {
xml_child_iter(input, a_child,
result = add_cib_object(update_section, a_child);
if(update_results(failed, a_child, op, result)) {
break;
}
);
} else {
result = add_cib_object(update_section, input);
update_results(failed, input, op, result);
}
if(xml_has_children(failed)) {
CRM_CHECK(result != cib_ok, result = cib_unknown);
}
if (result != cib_ok) {
crm_log_xml_err(failed, "CIB Update failures");
*answer = failed;
} else {
free_xml(failed);
}
return result;
}
enum cib_errors
cib_process_delete_absolute(
const char *op, int options, const char *section, xmlNode *req, xmlNode *input,
xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
{
xmlNode *failed = NULL;
enum cib_errors result = cib_ok;
xmlNode *update_section = NULL;
crm_debug_2("Processing \"%s\" event for section=%s", op, crm_str(section));
if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) {
section = NULL;
} else if(safe_str_eq(XML_TAG_CIB, section)) {
section = NULL;
} else if(safe_str_eq(crm_element_name(input), XML_TAG_CIB)) {
section = NULL;
}
CRM_CHECK(strcasecmp(CIB_OP_DELETE, op) == 0, return cib_operation);
if(input == NULL) {
crm_err("Cannot perform modification with no data");
return cib_NOOBJECT;
}
failed = create_xml_node(NULL, XML_TAG_FAILED);
update_section = get_object_root(section, *result_cib);
result = delete_cib_object(update_section, input);
update_results(failed, input, op, result);
if(xml_has_children(failed)) {
CRM_CHECK(result != cib_ok, result = cib_unknown);
}
if (result != cib_ok) {
crm_log_xml_err(failed, "CIB Update failures");
*answer = failed;
} else {
free_xml(failed);
}
return result;
}
gboolean
check_generation(xmlNode *newCib, xmlNode *oldCib)
{
if(cib_compare_generation(newCib, oldCib) >= 0) {
return TRUE;
}
crm_warn("Generation from update is older than the existing one");
return FALSE;
}
gboolean
update_results(
xmlNode *failed, xmlNode *target, const char* operation, int return_code)
{
xmlNode *xml_node = NULL;
gboolean was_error = FALSE;
const char *error_msg = NULL;
if (return_code != cib_ok) {
error_msg = cib_error2string(return_code);
xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB);
was_error = TRUE;
add_node_copy(xml_node, target);
crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target));
crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE, TYPE(target));
crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation);
crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg);
crm_warn("Action %s failed: %s (cde=%d)",
operation, error_msg, return_code);
}
return was_error;
}
-enum cib_errors
-revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags)
-{
- int cmp = 0;
- enum cib_errors rc = cib_ok;
- char *new_revision = NULL;
- const char *cur_revision = crm_element_value(
- cib_copy, XML_ATTR_CIB_REVISION);
-
- crm_validate_data(cib_update);
- crm_validate_data(cib_copy);
-
- if(crm_element_value(cib_update, XML_ATTR_CIB_REVISION) == NULL) {
- return cib_ok;
- }
-
- new_revision = crm_element_value_copy(cib_update,XML_ATTR_CIB_REVISION);
-
- cmp = compare_version(new_revision, CIB_FEATURE_SET);
- if(cmp > 0) {
- CRM_DEV_ASSERT(cib_is_master == FALSE);
- CRM_DEV_ASSERT((flags & cib_scope_local) == 0);
-
- if(cib_is_master) {
- crm_err("Update uses an unsupported tag/feature:"
- " %s vs %s", new_revision,CIB_FEATURE_SET);
- rc = cib_revision_unsupported;
-
- } else if(flags & cib_scope_local) {
- /* an admin has forced a local change using a tag we
- * dont understand... ERROR
- */
- crm_err("Local update uses an unsupported tag/feature:"
- " %s vs %s", new_revision,CIB_FEATURE_SET);
- rc = cib_revision_unsupported;
- }
-
- } else if(cur_revision == NULL) {
- crm_info("Updating CIB revision to %s", new_revision);
- crm_xml_add(cib_copy, XML_ATTR_CIB_REVISION, new_revision);
-
- } else {
- /* make sure we end up with the right value in the end */
- crm_xml_add(cib_update, XML_ATTR_CIB_REVISION, cur_revision);
- }
-
- crm_free(new_revision);
- return rc;
-}
-
#ifndef CIBPIPE
enum cib_errors
sync_our_cib(xmlNode *request, gboolean all)
{
enum cib_errors result = cib_ok;
const char *host = crm_element_value(request, F_ORIG);
const char *op = crm_element_value(request, F_CIB_OPERATION);
xmlNode *replace_request = cib_msg_copy(request, FALSE);
CRM_CHECK(the_cib != NULL, ;);
CRM_CHECK(replace_request != NULL, ;);
crm_info("Syncing CIB to %s", all?"all peers":host);
if(all == FALSE && host == NULL) {
crm_log_xml(LOG_ERR, "bad sync", request);
}
/* remove the "all == FALSE" condition
*
* sync_from was failing, the local client wasnt being notified
* because it didnt know it was a reply
* setting this does not prevent the other nodes from applying it
* if all == TRUE
*/
if(host != NULL) {
crm_xml_add(replace_request, F_CIB_ISREPLY, host);
}
crm_xml_add(replace_request, F_CIB_OPERATION, CIB_OP_REPLACE);
crm_xml_add(replace_request, "original_"F_CIB_OPERATION, op);
crm_xml_add(replace_request, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE);
add_message_xml(replace_request, F_CIB_CALLDATA, the_cib);
if(send_cluster_message(all?NULL:host, crm_msg_cib, replace_request, FALSE) == FALSE) {
result = cib_not_connected;
}
free_xml(replace_request);
return result;
}
#endif
diff --git a/crmd/cib.c b/crmd/cib.c
index b3a63a47c1..a9e4717037 100644
--- a/crmd/cib.c
+++ b/crmd/cib.c
@@ -1,301 +1,286 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> /* for access */
#include <clplumbing/cl_signal.h>
#include <clplumbing/realtime.h>
#include <sys/types.h> /* for calls to open */
#include <sys/stat.h> /* for calls to open */
#include <fcntl.h> /* for calls to open */
#include <pwd.h> /* for getpwuid */
#include <grp.h> /* for initgroups */
#include <sys/time.h> /* for getrlimit */
#include <sys/resource.h>/* for getrlimit */
#include <errno.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crm/cib.h>
#include <crmd.h>
struct crm_subsystem_s *cib_subsystem = NULL;
int cib_retries = 0;
static void
revision_check_callback(xmlNode *msg, int call_id, int rc,
xmlNode *output, void *user_data)
{
int cmp = -1;
const char *revision = NULL;
xmlNode *generation = NULL;
if(rc != cib_ok) {
fsa_data_t *msg_data = NULL;
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
generation = output;
CRM_CHECK(safe_str_eq(crm_element_name(generation), XML_TAG_CIB), return);
crm_debug_3("Checking our feature revision is allowed: %s", CIB_FEATURE_SET);
- revision = crm_element_value(generation, XML_ATTR_CIB_REVISION);
- cmp = compare_version(revision, CIB_FEATURE_SET);
-
- if(cmp > 0) {
- crm_err("This build (%s) does not support the current"
- " resource configuration", VERSION);
- crm_err("We can support up to CIB feature set %s (current=%s)",
- CIB_FEATURE_SET, revision);
- crm_err("Shutting down the CRM");
- /* go into a stall state */
- register_fsa_error_adv(
- C_FSA_INTERNAL, I_SHUTDOWN, NULL, NULL, __FUNCTION__);
- return;
- }
-
revision = crm_element_value(generation, XML_ATTR_CRM_VERSION);
cmp = compare_version(revision, CRM_FEATURE_SET);
if(cmp > 0) {
crm_err("This build (%s) does not support the current"
" resource configuration", VERSION);
crm_err("We can support up to CRM feature set %s (current=%s)",
revision, CRM_FEATURE_SET);
crm_err("Shutting down the CRM");
/* go into a stall state */
register_fsa_error_adv(
C_FSA_INTERNAL, I_SHUTDOWN, NULL, NULL, __FUNCTION__);
return;
}
}
static void
do_cib_replaced(const char *event, xmlNode *msg)
{
crm_debug("Updating the CIB after a replace: DC=%s", AM_I_DC?"true":"false");
if(AM_I_DC) {
/* start the join process again so we get everyone's LRM status */
populate_cib_nodes(FALSE);
do_update_cib_nodes(TRUE, __FUNCTION__);
register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL);
}
}
/* A_CIB_STOP, A_CIB_START, A_CIB_RESTART, */
void
do_cib_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
struct crm_subsystem_s *this_subsys = cib_subsystem;
long long stop_actions = A_CIB_STOP;
long long start_actions = A_CIB_START;
if(action & stop_actions) {
crm_info("Disconnecting CIB");
clear_bit_inplace(fsa_input_register, R_CIB_CONNECTED);
CRM_ASSERT(fsa_cib_conn != NULL);
if(fsa_cib_conn->state != cib_disconnected) {
fsa_cib_conn->cmds->set_slave(
fsa_cib_conn, cib_scope_local);
fsa_cib_conn->cmds->signoff(fsa_cib_conn);
}
}
if(action & start_actions) {
int rc = cib_ok;
CRM_ASSERT(fsa_cib_conn != NULL);
if(cur_state == S_STOPPING) {
crm_err("Ignoring request to start %s after shutdown",
this_subsys->name);
return;
}
rc = fsa_cib_conn->cmds->signon(
fsa_cib_conn, CRM_SYSTEM_CRMD, cib_command);
if(rc != cib_ok) {
/* a short wait that usually avoids stalling the FSA */
sleep(1);
rc = fsa_cib_conn->cmds->signon(
fsa_cib_conn, CRM_SYSTEM_CRMD, cib_command);
}
if(rc != cib_ok){
crm_debug("Could not connect to the CIB service");
} else if(cib_ok != fsa_cib_conn->cmds->set_connection_dnotify(
fsa_cib_conn, crmd_cib_connection_destroy)) {
crm_err("Could not set dnotify callback");
} else if(cib_ok != fsa_cib_conn->cmds->add_notify_callback(
fsa_cib_conn, T_CIB_REPLACE_NOTIFY,
do_cib_replaced)) {
crm_err("Could not set CIB notification callback");
} else {
set_bit_inplace(
fsa_input_register, R_CIB_CONNECTED);
}
if(is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
cib_retries++;
crm_warn("Couldn't complete CIB registration %d"
" times... pause and retry",
cib_retries);
if(cib_retries < 30) {
crm_timer_start(wait_timer);
crmd_fsa_stall(NULL);
} else {
crm_err("Could not complete CIB"
" registration %d times..."
" hard error", cib_retries);
register_fsa_error(
C_FSA_INTERNAL, I_ERROR, NULL);
}
} else {
int call_id = 0;
crm_info("CIB connection established");
call_id = fsa_cib_conn->cmds->query(
fsa_cib_conn, NULL, NULL, cib_scope_local);
add_cib_op_callback(fsa_cib_conn, call_id, FALSE, NULL,
revision_check_callback);
cib_retries = 0;
}
}
}
/* A_CIB_INVOKE, A_CIB_BUMPGEN, A_UPDATE_NODESTATUS */
void
do_cib_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
xmlNode *answer = NULL;
ha_msg_input_t *cib_msg = fsa_typed_data(fsa_dt_ha_msg);
const char *sys_from = crm_element_value(cib_msg->msg, F_CRM_SYS_FROM);
if(fsa_cib_conn->state == cib_disconnected) {
if(cur_state != S_STOPPING) {
crm_err("CIB is disconnected");
crm_log_xml(LOG_WARNING, "CIB Input", cib_msg->msg);
return;
}
crm_info("CIB is disconnected");
crm_log_xml(LOG_DEBUG, "CIB Input", cib_msg->msg);
return;
}
if(action & A_CIB_INVOKE) {
if(safe_str_eq(sys_from, CRM_SYSTEM_CRMD)) {
action = A_CIB_INVOKE_LOCAL;
} else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) {
action = A_CIB_INVOKE_LOCAL;
}
}
if(action & A_CIB_INVOKE || action & A_CIB_INVOKE_LOCAL) {
int call_options = 0;
enum cib_errors rc = cib_ok;
xmlNode *cib_frag = NULL;
const char *section = NULL;
const char *op = crm_element_value(cib_msg->msg, F_CRM_TASK);
section = crm_element_value(cib_msg->msg, F_CIB_SECTION);
crm_element_value_int(cib_msg->msg, F_CIB_CALLOPTS, &call_options);
crm_log_xml(LOG_MSG, "udpate", cib_msg->msg);
if(op == NULL) {
crm_err("Invalid CIB Message");
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
cib_frag = NULL;
rc = fsa_cib_conn->cmds->variant_op(
fsa_cib_conn, op, NULL, section,
cib_msg->xml, &cib_frag, call_options);
if(rc < cib_ok || (action & A_CIB_INVOKE)) {
answer = create_reply(cib_msg->msg, cib_frag);
crm_xml_add(answer,XML_ATTR_RESULT,cib_error2string(rc));
}
if(action & A_CIB_INVOKE) {
if(relay_message(answer, TRUE) == FALSE) {
crm_err("Confused what to do with cib result");
crm_log_xml(LOG_ERR, "reply", answer);
register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL);
}
free_xml(answer);
} else if(rc < cib_ok) {
ha_msg_input_t *input = NULL;
crm_err("Internal CRM/CIB command from %s() failed: %s",
msg_data->origin, cib_error2string(rc));
crm_log_xml(LOG_WARNING, "CIB Input", cib_msg->msg);
crm_log_xml(LOG_WARNING, "CIB Reply", answer);
input = new_ha_msg_input(answer);
register_fsa_input(C_FSA_INTERNAL, I_ERROR, input);
free_xml(answer);
crm_free(input);
}
} else {
crm_err("Unexpected action %s in %s",
fsa_action2string(action), __FUNCTION__);
}
}
diff --git a/crmd/election.c b/crmd/election.c
index 5d7cba704d..9885fa04f5 100644
--- a/crmd/election.c
+++ b/crmd/election.c
@@ -1,436 +1,435 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <heartbeat.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/cluster.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <clplumbing/Gmain_timeout.h>
#include <clplumbing/cl_uuid.h>
#include <ha_version.h>
GHashTable *voted = NULL;
uint highest_born_on = -1;
static int current_election_id = 1;
const char *get_hg_version(void);
const char *get_hg_version(void)
{
/* limit this #define's use to a single file to avoid rebuilding more than necessary */
return HA_HG_VERSION;
}
/* A_ELECTION_VOTE */
void
do_election_vote(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
gboolean not_voting = FALSE;
xmlNode *vote = NULL;
/* don't vote if we're in one of these states or wanting to shut down */
switch(cur_state) {
case S_RECOVERY:
case S_STOPPING:
case S_TERMINATE:
crm_warn("Not voting in election, we're in state %s",
fsa_state2string(cur_state));
not_voting = TRUE;
break;
default:
break;
}
if(not_voting == FALSE) {
if(is_set(fsa_input_register, R_STARTING)) {
not_voting = TRUE;
}
}
if(not_voting) {
if(AM_I_DC) {
register_fsa_input(C_FSA_INTERNAL, I_RELEASE_DC, NULL);
} else {
register_fsa_input(C_FSA_INTERNAL, I_PENDING, NULL);
}
return;
}
vote = create_request(
CRM_OP_VOTE, NULL, NULL,
CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
current_election_id++;
crm_xml_add(vote, F_CRM_ELECTION_OWNER, fsa_our_uuid);
crm_xml_add_int(vote, F_CRM_ELECTION_ID, current_election_id);
send_request(vote, NULL);
crm_debug("Destroying voted hash");
g_hash_table_destroy(voted);
free_xml(vote);
voted = NULL;
if(cur_state == S_ELECTION || cur_state == S_RELEASE_DC) {
crm_timer_start(election_timeout);
} else if(cur_state != S_INTEGRATION) {
crm_err("Broken? Voting in state %s",
fsa_state2string(cur_state));
}
return;
}
char *dc_hb_msg = NULL;
int beat_num = 0;
gboolean
do_dc_heartbeat(gpointer data)
{
return TRUE;
}
struct election_data_s
{
const char *winning_uname;
unsigned int winning_bornon;
};
static void
log_member_uname(gpointer key, gpointer value, gpointer user_data)
{
if(crm_is_member_active(value)) {
crm_err("%s: %s", (char*)user_data, (char*)key);
}
}
static void
log_node(gpointer key, gpointer value, gpointer user_data)
{
crm_err("%s: %s", (char*)user_data, (char*)key);
}
void
do_election_check(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
int voted_size = g_hash_table_size(voted);
int num_members = crm_active_members();
/* in the case of #voted > #members, it is better to
* wait for the timeout and give the cluster time to
* stabilize
*/
if(fsa_state != S_ELECTION) {
crm_debug("Ignore election check: we not in an election");
} else if(voted_size >= num_members) {
/* we won and everyone has voted */
crm_timer_stop(election_timeout);
register_fsa_input(C_FSA_INTERNAL, I_ELECTION_DC, NULL);
if(voted_size > num_members) {
char *data = NULL;
data = crm_strdup("member");
g_hash_table_foreach(crm_peer_cache, log_member_uname, data);
crm_free(data);
data = crm_strdup("voted");
g_hash_table_foreach(voted, log_node, data);
crm_free(data);
}
crm_debug("Destroying voted hash");
g_hash_table_destroy(voted);
voted = NULL;
} else {
crm_info("Still waiting on %d non-votes (%d total)",
num_members - voted_size, num_members);
}
return;
}
/* A_ELECTION_COUNT */
void
do_election_count_vote(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
int election_id = -1;
gboolean we_loose = FALSE;
static time_t last_election_loss = 0;
enum crmd_fsa_input election_result = I_NULL;
crm_node_t *our_node = NULL, *your_node = NULL;
ha_msg_input_t *vote = fsa_typed_data(fsa_dt_ha_msg);
const char *op = crm_element_value(vote->msg, F_CRM_TASK);
const char *vote_from = crm_element_value(vote->msg, F_CRM_HOST_FROM);
const char *your_version = crm_element_value(vote->msg, F_CRM_VERSION);
const char *election_owner= crm_element_value(vote->msg, F_CRM_ELECTION_OWNER);
/* if the membership copy is NULL we REALLY shouldnt be voting
* the question is how we managed to get here.
*/
CRM_CHECK(vote->msg != NULL, crm_err("Bogus data from %s", msg_data->origin); return);
CRM_CHECK(crm_peer_cache != NULL, return);
CRM_CHECK(vote_from != NULL, vote_from = fsa_our_uname);
our_node = g_hash_table_lookup(crm_peer_cache, fsa_our_uname);
your_node = g_hash_table_lookup(crm_peer_cache, vote_from);
if(your_node == NULL) {
crm_debug("Election ignore: The other side doesn't exist in CCM: %s", vote_from);
return;
}
if(voted == NULL) {
crm_debug("Created voted hash");
voted = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
crm_element_value_int(vote->msg, F_CRM_ELECTION_ID, &election_id);
crm_debug("Election %d, owner: %s", election_id, election_owner);
/* update the list of nodes that have voted */
if(crm_str_eq(fsa_our_uuid, election_owner, TRUE)
|| crm_str_eq(fsa_our_uname, election_owner, TRUE)) {
if(election_id == current_election_id) {
char *uname_copy = NULL;
char *op_copy = crm_strdup(op);
uname_copy = crm_strdup(your_node->uname);
g_hash_table_replace(voted, uname_copy, op_copy);
crm_info("Updated voted hash for %s to %s",
your_node->uname, op);
} else {
crm_debug("Ignore old '%s' from %s: %d vs. %d",
op, your_node->uname,
election_id, current_election_id);
return;
}
} else {
CRM_CHECK(safe_str_neq(op, CRM_OP_NOVOTE), return);
}
if(vote_from == NULL || crm_str_eq(vote_from, fsa_our_uname, TRUE)) {
/* don't count our own vote */
crm_info("Election ignore: our %s (%s)", op,crm_str(vote_from));
return;
} else if(crm_str_eq(op, CRM_OP_NOVOTE, TRUE)) {
crm_info("Election ignore: no-vote from %s", vote_from);
return;
}
crm_info("Election check: %s from %s", op, vote_from);
if(our_node == NULL || safe_str_neq(our_node->state, CRM_NODE_MEMBER)) {
crm_info("Election fail: we don't exist in CCM");
we_loose = TRUE;
} else if(compare_version(your_version, CRM_FEATURE_SET) < 0) {
crm_info("Election fail: version");
we_loose = TRUE;
} else if(compare_version(your_version, CRM_FEATURE_SET) > 0) {
crm_info("Election pass: version");
} else if(is_heartbeat_cluster() && your_node->born < our_node->born) {
crm_debug("Election fail: born_on");
we_loose = TRUE;
} else if(is_heartbeat_cluster() && your_node->born > our_node->born) {
crm_debug("Election pass: born_on");
} else if(fsa_our_uname == NULL
|| strcasecmp(fsa_our_uname, vote_from) > 0) {
crm_debug("Election fail: uname");
we_loose = TRUE;
} else {
CRM_CHECK(strcasecmp(fsa_our_uname, vote_from) != 0, ;);
crm_debug("Them: %s (born=%llu) Us: %s (born=%llu)",
vote_from, (unsigned long long)your_node->born,
fsa_our_uname, (unsigned long long)our_node->born);
/* cant happen...
* } else if(strcasecmp(fsa_our_uname, vote_from) == 0) {
*
* default...
* } else { // strcasecmp(fsa_our_uname, vote_from) < 0
* we win
*/
}
if(we_loose) {
gboolean vote_sent = FALSE;
xmlNode *novote = create_request(
CRM_OP_NOVOTE, NULL, vote_from,
CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
update_dc(NULL, FALSE);
crm_timer_stop(election_timeout);
crm_debug("Election lost to %s (%d)", vote_from, election_id);
if(fsa_input_register & R_THE_DC) {
crm_debug_3("Give up the DC to %s", vote_from);
election_result = I_RELEASE_DC;
} else {
crm_debug_3("We werent the DC anyway");
election_result = I_PENDING;
}
crm_xml_add(novote, F_CRM_ELECTION_OWNER, election_owner);
crm_xml_add_int(novote, F_CRM_ELECTION_ID, election_id);
vote_sent = send_request(novote, NULL);
CRM_DEV_ASSERT(vote_sent);
free_xml(novote);
fsa_cib_conn->cmds->set_slave(fsa_cib_conn, cib_scope_local);
last_election_loss = time(NULL);
} else {
int dampen = 2;
time_t tm_now = time(NULL);
if(tm_now - last_election_loss < (time_t)dampen) {
crm_debug("Election ignore: We already lost an election less than %ds ago", dampen);
return;
}
last_election_loss = 0;
election_result = I_ELECTION;
crm_info("Election won over %s", vote_from);
g_hash_table_destroy(voted);
voted = NULL;
}
register_fsa_input(C_FSA_INTERNAL, election_result, NULL);
}
/* A_ELECT_TIMER_START, A_ELECTION_TIMEOUT */
/* we won */
void
do_election_timer_ctrl(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
}
static void
feature_update_callback(xmlNode *msg, int call_id, int rc,
xmlNode *output, void *user_data)
{
if(rc != cib_ok) {
fsa_data_t *msg_data = NULL;
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
}
/* A_DC_TAKEOVER */
void
do_dc_takeover(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
int rc = cib_ok;
xmlNode *cib = NULL;
crm_info("Taking over DC status for this partition");
set_bit_inplace(fsa_input_register, R_THE_DC);
if(voted != NULL) {
crm_debug_2("Destroying voted hash");
g_hash_table_destroy(voted);
voted = NULL;
}
set_bit_inplace(fsa_input_register, R_JOIN_OK);
set_bit_inplace(fsa_input_register, R_INVOKE_PE);
fsa_cib_conn->cmds->set_slave_all(fsa_cib_conn, cib_none);
fsa_cib_conn->cmds->set_master(fsa_cib_conn, cib_none);
cib = createEmptyCib();
crm_xml_add(cib, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
- crm_xml_add(cib, XML_ATTR_CIB_REVISION, CIB_FEATURE_SET);
fsa_cib_update(XML_TAG_CIB, cib, cib_quorum_override, rc);
add_cib_op_callback(fsa_cib_conn, rc, FALSE, NULL, feature_update_callback);
update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG,
NULL, NULL, NULL, "dc-version", VERSION"-"HA_HG_VERSION, FALSE);
free_xml(cib);
}
/* A_DC_RELEASE */
void
do_dc_release(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
if(action & A_DC_RELEASE) {
crm_debug("Releasing the role of DC");
clear_bit_inplace(fsa_input_register, R_THE_DC);
} else if (action & A_DC_RELEASED) {
crm_info("DC role released");
#if 0
if( are there errors ) {
/* we cant stay up if not healthy */
/* or perhaps I_ERROR and go to S_RECOVER? */
result = I_SHUTDOWN;
}
#endif
register_fsa_input(C_FSA_INTERNAL, I_RELEASE_SUCCESS, NULL);
} else {
crm_err("Unknown action %s", fsa_action2string(action));
}
crm_debug_2("Am I still the DC? %s", AM_I_DC?XML_BOOLEAN_YES:XML_BOOLEAN_NO);
}
diff --git a/crmd/pengine.c b/crmd/pengine.c
index c28c865dff..114bdd6729 100644
--- a/crmd/pengine.c
+++ b/crmd/pengine.c
@@ -1,219 +1,218 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> /* for access */
#include <clplumbing/cl_signal.h>
#include <clplumbing/realtime.h>
#include <clplumbing/timers.h>
#include <sys/types.h> /* for calls to open */
#include <sys/stat.h> /* for calls to open */
#include <fcntl.h> /* for calls to open */
#include <pwd.h> /* for getpwuid */
#include <grp.h> /* for initgroups */
#include <sys/time.h> /* for getrlimit */
#include <sys/resource.h>/* for getrlimit */
#include <errno.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/cluster.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crm/cib.h>
#include <crmd.h>
#define CLIENT_EXIT_WAIT 30
struct crm_subsystem_s *pe_subsystem = NULL;
void do_pe_invoke_callback(xmlNode *msg, int call_id, int rc,
xmlNode *output, void *user_data);
static gboolean pe_msg_dispatch(IPC_Channel *client, gpointer user_data)
{
xmlNode *msg = NULL;
gboolean stay_connected = TRUE;
while(IPC_ISRCONN(client)) {
if(client->ops->is_message_pending(client) == 0) {
break;
}
msg = xmlfromIPC(client, 0);
if (msg != NULL) {
route_message(C_IPC_MESSAGE, msg);
free_xml(msg);
}
}
if (client->ch_status != IPC_CONNECT) {
stay_connected = FALSE;
pe_subsystem->ipc = NULL;
pe_subsystem->client = NULL;
crm_info("Received HUP from %s:[%d]", pe_subsystem->name, pe_subsystem->pid);
}
G_main_set_trigger(fsa_source);
return stay_connected;
}
/* A_PE_START, A_PE_STOP, A_TE_RESTART */
void
do_pe_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
struct crm_subsystem_s *this_subsys = pe_subsystem;
long long stop_actions = A_PE_STOP;
long long start_actions = A_PE_START;
if(action & stop_actions) {
stop_subsystem(this_subsys, FALSE);
}
if(action & start_actions) {
if(cur_state != S_STOPPING) {
if(start_subsystem(this_subsys) == FALSE) {
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
} else {
IPC_Channel *ch = NULL;
sleep(5);
init_client_ipc_comms(CRM_SYSTEM_PENGINE, pe_msg_dispatch, NULL, &ch);
if(ch != NULL) {
pe_subsystem->ipc = ch;
set_bit_inplace(fsa_input_register, pe_subsystem->flag_connected);
}
}
} else {
crm_info("Ignoring request to start %s while shutting down",
this_subsys->name);
}
}
}
int fsa_pe_query = 0;
char *fsa_pe_ref = NULL;
/* A_PE_INVOKE */
void
do_pe_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
if(is_set(fsa_input_register, R_PE_CONNECTED) == FALSE){
crm_info("Waiting for the PE to connect");
crmd_fsa_stall(NULL);
return;
}
if(is_set(fsa_input_register, R_HAVE_CIB) == FALSE) {
crm_err("Attempted to invoke the PE without a consistent"
" copy of the CIB!");
/* start the join from scratch */
register_fsa_input_before(C_FSA_INTERNAL, I_ELECTION, NULL);
return;
}
crm_debug("Requesting the current CIB: %s",fsa_state2string(fsa_state));
fsa_pe_query = fsa_cib_conn->cmds->query(
fsa_cib_conn, NULL, NULL, cib_scope_local);
if(FALSE == add_cib_op_callback(
fsa_cib_conn, fsa_pe_query, TRUE, NULL, do_pe_invoke_callback)) {
crm_err("Cant retrieve the CIB to invoke the %s subsystem with",
pe_subsystem->name);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
}
void
do_pe_invoke_callback(xmlNode *msg, int call_id, int rc,
xmlNode *output, void *user_data)
{
xmlNode *cmd = NULL;
if(call_id != fsa_pe_query) {
crm_debug_2("Skipping superceeded CIB query: %d (current=%d)",
call_id, fsa_pe_query);
return;
} else if(AM_I_DC == FALSE
|| is_set(fsa_input_register, R_PE_CONNECTED) == FALSE) {
crm_debug("No need to invoke the PE anymore");
return;
} else if(fsa_state != S_POLICY_ENGINE) {
crm_debug("Discarding PE request in state: %s",
fsa_state2string(fsa_state));
return;
} else if(last_peer_update != 0) {
crm_debug("Re-asking for the CIB: peer update %d still pending",
last_peer_update);
mssleep(500);
register_fsa_action(A_PE_INVOKE);
return;
} else if(fsa_state != S_POLICY_ENGINE) {
crm_err("Invoking PE in state: %s", fsa_state2string(fsa_state));
}
CRM_DEV_ASSERT(output != NULL);
CRM_DEV_ASSERT(crm_element_value(output, XML_ATTR_DC_UUID) != NULL);
crm_xml_add_int(output, XML_ATTR_HAVE_QUORUM, fsa_has_quorum);
- crm_xml_add_int(output, XML_ATTR_CCM_TRANSITION, crm_peer_seq);
if(fsa_pe_ref) {
crm_free(fsa_pe_ref);
fsa_pe_ref = NULL;
}
cmd = create_request(CRM_OP_PECALC, output, NULL,
CRM_SYSTEM_PENGINE, CRM_SYSTEM_DC, NULL);
fsa_pe_ref = crm_element_value_copy(cmd, XML_ATTR_REFERENCE);
if(send_ipc_message(pe_subsystem->ipc, cmd) == FALSE) {
crm_err("Could not contact the pengine");
}
crm_debug("Invoking the PE: ref=%s, seq=%llu, quorate=%d",
fsa_pe_ref, crm_peer_seq, fsa_has_quorum);
free_xml(cmd);
}
diff --git a/crmd/te_events.c b/crmd/te_events.c
index ba781c71cc..554892eea4 100644
--- a/crmd/te_events.c
+++ b/crmd/te_events.c
@@ -1,578 +1,576 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/msg.h>
#include <crm/common/xml.h>
#include <tengine.h>
#include <heartbeat.h>
#include <clplumbing/Gmain_timeout.h>
#include <lrm/lrm_api.h>
#include <crmd_fsa.h>
char *failed_stop_offset = NULL;
char *failed_start_offset = NULL;
xmlNode *need_abort(xmlNode *update);
void process_graph_event(xmlNode *event, const char *event_node);
int match_graph_event(int action_id, xmlNode *event, const char *event_node,
int op_status, int op_rc, int target_rc);
xmlNode *
need_abort(xmlNode *update)
{
xmlNode *section_xml = NULL;
const char *section = NULL;
if(update == NULL) {
return NULL;
}
xml_prop_iter(update, name, value,
if(safe_str_eq(name, XML_ATTR_HAVE_QUORUM)) {
goto do_abort; /* possibly not required */
- } else if(safe_str_eq(name, XML_ATTR_NUMPEERS)) {
- goto do_abort;
} else if(safe_str_eq(name, XML_ATTR_GENERATION)) {
goto do_abort;
} else if(safe_str_eq(name, XML_ATTR_GENERATION_ADMIN)) {
goto do_abort;
}
continue;
do_abort:
crm_debug("Aborting on change to %s", name);
crm_log_xml_debug(update, "Abort: CIB Attrs");
return update;
);
section = XML_CIB_TAG_NODES;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
section = XML_CIB_TAG_RESOURCES;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
section = XML_CIB_TAG_CONSTRAINTS;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
section = XML_CIB_TAG_CRMCONFIG;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
return NULL;
}
static gboolean
fail_incompletable_actions(crm_graph_t *graph, const char *down_node)
{
const char *target = NULL;
xmlNode *last_action = NULL;
slist_iter(
synapse, synapse_t, graph->synapses, lpc,
if (synapse->confirmed) {
continue;
}
slist_iter(
action, crm_action_t, synapse->actions, lpc,
if(action->type == action_type_pseudo || action->confirmed) {
continue;
}
target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
if(safe_str_eq(target, down_node)) {
action->failed = TRUE;
last_action = action->xml;
update_graph(graph, action);
crm_notice("Action %d (%s) is scheduled for %s (offline)",
action->id, ID(action->xml), down_node);
}
);
);
if(last_action != NULL) {
crm_warn("Node %s shutdown resulted in un-runnable actions", down_node);
abort_transition(INFINITY, tg_restart, "Node failure", last_action);
return TRUE;
}
return FALSE;
}
gboolean
extract_event(xmlNode *msg)
{
int shutdown = 0;
const char *shutdown_s = NULL;
const char *event_node = NULL;
/*
[cib fragment]
...
<status>
<node_state id="node1" state=CRMD_STATE_ACTIVE exp_state="active">
<lrm>
<lrm_resources>
<rsc_state id="" rsc_id="rsc4" node_id="node1" rsc_state="stopped"/>
*/
crm_debug_4("Extracting event from %s", crm_element_name(msg));
xml_child_iter_filter(
msg, node_state, XML_CIB_TAG_STATE,
xmlNode *attrs = NULL;
xmlNode *resources = NULL;
const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE);
const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM);
const char *crmd_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE);
/* Transient node attribute changes... */
event_node = crm_element_value(node_state, XML_ATTR_ID);
crm_debug_2("Processing state update from %s", event_node);
crm_log_xml_debug_3(node_state, "Processing");
attrs = find_xml_node(
node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE);
if(attrs != NULL) {
crm_info("Aborting on "XML_TAG_TRANSIENT_NODEATTRS" changes for %s", event_node);
abort_transition(INFINITY, tg_restart,
XML_TAG_TRANSIENT_NODEATTRS, attrs);
}
resources = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
resources = find_xml_node(
resources, XML_LRM_TAG_RESOURCES, FALSE);
/* LRM resource update... */
xml_child_iter(
resources, rsc,
xml_child_iter(
rsc, rsc_op,
crm_log_xml_debug_3(rsc_op, "Processing resource update");
process_graph_event(rsc_op, event_node);
);
);
/*
* node state update... possibly from a shutdown we requested
*/
if(safe_str_eq(ccm_state, XML_BOOLEAN_FALSE)
|| safe_str_eq(ha_state, DEADSTATUS)
|| safe_str_eq(crmd_state, CRMD_JOINSTATE_DOWN)) {
crm_action_t *shutdown = NULL;
shutdown = match_down_event(0, event_node, NULL);
if(shutdown != NULL) {
update_graph(transition_graph, shutdown);
trigger_graph();
} else {
crm_info("Stonith/shutdown of %s not matched", event_node);
abort_transition(INFINITY, tg_restart, "Node failure", node_state);
}
fail_incompletable_actions(transition_graph, event_node);
}
shutdown_s = crm_element_value(node_state, XML_CIB_ATTR_SHUTDOWN);
if(shutdown_s) {
shutdown = crm_parse_int(shutdown_s, NULL);
}
if(shutdown_s && shutdown > 0) {
crm_info("Aborting on "XML_CIB_ATTR_SHUTDOWN" attribute for %s", event_node);
abort_transition(INFINITY, tg_restart, "Shutdown request", node_state);
}
);
return TRUE;
}
static void
update_failcount(xmlNode *event, const char *event_node, int rc, int target_rc)
{
int interval = 0;
char *task = NULL;
char *rsc_id = NULL;
char *attr_name = NULL;
const char *id = ID(event);
const char *on_uuid = event_node;
const char *value = NULL;
if(rc == 99) {
/* this is an internal code for "we're busy, try again" */
return;
} else if(rc == target_rc) {
return;
}
if(failed_stop_offset == NULL) {
failed_stop_offset = crm_strdup(INFINITY_S);
}
if(failed_start_offset == NULL) {
failed_start_offset = crm_strdup(INFINITY_S);
}
CRM_CHECK(on_uuid != NULL, return);
CRM_CHECK(parse_op_key(id, &rsc_id, &task, &interval),
crm_err("Couldn't parse: %s", ID(event));
goto bail);
CRM_CHECK(task != NULL, goto bail);
CRM_CHECK(rsc_id != NULL, goto bail);
if(safe_str_eq(task, CRMD_ACTION_START)) {
interval = 1;
value = failed_start_offset;
} else if(safe_str_eq(task, CRMD_ACTION_STOP)) {
interval = 1;
value = failed_stop_offset;
}
if(value == NULL || safe_str_neq(value, INFINITY_S)) {
value = XML_NVPAIR_ATTR_VALUE"++";
}
if(interval > 0) {
int call_id = 0;
char *now = crm_itoa(time(NULL));
attr_name = crm_concat("fail-count", rsc_id, '-');
crm_warn("Updating failcount for %s on %s after failed %s:"
" rc=%d (update=%s, time=%s)", rsc_id, on_uuid, task, rc, value, now);
/* don't let notificatios of these updates cause new transitions */
call_id = update_attr(fsa_cib_conn, cib_inhibit_notify, XML_CIB_TAG_STATUS,
on_uuid, NULL,NULL, attr_name, value, FALSE);
add_cib_op_callback(fsa_cib_conn, call_id, FALSE, NULL, cib_failcount_updated);
crm_free(attr_name);
attr_name = crm_concat("last-failure", rsc_id, '-');
/* don't let notificatios of these updates cause new transitions */
call_id = update_attr(fsa_cib_conn, cib_inhibit_notify, XML_CIB_TAG_STATUS,
on_uuid, NULL,NULL, attr_name, now, FALSE);
add_cib_op_callback(fsa_cib_conn, call_id, FALSE, NULL, cib_failcount_updated);
crm_free(attr_name);
crm_free(now);
}
bail:
crm_free(rsc_id);
crm_free(task);
}
static int
status_from_rc(crm_action_t *action, int orig_status, int rc, int target_rc)
{
int status = orig_status;
if(target_rc == rc) {
crm_debug_2("Target rc: == %d", rc);
if(status != LRM_OP_DONE) {
crm_debug_2("Re-mapping op status to"
" LRM_OP_DONE for rc=%d", rc);
status = LRM_OP_DONE;
}
} else {
crm_debug_2("Target rc: != %d", rc);
if(status != LRM_OP_ERROR) {
crm_info("Re-mapping op status to"
" LRM_OP_ERROR for rc=%d", rc);
status = LRM_OP_ERROR;
}
}
/* 99 is the code we use for direct nack's */
if(rc != 99 && status != LRM_OP_DONE) {
const char *task, *uname;
task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
uname = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
crm_warn("Action %d (%s) on %s failed (target: %d vs. rc: %d): %s",
action->id, task, uname, target_rc, rc, op_status2text(status));
}
return status;
}
/*
* returns the ID of the action if a match is found
* returns -1 if a match was not found
* returns -2 if a match was found but the action failed (and was
* not allowed to)
*/
int
match_graph_event(int action_id, xmlNode *event, const char *event_node,
int op_status, int op_rc, int target_rc)
{
const char *target = NULL;
const char *allow_fail = NULL;
const char *this_event = ID(event);
crm_action_t *action = NULL;
action = get_action(action_id, FALSE);
if(action == NULL) {
return -1;
}
op_status = status_from_rc(action, op_status, op_rc, target_rc);
if(op_status != LRM_OP_DONE) {
update_failcount(event, event_node, op_rc, target_rc);
}
/* Process OP status */
switch(op_status) {
case LRM_OP_PENDING:
crm_debug("Ignoring pending operation");
return action->id;
break;
case LRM_OP_DONE:
break;
case LRM_OP_ERROR:
case LRM_OP_TIMEOUT:
case LRM_OP_NOTSUPPORTED:
action->failed = TRUE;
break;
case LRM_OP_CANCELLED:
/* do nothing?? */
crm_err("Dont know what to do for cancelled ops yet");
break;
default:
action->failed = TRUE;
crm_err("Unsupported action result: %d", op_status);
}
/* stop this event's timer if it had one */
stop_te_timer(action->timer);
action->confirmed = TRUE;
update_graph(transition_graph, action);
trigger_graph();
if(action->failed) {
allow_fail = g_hash_table_lookup(
action->params, crm_meta_name(XML_ATTR_TE_ALLOWFAIL));
if(crm_is_true(allow_fail)) {
action->failed = FALSE;
}
}
if(action->failed) {
abort_transition(action->synapse->priority+1,
tg_restart, "Event failed", event);
}
target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
te_log_action(LOG_INFO, "Action %s (%d) confirmed on %s (rc=%d)",
crm_str(this_event), action->id, crm_str(target),
op_status);
return action->id;
}
crm_action_t *
get_action(int id, gboolean confirmed)
{
slist_iter(
synapse, synapse_t, transition_graph->synapses, lpc,
slist_iter(
action, crm_action_t, synapse->actions, lpc2,
if(action->id == id) {
if(confirmed) {
stop_te_timer(action->timer);
action->confirmed = TRUE;
}
return action;
}
)
);
return NULL;
}
crm_action_t *
match_down_event(int id, const char *target, const char *filter)
{
const char *this_action = NULL;
const char *this_node = NULL;
crm_action_t *match = NULL;
slist_iter(
synapse, synapse_t, transition_graph->synapses, lpc,
/* lookup event */
slist_iter(
action, crm_action_t, synapse->actions, lpc2,
if(id > 0 && action->id == id) {
match = action;
break;
}
this_action = crm_element_value(
action->xml, XML_LRM_ATTR_TASK);
if(action->type != action_type_crm) {
continue;
} else if(safe_str_eq(this_action, CRM_OP_LRM_REFRESH)){
continue;
} else if(filter != NULL
&& safe_str_neq(this_action, filter)) {
continue;
}
this_node = crm_element_value(
action->xml, XML_LRM_ATTR_TARGET_UUID);
if(this_node == NULL) {
crm_log_xml_err(action->xml, "No node uuid");
}
if(safe_str_neq(this_node, target)) {
crm_debug("Action %d : Node mismatch: %s",
action->id, this_node);
continue;
}
match = action;
break;
);
if(match != NULL) {
/* stop this event's timer if it had one */
break;
}
);
if(match != NULL) {
/* stop this event's timer if it had one */
crm_debug("Match found for action %d: %s on %s", id,
crm_element_value(match->xml, XML_LRM_ATTR_TASK_KEY),
target);
stop_te_timer(match->timer);
match->confirmed = TRUE;
} else if(id > 0) {
crm_err("No match for action %d", id);
} else {
crm_warn("No match for shutdown action on %s", target);
}
return match;
}
void
process_graph_event(xmlNode *event, const char *event_node)
{
int rc = -1;
int status = -1;
int action = -1;
int target_rc = -1;
int transition_num = -1;
char *update_te_uuid = NULL;
gboolean passed = FALSE;
const char *id = NULL;
const char *magic = NULL;
CRM_ASSERT(event != NULL);
id = ID(event);
magic = crm_element_value(event, XML_ATTR_TRANSITION_MAGIC);
if(magic == NULL) {
/* non-change */
return;
}
CRM_CHECK(decode_transition_magic(
magic, &update_te_uuid, &transition_num, &action,
&status, &rc, &target_rc),
crm_err("Invalid event %s detected", id);
abort_transition(INFINITY, tg_restart,"Bad event", event);
);
if(status == LRM_OP_PENDING) {
goto bail;
}
if(transition_num == -1) {
crm_err("Action %s (%s) initiated outside of a transition",
id, magic);
abort_transition(INFINITY, tg_restart,"Unexpected event",event);
} else if(action < 0 || safe_str_neq(update_te_uuid, te_uuid)) {
crm_info("Action %s (%s) initiated by a different transitioner",
id, magic);
abort_transition(INFINITY, tg_restart,"Foreign event", event);
} else if(transition_graph->id != transition_num) {
crm_info("Detected action %s from a different transition:"
" %d vs. %d", id, transition_num, transition_graph->id);
abort_transition(INFINITY, tg_restart,"Old event", event);
} else if(transition_graph->complete) {
crm_info("Action %s arrived after a completed transition", id);
abort_transition(INFINITY, tg_restart, "Inactive graph", event);
} else if(match_graph_event(
action, event, event_node, status, rc, target_rc) < 0) {
crm_err("Unknown graph action %s", id);
abort_transition(INFINITY, tg_restart, "Unknown event", event);
} else {
passed = TRUE;
crm_debug_2("Processed update to %s: %s", id, magic);
}
if(passed == FALSE && rc != EXECRA_OK) {
update_failcount(event, event_node, rc, target_rc);
}
bail:
crm_free(update_te_uuid);
return;
}
diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index 701204f220..e8d5f60ce6 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,264 +1,260 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef XML_TAGS__H
#define XML_TAGS__H
#define CIB_OPTIONS_FIRST "cib-bootstrap-options"
#define F_CRM_TASK "crm_task"
#define F_CRM_HOST_TO "crm_host_to"
#define F_CRM_MSG_TYPE F_SUBTYPE
#define F_CRM_SYS_TO "crm_sys_to"
#define F_CRM_SYS_FROM "crm_sys_from"
#define F_CRM_HOST_FROM F_ORIG
#define F_CRM_REFERENCE XML_ATTR_REFERENCE
#define F_CRM_VERSION XML_ATTR_VERSION
#define F_CRM_ORIGIN "origin"
#define F_CRM_JOIN_ID "join_id"
#define F_CRM_ELECTION_ID "election-id"
#define F_CRM_ELECTION_OWNER "election-owner"
#define F_CRM_TGRAPH "crm-tgraph"
#define F_CRM_TGRAPH_INPUT "crm-tgraph-in"
/*---- Common tags/attrs */
#define XML_DIFF_MARKER "__crm_diff_marker__"
#define XML_ATTR_TAGNAME F_XML_TAGNAME
#define XML_TAG_CIB "cib"
#define XML_TAG_FAILED "failed"
-#define XML_ATTR_CRM_VERSION "crm-feature-set"
+#define XML_ATTR_CRM_VERSION "crm_feature_set"
#define XML_ATTR_DIGEST "digest"
#define XML_ATTR_VALIDATION "validate-with"
-#define XML_ATTR_NUMPEERS "num-peers"
#define XML_ATTR_HAVE_QUORUM "have-quorum"
-#define XML_ATTR_CCM_TRANSITION "ccm-transition"
#define XML_ATTR_GENERATION "epoch"
-#define XML_ATTR_GENERATION_ADMIN "admin-epoch"
-#define XML_ATTR_NUMUPDATES "num-updates"
+#define XML_ATTR_GENERATION_ADMIN "admin_epoch"
+#define XML_ATTR_NUMUPDATES "num_updates"
#define XML_ATTR_TIMEOUT "timeout"
#define XML_ATTR_ORIGIN "crm-debug-origin"
#define XML_ATTR_TSTAMP "crm-timestamp"
#define XML_CIB_ATTR_WRITTEN "cib-last-written"
#define XML_ATTR_VERSION "version"
#define XML_ATTR_DESC "description"
#define XML_ATTR_ID "id"
#define XML_ATTR_ID_LONG "long-id"
#define XML_ATTR_TYPE "type"
#define XML_ATTR_FILTER_TYPE "type-filter"
#define XML_ATTR_FILTER_ID "id-filter"
#define XML_ATTR_FILTER_PRIORITY "priority-filter"
#define XML_ATTR_VERBOSE "verbose"
#define XML_ATTR_OP "op"
#define XML_ATTR_DC "is_dc"
#define XML_ATTR_DC_UUID "dc-uuid"
-#define XML_ATTR_CIB_REVISION "cib-feature-revision"
-#define XML_ATTR_CIB_REVISION_MAX "cib-feature-revision-max"
#define XML_BOOLEAN_TRUE "true"
#define XML_BOOLEAN_FALSE "false"
#define XML_BOOLEAN_YES XML_BOOLEAN_TRUE
#define XML_BOOLEAN_NO XML_BOOLEAN_FALSE
#define XML_TAG_OPTIONS "options"
/*---- top level tags/attrs */
#define XML_MSG_TAG "crm_message"
#define XML_MSG_TAG_DATA "msg_data"
#define XML_ATTR_REQUEST "request"
#define XML_ATTR_RESPONSE "response"
#define XML_ATTR_UNAME "uname"
#define XML_ATTR_UUID "id"
#define XML_ATTR_REFERENCE "reference"
#define XML_FAIL_TAG_RESOURCE "failed_resource"
#define XML_FAILRES_ATTR_RESID "resource_id"
#define XML_FAILRES_ATTR_REASON "reason"
#define XML_FAILRES_ATTR_RESSTATUS "resource_status"
#define XML_CRM_TAG_PING "ping_response"
#define XML_PING_ATTR_STATUS "result"
#define XML_PING_ATTR_SYSFROM "crm_subsystem"
#define XML_TAG_FRAGMENT "cib_fragment"
#define XML_ATTR_RESULT "result"
#define XML_ATTR_SECTION "section"
#define XML_FAIL_TAG_CIB "failed_update"
#define XML_FAILCIB_ATTR_ID "id"
#define XML_FAILCIB_ATTR_OBJTYPE "object_type"
#define XML_FAILCIB_ATTR_OP "operation"
#define XML_FAILCIB_ATTR_REASON "reason"
/*---- CIB specific tags/attrs */
#define XML_CIB_TAG_SECTION_ALL "all"
#define XML_CIB_TAG_CONFIGURATION "configuration"
#define XML_CIB_TAG_STATUS "status"
#define XML_CIB_TAG_RESOURCES "resources"
#define XML_CIB_TAG_NODES "nodes"
#define XML_CIB_TAG_CONSTRAINTS "constraints"
#define XML_CIB_TAG_CRMCONFIG "crm_config"
#define XML_CIB_TAG_STATE "node_state"
#define XML_CIB_TAG_NODE "node"
#define XML_CIB_TAG_CONSTRAINT "constraint"
#define XML_CIB_TAG_NVPAIR "nvpair"
#define XML_CIB_TAG_PROPSET "cluster_property_set"
#define XML_TAG_ATTR_SETS "instance_attributes"
#define XML_TAG_META_SETS "meta_attributes"
#define XML_TAG_ATTRS "attributes"
#define XML_TAG_PARAMS "parameters"
#define XML_TAG_RESOURCE_REF "resource_ref"
#define XML_CIB_TAG_RESOURCE "primitive"
#define XML_CIB_TAG_GROUP "group"
#define XML_CIB_TAG_INCARNATION "clone"
#define XML_CIB_TAG_MASTER "master"
#define XML_RSC_ATTR_RESTART "restart-type"
#define XML_RSC_ATTR_ORDERED "ordered"
#define XML_RSC_ATTR_INTERLEAVE "interleave"
#define XML_RSC_ATTR_INCARNATION "clone"
#define XML_RSC_ATTR_INCARNATION_MAX "clone-max"
#define XML_RSC_ATTR_INCARNATION_NODEMAX "clone-node-max"
#define XML_RSC_ATTR_MASTER_MAX "master-max"
#define XML_RSC_ATTR_MASTER_NODEMAX "master-node-max"
#define XML_RSC_ATTR_STATE "clone-state"
#define XML_RSC_ATTR_MANAGED "is-managed"
#define XML_RSC_ATTR_TARGET_ROLE "target-role"
#define XML_RSC_ATTR_UNIQUE "globally-unique"
#define XML_RSC_ATTR_NOTIFY "notify"
#define XML_RSC_ATTR_STICKINESS "resource-stickiness"
#define XML_RSC_ATTR_FAIL_STICKINESS "migration-threshold"
#define XML_RSC_ATTR_FAIL_TIMEOUT "failure-timeout"
#define XML_RSC_ATTR_MULTIPLE "multiple-active"
#define XML_RSC_ATTR_PRIORITY "priority"
#define XML_OP_ATTR_ON_FAIL "on-fail"
#define XML_OP_ATTR_START_DELAY "start-delay"
#define XML_OP_ATTR_ALLOW_MIGRATE "allow-migrate"
#define XML_CIB_TAG_LRM "lrm"
#define XML_LRM_TAG_RESOURCES "lrm_resources"
#define XML_LRM_TAG_RESOURCE "lrm_resource"
#define XML_LRM_TAG_AGENTS "lrm_agents"
#define XML_LRM_TAG_AGENT "lrm_agent"
#define XML_LRM_TAG_RSC_OP "lrm_rsc_op"
#define XML_AGENT_ATTR_CLASS "class"
#define XML_AGENT_ATTR_PROVIDER "provider"
#define XML_LRM_TAG_ATTRIBUTES "attributes"
#define XML_CIB_ATTR_REPLACE "replace"
#define XML_CIB_ATTR_SOURCE "source"
#define XML_CIB_ATTR_HEALTH "health"
#define XML_CIB_ATTR_WEIGHT "weight"
#define XML_CIB_ATTR_PRIORITY "priority"
#define XML_CIB_ATTR_CLEAR "clear_on"
#define XML_CIB_ATTR_SOURCE "source"
#define XML_CIB_ATTR_JOINSTATE "join"
#define XML_CIB_ATTR_EXPSTATE "expected"
#define XML_CIB_ATTR_INCCM "in_ccm"
#define XML_CIB_ATTR_CRMDSTATE "crmd"
#define XML_CIB_ATTR_HASTATE "ha"
#define XML_CIB_ATTR_SHUTDOWN "shutdown"
#define XML_CIB_ATTR_STONITH "stonith"
#define XML_LRM_ATTR_INTERVAL "interval"
#define XML_LRM_ATTR_TASK "operation"
#define XML_LRM_ATTR_TASK_KEY "operation_key"
#define XML_LRM_ATTR_TARGET "on_node"
#define XML_LRM_ATTR_TARGET_UUID "on_node_uuid"
#define XML_LRM_ATTR_RSCID "rsc-id"
#define XML_LRM_ATTR_OPSTATUS "op-status"
#define XML_LRM_ATTR_RC "rc-code"
#define XML_LRM_ATTR_CALLID "call-id"
#define XML_LRM_ATTR_OP_DIGEST "op-digest"
#define XML_LRM_ATTR_OP_RESTART "op-force-restart"
#define XML_LRM_ATTR_RESTART_DIGEST "op-restart-digest"
#define XML_TAG_GRAPH "transition_graph"
#define XML_GRAPH_TAG_RSC_OP "rsc_op"
#define XML_GRAPH_TAG_PSEUDO_EVENT "pseudo_event"
#define XML_GRAPH_TAG_CRM_EVENT "crm_event"
#define XML_TAG_RULE "rule"
#define XML_RULE_ATTR_SCORE "score"
#define XML_RULE_ATTR_SCORE_ATTRIBUTE "score-attribute"
#define XML_RULE_ATTR_SCORE_MANGLED "score-attribute-mangled"
#define XML_RULE_ATTR_ROLE "role"
#define XML_RULE_ATTR_RESULT "result"
#define XML_RULE_ATTR_BOOLEAN_OP "boolean-op"
#define XML_TAG_EXPRESSION "expression"
#define XML_EXPR_ATTR_ATTRIBUTE "attribute"
#define XML_EXPR_ATTR_OPERATION "operation"
#define XML_EXPR_ATTR_VALUE "value"
#define XML_EXPR_ATTR_TYPE "type"
#define XML_CONS_TAG_RSC_DEPEND "rsc_colocation"
#define XML_CONS_TAG_RSC_ORDER "rsc_order"
#define XML_CONS_TAG_RSC_LOCATION "rsc_location"
#define XML_CONS_ATTR_SYMMETRICAL "symmetrical"
#define XML_COLOC_ATTR_SOURCE "rsc"
#define XML_COLOC_ATTR_SOURCE_ROLE "rsc-role"
#define XML_COLOC_ATTR_TARGET "with-rsc"
#define XML_COLOC_ATTR_TARGET_ROLE "with-rsc-role"
#define XML_COLOC_ATTR_NODE_ATTR "node_attribute"
#define XML_ORDER_ATTR_FIRST "first-rsc"
#define XML_ORDER_ATTR_THEN "then-rsc"
#define XML_ORDER_ATTR_FIRST_ACTION "first-action"
#define XML_ORDER_ATTR_THEN_ACTION "then-action"
#define XML_NVPAIR_ATTR_NAME "name"
#define XML_NVPAIR_ATTR_VALUE "value"
#define XML_NODE_ATTR_STATE "state"
#define XML_CONFIG_ATTR_DC_DEADTIME "dc_deadtime"
#define XML_CONFIG_ATTR_ELECTION_FAIL "election_timeout"
#define XML_CONFIG_ATTR_FORCE_QUIT "shutdown_escalation"
#define XML_CONFIG_ATTR_REANNOUNCE "join_reannouce"
#define XML_CONFIG_ATTR_RECHECK "cluster_recheck_interval"
#define XML_CIB_TAG_GENERATION_TUPPLE "generation_tuple"
#define XML_ATTR_TRANSITION_MAGIC "transition-magic"
#define XML_ATTR_TRANSITION_KEY "transition-key"
#define XML_ATTR_TE_NOWAIT "op_no_wait"
#define XML_ATTR_TE_TARGET_RC "op_target_rc"
#define XML_ATTR_TE_ALLOWFAIL "op_allow_fail"
#define XML_ATTR_LRM_PROBE "lrm-is-probe"
#define XML_TAG_TRANSIENT_NODEATTRS "transient_attributes"
#include <crm/common/xml.h>
#define ID(x) crm_element_value(x, XML_ATTR_ID)
#define INSTANCE(x) crm_element_value(x, XML_CIB_ATTR_INSTANCE)
#define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP)
#define TYPE(x) crm_element_name(x)
#endif
diff --git a/lib/crm/cib/cib_utils.c b/lib/crm/cib/cib_utils.c
index bd27102d73..c049ca0d18 100644
--- a/lib/crm/cib/cib_utils.c
+++ b/lib/crm/cib/cib_utils.c
@@ -1,819 +1,818 @@
/*
* 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 <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <glib.h>
#include <heartbeat.h>
#include <clplumbing/ipc.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <lib/crm/cib/cib_private.h>
struct config_root_s
{
const char *name;
const char *parent;
const char *path;
};
/*
* "//crm_config" will also work in place of "/cib/configuration/crm_config"
* The / prefix means find starting from the root, whereas the // prefix means
* find anywhere and risks multiple matches
*/
struct config_root_s known_paths[] = {
{ NULL, NULL, "/" },
{ "cib", NULL, "/" },
{ "status", "/cib", "/cib/status" },
{ "configuration", "/cib", "/cib/configuration" },
{ "crm_config", "/cib/configuration", "/cib/configuration/crm_config" },
{ "nodes", "/cib/configuration", "/cib/configuration/nodes" },
{ "resources", "/cib/configuration", "/cib/configuration/resources" },
{ "constraints", "/cib/configuration", "/cib/configuration/constraints" },
{ "op_defaults", "/cib/configuration", "/cib/configuration/op_defaults" },
{ "rsc_defaults", "/cib/configuration", "/cib/configuration/rsc_defaults" },
{ "all", NULL, "/" },
};
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 configured schema/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 = "<unknown error>";
}
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;
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;
}
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
*/
const char *get_object_path(const char *object_type)
{
int lpc = 0;
int max = DIMOF(known_paths);
for(; lpc < max; lpc++) {
if((object_type == NULL && known_paths[lpc].name == NULL)
|| safe_str_eq(object_type, known_paths[lpc].name)) {
return known_paths[lpc].path;
}
}
return NULL;
}
const char *get_object_parent(const char *object_type)
{
int lpc = 0;
int max = DIMOF(known_paths);
for(; lpc < max; lpc++) {
if(safe_str_eq(object_type, known_paths[lpc].name)) {
return known_paths[lpc].parent;
}
}
return NULL;
}
xmlNode*
get_object_root(const char *object_type, xmlNode *the_root)
{
xmlNode *result = NULL;
xmlXPathObjectPtr xpathObj = xpath_search(the_root, get_object_path(object_type));
if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) {
crm_debug("Object %s not found", crm_str(object_type));
} else if(xpathObj->nodesetval->nodeNr > 1) {
crm_err("Too many matches for %s", crm_str(object_type));
} else {
result = xpathObj->nodesetval->nodeTab[0];
if(result->type == XML_DOCUMENT_NODE) {
result = result->children;
}
CRM_CHECK(result->type == XML_ELEMENT_NODE, return NULL);
}
if(xpathObj) {
xmlXPathFreeObject(xpathObj);
}
return result;
}
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;
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;
}
CRM_CHECK(update_section != NULL, return NULL);
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 *current_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);
}
current_dtd = crm_element_value(scratch, "validate-with");
dtd_ok = validate_xml(scratch, NULL, TRUE);
if(dtd_ok == FALSE) {
crm_err("Updated CIB does not validate against %s schema/dtd", current_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) {
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 call_id, int rc)
{
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;
if(msg != NULL) {
crm_element_value_int(msg, F_CIB_RC, &rc);
crm_element_value_int(msg, F_CIB_CALLID, &call_id);
output = get_message_xml(msg, F_CIB_CALLDATA);
}
blob = g_hash_table_lookup(
cib_op_callback_table, GINT_TO_POINTER(call_id));
if(blob != NULL) {
local_blob = *blob;
blob = NULL;
remove_cib_op_callback(call_id, FALSE);
} else {
crm_debug_2("No callback found for call %d", call_id);
local_blob.callback = NULL;
}
if(cib == NULL) {
crm_debug("No cib object supplied");
}
if(rc == cib_diff_resync) {
/* This is an internal value that clients do not and should not care about */
rc = cib_ok;
}
if(local_blob.callback != NULL
&& (rc == cib_ok || local_blob.only_success == FALSE)) {
crm_debug_2("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
local_blob.callback(msg, call_id, rc, output, local_blob.user_data);
} else if(cib && 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 && cib->op_callback != NULL) {
crm_debug_2("Invoking global callback for call %d", call_id);
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...");
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 8, 6:25 PM (4 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002643
Default Alt Text
(180 KB)

Event Timeline