Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4638904
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
155 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/crm/cib/callbacks.c b/crm/cib/callbacks.c
index e4aca646e9..3b333cdc0c 100644
--- a/crm/cib/callbacks.c
+++ b/crm/cib/callbacks.c
@@ -1,1437 +1,1436 @@
-/* $Id: callbacks.c,v 1.78 2005/09/02 12:34:16 andrew Exp $ */
+/* $Id: callbacks.c,v 1.79 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 <portability.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 <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/cl_uuid.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.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 <crm/dmalloc_wrapper.h>
gint cib_GCompareFunc(gconstpointer a, gconstpointer b);
gboolean cib_msg_timeout(gpointer data);
void cib_GHFunc(gpointer key, gpointer value, gpointer user_data);
gboolean ghash_str_clfree(gpointer key, gpointer value, gpointer user_data);
gboolean can_write(int flags);
HA_Message *cib_msg_copy(const HA_Message *msg, gboolean with_data);
gboolean ccm_manual_check(gpointer data);
extern enum cib_errors revision_check(crm_data_t *cib_update, crm_data_t *cib_copy, int flags);
void send_cib_replace(const HA_Message *sync_request, const char *host);
void cib_process_request(const HA_Message *request, gboolean privileged,
gboolean from_peer, cib_client_t *cib_client);
gboolean syncd_once = FALSE;
GHashTable *peer_hash = NULL;
int next_client_id = 0;
gboolean cib_is_master = FALSE;
gboolean cib_have_quorum = FALSE;
char * ccm_transition_id = NULL;
GHashTable *client_list = NULL;
GHashTable *ccm_membership = NULL;
extern const char *cib_our_uname;
extern ll_cluster_t *hb_conn;
extern int set_connected_peers(crm_data_t *xml_obj);
/*
typedef struct cib_operation_s
{
const char* operation;
gboolean modifies_cib;
gboolean needs_privileges;
gboolean needs_quorum;
gboolean needs_section;
gboolean needs_data;
enum cib_errors (*fn)(
const char *, int, const char *,
crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**);
} cib_operation_t;
*/
/* technically bump does modify the cib...
* but we want to split the "bump" from the "sync"
*/
cib_operation_t cib_server_ops[] = {
{NULL, FALSE,FALSE,FALSE,FALSE,FALSE,cib_process_default},
{CRM_OP_NOOP, FALSE,FALSE,FALSE,FALSE,FALSE,cib_process_default},
{CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_diff},
{CIB_OP_SLAVE, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_readwrite},
{CIB_OP_SLAVEALL, TRUE, TRUE, FALSE,FALSE,FALSE,cib_process_readwrite},
{CIB_OP_SYNC_ONE, FALSE,TRUE, FALSE,TRUE, FALSE,cib_process_sync_one},
{CIB_OP_MASTER, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_readwrite},
{CIB_OP_ISMASTER, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_readwrite},
{CIB_OP_BUMP, TRUE, TRUE, TRUE, TRUE, FALSE,cib_process_bump},
{CIB_OP_REPLACE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_replace},
{CIB_OP_CREATE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_modify},
{CIB_OP_UPDATE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_modify},
{CIB_OP_DELETE, TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_delete},
{CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, TRUE, TRUE, cib_process_modify},
{CIB_OP_QUERY, FALSE,FALSE,FALSE,TRUE, FALSE,cib_process_query},
{CIB_OP_SYNC, FALSE,TRUE, FALSE,TRUE, FALSE,cib_process_sync},
{CRM_OP_QUIT, FALSE,TRUE, FALSE,FALSE,FALSE,cib_process_quit},
{CRM_OP_PING, FALSE,FALSE,FALSE,FALSE,FALSE,cib_process_ping},
{CIB_OP_ERASE, TRUE, TRUE, TRUE, TRUE, FALSE,cib_process_erase}
};
int send_via_callback_channel(HA_Message *msg, const char *token);
enum cib_errors cib_process_command(
const HA_Message *request, HA_Message **reply,
crm_data_t **cib_diff, gboolean privileged);
gboolean cib_common_callback(
IPC_Channel *channel, gpointer user_data, gboolean privileged);
enum cib_errors cib_get_operation_id(const HA_Message * msg, int *operation);
gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client);
gboolean
cib_client_connect(IPC_Channel *channel, gpointer user_data)
{
gboolean auth_failed = FALSE;
gboolean can_connect = TRUE;
gboolean (*client_callback)(IPC_Channel *channel, gpointer user_data) = NULL;
cib_client_t *new_client = NULL;
crm_debug_3("Connecting channel");
if (channel == NULL) {
crm_err("Channel was NULL");
can_connect = FALSE;
} else if (channel->ch_status == IPC_DISCONNECT) {
crm_err("Channel was disconnected");
can_connect = FALSE;
} else if(user_data == NULL) {
crm_err("user_data must contain channel name");
can_connect = FALSE;
} else {
crm_malloc0(new_client, sizeof(cib_client_t));
new_client->id = NULL;
new_client->callback_id = NULL;
new_client->source = NULL;
new_client->channel = channel;
new_client->channel_name = user_data;
new_client->delegated_calls = NULL;
crm_debug_3("Created channel %p for channel %s",
new_client, new_client->channel_name);
client_callback = NULL;
/* choose callback and do auth based on channel_name */
if(safe_str_eq(new_client->channel_name, cib_channel_callback)) {
client_callback = cib_null_callback;
} else {
cl_uuid_t client_id;
cl_uuid_generate(&client_id);
crm_malloc0(new_client->id, sizeof(char)*36);
cl_uuid_unparse(&client_id, new_client->id);
new_client->id[35] = EOS;
cl_uuid_generate(&client_id);
crm_malloc0(new_client->callback_id, sizeof(char)*36);
cl_uuid_unparse(&client_id, new_client->callback_id);
new_client->callback_id[35] = EOS;
client_callback = cib_ro_callback;
if(safe_str_eq(new_client->channel_name, cib_channel_rw)) {
client_callback = cib_rw_callback;
}
}
}
if(auth_failed) {
crm_err("Connection to %s channel failed authentication",
(char *)user_data);
can_connect = FALSE;
}
if(can_connect == FALSE) {
if(new_client) {
crm_free(new_client->id);
crm_free(new_client->callback_id);
}
crm_free(new_client);
return FALSE;
}
channel->ops->set_recv_qlen(channel, 100);
if(safe_str_eq(new_client->channel_name, cib_channel_callback)) {
channel->ops->set_send_qlen(channel, 400);
} else {
channel->ops->set_send_qlen(channel, 100);
}
if(client_callback != NULL) {
new_client->source = G_main_add_IPC_Channel(
G_PRIORITY_DEFAULT, channel, FALSE, client_callback,
new_client, default_ipc_connection_destroy);
}
if(client_callback != cib_null_callback) {
/* send msg to client with uuid to use when signing up for
* callback channel
*/
HA_Message *reg_msg = ha_msg_new(3);
ha_msg_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER);
ha_msg_add(reg_msg, F_CIB_CLIENTID, new_client->id);
ha_msg_add(
reg_msg, F_CIB_CALLBACK_TOKEN, new_client->callback_id);
send_ipc_message(channel, reg_msg);
/* make sure we can find ourselves later for sync calls
* redirected to the master instance
*/
g_hash_table_insert(client_list, new_client->id, new_client);
}
crm_debug_3("Channel %s connected for client %s",
new_client->channel_name, new_client->id);
return TRUE;
}
gboolean
cib_rw_callback(IPC_Channel *channel, gpointer user_data)
{
return cib_common_callback(channel, user_data, TRUE);
}
gboolean
cib_ro_callback(IPC_Channel *channel, gpointer user_data)
{
return cib_common_callback(channel, user_data, FALSE);
}
gboolean
cib_null_callback(IPC_Channel *channel, gpointer user_data)
{
gboolean did_disconnect = TRUE;
HA_Message *op_request = NULL;
cib_client_t *cib_client = user_data;
cib_client_t *hash_client = NULL;
const char *type = NULL;
const char *uuid_ticket = NULL;
const char *client_name = NULL;
gboolean register_failed = FALSE;
if(cib_client == NULL) {
crm_err("Discarding IPC message from unknown source"
" on callback channel.");
return FALSE;
}
while(channel->ops->is_message_pending(channel)) {
if (channel->ch_status != IPC_CONNECT) {
/* The message which was pending for us is that
* the channel is no longer fully connected.
*
* Dont read requests from disconnected clients
*/
break;
}
op_request = msgfromIPC_noauth(channel);
type = cl_get_string(op_request, F_CIB_OPERATION);
if(safe_str_eq(type, T_CIB_NOTIFY) ) {
/* Update the notify filters for this client */
int on_off = 0;
ha_msg_value_int(
op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
type = cl_get_string(op_request, F_CIB_NOTIFY_TYPE);
crm_info("Setting %s callbacks for %s: %s",
type, cib_client->name, on_off?"on":"off");
if(safe_str_eq(type, T_CIB_POST_NOTIFY)) {
cib_client->post_notify = on_off;
} else if(safe_str_eq(type, T_CIB_PRE_NOTIFY)) {
cib_client->pre_notify = on_off;
} else if(safe_str_eq(type, T_CIB_UPDATE_CONFIRM)) {
cib_client->confirmations = on_off;
} else if(safe_str_eq(type, T_CIB_DIFF_NOTIFY)) {
cib_client->diffs = on_off;
}
continue;
} else if(safe_str_neq(type, CRM_OP_REGISTER) ) {
crm_warn("Discarding IPC message from %s on callback channel",
cib_client->id);
crm_msg_del(op_request);
continue;
}
uuid_ticket = cl_get_string(op_request, F_CIB_CALLBACK_TOKEN);
client_name = cl_get_string(op_request, F_CIB_CLIENTNAME);
CRM_DEV_ASSERT(uuid_ticket != NULL);
if(crm_assert_failed) {
register_failed = crm_assert_failed;
}
CRM_DEV_ASSERT(client_name != NULL);
if(crm_assert_failed) {
register_failed = crm_assert_failed;
}
if(register_failed == FALSE) {
hash_client = g_hash_table_lookup(client_list, uuid_ticket);
if(hash_client != NULL) {
crm_err("Duplicate registration request..."
" disconnecting");
register_failed = TRUE;
}
}
if(register_failed) {
crm_err("Registration request failed... disconnecting");
crm_msg_del(op_request);
return FALSE;
}
cib_client->id = crm_strdup(uuid_ticket);
cib_client->name = crm_strdup(client_name);
g_hash_table_insert(client_list, cib_client->id, cib_client);
crm_debug_2("Registered %s on %s channel",
cib_client->id, cib_client->channel_name);
if(safe_str_eq(cib_client->name, CRM_SYSTEM_TENGINE)) {
/* The TE is _always_ interested in these
* Enable now to avoid timing issues
*/
cib_client->diffs = TRUE;
}
crm_msg_del(op_request);
op_request = ha_msg_new(2);
ha_msg_add(op_request, F_CIB_OPERATION, CRM_OP_REGISTER);
ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id);
send_ipc_message(channel, op_request);
}
did_disconnect = cib_process_disconnect(channel, cib_client);
if(did_disconnect) {
crm_debug_2("Client disconnected");
}
return did_disconnect;
}
gboolean
cib_common_callback(
IPC_Channel *channel, gpointer user_data, gboolean privileged)
{
int rc = cib_ok;
int lpc = 0;
HA_Message *op_request = NULL;
cib_client_t *cib_client = user_data;
if(cib_client == NULL) {
crm_err("Receieved call from unknown source. Discarding.");
return FALSE;
}
crm_debug_2("Callback for %s on %s channel",
cib_client->id, cib_client->channel_name);
while(channel->ops->is_message_pending(channel)) {
if (channel->ch_status != IPC_CONNECT) {
/* The message which was pending for us is that
* the channel is no longer fully connected.
*
* Dont read requests from disconnected clients
*/
break;
}
op_request = msgfromIPC(channel, 0);
if (op_request == NULL) {
perror("Receive failure:");
break;
}
crm_debug_2("Processing IPC message from %s on %s channel",
cib_client->id, cib_client->channel_name);
crm_log_message_adv(LOG_MSG, "Client[inbound]", op_request);
lpc++;
rc = cib_ok;
CRM_DEV_ASSERT(
ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id) == HA_OK);
if(crm_assert_failed == FALSE) {
cib_process_request(
op_request, privileged, FALSE, cib_client);
}
crm_debug_3("Cleaning up request");
crm_msg_del(op_request);
op_request = NULL;
}
crm_debug_2("Processed %d messages", lpc);
return cib_process_disconnect(channel, cib_client);
}
void
cib_process_request(const HA_Message *request, gboolean privileged,
gboolean from_peer, cib_client_t *cib_client)
{
int call_type = 0;
int call_options = 0;
gboolean process = TRUE;
gboolean needs_reply = TRUE;
gboolean local_notify = FALSE;
gboolean needs_forward = FALSE;
crm_data_t *result_diff = NULL;
enum cib_errors rc = cib_ok;
HA_Message *op_reply = NULL;
const char *op = cl_get_string(request, F_CIB_OPERATION);
const char *originator = cl_get_string(request, F_ORIG);
const char *host = cl_get_string(request, F_CIB_HOST);
const char *reply_to = cl_get_string(request, F_CIB_ISREPLY);
const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE);
const char *delegated = cl_get_string(request, F_CIB_DELEGATED);
const char *client_id = NULL;
crm_debug_4("%s Processing msg %s",
cib_our_uname, cl_get_string(request, F_SEQ));
if(host != NULL && strlen(host) == 0) {
host = NULL;
}
ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options);
crm_debug_4("Retrieved call options: %d", call_options);
crm_debug_2("Processing %s message (%s) to %s...",
from_peer?"peer":"local",originator, host?host:"master");
rc = cib_get_operation_id(request, &call_type);
if(rc != cib_ok) {
/* TODO: construct error reply */
crm_err("Pre-processing of command failed: %s",
cib_error2string(rc));
} else if(from_peer == FALSE) {
if(cib_server_ops[call_type].modifies_cib
&& !(call_options & cib_inhibit_bcast)) {
/* we need to send an update anyway */
needs_reply = TRUE;
} else {
needs_reply = FALSE;
}
if(host == NULL && (call_options & cib_scope_local)) {
crm_debug("Processing locally scoped %s op from %s",
op, cib_client->name);
local_notify = TRUE;
} else if(host == NULL && cib_is_master) {
crm_debug("Processing master %s op locally from %s",
op, cib_client->name);
local_notify = TRUE;
} else if(safe_str_eq(host, cib_our_uname)) {
crm_debug("Processing locally addressed %s op from %s",
op, cib_client->name);
local_notify = TRUE;
} else {
crm_debug("%s op from %s needs to be forwarded to %s",
op, cib_client->name,
host?host:"the master instance");
needs_forward = TRUE;
process = FALSE;
}
} else if(crm_is_true(update) && safe_str_eq(reply_to, cib_our_uname)) {
crm_debug("Processing global/peer update from %s"
" that originated from us", originator);
needs_reply = FALSE;
local_notify = TRUE;
} else if(crm_is_true(update)) {
crm_debug("Processing global/peer update from %s", originator);
needs_reply = FALSE;
} else if(host != NULL && safe_str_eq(host, cib_our_uname)) {
crm_debug("Processing request sent to us from %s", originator);
} else if(delegated != NULL && cib_is_master == TRUE) {
crm_debug("Processing request sent to master instance from %s",
originator);
} else if(reply_to != NULL && safe_str_eq(reply_to, cib_our_uname)) {
crm_debug("Forward reply sent from %s to local clients",
originator);
process = FALSE;
needs_reply = FALSE;
local_notify = TRUE;
} else if(delegated != NULL) {
crm_debug("Ignoring msg for master instance");
return;
} else if(host != NULL) {
/* this is for a specific instance and we're not it */
crm_debug("Ignoring msg for instance on %s", crm_str(host));
return;
} else if(reply_to == NULL && cib_is_master == FALSE) {
/* this is for the master instance and we're not it */
crm_debug("Ignoring reply to %s", crm_str(reply_to));
return;
} else {
crm_err("Nothing for us to do?");
crm_log_message_adv(LOG_ERR, "Peer[inbound]", request);
return;
}
crm_debug_3("Finished determining processing actions");
if(call_options & cib_discard_reply) {
needs_reply = cib_server_ops[call_type].modifies_cib;
local_notify = FALSE;
}
if(needs_forward) {
HA_Message *forward_msg = cib_msg_copy(request, TRUE);
ha_msg_add(forward_msg, F_CIB_DELEGATED, cib_our_uname);
- crm_log_message(LOG_MSG, forward_msg);
if(host != NULL) {
crm_debug("Forwarding %s op to %s", op, host);
send_ha_message(hb_conn, forward_msg, host, FALSE);
} else {
crm_debug("Forwarding %s op to master instance", op);
send_ha_message(hb_conn, forward_msg, NULL, FALSE);
}
if(call_options & cib_discard_reply) {
crm_debug_2("Client not interested in reply");
crm_msg_del(forward_msg);
} else if(call_options & cib_sync_call) {
/* keep track of the request so we can time it
* out if required
*/
crm_debug("Registering delegated call from %s",
cib_client->id);
cib_client->delegated_calls = g_list_append(
cib_client->delegated_calls, forward_msg);
} else {
crm_msg_del(forward_msg);
}
return;
}
if(process) {
crm_debug_2("Performing local processing:"
" op=%s origin=%s/%s,%s (update=%s)",
cl_get_string(request, F_CIB_OPERATION), originator,
cl_get_string(request, F_CIB_CLIENTID),
cl_get_string(request, F_CIB_CALLID), update);
rc = cib_process_command(request, &op_reply, &result_diff, TRUE);
crm_debug_2("Processing complete");
if(rc == cib_diff_resync || rc == cib_diff_failed
|| rc == cib_old_data) {
crm_warn("%s operation failed: %s",
crm_str(op), cib_error2string(rc));
} else if(rc != cib_ok) {
crm_err("%s operation failed: %s",
crm_str(op), cib_error2string(rc));
crm_log_message_adv(LOG_DEBUG, "CIB[output]", op_reply);
crm_debug("Input message");
crm_log_message(LOG_DEBUG, request);
}
if(op_reply == NULL && (needs_reply || local_notify)) {
crm_err("Unexpected NULL reply to message");
crm_log_message(LOG_ERR, request);
needs_reply = FALSE;
local_notify = FALSE;
}
}
crm_debug_3("processing response cases");
if(local_notify) {
/* send callback to originating child */
cib_client_t *client_obj = NULL;
HA_Message *client_reply = NULL;
enum cib_errors local_rc = cib_ok;
crm_debug_2("Performing notification");
if(process == FALSE) {
client_reply = cib_msg_copy(request, TRUE);
} else {
client_reply = cib_msg_copy(op_reply, TRUE);
}
client_id = cl_get_string(request, F_CIB_CLIENTID);
if(client_id != NULL) {
client_obj = g_hash_table_lookup(
client_list, client_id);
} else {
crm_debug("No client to sent the response to."
" F_CIB_CLIENTID not set.");
}
crm_debug_3("Sending callback to request originator");
if(client_obj != NULL) {
crm_debug_2("Sending %ssync response to %s %s",
(call_options & cib_sync_call)?"":"an a-",
client_obj->id,
from_peer?"(originator of delegated request)":"");
if(call_options & cib_sync_call) {
send_via_callback_channel(
client_reply, client_obj->id);
} else {
send_via_callback_channel(
client_reply, client_obj->callback_id);
}
} else {
crm_warn("Client %s may have left us",
crm_str(client_id));
crm_msg_del(client_reply);
}
if(local_rc != cib_ok) {
crm_warn("%sSync reply failed: %s",
(call_options & cib_sync_call)?"":"A-",
cib_error2string(local_rc));
crm_log_message(LOG_DEBUG, op_reply);
}
}
if(needs_reply == FALSE) {
/* nothing more to do...
* this was a non-originating slave update
*/
crm_debug_2("Completed slave update");
crm_msg_del(op_reply);
free_xml(result_diff);
return;
}
crm_debug_4("add the originator to message");
CRM_DEV_ASSERT(op_reply != NULL);
CRM_DEV_ASSERT(cib_server_ops[call_type].modifies_cib == FALSE
|| result_diff != NULL || rc != cib_ok);
/* from now on we are the server */
if(rc == cib_ok
&& result_diff != NULL
&& cib_server_ops[call_type].modifies_cib
&& !(call_options & cib_inhibit_bcast)) {
/* this (successful) call modified the CIB _and_ the
* change needs to be broadcast...
* send via HA to other nodes
*/
HA_Message *op_bcast = cib_msg_copy(request, FALSE);
int diff_add_updates = 0;
int diff_add_epoch = 0;
int diff_add_admin_epoch = 0;
int diff_del_updates = 0;
int diff_del_epoch = 0;
int diff_del_admin_epoch = 0;
cib_diff_version_details(
result_diff,
&diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
&diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
crm_debug("Sending update diff %d.%d.%d -> %d.%d.%d",
diff_del_admin_epoch,diff_del_epoch,diff_del_updates,
diff_add_admin_epoch,diff_add_epoch,diff_add_updates);
ha_msg_add(op_bcast, F_CIB_ISREPLY, originator);
ha_msg_add(op_bcast, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE);
ha_msg_mod(op_bcast, F_CIB_OPERATION, CIB_OP_APPLY_DIFF);
add_message_xml(op_bcast, F_CIB_UPDATE_DIFF, result_diff);
crm_log_message(LOG_DEBUG_3, op_bcast);
send_ha_message(hb_conn, op_bcast, NULL, TRUE);
crm_msg_del(op_bcast);
} else if((call_options & cib_discard_reply) == 0) {
crm_debug_2("Sending replies");
if(from_peer && originator != NULL) {
/* send reply via HA to originating node */
crm_debug("Sending request result to originator only");
ha_msg_add(op_reply, F_CIB_ISREPLY, originator);
send_ha_message(hb_conn, op_reply, originator, FALSE);
}
if(call_options & cib_inhibit_bcast ) {
crm_debug("Request not broadcast: inhibited");
}
if(cib_server_ops[call_type].modifies_cib == FALSE) {
crm_debug_2("Request not broadcast: R/O call");
}
if(rc != cib_ok) {
crm_warn("Request not broadcast: call failed: %s",
cib_error2string(rc));
}
}
crm_msg_del(op_reply);
free_xml(result_diff);
return;
}
enum cib_errors
cib_process_command(const HA_Message *request, HA_Message **reply,
crm_data_t **cib_diff, gboolean privileged)
{
crm_data_t *output = NULL;
crm_data_t *input = NULL;
crm_data_t *input_fragment = NULL;
crm_data_t *current_cib = the_cib;
crm_data_t *result_cib = NULL;
crm_data_t *local_diff = NULL;
int call_type = 0;
int call_options = 0;
enum cib_errors rc = cib_ok;
const char *op = NULL;
const char *call_id = NULL;
const char *section = NULL;
const char *tmp = NULL;
gboolean global_update = crm_is_true(
cl_get_string(request, F_CIB_GLOBAL_UPDATE));
CRM_DEV_ASSERT(reply != NULL);
if(reply) { *reply = NULL; }
/* Start processing the request... */
op = cl_get_string(request, F_CIB_OPERATION);
call_id = cl_get_string(request, F_CIB_CALLID);
ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options);
crm_debug_4("Processing call id: %s", call_id);
rc = cib_get_operation_id(request, &call_type);
if(rc == cib_ok &&
cib_server_ops[call_type].needs_privileges
&& privileged == FALSE) {
/* abort */
rc = cib_not_authorized;
}
if(rc == cib_ok
&& global_update == FALSE
&& cib_server_ops[call_type].needs_quorum
&& can_write(call_options) == FALSE) {
rc = cib_no_quorum;
}
/* prevent NUMUPDATES from being incrimented - apply the change as-is */
if(global_update) {
call_options |= cib_inhibit_bcast;
}
if(rc == cib_ok && cib_server_ops[call_type].needs_section) {
section = cl_get_string(request, F_CIB_SECTION);
}
if(rc == cib_ok && safe_str_eq(op, CIB_OP_APPLY_DIFF)) {
crm_debug_4("Unpacking diff data in %s", F_CIB_UPDATE_DIFF);
input_fragment = get_message_xml(request, F_CIB_UPDATE_DIFF);
if(global_update) {
call_options |= cib_force_diff;
} else {
- do_id_check(input_fragment);
+ do_id_check(input_fragment, NULL);
}
} else if(rc == cib_ok && safe_str_eq(op, CIB_OP_SYNC)) {
input_fragment = ha_msg_copy(request);
} else if(rc == cib_ok && safe_str_eq(op, CIB_OP_SYNC_ONE)) {
input_fragment = ha_msg_copy(request);
} else if(rc == cib_ok && cib_server_ops[call_type].needs_data) {
crm_debug_4("Unpacking data in %s", F_CIB_CALLDATA);
input_fragment = get_message_xml(request, F_CIB_CALLDATA);
}
/* extract the CIB from the fragment */
if(input_fragment != NULL
&& safe_str_eq(crm_element_name(input_fragment), XML_TAG_FRAGMENT)) {
input = find_xml_node(input_fragment, XML_TAG_CIB, TRUE);
} else {
input = input_fragment;
}
/* grab the section specified for the command */
if(input != NULL && safe_str_eq(crm_element_name(input), XML_TAG_CIB)){
rc = revision_check(input, current_cib, call_options);
if(rc == cib_ok) {
input = get_object_root(section, input);
- do_id_check(input);
+ do_id_check(input, NULL);
}
}
if(cib_server_ops[call_type].modifies_cib) {
cib_pre_notify(
call_options, op,
get_object_root(section, current_cib), input);
}
if(rc == cib_ok) {
rc = cib_server_ops[call_type].fn(
op, call_options, section, input,
current_cib, &result_cib, &output);
}
if(cib_server_ops[call_type].modifies_cib) {
if(safe_str_eq(op, CIB_OP_APPLY_DIFF)) {
local_diff = copy_xml(input);
} else if(result_cib != NULL && current_cib != result_cib) {
local_diff = diff_cib_object(
current_cib, result_cib, FALSE);
}
if(rc != cib_ok) {
free_xml(result_cib);
} else if(activateCibXml(result_cib,CIB_FILENAME) < 0){
crm_warn("Activation failed");
rc = cib_ACTIVATION;
}
cib_post_notify(call_options, op, input, rc, the_cib);
cib_diff_notify(call_options, op, input, rc, local_diff);
} else if(result_cib != NULL) {
crm_err("%s call modified the CIB", op);
free_xml(result_cib);
}
crm_debug_4("Processing reply cases");
if((call_options & cib_discard_reply)
&& cib_server_ops[call_type].modifies_cib == FALSE) {
crm_debug_3("No reply needed for call %s", call_id);
return rc;
} else if(reply == NULL) {
crm_debug("No reply possible for call %s", call_id);
return rc;
}
crm_debug_4("Creating a basic reply");
*reply = ha_msg_new(8);
ha_msg_add(*reply, F_TYPE, T_CIB);
ha_msg_add(*reply, F_CIB_OPERATION, op);
ha_msg_add(*reply, F_CIB_CALLID, call_id);
ha_msg_add_int(*reply, F_CIB_RC, rc);
tmp = cl_get_string(request, F_CIB_CLIENTID);
ha_msg_add(*reply, F_CIB_CLIENTID, tmp);
tmp = cl_get_string(request, F_CIB_CALLOPTS);
ha_msg_add(*reply, F_CIB_CALLOPTS, tmp);
crm_debug_4("Attaching output if necessary");
if(output != NULL) {
add_message_xml(*reply, F_CIB_CALLDATA, output);
} else {
crm_debug_3("No output for call %s", call_id);
}
crm_debug_4("Cleaning up");
if(cib_diff != NULL) {
*cib_diff = local_diff;
} else {
free_xml(local_diff);
}
free_xml(output);
free_xml(input_fragment);
return rc;
}
int
send_via_callback_channel(HA_Message *msg, const char *token)
{
cib_client_t *hash_client = NULL;
GList *list_item = NULL;
enum cib_errors rc = cib_ok;
crm_debug_3("Delivering msg %p to client %s", msg, token);
if(token == NULL) {
crm_err("No client id token, cant send message");
if(rc == cib_ok) {
rc = cib_missing;
}
} else {
/* A client that left before we could reply is not really
* _our_ error. Warn instead.
*/
hash_client = g_hash_table_lookup(client_list, token);
if(hash_client == NULL) {
crm_warn("Cannot find client for token %s", token);
rc = cib_client_gone;
} else if(hash_client->channel == NULL) {
crm_err("Cannot find channel for client %s", token);
rc = cib_client_corrupt;
} else if(hash_client->channel->ops->get_chan_status(
hash_client->channel) != IPC_CONNECT) {
crm_warn("Client %s has disconnected", token);
rc = cib_client_gone;
}
}
/* this is a more important error so overwriting rc is warrented */
if(msg == NULL) {
crm_err("No message to send");
rc = cib_reply_failed;
}
if(rc == cib_ok) {
list_item = g_list_find_custom(
hash_client->delegated_calls, msg, cib_GCompareFunc);
}
if(list_item != NULL) {
/* remove it - no need to time it out */
HA_Message *orig_msg = list_item->data;
crm_debug_3("Removing msg from delegated list");
hash_client->delegated_calls = g_list_remove(
hash_client->delegated_calls, orig_msg);
CRM_DEV_ASSERT(orig_msg != msg);
crm_msg_del(orig_msg);
}
if(rc == cib_ok) {
crm_debug_3("Delivering reply to client %s", token);
if(send_ipc_message(hash_client->channel, msg) == FALSE) {
crm_warn("Delivery of reply to client %s/%s failed",
hash_client->name, token);
rc = cib_reply_failed;
}
} else {
/* be consistent...
* send_ipc_message() will free the message, so we should do
* so manually if we dont try to send it.
*/
crm_msg_del(msg);
}
return rc;
}
gint cib_GCompareFunc(gconstpointer a, gconstpointer b)
{
const HA_Message *a_msg = a;
const HA_Message *b_msg = b;
int msg_a_id = 0;
int msg_b_id = 0;
ha_msg_value_int(a_msg, F_CIB_CALLID, &msg_a_id);
ha_msg_value_int(b_msg, F_CIB_CALLID, &msg_b_id);
if(msg_a_id == msg_b_id) {
return 0;
} else if(msg_a_id < msg_b_id) {
return -1;
}
return 1;
}
gboolean
cib_msg_timeout(gpointer data)
{
crm_debug_4("Checking if any clients have timed out messages");
g_hash_table_foreach(client_list, cib_GHFunc, NULL);
return TRUE;
}
void
cib_GHFunc(gpointer key, gpointer value, gpointer user_data)
{
cib_client_t *client = value;
GListPtr list = client->delegated_calls;
HA_Message *msg = NULL;
while(list != NULL) {
int seen = 0;
int timeout = 5; /* 1 iteration == 1 seconds */
HA_Message *reply = NULL;
const char *host_to = NULL;
msg = list->data;
ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen);
ha_msg_value_int(msg, F_CIB_TIMEOUT, &timeout);
host_to = cl_get_string(msg, F_CIB_HOST);
crm_debug_4("Timeout %d, seen %d", timeout, seen);
if(timeout > 0 && seen < timeout) {
int seen2 = 0;
crm_debug_4("Updating seen count for msg from client %s",
client->id);
seen++;
ha_msg_mod_int(msg, F_CIB_SEENCOUNT, seen);
ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen2);
list = list->next;
continue;
}
crm_warn("Sending operation timeout msg to client %s",
client->id);
reply = ha_msg_new(4);
ha_msg_add(reply, F_TYPE, T_CIB);
ha_msg_add(reply, F_CIB_OPERATION,
cl_get_string(msg, F_CIB_OPERATION));
ha_msg_add(reply, F_CIB_CALLID,
cl_get_string(msg, F_CIB_CALLID));
if(host_to == NULL) {
ha_msg_add_int(reply, F_CIB_RC, cib_master_timeout);
} else {
ha_msg_add_int(reply, F_CIB_RC, cib_remote_timeout);
}
send_ipc_message(client->channel, reply);
list = list->next;
client->delegated_calls = g_list_remove(
client->delegated_calls, msg);
crm_msg_del(msg);
}
}
gboolean
cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client)
{
if (channel->ch_status != IPC_CONNECT && cib_client != NULL) {
crm_info("Cleaning up after %s channel disconnect from client (%p) %s/%s",
cib_client->channel_name, cib_client,
crm_str(cib_client->id), crm_str(cib_client->name));
if(cib_client->id != NULL) {
g_hash_table_remove(client_list, cib_client->id);
}
if(cib_client->source != NULL) {
crm_debug_3("deleting the IPC Channel");
G_main_del_IPC_Channel(cib_client->source);
cib_client->source = NULL;
}
crm_debug_3("Freeing the cib client %s", crm_str(cib_client->id));
#if 0
/* todo - put this back in once i recheck its safe */
crm_free(cib_client->callback_id);
crm_free(cib_client->name);
crm_free(cib_client->id);
#endif
crm_free(cib_client);
crm_debug_3("Freed the cib client");
return FALSE;
} else if (channel->ch_status != IPC_CONNECT) {
crm_warn("Unknown client disconnected");
return FALSE;
}
return TRUE;
}
gboolean
cib_ha_dispatch(IPC_Channel *channel, gpointer user_data)
{
int lpc = 0;
ll_cluster_t *hb_cluster = (ll_cluster_t*)user_data;
while(lpc < 2 && hb_cluster->llc_ops->msgready(hb_cluster)) {
lpc++;
/* invoke the callbacks but dont block */
hb_cluster->llc_ops->rcvmsg(hb_cluster, 0);
}
crm_debug_4("%d HA messages dispatched", lpc);
if (channel && (channel->ch_status != IPC_CONNECT)) {
crm_crit("Lost connection to heartbeat service... exiting");
exit(100);
return FALSE;
}
return TRUE;
}
void
cib_peer_callback(const HA_Message * msg, void* private_data)
{
int call_type = 0;
const char *originator = cl_get_string(msg, F_ORIG);
const char *seq = cl_get_string(msg, F_SEQ);
const char *op = cl_get_string(msg, F_CIB_OPERATION);
crm_log_message_adv(LOG_MSG, "Peer[inbound]", msg);
crm_debug_2("Operation %s from peer %s", op, originator);
if(originator == NULL || safe_str_eq(originator, cib_our_uname)) {
crm_debug_3("Discarding message %s/%s from ourselves",
cl_get_string(msg, F_CIB_CLIENTID),
cl_get_string(msg, F_CIB_CALLID));
return;
} else if(ccm_membership == NULL) {
crm_debug_2("Discarding message %s/%s:"
" membership not established",
originator, seq);
return;
} else if(g_hash_table_lookup(ccm_membership, originator) == NULL) {
crm_debug_2("Discarding message %s/%s: not in our membership",
originator, cl_get_string(msg, F_CIB_CALLID));
return;
} else if(cib_get_operation_id(msg, &call_type) != cib_ok) {
crm_err("Invalid operation... discarding msg %s", seq);
return;
}
crm_debug("Processing msg %s (%s) from peer %s", seq, op, originator);
cib_process_request(msg, TRUE, TRUE, NULL);
return;
}
HA_Message *
cib_msg_copy(const HA_Message *msg, gboolean with_data)
{
int lpc = 0;
const char *field = NULL;
const char *value = NULL;
const HA_Message *value_struct = NULL;
const char *field_list[] = {
F_TYPE ,
F_CIB_CLIENTID ,
F_CIB_CALLOPTS ,
F_CIB_CALLID ,
F_CIB_OPERATION ,
F_CIB_ISREPLY ,
F_CIB_SECTION ,
F_CIB_HOST ,
F_CIB_RC ,
F_CIB_DELEGATED ,
F_CIB_OBJID ,
F_CIB_OBJTYPE ,
F_CIB_EXISTING ,
F_CIB_SEENCOUNT ,
F_CIB_TIMEOUT ,
F_CIB_CALLBACK_TOKEN ,
F_CIB_GLOBAL_UPDATE ,
F_CIB_CLIENTNAME ,
F_CIB_NOTIFY_TYPE ,
F_CIB_NOTIFY_ACTIVATE
};
const char *data_list[] = {
F_CIB_CALLDATA ,
F_CIB_UPDATE ,
F_CIB_UPDATE_RESULT
};
HA_Message *copy = ha_msg_new(10);
if(copy == NULL) {
return copy;
}
for(lpc = 0; lpc < DIMOF(field_list); lpc++) {
field = field_list[lpc];
value = cl_get_string(msg, field);
if(value != NULL) {
ha_msg_add(copy, field, value);
}
}
for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) {
field = data_list[lpc];
value_struct = cl_get_struct(msg, field);
if(value_struct != NULL) {
ha_msg_addstruct(copy, field, value_struct);
}
}
return copy;
}
enum cib_errors
cib_get_operation_id(const HA_Message * msg, int *operation)
{
int lpc = 0;
int max_msg_types = DIMOF(cib_server_ops);
const char *op = cl_get_string(msg, F_CIB_OPERATION);
for (lpc = 0; lpc < max_msg_types; lpc++) {
if (safe_str_eq(op, cib_server_ops[lpc].operation)) {
*operation = lpc;
return cib_ok;
}
}
crm_err("Operation %s is not valid", op);
*operation = -1;
return cib_operation;
}
void
cib_client_status_callback(const char * node, const char * client,
const char * status, void * private)
{
if(safe_str_eq(client, CRM_SYSTEM_CIB)) {
crm_debug_2("Status update: Client %s/%s now has status [%s]",
node, client, status);
g_hash_table_replace(peer_hash, crm_strdup(node), crm_strdup(status));
set_connected_peers(the_cib);
}
return;
}
extern oc_ev_t *cib_ev_token;
gboolean
ccm_manual_check(gpointer data)
{
int rc = 0;
oc_ev_t *ccm_token = cib_ev_token;
crm_debug("manual check");
rc = oc_ev_handle_event(ccm_token);
if(0 == rc) {
return TRUE;
} else {
crm_err("CCM connection appears to have failed: rc=%d.", rc);
return FALSE;
}
}
gboolean cib_ccm_dispatch(int fd, gpointer user_data)
{
int rc = 0;
oc_ev_t *ccm_token = (oc_ev_t*)user_data;
crm_debug("received callback");
rc = oc_ev_handle_event(ccm_token);
if(0 == rc) {
return TRUE;
} else {
crm_err("CCM connection appears to have failed: rc=%d.", rc);
return FALSE;
}
}
void
cib_ccm_msg_callback(
oc_ed_t event, void *cookie, size_t size, const void *data)
{
int instance = -1;
gboolean update_id = FALSE;
gboolean update_quorum = FALSE;
const oc_ev_membership_t *membership = data;
if(membership != NULL) {
instance = membership->m_instance;
}
crm_info("Process CCM event=%s (id=%d)",
ccm_event_name(event), instance);
switch(event) {
case OC_EV_MS_NEW_MEMBERSHIP:
case OC_EV_MS_INVALID:
update_id = TRUE;
update_quorum = TRUE;
break;
case OC_EV_MS_PRIMARY_RESTORED:
update_id = TRUE;
break;
case OC_EV_MS_NOT_PRIMARY:
crm_debug("Ignoring transitional CCM event: %s",
ccm_event_name(event));
break;
case OC_EV_MS_EVICTED:
crm_err("Evicted from CCM: %s", ccm_event_name(event));
update_quorum = TRUE;
break;
default:
crm_err("Unknown CCM event: %d", event);
}
if(update_id) {
CRM_DEV_ASSERT(membership != NULL);
if(crm_assert_failed) { return; }
if(ccm_transition_id != NULL) {
crm_free(ccm_transition_id);
ccm_transition_id = NULL;
}
ccm_transition_id = crm_itoa(instance);
set_transition(the_cib);
}
if(update_quorum) {
unsigned int members = 0;
int offset = 0;
unsigned int lpc = 0;
cib_have_quorum = ccm_have_quorum(event);
if(cib_have_quorum) {
crm_xml_add(
the_cib,XML_ATTR_HAVE_QUORUM,XML_BOOLEAN_TRUE);
} else {
crm_xml_add(
the_cib,XML_ATTR_HAVE_QUORUM,XML_BOOLEAN_FALSE);
}
crm_info("Quorum %s after event=%s (id=%d)",
cib_have_quorum?"(re)attained":"lost",
ccm_event_name(event), instance);
if(ccm_membership != NULL) {
g_hash_table_foreach_remove(
ccm_membership, ghash_str_clfree, NULL);
}
ccm_membership = g_hash_table_new(g_str_hash, g_str_equal);
if(membership != NULL) {
members = membership->m_n_member;
offset = membership->m_memb_idx;
}
for(lpc = 0; lpc < members; lpc++) {
oc_node_t a_node = membership->m_array[lpc+offset];
char *uname = crm_strdup(a_node.node_uname);
g_hash_table_insert(
ccm_membership, uname, uname);
}
}
oc_ev_callback_done(cookie);
return;
}
gboolean
ghash_str_clfree(gpointer key, gpointer value, gpointer user_data)
{
if(key != NULL) {
crm_free(key);
}
return TRUE;
}
gboolean
can_write(int flags)
{
const char *value = NULL;
if(cib_have_quorum) {
return TRUE;
}
value = get_crm_option(the_cib, "no_quorum_policy", TRUE);
if(safe_str_eq(value, "ignore")) {
return TRUE;
}
if((flags & cib_quorum_override) != 0) {
crm_debug_2("Overriding \"no quorum\" condition");
return TRUE;
}
return FALSE;
}
diff --git a/crm/cib/cibprimatives.h b/crm/cib/cibprimatives.h
index 3aa2d5d6a2..b173081dcd 100644
--- a/crm/cib/cibprimatives.h
+++ b/crm/cib/cibprimatives.h
@@ -1,81 +1,79 @@
-/* $Id: cibprimatives.h,v 1.12 2005/07/11 12:13:07 andrew Exp $ */
+/* $Id: cibprimatives.h,v 1.13 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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_PRIMATIVES__H
#define CIB_PRIMATIVES__H
#include <portability.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>
#define IS_DAEMON
#define IPC_COMMS
typedef crm_data_t cibStatus;
typedef crm_data_t cibResource;
typedef crm_data_t cibConstraint;
typedef crm_data_t cibHaNode;
/* extern gboolean initialized; */
/* extern crm_data_t *the_cib; */
/* extern crm_data_t *node_search; */
/* extern crm_data_t *resource_search; */
/* extern crm_data_t *constraint_search; */
/* extern crm_data_t *status_search; */
/* extern const char* crm_system_name; */
extern crm_data_t *get_the_CIB(void);
extern int addResource (crm_data_t *cib, crm_data_t *anXmlNode);
extern int addConstraint(crm_data_t *cib, crm_data_t *anXmlNode);
extern int addHaNode (crm_data_t *cib, crm_data_t *anXmlNode);
extern int addStatus (crm_data_t *cib, crm_data_t *anXmlNode);
extern crm_data_t *findResource (crm_data_t *cib, const char *id);
extern crm_data_t *findConstraint(crm_data_t *cib, const char *id);
extern crm_data_t *findHaNode (crm_data_t *cib, const char *id);
extern crm_data_t *findStatus (crm_data_t *cib, const char *id);
extern int updateResource (crm_data_t *cib, crm_data_t *anXmlNode);
extern int updateConstraint(crm_data_t *cib, crm_data_t *anXmlNode);
extern int updateHaNode (crm_data_t *cib, crm_data_t *anXmlNode);
extern int updateStatus (crm_data_t *cib, crm_data_t *anXmlNode);
extern int delResource (crm_data_t *cib, crm_data_t *delete_spec);
extern int delConstraint(crm_data_t *cib, crm_data_t *delete_spec);
extern int delHaNode (crm_data_t *cib, crm_data_t *delete_spec);
extern int delStatus (crm_data_t *cib, crm_data_t *delete_spec);
extern int add_cib_object (crm_data_t *parent, crm_data_t *new_obj);
extern int delete_cib_object(crm_data_t *parent, crm_data_t *delete_spec);
extern int update_cib_object(crm_data_t *parent, crm_data_t *new_obj);
-void do_id_check(crm_data_t *xml_obj);
-
#endif
diff --git a/crm/cib/io.c b/crm/cib/io.c
index 88ed4adf18..d61d5c1697 100644
--- a/crm/cib/io.c
+++ b/crm/cib/io.c
@@ -1,528 +1,528 @@
-/* $Id: io.c,v 1.30 2005/09/02 12:31:07 andrew Exp $ */
+/* $Id: io.c,v 1.31 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 <portability.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.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/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <clplumbing/cl_misc.h>
#include <cibprimatives.h>
#include <crm/dmalloc_wrapper.h>
const char * local_resource_path[] =
{
XML_CIB_TAG_STATUS,
};
const char * resource_path[] =
{
XML_CIB_TAG_RESOURCES,
};
const char * node_path[] =
{
XML_CIB_TAG_NODES,
};
const char * constraint_path[] =
{
XML_CIB_TAG_CONSTRAINTS,
};
gboolean initialized = FALSE;
crm_data_t *the_cib = NULL;
crm_data_t *node_search = NULL;
crm_data_t *resource_search = NULL;
crm_data_t *constraint_search = NULL;
crm_data_t *status_search = NULL;
gboolean cib_writes_enabled = TRUE;
extern char *ccm_transition_id;
extern gboolean cib_have_quorum;
extern GHashTable *peer_hash;
int set_connected_peers(crm_data_t *xml_obj);
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data);
/*
* It is the callers responsibility to free the output of this function
*/
crm_data_t*
readCibXml(char *buffer)
{
crm_data_t *root = NULL;
if(buffer != NULL) {
root = string2xml(buffer);
}
- do_id_check(root);
+ do_id_check(root, NULL);
if (verifyCibXml(root) == FALSE) {
free_xml(root);
root = createEmptyCib();
crm_xml_add(root, XML_ATTR_GENERATION_ADMIN, "0");
crm_xml_add(root, XML_ATTR_GENERATION, "0");
crm_xml_add(root, XML_ATTR_NUMUPDATES, "0");
}
return root;
}
/*
* It is the callers responsibility to free the output of this function
*/
crm_data_t*
readCibXmlFile(const char *filename)
{
int s_res = -1;
struct stat buf;
crm_data_t *root = NULL;
if(filename != NULL) {
s_res = stat(filename, &buf);
}
if (s_res == 0) {
FILE *cib_file = fopen(filename, "r");
root = file2xml(cib_file);
crm_xml_add(root, "generated", XML_BOOLEAN_FALSE);
fclose(cib_file);
} else {
crm_warn("Stat of (%s) failed, file does not exist.",
CIB_FILENAME);
}
if(root != NULL) {
int lpc = 0;
const char *value = NULL;
const char *name = NULL;
crm_data_t *status = get_object_root(XML_CIB_TAG_STATUS, root);
for (; status != NULL && lpc < status->nfields; ) {
if(status->types[lpc] != FT_STRUCT) {
lpc++;
continue;
}
CRM_DEV_ASSERT(cl_msg_remove_offset(status, lpc) == HA_OK);
/* dont get stuck in an infinite loop */
if(crm_assert_failed) {
lpc++;
}
}
name = XML_ATTR_GENERATION_ADMIN;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add(root, name, "0");
}
name = XML_ATTR_GENERATION;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add(root, name, "0");
}
name = XML_ATTR_NUMUPDATES;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add(root, name, "0");
}
- do_id_check(root);
+ do_id_check(root, NULL);
}
if (verifyCibXml(root) == FALSE) {
free_xml(root);
root = NULL;
}
return root;
}
/*
* The caller should never free the return value
*/
crm_data_t*
get_the_CIB(void)
{
return the_cib;
}
gboolean
uninitializeCib(void)
{
crm_data_t *tmp_cib = the_cib;
if(tmp_cib == NULL) {
crm_err("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_err("Deallocating the CIB.");
free_xml(tmp_cib);
crm_err("The CIB has been deallocated.");
return TRUE;
}
/*
* This method will not free the old CIB pointer or the new one.
* We rely on the caller to have saved a pointer to the old CIB
* and to free the old/bad one depending on what is appropriate.
*/
gboolean
initializeCib(crm_data_t *new_cib)
{
#if 0
if(new_cib != NULL) {
crm_set_element_parent(new_cib, NULL);
}
#endif
if (verifyCibXml(new_cib)) {
initialized = FALSE;
the_cib = new_cib;
/* update search paths */
/* not used yet...
node_search =
get_object_root(XML_CIB_TAG_NODES, new_cib);
resource_search =
get_object_root(XML_CIB_TAG_RESOURCES, new_cib);
constraint_search =
get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib);
status_search =
get_object_root(XML_CIB_TAG_STATUS, new_cib);
*/
initialized = TRUE;
}
if(initialized == FALSE) {
crm_warn("CIB Verification failed");
the_cib = NULL;
} else {
const char *option = "suppress_cib_writes";
const char *value = NULL;
crm_data_t *config = get_object_root(
XML_CIB_TAG_CRMCONFIG, new_cib);
crm_data_t * a_default = find_entity(
config, XML_CIB_TAG_NVPAIR, option);
if(a_default != NULL) {
value = crm_element_value(
a_default, XML_NVPAIR_ATTR_VALUE);
}
if(value == NULL) {
crm_warn("Option %s not set", option);
if(cib_writes_enabled == FALSE) {
crm_debug("Disk writes to %s enabled",
CIB_FILENAME);
}
cib_writes_enabled = TRUE;
} else {
gboolean suppress = FALSE;
cl_str_to_boolean(value, &suppress);
if(cib_writes_enabled == suppress) {
cib_writes_enabled = !suppress;
if(cib_writes_enabled) {
crm_debug("Disk writes to %s enabled",
CIB_FILENAME);
} else {
crm_notice("Disabling CIB disk writes");
}
}
}
crm_debug_2("Disk writes to %s %s", CIB_FILENAME,
cib_writes_enabled?"enabled":"DISABLED");
set_connected_peers(the_cib);
set_transition(the_cib);
if(cib_have_quorum) {
crm_xml_add(
the_cib,XML_ATTR_HAVE_QUORUM,XML_BOOLEAN_TRUE);
} else {
crm_xml_add(
the_cib,XML_ATTR_HAVE_QUORUM,XML_BOOLEAN_FALSE);
}
}
return initialized;
}
int
moveFile(const char *oldname,
const char *newname,
gboolean backup,
char *ext)
{
/* move 'oldname' to 'newname' by creating a hard link to it
* and then removing the original hard link
*/
int res = 0;
struct stat tmp;
int s_res = stat(newname, &tmp);
if (s_res >= 0)
{
if (backup == TRUE) {
char backname[1024];
static const char *back_ext = "bak";
if (ext != NULL) { back_ext = (char*)ext; }
snprintf(backname, sizeof(backname)-1,
"%s.%s", newname, back_ext);
moveFile(newname, backname, FALSE, NULL);
} else {
res = unlink(newname);
if (res < 0) {
perror("Could not remove the current backup of Cib");
return -1;
}
}
}
s_res = stat(oldname, &tmp);
if (s_res >= 0) {
res = link(oldname, newname);
if (res < 0) {
perror("Could not create backup of current Cib");
return -2;
}
res = unlink(oldname);
if (res < 0) {
perror("Could not unlink the current Cib");
return -3;
}
}
return 0;
}
int
activateCibBuffer(char *buffer, const char *filename)
{
int result = -1;
crm_data_t *local_cib = NULL;
local_cib = readCibXml(buffer);
result = activateCibXml(local_cib, filename);
return result;
}
/*
* This method will free the old CIB pointer on success and the new one
* on failure.
*/
#define ACTIVATION_DIFFS 0
int
activateCibXml(crm_data_t *new_cib, const char *filename)
{
int error_code = cib_ok;
crm_data_t *diff = NULL;
crm_data_t *saved_cib = get_the_CIB();
const char *filename_bak = CIB_BACKUP; /* calculate */
crm_log_xml_debug_4(new_cib, "Attempting to activate CIB");
CRM_ASSERT(new_cib != saved_cib);
crm_validate_data(new_cib);
if(saved_cib != NULL) {
crm_validate_data(saved_cib);
}
if (initializeCib(new_cib) == FALSE) {
crm_warn("Ignoring invalid or NULL Cib");
error_code = -5;
} else if(cib_writes_enabled) {
if(saved_cib != NULL) {
CRM_DEV_ASSERT(0 >= moveFile(filename,
filename_bak,
FALSE, NULL));
if (crm_assert_failed) {
crm_warn("Could not make backup of the current"
" Cib... aborting update.");
error_code = -1;
}
}
if(error_code == cib_ok) {
crm_debug_3("Writing CIB out to %s", CIB_FILENAME);
CRM_DEV_ASSERT(new_cib != NULL);
CRM_DEV_ASSERT(write_xml_file(
new_cib, CIB_FILENAME) >= 0);
if (crm_assert_failed) {
error_code = -4;
}
}
if(error_code == -4 && saved_cib != NULL) {
CRM_DEV_ASSERT(moveFile(filename_bak,
filename, FALSE, NULL) >= 0);
if (crm_assert_failed){
crm_crit("Could not restore the backup of the "
" current Cib... panic!");
error_code = -2;
/* should probably exit here */
}
}
CRM_DEV_ASSERT(saved_cib != NULL || error_code == cib_ok);
if(crm_assert_failed) {
/* oh we are so dead */
crm_crit("Could not write out new CIB and no saved"
" version to revert to");
error_code = -3;
} else if(error_code != cib_ok) {
crm_crit("Update of Cib failed (%d)... reverting"
" to last known valid version",
error_code);
CRM_DEV_ASSERT(initializeCib(saved_cib));
if (crm_assert_failed) {
/* oh we are so dead */
crm_crit("Could not re-initialize with the old"
" CIB. Can anyone say corruption?");
error_code = -3;
}
}
}
#if ACTIVATION_DIFFS
/* Make sure memory is cleaned up appropriately */
if(saved_cib != NULL && new_cib != NULL) {
diff = diff_cib_object(saved_cib, new_cib, -1);
}
if (error_code != cib_ok) {
crm_err("Changes could not be activated: %s",
cib_error2string(error_code));
log_cib_diff(LOG_WARNING, diff, __FUNCTION__);
free_xml(new_cib);
} else if(saved_cib != NULL) {
crm_debug_2("Changes activated");
log_cib_diff(LOG_DEBUG, diff, __FUNCTION__);
crm_validate_data(saved_cib);
free_xml(saved_cib);
}
free_xml(diff);
#else
if (error_code != cib_ok) {
crm_err("Changes could not be activated: %s",
cib_error2string(error_code));
free_xml(new_cib);
} else if(saved_cib != NULL) {
crm_debug_2("Changes activated");
crm_validate_data(saved_cib);
free_xml(saved_cib);
}
#endif
diff = NULL;
return error_code;
}
void
set_transition(crm_data_t *xml_obj)
{
const char *current = crm_element_value(
xml_obj, XML_ATTR_CCM_TRANSITION);
if(safe_str_neq(current, ccm_transition_id)) {
crm_debug("Set transition to %s", ccm_transition_id);
crm_xml_add(the_cib, XML_ATTR_CCM_TRANSITION,ccm_transition_id);
}
}
int
set_connected_peers(crm_data_t *xml_obj)
{
int active = 0;
int current = 0;
char *peers_s = NULL;
const char *current_s = crm_element_value(
xml_obj, XML_ATTR_NUMPEERS);
g_hash_table_foreach(peer_hash, GHFunc_count_peers, &active);
current = crm_atoi(current_s, "0");
if(current != active) {
peers_s = crm_itoa(active);
crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s);
crm_debug("Set peers to %s", peers_s);
crm_free(peers_s);
}
return active;
}
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/crm/cib/primatives.c b/crm/cib/primatives.c
index d1bd233ad4..d2e953f1be 100644
--- a/crm/cib/primatives.c
+++ b/crm/cib/primatives.c
@@ -1,633 +1,566 @@
-/* $Id: primatives.c,v 1.26 2005/08/25 08:26:48 andrew Exp $ */
+/* $Id: primatives.c,v 1.27 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 <portability.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/cl_log.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <cibprimatives.h>
#include <notify.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/dmalloc_wrapper.h>
/*
* In case of confusion, this is the memory management policy for
* all functions in this file.
*
* All add/modify functions use copies of supplied data.
* It is therefore appropriate that the callers free the supplied data
* at some point after the function has finished.
*
* All delete functions will handle the freeing of deleted data
* but not the function arguments.
*/
void update_node_state(crm_data_t *existing_node, crm_data_t *update);
/* --- Resource */
int
addResource(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Adding " XML_CIB_TAG_RESOURCE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_RESOURCES, cib);
return add_cib_object(root, anXmlNode);
}
crm_data_t*
findResource(crm_data_t *cib, const char *id)
{
crm_data_t *root = NULL, *ret = NULL;
root = get_object_root(XML_CIB_TAG_RESOURCES, cib);
ret = find_entity(root, XML_CIB_TAG_RESOURCE, id);
return ret;
}
int
updateResource(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root = NULL;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Updating " XML_CIB_TAG_RESOURCE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_RESOURCES, cib);
return update_cib_object(root, anXmlNode);
}
int
delResource(crm_data_t *cib, crm_data_t *delete_spec)
{
const char *id = ID(delete_spec);
crm_data_t *root;
if(id == NULL || strlen(id) == 0) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Deleting " XML_CIB_TAG_RESOURCE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_RESOURCES, cib);
return delete_cib_object(root, delete_spec);
}
/* --- Constraint */
int
addConstraint(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Adding " XML_CIB_TAG_CONSTRAINT " (%s)...", id);
root = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
return add_cib_object(root, anXmlNode);
}
crm_data_t*
findConstraint(crm_data_t *cib, const char *id)
{
crm_data_t *root = NULL, *ret = NULL;
root = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
ret = find_entity(root, XML_CIB_TAG_CONSTRAINT, id);
return ret;
}
int
updateConstraint(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Updating " XML_CIB_TAG_CONSTRAINT " (%s)...", id);
root = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
return update_cib_object(root, anXmlNode);
}
int
delConstraint(crm_data_t *cib, crm_data_t *delete_spec)
{
const char *id = ID(delete_spec);
crm_data_t *root;
if(id == NULL || strlen(id) == 0) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Deleting " XML_CIB_TAG_CONSTRAINT " (%s)...", id);
root = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
return delete_cib_object(root, delete_spec);
}
/* --- HaNode */
int
addHaNode(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Adding " XML_CIB_TAG_NODE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_NODES, cib);
return add_cib_object(root, anXmlNode);
}
crm_data_t*
findHaNode(crm_data_t *cib, const char *id)
{
crm_data_t *root = NULL, *ret = NULL;
root = get_object_root(XML_CIB_TAG_NODES, cib);
ret = find_entity(root, XML_CIB_TAG_NODE, id);
return ret;
}
int
updateHaNode(crm_data_t *cib, cibHaNode *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Updating " XML_CIB_TAG_NODE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_NODES, cib);
return update_cib_object(root, anXmlNode);
}
int
delHaNode(crm_data_t *cib, crm_data_t *delete_spec)
{
const char *id = ID(delete_spec);
crm_data_t *root;
if(id == NULL || strlen(id) == 0) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Deleting " XML_CIB_TAG_NODE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
return delete_cib_object(root, delete_spec);
}
/* --- Status */
int
addStatus(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Adding " XML_CIB_TAG_NODE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_STATUS, cib);
return add_cib_object(root, anXmlNode);
}
crm_data_t*
findStatus(crm_data_t *cib, const char *id)
{
crm_data_t *root = NULL, *ret = NULL;
root = get_object_root(XML_CIB_TAG_STATUS, cib);
ret = find_entity(root, XML_CIB_TAG_STATE, id);
return ret;
}
int
updateStatus(crm_data_t *cib, crm_data_t *anXmlNode)
{
const char *id = ID(anXmlNode);
crm_data_t *root;
if (id == NULL || strlen(id) < 1) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Updating " XML_CIB_TAG_NODE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_STATUS, cib);
return update_cib_object(root, anXmlNode);
}
int
delStatus(crm_data_t *cib, crm_data_t *delete_spec)
{
const char *id = ID(delete_spec);
crm_data_t *root;
if(id == NULL || strlen(id) == 0) {
return CIBRES_MISSING_ID;
}
crm_debug_2("Deleting " XML_CIB_TAG_STATE " (%s)...", id);
root = get_object_root(XML_CIB_TAG_STATUS, cib);
return delete_cib_object(root, delete_spec);
}
int
delete_cib_object(crm_data_t *parent, crm_data_t *delete_spec)
{
const char *object_name = NULL;
const char *object_id = NULL;
crm_data_t *equiv_node = NULL;
int result = cib_ok;
if(delete_spec != NULL) {
object_name = crm_element_name(delete_spec);
}
object_id = crm_element_value(delete_spec, XML_ATTR_ID);
crm_debug_2("Processing: <%s id=%s>",
crm_str(object_name), crm_str(object_id));
if(delete_spec == NULL) {
result = cib_NOOBJECT;
} else if(parent == NULL) {
result = cib_NOPARENT;
} else if(object_id == NULL) {
/* placeholder object */
equiv_node = find_xml_node(parent, object_name, FALSE);
} else {
equiv_node = find_entity(parent, object_name, object_id);
}
if(result != cib_ok) {
; /* nothing */
} else if(equiv_node == NULL) {
result = cib_NOTEXISTS;
} else if(xml_has_children(delete_spec) == FALSE) {
/* only leaves are deleted */
crm_debug("Removing leaf: <%s id=%s>",
crm_str(object_name), crm_str(object_id));
zap_xml_from_parent(parent, equiv_node);
} else {
xml_child_iter(
delete_spec, child, NULL,
int tmp_result = delete_cib_object(equiv_node, child);
/* only the first error is likely to be interesting */
if(result == cib_ok) {
result = tmp_result;
}
);
}
return result;
}
int
add_cib_object(crm_data_t *parent, crm_data_t *new_obj)
{
enum cib_errors result = cib_ok;
const char *object_name = NULL;
const char *object_id = NULL;
crm_data_t *equiv_node = NULL;
if(new_obj != NULL) {
object_name = crm_element_name(new_obj);
}
object_id = crm_element_value(new_obj, XML_ATTR_ID);
if(new_obj == NULL) {
result = cib_NOOBJECT;
} else if(parent == NULL) {
result = cib_NOPARENT;
} else if(object_id == NULL) {
/* placeholder object */
equiv_node = find_xml_node(parent, object_name, FALSE);
} else {
equiv_node = find_entity(parent, object_name, object_id);
}
if(result != cib_ok) {
; /* do nothing */
} else if(equiv_node != NULL) {
result = cib_EXISTS;
} else if(add_node_copy(parent, new_obj) == NULL) {
result = cib_NODECOPY;
}
return result;
}
int
update_cib_object(crm_data_t *parent, crm_data_t *update)
{
const char *replace = NULL;
const char *object_name = NULL;
const char *object_id = NULL;
crm_data_t *target = NULL;
int result = cib_ok;
CRM_DEV_ASSERT(update != NULL);
if(crm_assert_failed) { return cib_NOOBJECT; }
CRM_DEV_ASSERT(parent != NULL);
if(crm_assert_failed) { return cib_NOPARENT; }
object_name = crm_element_name(update);
object_id = ID(update);
CRM_DEV_ASSERT(object_name != NULL);
if(crm_assert_failed) { return cib_NOOBJECT; }
if(object_id == NULL) {
/* placeholder object */
target = find_xml_node(parent, object_name, FALSE);
} else {
target = find_entity(parent, object_name, object_id);
}
if(target == NULL) {
target = add_node_copy(parent, update);
crm_debug_2("Added <%s id=%s>",
crm_str(object_name), crm_str(object_id));
CRM_DEV_ASSERT(target != NULL);
if(crm_assert_failed) { return cib_NODECOPY; }
return cib_ok;
}
crm_debug_2("Found node <%s id=%s> to update",
crm_str(object_name), crm_str(object_id));
replace = crm_element_value(update, XML_CIB_ATTR_REPLACE);
if(replace != NULL) {
crm_data_t *remove = find_xml_node(target, replace, FALSE);
if(remove != NULL) {
crm_debug_3("Replacing node <%s> in <%s>",
replace, crm_element_name(target));
zap_xml_from_parent(target, remove);
}
xml_remove_prop(update, XML_CIB_ATTR_REPLACE);
xml_remove_prop(target, XML_CIB_ATTR_REPLACE);
}
if(safe_str_eq(XML_CIB_TAG_STATE, object_name)){
update_node_state(target, update);
} else {
copy_in_properties(target, update);
}
CRM_DEV_ASSERT(cl_is_allocated(object_name));
if(object_id != NULL) {
CRM_DEV_ASSERT(cl_is_allocated(object_id));
}
crm_debug_3("Processing children of <%s id=%s>",
crm_str(object_name), crm_str(object_id));
xml_child_iter(
update, a_child, NULL,
int tmp_result = 0;
crm_debug_3("Updating child <%s id=%s>",
crm_element_name(a_child), ID(a_child));
tmp_result = update_cib_object(target, a_child);
/* only the first error is likely to be interesting */
if(tmp_result != cib_ok) {
crm_err("Error updating child <%s id=%s>",
crm_element_name(a_child), ID(a_child));
if(result == cib_ok) {
result = tmp_result;
}
}
);
crm_debug_3("Finished with <%s id=%s>",
crm_str(object_name), crm_str(object_id));
return result;
}
void
update_node_state(crm_data_t *target, crm_data_t *update)
{
const char *source = NULL;
gboolean any_updates = FALSE;
gboolean clear_stonith = FALSE;
gboolean clear_shutdown = FALSE;
xml_prop_iter(
update, local_prop_name, local_prop_value,
if(local_prop_name == NULL) {
/* error */
} else if(strcmp(local_prop_name, XML_ATTR_ID) == 0) {
} else if(strcmp(local_prop_name, XML_ATTR_TSTAMP) == 0) {
} else if(strcmp(local_prop_name, XML_CIB_ATTR_CLEAR_SHUTDOWN) == 0) {
clear_shutdown = TRUE;
} else if(strcmp(local_prop_name, XML_CIB_ATTR_CLEAR_STONITH) == 0) {
clear_stonith = TRUE;
clear_shutdown = TRUE;
} else if(strcmp(local_prop_name, XML_CIB_ATTR_SOURCE) == 0) {
source = local_prop_value;
} else {
any_updates = TRUE;
crm_xml_add(target, local_prop_name, local_prop_value);
}
);
xml_remove_prop(target, XML_CIB_ATTR_CLEAR_SHUTDOWN);
if(clear_shutdown) {
/* unset XML_CIB_ATTR_SHUTDOWN */
if(crm_element_value(target, XML_CIB_ATTR_SHUTDOWN) != NULL) {
crm_debug_2("Clearing %s", XML_CIB_ATTR_SHUTDOWN);
xml_remove_prop(target, XML_CIB_ATTR_SHUTDOWN);
any_updates = TRUE;
}
}
xml_remove_prop(target, XML_CIB_ATTR_CLEAR_STONITH);
if(clear_stonith) {
/* unset XML_CIB_ATTR_STONITH */
if(crm_element_value(target, XML_CIB_ATTR_STONITH) != NULL) {
crm_debug_2("Clearing %s", XML_CIB_ATTR_STONITH);
xml_remove_prop(target, XML_CIB_ATTR_STONITH);
any_updates = TRUE;
}
}
if(any_updates) {
set_node_tstamp(target);
crm_xml_add(target, XML_CIB_ATTR_SOURCE, source);
}
}
-void
-do_id_check(crm_data_t *xml_obj)
-{
- int lpc = 0;
- cl_uuid_t new_uuid;
- char *new_uuid_s = NULL;
- const char *tag_name = NULL;
- const char *new_uuid_s2 = NULL;
-
- const char *allowed_list[] = {
- XML_TAG_CIB,
- XML_CIB_TAG_NODES,
- XML_CIB_TAG_RESOURCES,
- XML_CIB_TAG_CONSTRAINTS,
- XML_CIB_TAG_STATUS,
- XML_CIB_TAG_LRM,
- XML_LRM_TAG_RESOURCES,
- "operations",
- };
-
- if(xml_obj == NULL) {
- return;
- }
-
- xml_child_iter(
- xml_obj, xml_child, NULL,
- do_id_check(xml_child);
- );
-
- tag_name = TYPE(xml_obj);
-
- xml_prop_iter(
- xml_obj, local_prop_name, local_prop_value,
- if(safe_str_eq(local_prop_name, XML_ATTR_ID)) {
- continue;
-
- } else if(ID(xml_obj) != NULL) {
- return;
- }
-
- for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) {
- if(safe_str_eq(tag_name, allowed_list[lpc])) {
- /* this tag is never meant to have an ID */
- return;
- }
- }
-
- /* create a UUID and assign it as the ID */
- crm_malloc0(new_uuid_s, sizeof(char)*38);
- cl_uuid_generate(&new_uuid);
- cl_uuid_unparse(&new_uuid, new_uuid_s);
-
- new_uuid_s2 = crm_xml_add(xml_obj, XML_ATTR_ID, new_uuid_s);
- crm_err("Object with attributes but no ID field detected."
- " Assigned: %s", ID(xml_obj));
- crm_log_xml_warn(xml_obj, "Updated object");
-
- CRM_DEV_ASSERT(cl_is_allocated(new_uuid_s));
- CRM_DEV_ASSERT(cl_is_allocated(new_uuid_s2));
-
- crm_free(new_uuid_s);
-
- CRM_DEV_ASSERT(cl_is_allocated(new_uuid_s2));
- return;
-
- );
-}
diff --git a/include/crm/common/util.h b/include/crm/common/util.h
index 14d96de828..f169b76554 100644
--- a/include/crm/common/util.h
+++ b/include/crm/common/util.h
@@ -1,110 +1,111 @@
-/* $Id: util.h,v 1.21 2005/08/08 12:05:02 andrew Exp $ */
+/* $Id: util.h,v 1.22 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 CRM_COMMON_UTIL__H
#define CRM_COMMON_UTIL__H
#include <signal.h>
#include <crm/common/xml.h>
#include <hb_api.h>
#include <ocf/oc_event.h>
#include <lrm/lrm_api.h>
#define DEBUG_INC SIGUSR1
#define DEBUG_DEC SIGUSR2
extern unsigned int crm_log_level;
extern gboolean crm_log_init(const char *entity);
extern void do_crm_log(int log_level, const char *file, const char *function,
const char *format, ...) G_GNUC_PRINTF(4,5);
/* returns the old value */
extern unsigned int set_crm_log_level(unsigned int level);
extern unsigned int get_crm_log_level(void);
extern char *crm_itoa(int an_int);
extern char *crm_strdup(const char *a);
extern char *generate_hash_key(const char *crm_msg_reference,
const char *sys);
extern char *generate_hash_value(const char *src_node,
const char *src_subsys);
extern gboolean decode_hash_value(gpointer value,
char **node,
char **subsys);
extern gboolean decodeNVpair(const char *srcstring,
char separator,
char **name,
char **value);
extern int compare_version(const char *version1, const char *version2);
extern char *generateReference(const char *custom1, const char *custom2);
extern void alter_debug(int nsig);
extern void g_hash_destroy_str(gpointer data);
extern void set_uuid(
ll_cluster_t* hb, crm_data_t *node, const char *attr, const char *uname);
extern void crm_set_ha_options(ll_cluster_t *hb_cluster);
extern gboolean crm_is_true(const char * s);
extern int crm_str_to_boolean(const char * s, int * ret);
extern long crm_get_msec(const char * input);
extern gboolean ccm_have_quorum(oc_ed_t event);
extern const char *ccm_event_name(oc_ed_t event);
extern const char *op_status2text(op_status_t status);
extern char *generate_op_key(
const char *rsc_id, const char *op_type, int interval);
extern char *generate_notify_key(
const char *rsc_id, const char *notify_type, const char *op_type);
extern gboolean crm_mem_stats(volatile cl_mem_stats_t *mem_stats);
extern void crm_zero_mem_stats(volatile cl_mem_stats_t *stats);
extern char *generate_transition_magic(
const char *transition_key, int op_status);
extern gboolean decode_transition_magic(
const char *magic, char **uuid, int *transition_id, int *op_status);
extern char *generate_transition_key(int transition_id, const char *node);
extern gboolean decode_transition_key(
const char *key, char **uuid, int *transition_id);
+extern char *crm_concat(const char *prefix, const char *suffix, char join);
#endif
diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h
index 1b2787c406..0bbec5f6f3 100644
--- a/include/crm/common/xml.h
+++ b/include/crm/common/xml.h
@@ -1,281 +1,282 @@
-/* $Id: xml.h,v 1.28 2005/08/09 07:58:10 andrew Exp $ */
+/* $Id: xml.h,v 1.29 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 CRM_COMMON_XML__H
#define CRM_COMMON_XML__H
#include <portability.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <ha_msg.h>
#include <clplumbing/cl_log.h>
/* #define USE_LIBXML 1 */
#define XML_PARANOIA_CHECKS 1
#ifdef USE_LIBXML
# include <libxml/tree.h>
typedef xmlNode crm_data_t;
#else
typedef struct ha_msg crm_data_t;
#endif
extern gboolean add_message_xml(
HA_Message *msg, const char *field, const crm_data_t *xml);
extern crm_data_t *get_message_xml(const HA_Message *msg, const char *field);
extern GHashTable *xml2list(crm_data_t *parent);
+extern void do_id_check(crm_data_t *xml_obj, GHashTable *id_hash);
/*
* Replacement function for xmlCopyPropList which at the very least,
* doesnt work the way *I* would expect it to.
*
* Copy all the attributes/properties from src into target.
*
* Not recursive, does not return anything.
*
*/
extern void copy_in_properties(crm_data_t *target, const crm_data_t *src);
/*
* Find a child named search_path[i] at level i in the XML fragment where i=0
* is an immediate child of <i>root</i>.
*
* Terminate with success if i == len, or search_path[i] == NULL.
*
* On success, returns the sub-fragment described by search_path.
* On failure, returns NULL.
*/
extern crm_data_t *find_xml_node_nested(
crm_data_t *root, const char **search_path, int len);
/*
* Find a child named search_path[i] at level i in the XML fragment where i=0
* is an immediate child of <i>root</i>.
*
* Once the last child specified by node_path is found, find the value
* of attr_name.
*
* If <i>error<i> is set to TRUE, then it is an error for the attribute not
* to be found and the function will log accordingly.
*
* On success, returns the value of attr_name.
* On failure, returns NULL.
*/
extern const char *get_xml_attr_nested(crm_data_t *parent,
const char **node_path, int length,
const char *attr_name, gboolean error);
/*
* Free the XML "stuff" associated with a_node
*
* If a_node is part of another XML blob, barf.
* (Should be using free_xml_from_parent)
*
* Otherwise, free everything recursivly
*
* Wont barf on NULL.
*
*/
extern void free_xml_fn(crm_data_t *a_node);
#if 1
# define free_xml(xml_obj) free_xml_fn(xml_obj); xml_obj = NULL
#else
# define free_xml(xml_obj) xml_obj = NULL
#endif
void free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node);
#define zap_xml_from_parent(parent, xml_obj) free_xml_from_parent(parent, xml_obj); xml_obj = NULL
/*
* Create a node named "name" as a child of "parent"
* If parent is NULL, creates an unconnected node.
*
* Returns the created node
*
*/
extern crm_data_t *create_xml_node(crm_data_t *parent, const char *name);
/*
* Make a copy of name and value and use the copied memory to create
* an attribute for node.
*
* If node, name or value are NULL, nothing is done.
*
* If name or value are an empty string, nothing is done.
*
* Returns FALSE on failure and TRUE on success.
*
*/
extern const char *crm_xml_add(
crm_data_t *node, const char *name, const char *value);
extern const char *crm_xml_add_int(
crm_data_t* node, const char *name, int value);
/*
* Unlink the node and set its doc pointer to NULL so free_xml()
* will act appropriately
*/
extern void unlink_xml_node(crm_data_t *node);
/*
* Set a timestamp attribute on a_node
*/
extern void set_node_tstamp(crm_data_t *a_node);
/*
* Returns a deep copy of src_node
*
*/
extern crm_data_t *copy_xml(const crm_data_t *src_node);
/*
* Add a copy of xml_node to new_parent
*/
extern crm_data_t *add_node_copy(
crm_data_t *new_parent, const crm_data_t *xml_node);
/*
* XML I/O Functions
*
* Whitespace between tags is discarded.
*/
extern crm_data_t *file2xml(FILE *input);
extern crm_data_t *stdin2xml(void);
extern crm_data_t *string2xml(const char *input);
extern int write_xml_file(crm_data_t *xml_node, const char *filename);
extern char *dump_xml_formatted(const crm_data_t *msg);
extern char *dump_xml_unformatted(const crm_data_t *msg);
extern void print_xml_formatted(
int log_level, const char *function,
const crm_data_t *an_xml_node, const char *text);
/*
* Diff related Functions
*/
extern crm_data_t *diff_xml_object(
crm_data_t *left, crm_data_t *right, gboolean suppress);
extern void log_xml_diff(unsigned int log_level, crm_data_t *diff, const char *function);
extern gboolean apply_xml_diff(
crm_data_t *old, crm_data_t *diff, crm_data_t **new);
/*
* Searching & Modifying
*/
extern crm_data_t *find_xml_node(
crm_data_t *cib, const char * node_path, gboolean must_find);
extern crm_data_t *find_entity(
crm_data_t *parent, const char *node_name, const char *id);
extern crm_data_t *subtract_xml_object(
crm_data_t *left, crm_data_t *right, gboolean suppress);
extern int add_xml_object(
crm_data_t *parent, crm_data_t *target, const crm_data_t *update);
extern void xml_remove_prop(crm_data_t *obj, const char *name);
extern void crm_set_element_parent(crm_data_t *data, crm_data_t *parent);
extern gboolean delete_xml_child(
crm_data_t *parent, crm_data_t *child, crm_data_t *to_delete);
/*
*
*/
extern const char *crm_element_value(const crm_data_t *data, const char *name);
extern char *crm_element_value_copy(const crm_data_t *data, const char *name);
extern const char *crm_element_name(const crm_data_t *data);
extern void crm_validate_data(const crm_data_t *root);
extern void crm_update_parents(crm_data_t *root);
extern gboolean xml_has_children(crm_data_t *root);
# define xmlGetNodePath(data) crm_element_value(data, XML_ATTR_TAGNAME)
# define xml_child_iter(parent, child, filter, loop_code) \
if(parent != NULL) { \
int __counter = 0; \
crm_data_t *child = NULL; \
crm_validate_data(parent); \
for (__counter = 0; __counter < parent->nfields; __counter++) { \
if(parent->types[__counter] != FT_STRUCT) { \
continue; \
} \
child = parent->values[__counter]; \
if(child == NULL) { \
crm_debug_4("Skipping %s == NULL", \
parent->names[__counter]); \
} else if(filter == NULL \
|| safe_str_eq(filter, parent->names[__counter])) { \
loop_code; \
} else { \
crm_debug_4("Skipping <%s../>", \
parent->names[__counter]); \
} \
} \
} else { \
crm_debug_4("Parent of loop was NULL"); \
}
#define xml_prop_iter(parent, prop_name, prop_value, code) if(parent != NULL) { \
const char *prop_name = NULL; \
const char *prop_value = NULL; \
int __counter = 0; \
crm_validate_data(parent); \
crm_debug_5("Searching %d fields", parent->nfields); \
for (__counter = 0; __counter < parent->nfields; __counter++) { \
crm_debug_5("Searching field %d", __counter); \
if(parent->types[__counter] != FT_STRING) { \
continue; \
} else if(safe_str_eq(parent->names[__counter], F_XML_TAGNAME)) { \
continue; \
} else if(safe_str_eq(parent->names[__counter], F_XML_PARENT)) { \
continue; \
} \
prop_name = parent->names[__counter]; \
prop_value = parent->values[__counter]; \
code; \
} \
} else { \
crm_debug_4("Parent of loop was NULL"); \
}
#endif
diff --git a/lib/crm/common/utils.c b/lib/crm/common/utils.c
index a10523fe68..a08e0b04f8 100644
--- a/lib/crm/common/utils.c
+++ b/lib/crm/common/utils.c
@@ -1,1028 +1,1025 @@
-/* $Id: utils.c,v 1.23 2005/08/17 09:03:24 andrew Exp $ */
+/* $Id: utils.c,v 1.24 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 <portability.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <heartbeat.h>
#include <ha_msg.h>
#include <clplumbing/cl_log.h>
#include <clplumbing/cl_signal.h>
#include <clplumbing/cl_syslog.h>
#include <clplumbing/cl_misc.h>
#include <clplumbing/coredumps.h>
#include <time.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/dmalloc_wrapper.h>
#ifndef MAXLINE
# define MAXLINE 512
#endif
static uint ref_counter = 0;
gboolean crm_assert_failed = FALSE;
unsigned int crm_log_level = LOG_INFO;
void crm_set_env_options(void);
char *
generateReference(const char *custom1, const char *custom2)
{
const char *local_cust1 = custom1;
const char *local_cust2 = custom2;
int reference_len = 4;
char *since_epoch = NULL;
reference_len += 20; /* too big */
reference_len += 40; /* too big */
if(local_cust1 == NULL) { local_cust1 = "_empty_"; }
reference_len += strlen(local_cust1);
if(local_cust2 == NULL) { local_cust2 = "_empty_"; }
reference_len += strlen(local_cust2);
crm_malloc0(since_epoch, reference_len*(sizeof(char)));
if(since_epoch != NULL) {
sprintf(since_epoch, "%s-%s-%ld-%u",
local_cust1, local_cust2,
(unsigned long)time(NULL), ref_counter++);
}
return since_epoch;
}
gboolean
decodeNVpair(const char *srcstring, char separator, char **name, char **value)
{
int lpc = 0;
int len = 0;
const char *temp = NULL;
crm_debug_4("Attempting to decode: [%s]", srcstring);
if (srcstring != NULL) {
len = strlen(srcstring);
while(lpc <= len) {
if (srcstring[lpc] == separator
|| srcstring[lpc] == '\0') {
crm_malloc0(*name, sizeof(char)*lpc+1);
if(*name == NULL) {
break; /* and return FALSE */
}
strncpy(*name, srcstring, lpc);
(*name)[lpc] = '\0';
/* this sucks but as the strtok manpage says..
* it *is* a bug
*/
len = len-lpc; len--;
if(len <= 0) {
*value = NULL;
} else {
crm_malloc0(*value, sizeof(char)*len+1);
if(*value == NULL) {
crm_free(*name);
break; /* and return FALSE */
}
temp = srcstring+lpc+1;
strncpy(*value, temp, len);
(*value)[len] = '\0';
}
return TRUE;
}
lpc++;
}
}
*name = NULL;
*value = NULL;
return FALSE;
}
+char *
+crm_concat(const char *prefix, const char *suffix, char join)
+{
+ int len = 0;
+ char *new_str = NULL;
+ CRM_ASSERT(prefix != NULL);
+ CRM_ASSERT(suffix != NULL);
+ len = strlen(prefix) + strlen(suffix) + 2;
+
+ crm_malloc0(new_str, sizeof(char)*(len));
+ sprintf(new_str, "%s%c%s", prefix, join, suffix);
+ new_str[len-1] = 0;
+ return new_str;
+}
+
+
char *
generate_hash_key(const char *crm_msg_reference, const char *sys)
{
- int ref_len = strlen(sys?sys:"none") + strlen(crm_msg_reference) + 2;
- char *hash_key = NULL;
- crm_malloc0(hash_key, sizeof(char)*(ref_len));
-
- if(hash_key != NULL) {
- sprintf(hash_key, "%s_%s", sys?sys:"none", crm_msg_reference);
- hash_key[ref_len-1] = '\0';
- crm_debug_3("created hash key: (%s)", hash_key);
- }
+ char *hash_key = crm_concat(sys?sys:"none", crm_msg_reference, '_');
+ crm_debug_3("created hash key: (%s)", hash_key);
return hash_key;
}
char *
generate_hash_value(const char *src_node, const char *src_subsys)
{
- int ref_len;
char *hash_value = NULL;
if (src_node == NULL || src_subsys == NULL) {
return NULL;
}
if (strcmp(CRM_SYSTEM_DC, src_subsys) == 0) {
hash_value = crm_strdup(src_subsys);
if (!hash_value) {
crm_err("memory allocation failed in "
"generate_hash_value()");
- return NULL;
}
return hash_value;
}
-
- ref_len = strlen(src_subsys) + strlen(src_node) + 2;
- crm_malloc0(hash_value, sizeof(char)*(ref_len));
- if (!hash_value) {
- crm_err("memory allocation failed in "
- "generate_hash_value()");
- return NULL;
- }
-
- snprintf(hash_value, ref_len-1, "%s_%s", src_node, src_subsys);
- hash_value[ref_len-1] = '\0';/* make sure it is null terminated */
+ hash_value = crm_concat(src_node, src_subsys, '_');
crm_info("created hash value: (%s)", hash_value);
return hash_value;
}
gboolean
decode_hash_value(gpointer value, char **node, char **subsys)
{
char *char_value = (char*)value;
int value_len = strlen(char_value);
crm_info("Decoding hash value: (%s:%d)", char_value, value_len);
if (strcmp(CRM_SYSTEM_DC, (char*)value) == 0) {
*node = NULL;
*subsys = (char*)crm_strdup(char_value);
if (*subsys == NULL) {
crm_err("memory allocation failed in "
"decode_hash_value()");
return FALSE;
}
crm_info("Decoded value: (%s:%d)", *subsys,
(int)strlen(*subsys));
return TRUE;
} else if (char_value != NULL) {
if (decodeNVpair(char_value, '_', node, subsys)) {
return TRUE;
} else {
*node = NULL;
*subsys = NULL;
return FALSE;
}
}
return FALSE;
}
char *
crm_itoa(int an_int)
{
int len = 32;
char *buffer = NULL;
crm_malloc0(buffer, sizeof(char)*(len+1));
if(buffer != NULL) {
snprintf(buffer, len, "%d", an_int);
}
return buffer;
}
extern int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
gboolean
crm_log_init(const char *entity)
{
/* const char *test = "Testing log daemon connection"; */
/* Redirect messages from glib functions to our handler */
/* cl_malloc_forced_for_glib(); */
g_log_set_handler(NULL,
G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE
| G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG
| G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL,
cl_glib_msg_handler, NULL);
/* and for good measure... - this enum is a bit field (!) */
g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/
cl_log_set_entity(entity);
cl_log_set_facility(LOG_LOCAL7);
cl_set_corerootdir(HA_COREDIR);
cl_cdtocoredir();
crm_set_env_options();
CL_SIGNAL(DEBUG_INC, alter_debug);
CL_SIGNAL(DEBUG_DEC, alter_debug);
return TRUE;
}
/* returns the old value */
unsigned int
set_crm_log_level(unsigned int level)
{
unsigned int old = crm_log_level;
while(crm_log_level < level) {
alter_debug(DEBUG_INC);
}
while(crm_log_level > level) {
alter_debug(DEBUG_DEC);
}
return old;
}
unsigned int
get_crm_log_level(void)
{
return crm_log_level;
}
void
crm_log_message_adv(int level, const char *prefix, const HA_Message *msg)
{
if((int)crm_log_level >= level) {
do_crm_log(level, NULL, NULL, "#========= %s message start ==========#", prefix?prefix:"");
if(level > LOG_DEBUG) {
cl_log_message(LOG_DEBUG, msg);
} else {
cl_log_message(level, msg);
}
}
}
void
do_crm_log(int log_level, const char *file, const char *function,
const char *fmt, ...)
{
int log_as = log_level;
gboolean do_log = FALSE;
if(log_level <= (int)crm_log_level) {
do_log = TRUE;
if(log_level > LOG_INFO) {
log_as = LOG_DEBUG;
}
}
if(do_log) {
va_list ap;
int nbytes;
char buf[MAXLINE];
va_start(ap, fmt);
nbytes=vsnprintf(buf, MAXLINE, fmt, ap);
va_end(ap);
log_level -= LOG_INFO;
if(log_level > 1) {
if(file == NULL && function == NULL) {
cl_log(log_as, "[%d] %s", log_level, buf);
} else {
cl_log(log_as, "mask(%s%s%s [%d]): %s",
file?file:"",
(file !=NULL && function !=NULL)?":":"",
function?function:"", log_level, buf);
}
} else {
if(file == NULL && function == NULL) {
cl_log(log_as, "%s", buf);
} else {
cl_log(log_as, "mask(%s%s%s): %s",
file?file:"",
(file !=NULL && function !=NULL)?":":"",
function?function:"", buf);
}
}
if(nbytes > MAXLINE) {
cl_log(LOG_WARNING, "Log from %s() was truncated",
crm_str(function));
}
}
}
int
compare_version(const char *version1, const char *version2)
{
int lpc = 0;
char *step1 = NULL, *step2 = NULL;
char *rest1 = NULL, *rest2 = NULL;
if(version1 == NULL && version2 == NULL) {
return 0;
} else if(version1 == NULL) {
return -1;
} else if(version2 == NULL) {
return 1;
}
if(version1 != NULL) {
rest1 = crm_strdup(version1);
} else {
version1 = "<null>";
}
if(version2 != NULL) {
rest2 = crm_strdup(version2);
} else {
version2 = "<null>";
}
while(1) {
int cmp = 0;
int step1_i = 0;
int step2_i = 0;
char *tmp1 = NULL, *tmp2 = NULL;
decodeNVpair(rest1, '.', &step1, &tmp1);
decodeNVpair(rest2, '.', &step2, &tmp2);
if(step1 != NULL) {
step1_i = atoi(step1);
}
if(step2 != NULL) {
step2_i = atoi(step2);
}
if(step1_i < step2_i){
cmp = -1;
} else if (step1_i > step2_i){
cmp = 1;
}
crm_debug_4("compare[%d (%d)]: %d(%s) %d(%s)",
lpc++, cmp,
step1_i, crm_str(step1),
step2_i, crm_str(step2));
crm_free(rest1);
crm_free(rest2);
rest1 = tmp1;
rest2 = tmp2;
if(step1 == NULL && step2 == NULL) {
break;
}
crm_free(step1);
crm_free(step2);
if(cmp < 0) {
crm_debug_2("%s < %s", version1, version2);
return -1;
} else if(cmp > 0) {
crm_debug_2("%s > %s", version1, version2);
return 1;
}
}
crm_debug_2("%s == %s", version1, version2);
return 0;
}
gboolean do_stderr = FALSE;
void
alter_debug(int nsig)
{
CL_SIGNAL(DEBUG_INC, alter_debug);
CL_SIGNAL(DEBUG_DEC, alter_debug);
switch(nsig) {
case DEBUG_INC:
crm_log_level++;
crm_debug("Upped log level to %d", crm_log_level);
break;
case DEBUG_DEC:
crm_log_level--;
crm_debug("Reduced log level to %d", crm_log_level);
break;
default:
fprintf(stderr, "Unknown signal %d\n", nsig);
cl_log(LOG_ERR, "Unknown signal %d", nsig);
break;
}
}
void g_hash_destroy_str(gpointer data)
{
crm_free(data);
}
gboolean
safe_str_eq(const char *a, const char *b)
{
if(a == b) {
return TRUE;
} else if(a == NULL || b == NULL) {
return FALSE;
} else if(strcmp(a, b) == 0) {
return TRUE;
}
return FALSE;
}
gboolean
safe_str_neq(const char *a, const char *b)
{
if(a == b) {
return FALSE;
} else if(a==NULL || b==NULL) {
return TRUE;
} else if(strcmp(a, b) == 0) {
return FALSE;
}
return TRUE;
}
char *
crm_strdup(const char *a)
{
char *ret = NULL;
CRM_DEV_ASSERT(a != NULL);
if(a != NULL) {
ret = cl_strdup(a);
} else {
crm_warn("Cannot dup NULL string");
}
return ret;
}
static GHashTable *crm_uuid_cache = NULL;
void
set_uuid(ll_cluster_t *hb,crm_data_t *node,const char *attr,const char *uname)
{
char *uuid_calc = NULL;
if(crm_uuid_cache == NULL) {
crm_uuid_cache = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
CRM_DEV_ASSERT(uname != NULL);
/* avoid blocking calls where possible */
uuid_calc = g_hash_table_lookup(crm_uuid_cache, uname);
if(uuid_calc != NULL) {
crm_xml_add(node, attr, uuid_calc);
return;
}
crm_malloc0(uuid_calc, sizeof(char)*50);
if(uuid_calc != NULL) {
cl_uuid_t uuid_raw;
if(hb->llc_ops->get_uuid_by_name(
hb, uname, &uuid_raw) == HA_FAIL) {
crm_err("Could not calculate UUID for %s", uname);
crm_free(uuid_calc);
uuid_calc = crm_strdup(uname);
} else {
cl_uuid_unparse(&uuid_raw, uuid_calc);
g_hash_table_insert(
crm_uuid_cache,
crm_strdup(uname), crm_strdup(uuid_calc));
}
crm_xml_add(node, attr, uuid_calc);
}
crm_free(uuid_calc);
}/*memory leak*/ /* BEAM BUG - this is not a memory leak */
void
crm_set_ha_options(ll_cluster_t *hb_cluster)
{
#if 0
int facility;
char *param_val = NULL;
const char *param_name = NULL;
if(hb_cluster == NULL) {
crm_set_env_options();
return;
}
/* change the logging facility to the one used by heartbeat daemon */
crm_debug("Switching to Heartbeat logger");
if (( facility =
hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) {
cl_log_set_facility(facility);
}
crm_debug_2("Facility: %d", facility);
param_name = KEY_LOGFILE;
param_val = hb_cluster->llc_ops->get_parameter(hb_cluster, param_name);
crm_debug_3("%s = %s", param_name, param_val);
if(param_val != NULL) {
cl_log_set_logfile(param_val);
cl_free(param_val);
param_val = NULL;
}
param_name = KEY_DBGFILE;
param_val = hb_cluster->llc_ops->get_parameter(hb_cluster, param_name);
crm_debug_3("%s = %s", param_name, param_val);
if(param_val != NULL) {
cl_log_set_debugfile(param_val);
cl_free(param_val);
param_val = NULL;
}
param_name = KEY_DEBUGLEVEL;
param_val = hb_cluster->llc_ops->get_parameter(hb_cluster, param_name);
crm_debug_3("%s = %s", param_name, param_val);
if(param_val != NULL) {
int debug_level = atoi(param_val);
if(debug_level > 0 && (debug_level+LOG_INFO) > (int)crm_log_level) {
set_crm_log_level(LOG_INFO + debug_level);
}
cl_free(param_val);
param_val = NULL;
}
param_name = KEY_LOGDAEMON;
param_val = hb_cluster->llc_ops->get_parameter(hb_cluster, param_name);
crm_debug_3("%s = %s", param_name, param_val);
if(param_val != NULL) {
int uselogd;
cl_str_to_boolean(param_val, &uselogd);
cl_log_set_uselogd(uselogd);
if(cl_log_get_uselogd()) {
cl_set_logging_wqueue_maxlen(500);
}
cl_free(param_val);
param_val = NULL;
}
param_name = KEY_CONNINTVAL;
param_val = hb_cluster->llc_ops->get_parameter(hb_cluster, param_name);
crm_debug_3("%s = %s", param_name, param_val);
if(param_val != NULL) {
int logdtime;
logdtime = crm_get_msec(param_val);
cl_log_set_logdtime(logdtime);
cl_free(param_val);
param_val = NULL;
}
#endif
}
#define ENV_PREFIX "HA_"
void
crm_set_env_options(void)
{
char *param_val = NULL;
const char *param_name = NULL;
/* apparently we're not allowed to free the result of getenv */
param_name = ENV_PREFIX "" KEY_DEBUGLEVEL;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int debug_level = atoi(param_val);
if(debug_level > 0 && (debug_level+LOG_INFO) > (int)crm_log_level) {
set_crm_log_level(LOG_INFO + debug_level);
}
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_FACILITY;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int facility = cl_syslogfac_str2int(param_val);
if(facility > 0) {
cl_log_set_facility(facility);
}
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_LOGFILE;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
if(safe_str_eq("/dev/null", param_val)) {
param_val = NULL;
}
cl_log_set_logfile(param_val);
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_DBGFILE;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
if(safe_str_eq("/dev/null", param_val)) {
param_val = NULL;
}
cl_log_set_debugfile(param_val);
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_LOGDAEMON;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int uselogd;
cl_str_to_boolean(param_val, &uselogd);
cl_log_set_uselogd(uselogd);
if(uselogd) {
cl_set_logging_wqueue_maxlen(500);
cl_log_set_logd_channel_source(NULL, NULL);
}
param_val = NULL;
}
param_name = ENV_PREFIX "" KEY_CONNINTVAL;
param_val = getenv(param_name);
crm_debug("%s = %s", param_name, param_val);
if(param_val != NULL) {
int logdtime;
logdtime = crm_get_msec(param_val);
cl_log_set_logdtime(logdtime);
param_val = NULL;
}
}
gboolean
crm_is_true(const char * s)
{
gboolean ret = FALSE;
if(s != NULL) {
cl_str_to_boolean(s, &ret);
}
return ret;
}
int
crm_str_to_boolean(const char * s, int * ret)
{
if(s == NULL) {
return -1;
} else if (strcasecmp(s, "true") == 0
|| strcasecmp(s, "on") == 0
|| strcasecmp(s, "yes") == 0
|| strcasecmp(s, "y") == 0
|| strcasecmp(s, "1") == 0){
*ret = TRUE;
return 1;
} else if (strcasecmp(s, "false") == 0
|| strcasecmp(s, "off") == 0
|| strcasecmp(s, "no") == 0
|| strcasecmp(s, "n") == 0
|| strcasecmp(s, "0") == 0){
*ret = FALSE;
return 1;
}
return -1;
}
#ifndef NUMCHARS
# define NUMCHARS "0123456789."
#endif
#ifndef WHITESPACE
# define WHITESPACE " \t\n\r\f"
#endif
long
crm_get_msec(const char * input)
{
const char * cp = input;
const char * units;
long multiplier = 1000;
long divisor = 1;
long ret = -1;
double dret;
if(input == NULL) {
return 0;
}
cp += strspn(cp, WHITESPACE);
units = cp + strspn(cp, NUMCHARS);
units += strspn(units, WHITESPACE);
if (strchr(NUMCHARS, *cp) == NULL) {
return ret;
}
if (strncasecmp(units, "ms", 2) == 0
|| strncasecmp(units, "msec", 4) == 0) {
multiplier = 1;
divisor = 1;
}else if (strncasecmp(units, "us", 2) == 0
|| strncasecmp(units, "usec", 4) == 0) {
multiplier = 1;
divisor = 1000;
}else if (strncasecmp(units, "s", 1) == 0
|| strncasecmp(units, "sec", 3) == 0) {
multiplier = 1000;
divisor = 1;
}else if (strncasecmp(units, "m", 1) == 0
|| strncasecmp(units, "min", 3) == 0) {
multiplier = 60*1000;
divisor = 1;
}else if (*units != EOS && *units != '\n'
&& *units != '\r') {
return ret;
}
dret = atof(cp);
dret *= (double)multiplier;
dret /= (double)divisor;
dret += 0.5;
ret = (long)dret;
return(ret);
}
gboolean
ccm_have_quorum(oc_ed_t event)
{
if(event==OC_EV_MS_NEW_MEMBERSHIP) {
return TRUE;
}
return FALSE;
}
const char *
ccm_event_name(oc_ed_t event)
{
if(event==OC_EV_MS_NEW_MEMBERSHIP) {
return "NEW MEMBERSHIP";
} else if(event==OC_EV_MS_NOT_PRIMARY) {
return "NOT PRIMARY";
} else if(event==OC_EV_MS_PRIMARY_RESTORED) {
return "PRIMARY RESTORED";
} else if(event==OC_EV_MS_EVICTED) {
return "EVICTED";
} else if(event==OC_EV_MS_INVALID) {
return "INVALID";
}
return "NO QUORUM MEMBERSHIP";
}
const char *
op_status2text(op_status_t status)
{
switch(status) {
case LRM_OP_PENDING:
return "pending";
break;
case LRM_OP_DONE:
return "complete";
break;
case LRM_OP_ERROR:
return "ERROR";
break;
case LRM_OP_TIMEOUT:
return "TIMED OUT";
break;
case LRM_OP_NOTSUPPORTED:
return "NOT SUPPORTED";
break;
case LRM_OP_CANCELLED:
return "cancelled";
break;
}
CRM_DEV_ASSERT(status >= LRM_OP_PENDING && status <= LRM_OP_CANCELLED);
crm_err("Unknown status: %d", status);
return "UNKNOWN!";
}
char *
generate_op_key(const char *rsc_id, const char *op_type, int interval)
{
int len = 35;
char *op_id = NULL;
CRM_DEV_ASSERT(rsc_id != NULL); if(crm_assert_failed) { return NULL; }
CRM_DEV_ASSERT(op_type != NULL); if(crm_assert_failed) { return NULL; }
len += strlen(op_type);
len += strlen(rsc_id);
crm_malloc0(op_id, sizeof(char)*len);
if(op_id != NULL) {
sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
}
return op_id;
}
char *
generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
{
int len = 12;
char *op_id = NULL;
CRM_DEV_ASSERT(rsc_id != NULL); if(crm_assert_failed) { return NULL; }
CRM_DEV_ASSERT(op_type != NULL); if(crm_assert_failed) { return NULL; }
CRM_DEV_ASSERT(notify_type != NULL); if(crm_assert_failed) { return NULL; }
len += strlen(op_type);
len += strlen(rsc_id);
len += strlen(notify_type);
crm_malloc0(op_id, sizeof(char)*len);
if(op_id != NULL) {
sprintf(op_id, "%s_%s_notify_%s_0", rsc_id, notify_type, op_type);
}
return op_id;
}
char *
generate_transition_magic(const char *transition_key, int op_status)
{
int len = 40;
char *fail_state = NULL;
CRM_DEV_ASSERT(transition_key != NULL);
if(crm_assert_failed) { return NULL; }
len += strlen(transition_key);
crm_malloc0(fail_state, sizeof(char)*len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%s", op_status, transition_key);
}
return fail_state;
}
gboolean
decode_transition_magic(
const char *magic, char **uuid, int *transition_id, int *op_status)
{
char *key = NULL;
char *status = NULL;
if(decodeNVpair(magic, ':', &status, &key) == FALSE) {
return FALSE;
}
if(decode_transition_key(key, uuid, transition_id) == FALSE) {
return FALSE;
}
*op_status = atoi(status);
crm_free(key);
crm_free(status);
return TRUE;
}
char *
generate_transition_key(int transition_id, const char *node)
{
int len = 40;
char *fail_state = NULL;
CRM_DEV_ASSERT(node != NULL); if(crm_assert_failed) { return NULL; }
len += strlen(node);
crm_malloc0(fail_state, sizeof(char)*len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%s", transition_id, node);
}
return fail_state;
}
gboolean
decode_transition_key(const char *key, char **uuid, int *transition_id)
{
char *transition = NULL;
if(decodeNVpair(key, ':', &transition, uuid) == FALSE) {
return FALSE;
}
*transition_id = atoi(transition);
crm_free(transition);
return TRUE;
}
gboolean
crm_mem_stats(volatile cl_mem_stats_t *mem_stats)
{
volatile cl_mem_stats_t *active_stats = mem_stats;
if(active_stats == NULL) {
active_stats = cl_malloc_getstats();
}
CRM_DEV_ASSERT(active_stats != NULL);
#ifndef CRM_USE_MALLOC
if(active_stats->numalloc > active_stats->numfree) {
crm_err("Potential memory leak detected:"
" %lu alloc's vs. %lu free's (%lu)"
" (%lu bytes not freed: req=%lu, alloc'd=%lu)",
active_stats->numalloc, active_stats->numfree,
active_stats->numalloc - active_stats->numfree,
active_stats->nbytes_alloc, active_stats->nbytes_req,
active_stats->mallocbytes);
return TRUE;
} else if(active_stats->numalloc < active_stats->numfree) {
crm_debug("Process shrank: %lu alloc's vs. %lu free's (%lu)",
active_stats->numalloc, active_stats->numfree,
active_stats->numalloc - active_stats->numfree);
}
#endif
return FALSE;
}
void
crm_zero_mem_stats(volatile cl_mem_stats_t *stats)
{
volatile cl_mem_stats_t *active_stats = NULL;
if(stats != NULL) {
cl_malloc_setstats(stats);
}
active_stats = cl_malloc_getstats();
active_stats->numalloc = 0;
active_stats->numfree = 0;
active_stats->numrealloc = 0;
active_stats->nbytes_req = 0;
active_stats->nbytes_alloc = 0;
active_stats->mallocbytes = 0;
active_stats->arena = 0;
}
diff --git a/lib/crm/common/xml.c b/lib/crm/common/xml.c
index 868ab21eeb..67f6de0fa5 100644
--- a/lib/crm/common/xml.c
+++ b/lib/crm/common/xml.c
@@ -1,2006 +1,2129 @@
-/* $Id: xml.c,v 1.30 2005/09/08 09:49:18 andrew Exp $ */
+/* $Id: xml.c,v 1.31 2005/09/12 11:00:19 andrew Exp $ */
/*
* 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 <portability.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <clplumbing/ipc.h>
#include <clplumbing/cl_log.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/dmalloc_wrapper.h>
int is_comment_start(const char *input);
int is_comment_end(const char *input);
gboolean drop_comments(const char *input, int *offset);
void dump_array(
int log_level, const char *message, const char **array, int depth);
int print_spaces(char *buffer, int spaces);
int log_data_element(const char *function, const char *prefix, int log_level,
int depth, const crm_data_t *data, gboolean formatted);
int dump_data_element(
int depth, char **buffer, const crm_data_t *data, gboolean formatted);
crm_data_t *parse_xml(const char *input, int *offset);
int get_tag_name(const char *input);
int get_attr_name(const char *input);
int get_attr_value(const char *input);
gboolean can_prune_leaf(crm_data_t *xml_node);
void diff_filter_context(int context, int upper_bound, int lower_bound,
crm_data_t *xml_node, crm_data_t *parent);
int in_upper_context(int depth, int context, crm_data_t *xml_node);
crm_data_t *
find_xml_node(crm_data_t *root, const char * search_path, gboolean must_find)
{
if(must_find || root != NULL) {
crm_validate_data(root);
}
if(search_path == NULL) {
crm_warn("Will never find <NULL>");
return NULL;
}
xml_child_iter(
root, a_child, search_path,
/* crm_debug_5("returning node (%s).", xmlGetNodePath(a_child)); */
crm_log_xml_debug_5(a_child, "contents\t%s");
crm_log_xml_debug_5(root, "found in\t%s");
crm_validate_data(a_child);
return a_child;
);
if(must_find) {
crm_warn("Could not find %s in %s.", search_path, xmlGetNodePath(root));
} else if(root != NULL) {
crm_debug_3("Could not find %s in %s.", search_path, xmlGetNodePath(root));
} else {
crm_debug_3("Could not find %s in <NULL>.", search_path);
}
return NULL;
}
crm_data_t*
find_xml_node_nested(crm_data_t *root, const char **search_path, int len)
{
int j;
gboolean is_found = TRUE;
crm_data_t *match = NULL;
crm_data_t *lastMatch = root;
crm_validate_data(root);
if(search_path == NULL || search_path[0] == NULL) {
crm_warn("Will never find NULL");
return NULL;
}
dump_array(LOG_DEBUG_5, "Looking for.", search_path, len);
for (j=0; j < len; ++j) {
if (search_path[j] == NULL) {
/* a NULL also means stop searching */
break;
}
match = find_xml_node(lastMatch, search_path[j], FALSE);
if(match == NULL) {
is_found = FALSE;
break;
} else {
lastMatch = match;
}
}
if (is_found) {
crm_debug_5("returning node (%s).",
xmlGetNodePath(lastMatch));
crm_log_xml_debug_5(lastMatch, "found\t%s");
crm_log_xml_debug_5(root, "in \t%s");
crm_validate_data(lastMatch);
return lastMatch;
}
dump_array(LOG_DEBUG_2,
"Could not find the full path to the node you specified.",
search_path, len);
crm_debug_2("Closest point was node (%s) starting from %s.",
xmlGetNodePath(lastMatch), crm_element_name(root));
return NULL;
}
const char *
get_xml_attr_nested(crm_data_t *parent,
const char **node_path, int length,
const char *attr_name, gboolean error)
{
const char *attr_value = NULL;
crm_data_t *attr_parent = NULL;
if(error || parent != NULL) {
crm_validate_data(parent);
}
if(parent == NULL) {
crm_debug_3("Can not find attribute %s in NULL parent",attr_name);
return NULL;
}
if(attr_name == NULL || strlen(attr_name) == 0) {
crm_err("Can not find attribute with no name in %s",
xmlGetNodePath(parent));
return NULL;
}
if(length == 0) {
attr_parent = parent;
} else {
attr_parent = find_xml_node_nested(parent, node_path, length);
if(attr_parent == NULL && error) {
crm_err("No node at the path you specified.");
return NULL;
}
}
attr_value = crm_element_value(attr_parent, attr_name);
if((attr_value == NULL || strlen(attr_value) == 0) && error) {
crm_err("No value present for %s at %s",
attr_name, xmlGetNodePath(attr_parent));
return NULL;
}
return attr_value;
}
crm_data_t*
find_entity(crm_data_t *parent, const char *node_name, const char *id)
{
crm_validate_data(parent);
xml_child_iter(
parent, a_child, node_name,
if(id == NULL
|| safe_str_eq(id,crm_element_value(a_child,XML_ATTR_ID))){
crm_debug_4("returning node (%s).",
xmlGetNodePath(a_child));
return a_child;
}
);
crm_debug_3("node <%s id=%s> not found in %s.",
node_name, id, xmlGetNodePath(parent));
return NULL;
}
void
copy_in_properties(crm_data_t* target, const crm_data_t *src)
{
crm_validate_data(src);
crm_validate_data(target);
if(src == NULL) {
crm_warn("No node to copy properties from");
} else if (target == NULL) {
crm_err("No node to copy properties into");
} else {
xml_prop_iter(
src, local_prop_name, local_prop_value,
crm_xml_add(target, local_prop_name, local_prop_value);
);
crm_validate_data(target);
}
return;
}
crm_data_t*
add_node_copy(crm_data_t *new_parent, const crm_data_t *xml_node)
{
crm_data_t *node_copy = NULL;
crm_validate_data(new_parent);
crm_validate_data(xml_node);
if(xml_node != NULL && new_parent != NULL) {
const char *name = crm_element_name(xml_node);
CRM_DEV_ASSERT(
HA_OK == ha_msg_addstruct(new_parent, name, xml_node));
node_copy = find_entity(
new_parent, crm_element_name(xml_node), ID(xml_node));
crm_validate_data(node_copy);
crm_update_parents(new_parent);
crm_validate_data(new_parent);
} else if(xml_node == NULL) {
crm_err("Could not add copy of NULL node");
} else {
crm_err("Could not add copy of node to NULL parent");
}
crm_validate_data(node_copy);
return node_copy;
}
const char *
crm_xml_add(crm_data_t* node, const char *name, const char *value)
{
const char *parent_name = NULL;
if(node != NULL) {
parent_name = crm_element_name(node);
}
crm_debug_5("[%s] Setting %s to %s", crm_str(parent_name), name, value);
if (name == NULL || strlen(name) <= 0) {
} else if(node == NULL) {
} else if(parent_name == NULL && strcmp(name, F_XML_TAGNAME) != 0) {
} else if (value == NULL || strlen(value) <= 0) {
xml_remove_prop(node, name);
return NULL;
} else {
const char *new_value = NULL;
crm_validate_data(node);
ha_msg_mod(node, name, value);
new_value = crm_element_value(node, name);
CRM_DEV_ASSERT(cl_is_allocated(new_value));
return new_value;
}
return NULL;
}
const char *
crm_xml_add_int(crm_data_t* node, const char *name, int value)
{
const char *parent_name = NULL;
if(node != NULL) {
parent_name = crm_element_name(node);
}
crm_debug_5("[%s] Setting %s to %d", crm_str(parent_name), name, value);
if (name == NULL || strlen(name) <= 0) {
} else if(node == NULL) {
} else if(parent_name == NULL && strcmp(name, F_XML_TAGNAME) != 0) {
} else {
crm_validate_data(node);
ha_msg_mod_int(node, name, value);
return crm_element_value(node, name);
}
return NULL;
}
crm_data_t*
create_xml_node(crm_data_t *parent, const char *name)
{
const char *local_name = NULL;
const char *parent_name = NULL;
crm_data_t *ret_value = NULL;
if (name == NULL || strlen(name) < 1) {
ret_value = NULL;
} else {
local_name = name;
ret_value = ha_msg_new(1);
CRM_DEV_ASSERT(ret_value != NULL);
crm_xml_add(ret_value, F_XML_TAGNAME, name);
crm_validate_data(ret_value);
if(parent) {
crm_validate_data(parent);
parent_name = crm_element_name(parent);
crm_debug_5("Attaching %s to parent %s",
local_name, parent_name);
CRM_DEV_ASSERT(HA_OK == ha_msg_addstruct(
parent, name, ret_value));
crm_msg_del(ret_value);
crm_update_parents(parent);
crm_validate_data(parent);
ret_value = parent->values[parent->nfields-1];
crm_validate_data(ret_value);
}
}
crm_debug_5("Created node [%s [%s]]",
crm_str(parent_name), crm_str(local_name));
/* set_node_tstamp(ret_value); */
return ret_value;
}
void
free_xml_from_parent(crm_data_t *parent, crm_data_t *a_node)
{
CRM_DEV_ASSERT(parent != NULL);
if(parent == NULL) {
return;
} else if(a_node == NULL) {
return;
}
crm_validate_data(parent);
cl_msg_remove_value(parent, a_node);
crm_validate_data(parent);
}
void
free_xml_fn(crm_data_t *a_node)
{
if(a_node == NULL) {
; /* nothing to do */
} else {
int has_parent = 0;
crm_validate_data(a_node);
ha_msg_value_int(a_node, F_XML_PARENT, &has_parent);
/* there is no way in hell we should be deleting anything
* with a parent and without the parent knowning
*/
CRM_DEV_ASSERT(has_parent == 0);
if(has_parent == 0) {
crm_validate_data(a_node);
crm_msg_del(a_node);
}
}
return;
}
void
set_node_tstamp(crm_data_t *a_node)
{
#if 0
char *since_epoch = NULL;
time_t a_time = time(NULL);
crm_validate_data(a_node);
if(a_time == (time_t)-1) {
cl_perror("set_node_tstamp(): Invalid time returned");
return;
}
crm_malloc0(since_epoch, 128*(sizeof(char)));
if(since_epoch != NULL) {
sprintf(since_epoch, "%ld", (unsigned long)a_time);
ha_msg_mod(a_node, XML_ATTR_TSTAMP, since_epoch);
crm_validate_data(a_node);
crm_free(since_epoch);
}
#endif
}
crm_data_t*
copy_xml(const crm_data_t *src_node)
{
crm_data_t *new_xml = NULL;
CRM_DEV_ASSERT(src_node != NULL);
CRM_DEV_ASSERT(crm_element_name(src_node) != NULL);
if(src_node == NULL) {
crm_warn("Attempt to dup NULL XML");
return NULL;
} else if(crm_element_name(src_node) == NULL) {
crm_log_xml_err(src_node, "Attempt to dup XML with no name");
return NULL;
}
crm_validate_data(src_node);
new_xml = ha_msg_copy(src_node);
crm_set_element_parent(new_xml, NULL);
crm_update_parents(new_xml);
crm_validate_data(new_xml);
return new_xml;
}
crm_data_t*
string2xml(const char *input)
{
crm_data_t *output = parse_xml(input, NULL);
if(output != NULL) {
crm_update_parents(output);
crm_validate_data(output);
}
return output;
}
crm_data_t *
stdin2xml(void)
{
int lpc = 0;
int MAX_XML_BUFFER = 20000;
int ch = 0;
gboolean more = TRUE;
gboolean inTag = FALSE;
FILE *input = stdin;
char *xml_buffer = NULL;
crm_data_t *xml_obj = NULL;
crm_malloc0(xml_buffer, sizeof(char)*(MAX_XML_BUFFER+1));
while (more && lpc < MAX_XML_BUFFER) {
ch = fgetc(input);
/* crm_debug_3("Got [%c]", ch); */
switch(ch) {
case EOF:
case 0:
ch = 0;
more = FALSE;
xml_buffer[lpc++] = ch;
break;
case '>':
case '<':
inTag = TRUE;
if(ch == '>') { inTag = FALSE; }
xml_buffer[lpc++] = ch;
break;
case '\n':
case '\r':
case '\t':
case ' ':
ch = ' ';
if(inTag) {
xml_buffer[lpc++] = ch;
}
break;
default:
xml_buffer[lpc++] = ch;
break;
}
}
xml_buffer[MAX_XML_BUFFER] = 0;
xml_obj = string2xml(xml_buffer);
crm_free(xml_buffer);
crm_log_xml_debug_3(xml_obj, "Created fragment");
return xml_obj;
}
crm_data_t*
file2xml(FILE *input)
{
char *buffer = NULL;
crm_data_t *new_obj = NULL;
int start = 0, length = 0, read_len = 0;
/* see how big the file is */
start = ftell(input);
fseek(input, 0L, SEEK_END);
length = ftell(input);
fseek(input, 0L, start);
if(start != ftell(input)) {
crm_err("fseek not behaving");
return NULL;
}
crm_debug_3("Reading %d bytes from file", length);
crm_malloc0(buffer, sizeof(char) * (length+1));
read_len = fread(buffer, sizeof(char), length, input);
if(read_len != length) {
crm_err("Calculated and read bytes differ: %d vs. %d",
length, read_len);
} else if(length > 0) {
new_obj = string2xml(buffer);
} else {
crm_warn("File contained no XML");
}
crm_free(buffer);
return new_obj;
}
void
dump_array(int log_level, const char *message, const char **array, int depth)
{
int j;
if(message != NULL) {
do_crm_log(log_level, __FILE__, __FUNCTION__, "%s", message);
}
do_crm_log(log_level, __FILE__, __FUNCTION__, "Contents of the array:");
if(array == NULL || array[0] == NULL || depth == 0) {
do_crm_log(log_level, __FILE__, __FUNCTION__, "\t<empty>");
return;
}
for (j=0; j < depth && array[j] != NULL; j++) {
if (array[j] == NULL) { break; }
do_crm_log(log_level, __FILE__, __FUNCTION__, "\t--> (%s).", array[j]);
}
}
int
write_xml_file(crm_data_t *xml_node, const char *filename)
{
int res = 0;
char *now_str = NULL;
time_t now;
CRM_DEV_ASSERT(filename != NULL);
crm_debug_3("Writing XML out to %s", filename);
crm_validate_data(xml_node);
if (xml_node == NULL) {
crm_err("Cannot write NULL to %s", filename);
return -1;
}
crm_validate_data(xml_node);
crm_log_xml_debug_4(xml_node, "Writing out");
crm_validate_data(xml_node);
now = time(NULL);
now_str = ctime(&now);
now_str[24] = EOS; /* replace the newline */
crm_xml_add(xml_node, "last_written", now_str);
crm_validate_data(xml_node);
{
FILE *file_output_strm = fopen(filename, "w");
if(file_output_strm == NULL) {
res = -1;
cl_perror("Cannot write to %s", filename);
} else {
char *buffer = dump_xml_formatted(xml_node);
CRM_DEV_ASSERT(buffer != NULL && strlen(buffer) > 0);
if(buffer != NULL && strlen(buffer) > 0) {
res = fprintf(file_output_strm, "%s", buffer);
if(res < 0) {
cl_perror("Cannot write output to %s",
filename);
}
}
fflush(file_output_strm);
fclose(file_output_strm);
crm_free(buffer);
}
}
crm_debug_3("Saved %d bytes to the Cib as XML", res);
return res;
}
void
print_xml_formatted(int log_level, const char *function,
const crm_data_t *msg, const char *text)
{
if(msg == NULL) {
do_crm_log(log_level,NULL,function, "%s: NULL", crm_str(text));
return;
}
crm_validate_data(msg);
log_data_element(function, text, log_level, 0, msg, TRUE);
return;
}
crm_data_t *
get_message_xml(const HA_Message *msg, const char *field)
{
crm_data_t *xml_node = NULL;
crm_data_t *tmp_node = NULL;
crm_validate_data(msg);
tmp_node = cl_get_struct(msg, field);
if(tmp_node != NULL) {
xml_node = copy_xml(tmp_node);
}
return xml_node;
}
gboolean
add_message_xml(HA_Message *msg, const char *field, const crm_data_t *xml)
{
crm_validate_data(xml);
crm_validate_data(msg);
ha_msg_addstruct(msg, field, xml);
crm_update_parents(msg);
return TRUE;
}
char *
dump_xml_formatted(const crm_data_t *an_xml_node)
{
char *buffer = NULL;
char *mutable_ptr = NULL;
crm_malloc0(buffer, 3*get_stringlen(an_xml_node));
mutable_ptr = buffer;
crm_validate_data(an_xml_node);
CRM_DEV_ASSERT(dump_data_element(
0, &mutable_ptr, an_xml_node, TRUE) >= 0);
if(crm_assert_failed) {
crm_crit("Could not dump the whole message");
}
crm_debug_4("Dumped: %s", buffer);
return buffer;
}
char *
dump_xml_unformatted(const crm_data_t *an_xml_node)
{
char *buffer = NULL;
char *mutable_ptr = NULL;
crm_malloc0(buffer, 2*get_stringlen(an_xml_node));
mutable_ptr = buffer;
crm_validate_data(an_xml_node);
CRM_DEV_ASSERT(dump_data_element(
0, &mutable_ptr, an_xml_node, TRUE) >= 0);
if(crm_assert_failed) {
crm_crit("Could not dump the whole message");
}
crm_debug_4("Dumped: %s", buffer);
return buffer;
}
#define update_buffer_head(buffer, len) if(len < 0) { \
(*buffer) = EOS; return -1; \
} else { \
buffer += len; \
}
int
print_spaces(char *buffer, int depth)
{
int lpc = 0;
int spaces = 2*depth;
/* <= so that we always print 1 space - prevents problems with syslog */
for(lpc = 0; lpc <= spaces; lpc++) {
if(sprintf(buffer, "%c", ' ') < 1) {
return -1;
}
buffer += 1;
}
return lpc;
}
int
log_data_element(
const char *function, const char *prefix, int log_level, int depth,
const crm_data_t *data, gboolean formatted)
{
int printed = 0;
int child_result = 0;
int has_children = 0;
char print_buffer[1000];
char *buffer = print_buffer;
const char *name = crm_element_name(data);
crm_debug_5("Dumping %s...", name);
crm_validate_data(data);
if(data == NULL) {
crm_warn("No data to dump as XML");
return 0;
} else if(name == NULL && depth == 0) {
xml_child_iter(
data, a_child, NULL,
child_result = log_data_element(
function, prefix, log_level, depth, a_child, formatted);
if(child_result < 0) {
return child_result;
}
);
return 0;
} else if(name == NULL) {
crm_err("Cannot dump NULL element at depth %d", depth);
return -1;
}
if(formatted) {
printed = print_spaces(buffer, depth);
update_buffer_head(buffer, printed);
}
printed = sprintf(buffer, "<%s", name);
update_buffer_head(buffer, printed);
xml_prop_iter(
data, prop_name, prop_value,
if(safe_str_eq(F_XML_TAGNAME, prop_name)) {
continue;
} else if(safe_str_eq(F_XML_PARENT, prop_name)) {
continue;
}
crm_debug_5("Dumping <%s %s=\"%s\"...",
name, prop_name, prop_value);
printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value);
update_buffer_head(buffer, printed);
);
xml_child_iter(
data, child, NULL,
if(child != NULL) {
has_children++;
break;
}
);
printed = sprintf(buffer, "%s>", has_children==0?"/":"");
update_buffer_head(buffer, printed);
do_crm_log(log_level, function, NULL, "%s%s",
prefix?prefix:"", print_buffer);
buffer = print_buffer;
if(has_children == 0) {
return 0;
}
xml_child_iter(
data, a_child, NULL,
child_result = log_data_element(
function, prefix, log_level, depth+1, a_child, formatted);
if(child_result < 0) { return -1; }
);
if(formatted) {
printed = print_spaces(buffer, depth);
update_buffer_head(buffer, printed);
}
do_crm_log(log_level, function, NULL, "%s%s</%s>",
prefix?prefix:"", print_buffer, name);
crm_debug_5("Dumped %s...", name);
return has_children;
}
int
dump_data_element(
int depth, char **buffer, const crm_data_t *data, gboolean formatted)
{
int printed = 0;
int child_result = 0;
int has_children = 0;
const char *name = crm_element_name(data);
crm_debug_5("Dumping %s...", name);
crm_validate_data(data);
if(buffer == NULL || *buffer == NULL) {
crm_err("No buffer supplied to dump XML into");
return -1;
} else if(data == NULL) {
crm_warn("No data to dump as XML");
(*buffer)[0] = EOS;
return 0;
} else if(name == NULL && depth == 0) {
xml_child_iter(
data, a_child, NULL,
child_result = dump_data_element(
depth, buffer, a_child, formatted);
if(child_result < 0) {
return child_result;
}
);
return 0;
} else if(name == NULL) {
crm_err("Cannot dump NULL element at depth %d", depth);
return -1;
}
if(formatted) {
printed = print_spaces(*buffer, depth);
update_buffer_head(*buffer, printed);
}
printed = sprintf(*buffer, "<%s", name);
update_buffer_head(*buffer, printed);
xml_prop_iter(data, prop_name, prop_value,
if(safe_str_eq(F_XML_TAGNAME, prop_name)) {
continue;
} else if(safe_str_eq(F_XML_PARENT, prop_name)) {
continue;
}
crm_debug_5("Dumping <%s %s=\"%s\"...",
name, prop_name, prop_value);
printed = sprintf(*buffer, " %s=\"%s\"", prop_name, prop_value);
update_buffer_head(*buffer, printed);
);
xml_child_iter(
data, child, NULL,
if(child != NULL) {
has_children++;
break;
}
);
printed = sprintf(*buffer, "%s>%s",
has_children==0?"/":"", formatted?"\n":"");
update_buffer_head(*buffer, printed);
if(has_children == 0) {
return 0;
}
xml_child_iter(
data, child, NULL,
child_result = dump_data_element(
depth+1, buffer, child, formatted);
if(child_result < 0) { return -1; }
);
if(formatted) {
printed = print_spaces(*buffer, depth);
update_buffer_head(*buffer, printed);
}
printed = sprintf(*buffer, "</%s>%s", name, formatted?"\n":"");
update_buffer_head(*buffer, printed);
crm_debug_5("Dumped %s...", name);
return has_children;
}
gboolean
xml_has_children(crm_data_t *xml_root)
{
crm_validate_data(xml_root);
xml_child_iter(
xml_root, a_child, NULL,
return TRUE;
);
return FALSE;
}
void
crm_validate_data(const crm_data_t *xml_root)
{
#ifndef XML_PARANOIA_CHECKS
CRM_DEV_ASSERT(xml_root != NULL);
#else
int lpc = 0;
CRM_ASSERT(xml_root != NULL);
CRM_ASSERT(crm_is_allocated(xml_root) == 1);
CRM_ASSERT(xml_root->nfields < 500);
for (lpc = 0; lpc < xml_root->nfields; lpc++) {
void *child = xml_root->values[lpc];
CRM_ASSERT(crm_is_allocated(xml_root->names[lpc]) == 1);
if(child == NULL) {
} else if(xml_root->types[lpc] == FT_STRUCT) {
crm_validate_data(child);
} else if(xml_root->types[lpc] == FT_STRING) {
CRM_ASSERT(crm_is_allocated(child) == 1);
/* } else { */
/* CRM_DEV_ASSERT(FALSE); */
}
}
#endif
}
void
crm_set_element_parent(crm_data_t *data, crm_data_t *parent)
{
crm_validate_data(data);
if(parent != NULL) {
ha_msg_mod_int(data, F_XML_PARENT, 1);
} else {
ha_msg_mod_int(data, F_XML_PARENT, 0);
}
}
const char *
crm_element_value(const crm_data_t *data, const char *name)
{
const char *value = NULL;
crm_validate_data(data);
value = cl_get_string(data, name);
if(value != NULL) {
CRM_DEV_ASSERT(crm_is_allocated(value) == 1);
}
return value;
}
char *
crm_element_value_copy(const crm_data_t *data, const char *name)
{
const char *value = NULL;
char *value_copy = NULL;
crm_validate_data(data);
value = cl_get_string(data, name);
if(value != NULL) {
CRM_DEV_ASSERT(crm_is_allocated(value) == 1);
}
CRM_DEV_ASSERT(value != NULL);
if(value != NULL) {
value_copy = crm_strdup(value);
}
return value_copy;
}
const char *
crm_element_name(const crm_data_t *data)
{
crm_validate_data(data);
return cl_get_string(data, F_XML_TAGNAME);
}
void
xml_remove_prop(crm_data_t *obj, const char *name)
{
if(crm_element_value(obj, name) != NULL) {
cl_msg_remove(obj, name);
}
}
void
crm_update_parents(crm_data_t *xml_root)
{
crm_validate_data(xml_root);
xml_child_iter(
xml_root, a_child, NULL,
crm_set_element_parent(a_child, xml_root);
crm_update_parents(a_child);
);
}
int
get_tag_name(const char *input)
{
int lpc = 0;
char ch = 0;
const char *error = NULL;
gboolean do_special = FALSE;
for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) {
ch = input[lpc];
crm_debug_5("Processing char %c [%d]", ch, lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '?':
if(lpc == 0) {
/* weird xml tag that we dont care about */
do_special = TRUE;
} else {
return lpc;
}
break;
case '/':
case '>':
case '\t':
case '\n':
case ' ':
if(!do_special) {
return lpc;
}
break;
default:
if(do_special) {
} else if('a' <= ch && ch <= 'z') {
} else if('A' <= ch && ch <= 'Z') {
} else if(ch == '_') {
} else if(ch == '-') {
} else {
error = "bad character, not in [a-zA-Z_-]";
}
break;
}
}
crm_err("Error parsing token near %.15s: %s", input, crm_str(error));
return -1;
}
int
get_attr_name(const char *input)
{
int lpc = 0;
char ch = 0;
const char *error = NULL;
for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) {
ch = input[lpc];
crm_debug_5("Processing char %c[%d]", ch, lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '\t':
case '\n':
case ' ':
error = "unexpected whitespace";
break;
case '=':
return lpc;
default:
if('a' <= ch && ch <= 'z') {
} else if('A' <= ch && ch <= 'Z') {
} else if(ch == '_') {
} else if(ch == '-') {
} else {
error = "bad character, not in [a-zA-Z_-]";
}
break;
}
}
crm_err("Error parsing token near %.15s: %s", input, crm_str(error));
return -1;
}
int
get_attr_value(const char *input)
{
int lpc = 0;
char ch = 0;
const char *error = NULL;
for(lpc = 0; error == NULL && lpc < (ssize_t)strlen(input); lpc++) {
ch = input[lpc];
crm_debug_5("Processing char %c [%d]", ch, lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '\\':
if(input[lpc+1] == '"') {
/* skip over the next char */
lpc++;
break;
}
/*fall through*/
case '"':
return lpc;
default:
break;
}
}
crm_err("Error parsing token near %.15s: %s", input, crm_str(error));
return -1;
}
int
is_comment_start(const char *input)
{
CRM_DEV_ASSERT(input != NULL);
if(crm_assert_failed) {
return 0;
}
if(strlen(input) > 4
&& input[0] == '<'
&& input[1] == '!'
&& input[2] == '-'
&& input[3] == '-') {
crm_debug_6("Found comment start: <!--");
return 4;
} else if(strlen(input) > 2
&& input[0] == '<'
&& input[1] == '!') {
crm_debug_6("Found comment start: <!");
return 2;
} else if(strlen(input) > 2
&& input[0] == '<'
&& input[1] == '?') {
crm_debug_6("Found comment start: <?");
return 2;
}
if(strlen(input) > 3) {
crm_debug_6("Not comment start: %c%c%c%c", input[0], input[1], input[2], input[3]);
} else {
crm_debug_6("Not comment start");
}
return 0;
}
int
is_comment_end(const char *input)
{
CRM_DEV_ASSERT(input != NULL);
if(crm_assert_failed) {
return 0;
}
if(strlen(input) > 2
&& input[0] == '-'
&& input[1] == '-'
&& input[2] == '>') {
crm_debug_6("Found comment end: -->");
return 3;
} else if(strlen(input) > 1
&& input[0] == '?'
&& input[1] == '>') {
crm_debug_6("Found comment end: ?>");
return 2;
}
if(strlen(input) > 2) {
crm_debug_6("Not comment end: %c%c%c", input[0], input[1], input[2]);
} else {
crm_debug_6("Not comment end");
}
return 0;
}
gboolean
drop_comments(const char *input, int *offset)
{
gboolean more = TRUE;
gboolean in_directive = FALSE;
int in_comment = FALSE;
const char *our_input = input;
int len = 0, lpc = 0;
int tag_len = 0;
char ch = 0;
if(input == NULL) {
return FALSE;
}
if(offset != NULL) {
our_input = input + (*offset);
}
len = strlen(our_input);
while(lpc < len && more) {
ch = our_input[lpc];
crm_debug_6("Processing char %c[%d]", ch, lpc);
switch(ch) {
case 0:
if(in_comment == FALSE) {
more = FALSE;
} else {
crm_err("unexpected EOS");
crm_warn("Parsing error at or before: %s", our_input);
}
break;
case '<':
tag_len = is_comment_start(our_input + lpc);
if(tag_len > 0) {
if(in_comment) {
crm_err("Nested XML comments are not supported!");
crm_warn("Parsing error at or before: %s", our_input);
crm_warn("Netsed comment found at: %s", our_input + lpc + tag_len);
}
in_comment = TRUE;
lpc+=tag_len;
if(tag_len == 2) {
#if 1
if(our_input[lpc-1] == '!') {
in_directive = TRUE;
}
#else
tag_len = get_tag_name(our_input + lpc);
crm_debug_2("Tag length: %d", len);
if(strncmp("DOCTYPE", our_input+lpc, tag_len) == 0) {
in_directive = TRUE;
}
#endif
}
} else if(in_comment == FALSE){
more = FALSE;
} else {
lpc++;
crm_debug_6("Skipping comment char %c", our_input[lpc]);
}
break;
case '>':
lpc++;
if(in_directive) {
in_directive = FALSE;
in_comment = FALSE;
}
break;
case '-':
case '?':
tag_len = is_comment_end(our_input + lpc);
if(tag_len > 0) {
lpc+=tag_len;
in_comment = FALSE;
} else {
lpc++;
crm_debug_6("Skipping comment char %c", our_input[lpc]);
}
break;
case ' ':
case '\t':
case '\n':
case '\r':
lpc++;
crm_debug_6("Skipping whitespace char %d", our_input[lpc]);
break;
default:
lpc++;
crm_debug_6("Skipping comment char %c", our_input[lpc]);
break;
}
}
crm_debug_4("Finished processing comments");
if(offset != NULL) {
(*offset) += lpc;
}
if(lpc > 0) {
crm_debug_5("Skipped %d comment chars", lpc);
return TRUE;
}
return FALSE;
}
crm_data_t*
parse_xml(const char *input, int *offset)
{
int len = 0, lpc = 0;
char ch = 0;
char *tag_name = NULL;
char *attr_name = NULL;
char *attr_value = NULL;
gboolean more = TRUE;
gboolean were_comments = TRUE;
const char *error = NULL;
const char *our_input = input;
crm_data_t *new_obj = NULL;
if(input == NULL) {
return NULL;
}
if(offset != NULL) {
our_input = input + (*offset);
}
len = strlen(our_input);
while(lpc < len && were_comments) {
were_comments = drop_comments(our_input, &lpc);
}
CRM_DEV_ASSERT(our_input[lpc] == '<');
if(crm_assert_failed) {
return NULL;
}
lpc++;
len = get_tag_name(our_input + lpc);
crm_debug_5("Tag length: %d", len);
if(len < 0) {
return NULL;
}
crm_malloc0(tag_name, len+1);
strncpy(tag_name, our_input + lpc, len+1);
tag_name[len] = EOS;
crm_debug_4("Processing tag %s", tag_name);
new_obj = ha_msg_new(1);
CRM_DEV_ASSERT(crm_is_allocated(new_obj) == 1);
ha_msg_add(new_obj, F_XML_TAGNAME, tag_name);
lpc += len;
for(; more && error == NULL && lpc < (ssize_t)strlen(input); lpc++) {
ch = our_input[lpc];
crm_debug_5("Processing char %c[%d]", ch, lpc);
switch(ch) {
case 0:
error = "unexpected EOS";
break;
case '/':
if(our_input[lpc+1] == '>') {
more = FALSE;
}
break;
case '<':
if(our_input[lpc+1] != '/') {
crm_data_t *child = NULL;
gboolean any_comments = FALSE;
do {
were_comments = drop_comments(our_input, &lpc);
any_comments = any_comments || were_comments;
} while(lpc < len && were_comments);
if(any_comments) {
lpc--;
break;
}
crm_debug_4("Start parsing child...");
child = parse_xml(our_input, &lpc);
if(child == NULL) {
error = "error parsing child";
} else {
CRM_DEV_ASSERT(crm_is_allocated(child) == 1);
ha_msg_addstruct(
new_obj, crm_element_name(child), child);
crm_debug_4("Finished parsing child: %s",
crm_element_name(child));
ha_msg_del(child);
if(our_input[lpc] == '<') {
/* lpc is about to get incrimented
* make sure we process the '<' that
* we're currently looking at
*/
lpc--;
}
/* lpc++; /\* > *\/ */
}
} else {
lpc += 2; /* </ */
len = get_tag_name(our_input+lpc);
if(len < 0) {
error = "couldnt find tag";
} else if(strncmp(our_input+lpc, tag_name, len) == 0) {
more = FALSE;
lpc += len;
/* lpc++; /\* > *\/ */
if(our_input[lpc] != '>') {
error = "clase tag cannot contain attrs";
}
crm_debug_4("Finished parsing ourselves: %s",
crm_element_name(new_obj));
} else {
error = "Mismatching close tag";
crm_err("Expected: %s", tag_name);
}
}
break;
case '=':
lpc++; /* = */
/*fall through*/
case '"':
lpc++; /* " */
len = get_attr_value(our_input+lpc);
if(len < 0) {
error = "couldnt find attr_value";
} else {
crm_malloc0(attr_value, len+1);
strncpy(attr_value, our_input+lpc, len+1);
attr_value[len] = EOS;
lpc += len;
/* lpc++; /\* " *\/ */
crm_debug_4("creating nvpair: <%s %s=\"%s\"...",
tag_name,
crm_str(attr_name),
crm_str(attr_value));
ha_msg_add(new_obj, attr_name, attr_value);
crm_free(attr_name);
crm_free(attr_value);
}
break;
case '>':
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
len = get_attr_name(our_input+lpc);
if(len < 0) {
error = "couldnt find attr_name";
} else {
crm_malloc0(attr_name, len+1);
strncpy(attr_name, our_input+lpc, len+1);
attr_name[len] = EOS;
lpc += len;
crm_debug_4("found attr name: %s", attr_name);
lpc--; /* make sure the '=' is seen next time around */
}
break;
}
}
if(error) {
crm_err("Error parsing token: %s", error);
crm_err("Error at or before: %s", our_input+lpc-3);
return NULL;
}
crm_debug_4("Finished processing %s tag", tag_name);
crm_free(tag_name);
if(offset != NULL) {
(*offset) += lpc;
}
CRM_DEV_ASSERT(crm_is_allocated(new_obj) == 1);
return new_obj;
}
void
log_xml_diff(unsigned int log_level, crm_data_t *diff, const char *function)
{
crm_data_t *added = find_xml_node(diff, "diff-added", FALSE);
crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE);
gboolean is_first = TRUE;
xml_child_iter(
removed, child, NULL,
log_data_element(function, "-", log_level, 0, child, TRUE);
if(is_first) {
is_first = FALSE;
} else {
crm_log_maybe(log_level, " --- ");
}
);
/* crm_log_maybe(log_level, " === "); */
is_first = TRUE;
xml_child_iter(
added, child, NULL,
log_data_element(function, "+", log_level, 0, child, TRUE);
if(is_first) {
is_first = FALSE;
} else {
crm_log_maybe(log_level, " --- ");
}
);
}
gboolean
apply_xml_diff(crm_data_t *old, crm_data_t *diff, crm_data_t **new)
{
gboolean result = TRUE;
crm_data_t *added = find_xml_node(diff, "diff-added", FALSE);
crm_data_t *removed = find_xml_node(diff, "diff-removed", FALSE);
crm_data_t *intermediate = NULL;
crm_data_t *diff_of_diff = NULL;
int root_nodes_seen = 0;
CRM_DEV_ASSERT(new != NULL);
if(crm_assert_failed) { return FALSE; }
crm_debug_2("Substraction Phase");
xml_child_iter(removed, child_diff, NULL,
CRM_DEV_ASSERT(root_nodes_seen == 0);
if(root_nodes_seen == 0) {
*new = subtract_xml_object(old, child_diff, FALSE);
}
root_nodes_seen++;
);
if(root_nodes_seen == 0) {
*new = copy_xml(old);
} else if(root_nodes_seen > 1) {
crm_err("(-) Diffs cannot contain more than one change set..."
" saw %d", root_nodes_seen);
result = FALSE;
}
root_nodes_seen = 0;
crm_debug_2("Addition Phase");
if(result) {
xml_child_iter(added, child_diff, NULL,
CRM_DEV_ASSERT(root_nodes_seen == 0);
if(root_nodes_seen == 0) {
add_xml_object(NULL, *new, child_diff);
}
root_nodes_seen++;
);
}
if(root_nodes_seen > 1) {
crm_err("(+) Diffs cannot contain more than one change set..."
" saw %d", root_nodes_seen);
result = FALSE;
#if CRM_DEV_BUILD
} else if(result) {
crm_debug_2("Verification Phase");
intermediate = diff_xml_object(old, *new, FALSE);
diff_of_diff = diff_xml_object(intermediate, diff, TRUE);
if(diff_of_diff != NULL) {
crm_warn("Diff application failed!");
/* log_xml_diff(LOG_DEBUG, diff_of_diff, "diff:diff_of_diff"); */
log_xml_diff(LOG_DEBUG, intermediate, "diff:actual_diff");
result = FALSE;
}
crm_free(diff_of_diff);
crm_free(intermediate);
#endif
diff_of_diff = NULL;
intermediate = NULL;
}
if(result == FALSE) {
log_xml_diff(LOG_DEBUG, diff, "diff:input_diff");
log_data_element("diff:input", NULL, LOG_DEBUG_2, 0, old, TRUE);
/* CRM_DEV_ASSERT(diff_of_diff != NULL); */
result = FALSE;
}
return result;
}
crm_data_t *
diff_xml_object(crm_data_t *old, crm_data_t *new, gboolean suppress)
{
crm_data_t *diff = NULL;
crm_data_t *tmp1 = NULL;
crm_data_t *added = NULL;
crm_data_t *removed = NULL;
tmp1 = subtract_xml_object(old, new, suppress);
if(tmp1 != NULL) {
diff = create_xml_node(NULL, "diff");
if(can_prune_leaf(tmp1)) {
ha_msg_del(tmp1);
tmp1 = NULL;
} else {
removed = create_xml_node(diff, "diff-removed");
added = create_xml_node(diff, "diff-added");
add_node_copy(removed, tmp1);
}
free_xml(tmp1);
}
tmp1 = subtract_xml_object(new, old, suppress);
if(tmp1 != NULL) {
if(diff == NULL) {
diff = create_xml_node(NULL, "diff");
}
if(can_prune_leaf(tmp1)) {
ha_msg_del(tmp1);
tmp1 = NULL;
} else {
if(removed == NULL) {
removed = create_xml_node(diff, "diff-removed");
}
if(added == NULL) {
added = create_xml_node(diff, "diff-added");
}
add_node_copy(added, tmp1);
}
free_xml(tmp1);
}
return diff;
}
gboolean
can_prune_leaf(crm_data_t *xml_node)
{
gboolean can_prune = TRUE;
/* return FALSE; */
xml_prop_iter(xml_node, prop_name, prop_value,
if(safe_str_eq(prop_name, XML_ATTR_ID)) {
continue;
} else if(safe_str_eq(prop_name, XML_ATTR_TSTAMP)) {
continue;
}
can_prune = FALSE;
);
xml_child_iter(xml_node, child, NULL,
if(can_prune_leaf(child)) {
cl_msg_remove_value(xml_node, child);
__counter--;
} else {
can_prune = FALSE;
}
);
return can_prune;
}
void
diff_filter_context(int context, int upper_bound, int lower_bound,
crm_data_t *xml_node, crm_data_t *parent)
{
crm_data_t *us = NULL;
crm_data_t *new_parent = parent;
const char *name = crm_element_name(xml_node);
CRM_DEV_ASSERT(xml_node != NULL && name != NULL);
if(crm_assert_failed) { return; }
us = create_xml_node(parent, name);
xml_prop_iter(xml_node, prop_name, prop_value,
lower_bound = context;
crm_xml_add(us, prop_name, prop_value);
);
if(lower_bound >= 0 || upper_bound >= 0) {
crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
new_parent = us;
} else {
upper_bound = in_upper_context(0, context, xml_node);
if(upper_bound >= 0) {
crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
new_parent = us;
} else {
free_xml(us);
us = NULL;
}
}
xml_child_iter(us, child, NULL,
diff_filter_context(
context, upper_bound-1, lower_bound-1,
child, new_parent);
);
}
int
in_upper_context(int depth, int context, crm_data_t *xml_node)
{
gboolean has_attributes = FALSE;
if(context == 0) {
return 0;
}
xml_prop_iter(xml_node, prop_name, prop_value,
has_attributes = TRUE;
break;
);
if(has_attributes) {
return depth;
} else if(depth < context) {
xml_child_iter(xml_node, child, NULL,
if(in_upper_context(depth+1, context, child)) {
return depth;
}
);
}
return 0;
}
crm_data_t *
subtract_xml_object(crm_data_t *left, crm_data_t *right, gboolean suppress)
{
gboolean skip = FALSE;
gboolean differences = FALSE;
crm_data_t *diff = NULL;
crm_data_t *child_diff = NULL;
crm_data_t *right_child = NULL;
const char *right_val = NULL;
const char *name = NULL;
int lpc = 0;
const char *filter[] = {
XML_ATTR_TSTAMP,
"last_written",
"debug_source",
"origin"
};
if(left == NULL) {
return NULL;
} else if(right == NULL) {
crm_debug_4("Processing <%s id=%s> (complete copy)",
crm_element_name(left), ID(left));
return copy_xml(left);
}
name = crm_element_name(left);
/* sanity check */
CRM_DEV_ASSERT(name != NULL);
if(crm_assert_failed) { return NULL; }
CRM_DEV_ASSERT(safe_str_eq(crm_element_name(left),
crm_element_name(right)));
if(crm_assert_failed) { return NULL; }
CRM_DEV_ASSERT(safe_str_eq(ID(left), ID(right)));
if(crm_assert_failed) { return NULL; }
diff = create_xml_node(NULL, name);
/* changes to name/value pairs */
crm_debug_4("Processing <%s id=%s>", crm_str(name), ID(left));
xml_prop_iter(left, prop_name, left_value,
skip = FALSE;
if(safe_str_eq(prop_name, XML_ATTR_ID)) {
skip = TRUE;
}
for(lpc = 0;
skip == FALSE && suppress && lpc < DIMOF(filter);
lpc++) {
if(safe_str_eq(prop_name, filter[lpc])) {
skip = TRUE;
}
}
if(skip) { continue; }
right_val = crm_element_value(right, prop_name);
if(right_val == NULL) {
differences = TRUE;
crm_xml_add(diff, prop_name, left_value);
crm_debug_5("\t%s: %s", crm_str(prop_name),
crm_str(left_value));
} else if(safe_str_eq(left_value, right_val)) {
crm_debug_4("\t%s: %s (removed)",
crm_str(prop_name),
crm_str(left_value));
} else {
differences = TRUE;
crm_xml_add(diff, prop_name, left_value);
crm_debug_4("\t%s: %s->%s",
crm_str(prop_name),
crm_str(left_value),
right_val);
}
);
/* changes to child objects */
xml_child_iter(
left, left_child, NULL,
right_child = find_entity(
right, crm_element_name(left_child), ID(left_child));
child_diff = subtract_xml_object(
left_child, right_child, suppress);
if(child_diff != NULL) {
differences = TRUE;
add_node_copy(diff, child_diff);
free_xml(child_diff);
}
);
if(differences == FALSE) {
free_xml(diff);
crm_debug_4("\tNo changes");
return NULL;
}
crm_xml_add(diff, XML_ATTR_ID, ID(left));
return diff;
}
int
add_xml_object(crm_data_t *parent, crm_data_t *target, const crm_data_t *update)
{
const char *object_id = NULL;
const char *object_name = NULL;
const char *right_val = NULL;
int result = 0;
CRM_DEV_ASSERT(update != NULL);
if(crm_assert_failed) { return 0; }
object_name = crm_element_name(update);
object_id = ID(update);
CRM_DEV_ASSERT(object_name != NULL);
if(crm_assert_failed) { return 0; }
if(target == NULL && object_id == NULL) {
/* placeholder object */
target = find_xml_node(parent, object_name, FALSE);
} else if(target == NULL) {
target = find_entity(parent, object_name, object_id);
}
if(target == NULL) {
target = add_node_copy(parent, update);
crm_debug_2("Added <%s id=%s>",
crm_str(object_name), crm_str(object_id));
CRM_DEV_ASSERT(target != NULL);
return 0;
}
crm_debug_2("Found node <%s id=%s> to update",
crm_str(object_name), crm_str(object_id));
xml_prop_iter(update, prop_name, left_value,
right_val = crm_element_value(target, prop_name);
if(right_val == NULL) {
crm_xml_add(target, prop_name, left_value);
crm_debug_2("\t%s: %s (added)",
crm_str(prop_name),
crm_str(left_value));
} else if(safe_str_neq(left_value, right_val)) {
crm_xml_add(target, prop_name, left_value);
crm_debug_2("\t%s: %s->%s",
crm_str(prop_name),
crm_str(left_value),
right_val);
}
);
CRM_DEV_ASSERT(cl_is_allocated(object_name));
if(object_id != NULL) {
CRM_DEV_ASSERT(cl_is_allocated(object_id));
}
crm_debug_3("Processing children of <%s id=%s>",
crm_str(object_name), crm_str(object_id));
xml_child_iter(
update, a_child, NULL,
int tmp_result = 0;
crm_debug_3("Updating child <%s id=%s>",
crm_element_name(a_child), ID(a_child));
tmp_result = add_xml_object(target, NULL, a_child);
if(tmp_result < 0) {
crm_err("Error updating child <%s id=%s>",
crm_element_name(a_child), ID(a_child));
/* only the first error is likely to be interesting */
if(result >= 0) {
result = tmp_result;
}
}
);
crm_debug_3("Finished with <%s id=%s>",
crm_str(object_name), crm_str(object_id));
return result;
}
gboolean
delete_xml_child(crm_data_t *parent, crm_data_t *child, crm_data_t *to_delete)
{
gboolean can_delete = FALSE;
const char *right_val = NULL;
CRM_DEV_ASSERT(child != NULL);
if(crm_assert_failed) { return FALSE; }
CRM_DEV_ASSERT(to_delete != NULL);
if(crm_assert_failed) { return FALSE; }
if(safe_str_eq(crm_element_name(to_delete), crm_element_name(child))) {
can_delete = TRUE;
}
xml_prop_iter(to_delete, prop_name, left_value,
if(can_delete == FALSE) {
break;
}
right_val = crm_element_value(child, prop_name);
if(safe_str_neq(left_value, right_val)) {
can_delete = FALSE;
}
);
if(can_delete && parent != NULL) {
crm_log_xml_debug(child, "Delete match found...");
cl_msg_remove_value(parent, child);
child = NULL;
} else if(can_delete) {
crm_log_xml_debug(child, "Cannot delete the search root");
}
xml_child_iter(
child, child_of_child, NULL,
/* only delete the first one */
if(can_delete) {
break;
}
can_delete = delete_xml_child(child, child_of_child, to_delete);
);
return can_delete;
}
GHashTable *
xml2list(crm_data_t *parent)
{
crm_data_t *nvpair_list = NULL;
GHashTable *nvpair_hash = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
CRM_DEV_ASSERT(parent != NULL);
if(parent != NULL) {
nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
if(nvpair_list == NULL) {
crm_debug("No attributes in %s",
crm_element_name(parent));
crm_log_xml_debug_2(parent,"No attributes for resource op");
}
}
xml_child_iter(
nvpair_list, node_iter, XML_CIB_TAG_NVPAIR,
const char *key = crm_element_value(
node_iter, XML_NVPAIR_ATTR_NAME);
const char *value = crm_element_value(
node_iter, XML_NVPAIR_ATTR_VALUE);
crm_debug_2("Added %s=%s", key, value);
g_hash_table_insert(
nvpair_hash, crm_strdup(key), crm_strdup(value));
);
return nvpair_hash;
}
+
+
+static void
+assign_uuid(crm_data_t *xml_obj)
+{
+ cl_uuid_t new_uuid;
+ char *new_uuid_s = NULL;
+ const char *new_uuid_s2 = NULL;
+
+ crm_malloc0(new_uuid_s, sizeof(char)*38);
+ cl_uuid_generate(&new_uuid);
+ cl_uuid_unparse(&new_uuid, new_uuid_s);
+
+ new_uuid_s2 = crm_xml_add(xml_obj, XML_ATTR_ID, new_uuid_s);
+ crm_log_xml_warn(xml_obj, "Updated object");
+
+ CRM_DEV_ASSERT(cl_is_allocated(new_uuid_s));
+ CRM_DEV_ASSERT(cl_is_allocated(new_uuid_s2));
+
+ crm_free(new_uuid_s);
+
+ CRM_DEV_ASSERT(cl_is_allocated(new_uuid_s2));
+}
+
+void
+do_id_check(crm_data_t *xml_obj, GHashTable *id_hash)
+{
+ int lpc = 0;
+ char *lookup_id = NULL;
+
+ const char *tag_id = NULL;
+ const char *tag_name = NULL;
+ const char *lookup_value = NULL;
+
+ gboolean created_hash = FALSE;
+
+ const char *allowed_list[] = {
+ XML_TAG_CIB,
+ XML_CIB_TAG_NODES,
+ XML_CIB_TAG_RESOURCES,
+ XML_CIB_TAG_CONSTRAINTS,
+ XML_CIB_TAG_STATUS,
+ XML_CIB_TAG_LRM,
+ XML_LRM_TAG_RESOURCES,
+ "operations",
+ };
+
+ const char *non_unique[] = {
+ XML_LRM_TAG_RESOURCE,
+ XML_LRM_TAG_RSC_OP,
+ };
+
+ if(xml_obj == NULL) {
+ return;
+
+ } else if(id_hash == NULL) {
+ created_hash = TRUE;
+ id_hash = g_hash_table_new_full(
+ g_str_hash, g_str_equal,
+ g_hash_destroy_str, g_hash_destroy_str);
+ }
+
+ xml_child_iter(
+ xml_obj, xml_child, NULL,
+ do_id_check(xml_child, id_hash);
+ );
+
+ tag_id = ID(xml_obj);
+ tag_name = TYPE(xml_obj);
+
+ xml_prop_iter(
+ xml_obj, local_prop_name, local_prop_value,
+
+ if(ID(xml_obj) != NULL) {
+ for(lpc = 0; lpc < DIMOF(non_unique); lpc++) {
+ if(safe_str_eq(tag_name, non_unique[lpc])) {
+ /* this tag is never meant to have an ID */
+ break;
+ }
+ }
+ if(lpc < DIMOF(non_unique)) {
+ break;
+ }
+ lookup_id = crm_concat(tag_name, tag_id, '-');
+ lookup_value = g_hash_table_lookup(id_hash, lookup_id);
+ if(lookup_value != NULL) {
+ assign_uuid(xml_obj);
+ crm_err("\"id\" collision detected.");
+ crm_err(" Multiple %s entries with id=\"%s\","
+ " assigned id=\"%s\"",
+ tag_name, lookup_value, ID(xml_obj));
+ crm_free(lookup_id);
+ break;
+
+ } else {
+ g_hash_table_insert(
+ id_hash, lookup_id, crm_strdup(tag_id));
+ break;
+ }
+ }
+
+ for(lpc = 0; lpc < DIMOF(allowed_list); lpc++) {
+ if(safe_str_eq(tag_name, allowed_list[lpc])) {
+ /* this tag is never meant to have an ID */
+ break;
+ }
+ }
+ if(lpc < DIMOF(allowed_list)) {
+ break;
+ }
+ assign_uuid(xml_obj);
+ crm_err("Object with attributes but no ID field detected."
+ " Assigned: %s", ID(xml_obj));
+ break;
+
+ );
+
+ if(created_hash) {
+ g_hash_table_destroy(id_hash);
+ }
+
+}
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 10, 1:38 AM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2009554
Default Alt Text
(155 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment